|
|
View previous topic :: View next topic |
Author |
Message |
dorinm
Joined: 07 Jan 2006 Posts: 38
|
timer interrupted delay routine bug? |
Posted: Wed Apr 27, 2011 12:55 am |
|
|
Hello all,
first of all, here's a topic I found on this forum regarding my issue:
http://pic-c.ccsinfo.com/forum/viewtopic.php?p=26
...basically, I am experiencing the same problem, that the delay routine built into CCS somehow get stucked. I don't have the time to debug the asm so I'm asking if someone experienced this recently and what would be the workaround, excepting the custom delay routine one.
I've tried to compile the following code using 3.xxx version of CCS, also the latest 4.120(demo), the same result: the TIMER1 should fire every~18msec but it fires every ~250msec; if I comment the delay in main loop, everything works as expected:
Code: |
#include <18f458.h>
#device *=16
#device ADC=10
#fuses HS, NOOSCSEN, NOWDT, NOPUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NOWRTD, NOWRTB, NOCPB, NOWRTC, NOEBTR, NOEBTRB, STVREN
#use delay(clock=20000000)
#define TEST PIN_B0
#INT_TIMER1
void timer1_isr()
{
output_bit(TEST, 1);
//this delay does not affect the isr
delay_ms(1);
output_bit(TEST, 0);
set_timer1(0xD600);
}
void main() {
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
set_tris_a (0b00000000);
set_tris_b (0b00000000);
set_tris_c (0b10000110);
set_tris_d (0b00000000);
set_tris_e (0b00000000);
output_bit(TEST,0);
set_timer1(0xD600);
setup_timer_1 (T1_INTERNAL|T1_DIV_BY_8);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(TRUE) {
// this is the delay being interrupted by timer isr
delay_ms(500);
};
}
|
Thank you, I'm looking forward for your suggestions.
Dorin |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Apr 27, 2011 2:31 am |
|
|
Your issue is not the one in the thread.
The PIC, does not have a data stack.
Because of this, local registers in functions can't be saved.
Because of this, functions can't (easily) be made 're-entrant' (you can't call a function inside itself).
Hence your problem.
Your interrupt code, uses the delay_ms routine. So does your 'main' code.
So to _ensure_ that the delay is not called inside itself, interrupts will be disabled in the 'delay_ms(500)', in the main.
Now, I'd not expect the interrupt to fire every '~250mSec', but every 500mSec. Effectively the code sits in the delay in 'main', with the interrupts disabled, then just for the single instruction as you loop back, interrupts are turned on. At this moment, the timer code gets called. Your code is not stuck in the delay, the delay is fine. It is that interrupts are turned off in the delay, because you are using delays in the timer interrupt.
Basically, interrupt handlers in general, should do just the job of handling the interrupt. Delays inside handlers are 'poor practice'. It is possible to make the compiler generate a separate delay routine for inside the interrupt, but 'better' to use the hardware to fire the delay.
So:
Code: |
#include <18f458.h>
//#device *=16 Not needed - only used with PIC16 chips
#device ADC=10
#fuses HS, NOOSCSEN, NOWDT, NOPUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NOWRTD, NOWRTB, NOCPB, NOWRTC, NOEBTR, NOEBTRB, STVREN
#use delay(clock=20000000)
#define TEST PIN_B0
#INT_TIMER1
void timer1_isr() {
static int1 toggle=0;
if (toggle==1) {
set_timer1(0xFDA4);
toggle=0;
output_low(TEST);
}
else {
set_timer1(0xD691);
toggle=1;
output_high(TEST);
}
}
void main() {
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
/*set_tris_a (0b00000000);
set_tris_b (0b00000000);
set_tris_c (0b10000110);
set_tris_d (0b00000000);
set_tris_e (0b00000000);*/
//These are not needed - If you want to control the TRIS, switch to fast_io
//Currently all I/O operations _will_ override these settings
output_bit(TEST,0);
set_timer1(0xD691);
setup_timer_1 (T1_INTERNAL|T1_DIV_BY_8);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(TRUE) {
// this is the delay being interrupted by timer isr
//No. This is the delay _stopping_ the ISR.....
delay_ms(500);
} //; here not needed
}
|
Now, I have made some other comments 'in code', and have roughly compensated the timer setting values for the time it takes to get into the ISR (about 17 counts of the timer).
The compiler should actually be giving you a warning, when you compile the original code, telling you that interrupts are disabled to prevent re-entrancy, and then naming the delay routine.
You can use a separate copy of the delay routines to solve the original problem, but if you do, the delay in main, will run slow (about 540mSec).
Best Wishes |
|
|
dorinm
Joined: 07 Jan 2006 Posts: 38
|
|
Posted: Wed Apr 27, 2011 4:17 am |
|
|
Just made some more testing on a friend computer ...basically what I've discovered is that the code compiled with retail 4.120 runs fine, but in 4.120 demo and older version (i think last one i've been trying was 4.114, also 3.xxx) is happening what you are saying.
Your point makes sense yet I don't understand why if I keep the delay in isr short (~10-6-700us), everything works ok, but not with msecs.
I also don't understand why other isr's (like uart, rb) don't seem to be affected by the delay (delays) in main loop ....i've been using delays (like everybody else) for many years now and this is the first time (I'm using timers with delay in them) I see something like this.
Thank you, I think it's time to buy CCS as far as the demo version doesn't compile workable code and the retail version does |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Apr 27, 2011 8:12 am |
|
|
Delay_us, is a different routine to delay_ms. The problem will then disappear, since you are not using the same routine both inside and outside the interrupt.....
Interrupts like int_rda, _will_ be affected. But remember that there are a couple of characters of buffering in the UART. Provided no more than a couple of characters arrive while the interrupts are disabled, things will still work.
Using delays inside the interrupt handlers, is _not_ 'like everybody else'. Most professional programmers use something like a 'tick' event (an interrupt at perhaps 100*/sec), and use this for all their main delays, rather than using the CCS delay code. You can then just set a counter, which is decremented in this 'tick', and do something else while it is not zero.
Best Wishes |
|
|
dorinm
Joined: 07 Jan 2006 Posts: 38
|
|
Posted: Thu Apr 28, 2011 9:01 am |
|
|
thank you, this is a better way to do delays I never thought!
About delay_us, delay_ms, I'm not so sure that they don't have the same behaviour (even if they are slightly different routines) as far as even if I tried to use delay_us(2000) insead of delay_ms(2), the code behaviour was exactly the same
Neverthless, now I've learned something... not to rely on built-in routines and instead use some good programming practices like make my own routines for what is not standard programing; maybe some bugs are just optimization bugs yet I don't have the time to test everything in every combination )
Thanks again! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Apr 29, 2011 4:08 am |
|
|
It would be.
They use a slightly different code, dependant on the _length_ of the delay. Delay_us values that are _less_ than a mSec, use 'unique' delay_us code. With delay_us(2000), the compiler will substitute a call to the simpler (but lower resolution) delay_ms code.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|