View previous topic :: View next topic |
Author |
Message |
cleberalbert
Joined: 25 Feb 2014 Posts: 34 Location: Brazil
|
Timer0 configured with Project Wizard not working properly |
Posted: Wed Aug 24, 2016 7:44 am |
|
|
Hello all!
I'm trying to implement a timer0 interruption using PIC16F627A but it doesn't seem to work as I expect to.
I expected to get a signal with period of 4ms (1ms high + 3ms low)
But actually it's giving me a signal of 16,4ms (4ms high + 12,4ms)
I configured the timer0 using Project Wizard.
Please, could someone pointing out my mistake?
Tks in advance!
My code:
Code: |
#include <16F627A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(internal=4000000,restart_wdt)
#use FIXED_IO( A_outputs=PIN_A1)
#use FIXED_IO( B_outputs=PIN_B4)
#define OUT1 PIN_A1
#define LED PIN_B4
#INT_TIMER0
void TIMER0_isr(void)
{
output_bit(OUT1, TRUE); // Putting the data pin high
delay_us(1000);
output_bit(OUT1, FALSE); // Lowering the pin
}
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_16|RTCC_8_bit); //4,0 ms overflow
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(TRUE)
{
//TODO: User Code
output_bit(LED,true); // Putting the data pin high
delay_ms(1000); // Delaying the pulse for 'time' period
output_bit(LED,false); // Putting the data pin high
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19530
|
|
Posted: Wed Aug 24, 2016 8:18 am |
|
|
The problem is not the wizard. It's using delays inside an interrupt.
You should be getting a compiler warning 'interrupts disabled to prevent re-entrancy'.
Code cannot be called inside 'itself'. Problem is that the delay code you are using in the main, is the same code as the delay you are using in the interrupt. So the interrupt waits till the main delay finishes before it is called.....
Honestly, just set the interrupt to trigger at 1mSec, and code the interrupt as:
Code: |
#INT_TIMER0
void TIMER0_isr(void)
{
static int8 operation=0;
if (operation==0)
output_bit(OUT1, TRUE); // Putting the data pin high
else
output_bit(OUT1, FALSE); // Lowering the pin
if (operation++ == 3)
operation=0;
}
void main(void)
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4|RTCC_8_bit); //1,0 ms overflow
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(TRUE)
{
//TODO: User Code
output_bit(LED,true); // Putting the data pin high
delay_ms(1000); // Delaying the pulse for 'time' period
output_bit(LED,false); // Putting the data pin high
}
}
|
The LED will be on for one interrupt in 4. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Aug 24, 2016 8:21 am |
|
|
compile this instead
Code: |
#include <16F627A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP
#use delay(internal=4000000)
#define OUT1 PIN_A1
#define LED PIN_B4
#INT_TIMER0
void TIMER0_isr(void)
{ output_toggle(out1); }
void main()
{
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_16|RTCC_8_bit);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(1)
{ delay_cycles(1); }
}
|
your a1 pin should have a symmetric 4ms on 4 ms off duty now
-does it ? |
|
|
cleberalbert
Joined: 25 Feb 2014 Posts: 34 Location: Brazil
|
|
Posted: Wed Aug 24, 2016 12:42 pm |
|
|
I'm sorry but my objective is just quite a bit deeper than I said previously. The code I posted was just a pseudo-prototype to try to be more simple and easy to understand. But will give more details:
All what I need is to control 5 servomotors individually (20ms/5 = 4ms) and their duty cycle will vary depending of a data received through serial port. It justifies why I need to vary the period-on with delay instead of other timer0 overflow.
I've already implemented successfully a code to perform this PWM control with the PIC16F676. What I need now is to change to PIC16F627A.
To change I just edit the first line to Code: | #include <16F627A.h> | and change the output ports. I expected it to work but it doesn't.
Ttelmah,
I think now I could explain to you why I need to use a delay inside an interrupt instead to use another overflow to set the level down. Because the on-period will vary unpredictably between 1 and 2 milliseconds.
However I ran your code and it gave me a signal around 4ms on and 12ms off.
asmboy,
Your code gave me a symmetric 16.4ms on 16.4ms off.
My code for PIC16F676:
Code: |
#include <16F676.h>
#FUSES NOWDT
#FUSES INTRC_IO
#FUSES NOPUT
#FUSES NOPROTECT
#FUSES NOBROWNOUT
#FUSES NOMCLR
#FUSES NOCPD
#FUSES RESERVED //Used to set the reserved FUSE bits
#use delay(clock=4000000)
#define SERVO1_OUT PIN_A5
#define SERVO2_OUT PIN_C1
#define SERVO3_OUT PIN_C2
#define SERVO4_OUT PIN_C3
#define SERVO5_OUT PIN_C4
void PWM(int8 servo_out)
{
unsigned int16 serial_read;
serial_read = 0; //Serial reading will be implemented yet
output_bit(servo_out, TRUE); // Putting the data pin high
delay_us(1000 + serial_read); // Delaying the pulse for 'time' period. Angle'll vary between 0° and 180°
output_bit(servo_out, FALSE); // Lowering the pin
serial_read = 0;
}
void main()
{
int8 servo = 0;
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_16|RTCC_8_bit); //4,0 ms overflow
while(TRUE)
{
if (interrupt_active(INT_RTCC))
{
clear_interrupt(INT_RTCC);
switch(++servo)
{
case 1:
//SERVO 1
PWM(SERVO1_OUT);
break;
case 2:
//SERVO 2
PWM(SERVO2_OUT);
break;
case 3:
//SERVO 3
PWM(SERVO3_OUT);
break;
case 4:
//SERVO 4
PWM(SERVO4_OUT);
break;
case 5:
//SERVO 5
PWM(SERVO5_OUT);
servo = 0;
break;
}
}
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19530
|
|
Posted: Wed Aug 24, 2016 2:35 pm |
|
|
Honestly the answer is not to use a timer interrupt.
Servos have no requirement for accurate 'off' times.
In fact you can handle four servos 'sequentially', just doing the pulse for servo 1, then the pulse for servo 2, then servo 3, then servo 4. Then start again. This is actually how they are done in a real 'historical' RC. On these the pulses for all the servos are sent 'one after the other' in the radio stream, with a long 'pause' at the end. The receiver detects the pause, and routes the first pulse after the pause to the first servo, then the second to the second servo etc.. Once all the pulses have been sent, it goes back to looking for the pause. Trying to do all four at once, is making life harder than it has to be....
You can get really accurate pulse times using the CCP, and big advantage of this, is that the times will not be changed if your code is doing other things (like receiving serial etc..). I've posted code to do this here a little while ago, and PCM_Programmer has also done this (mine was showing how to use polling in the main loop, his using the CCP interrupt). |
|
|
cleberalbert
Joined: 25 Feb 2014 Posts: 34 Location: Brazil
|
|
Posted: Wed Aug 24, 2016 3:08 pm |
|
|
Tks for replying!!
I can see my solution is not the best or even not elegant, but honestly I prefer try to adapt my old code for PIC16F676 to PIC16F627A because it would be faster than pause and try to understand and implement your or PCM_Programmer's code (would be more adequade I know that) but this little board is just a small part of a too much bigger system in which I need to work on and worry about deadline. It is just a case of scheduling priorities.
Sure another time when I'm more loose I'll care more about accuracy so, study your code but for now could you help me to make this code work on PIC16F627A as well as it works on PIC16F676? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19530
|
|
Posted: Thu Aug 25, 2016 1:09 am |
|
|
Why are you using INT_RTCC?.
This does nothing as posted. The interrupt will be set every 4mSec. Since your servo output code routines together take an absolute minimum of 4mSec, it'll always be true when you test it. So it is pointless.... |
|
|
|