|
|
View previous topic :: View next topic |
Author |
Message |
mbarrett
Joined: 19 Sep 2003 Posts: 8
|
problem with interrupt timing and delay_ms function. |
Posted: Thu Mar 18, 2010 3:26 am |
|
|
using 18F6723, CCS PCH C Compiler, Version 4.105, 50381
MPlab 8.3 and ICD3. Home build board.
I some code to generate an interrupt every 0.1Sec, part of a simple timer. This works fine but if I put a delay_ms(500) statement in the main() the interval changes to 0.25Sec. Have put some code into the interrupt routine to flash an LED and have checked the timing with a scope.
I thought the delay_ms statement would not affect the interrupt? Cant see what I am doing wrong here. If I change the delay period in the delay_ms the interrupt period changes. Obviously the final code wont have a delay_ms(500) in it this is just the bare minimum test code.
suggestions welcome!
mike
Code: |
// Control program V3.00
//
#include <18f6723.h>
#device ICD=true
#DEVICE *=16 adc=10
#fuses NOBROWNOUT, NOWDT, NOLVP , debug
#fuses HS
#use delay (clock=8000000)
#use rs232(baud=4800, xmit=PIN_C6, rcv=PIN_C7, parity=N, BITS =8, STOP=1)
// ====
//#use i2c(master, SDA=PIN_C4, SCL=PIN_C3)
//#define SLAVE1_WRT_ADDR 0x12
//#define SLAVE1_READ_ADDR 0x13
//#include <cnt3_setup.c> // all defines setup in here
#byte PIR2 = 0xFA1
// ====defines====
#define led0 PIN_E0
#define led1 PIN_E1
// Variable definitions
int valve_overlap; // overlap between firing and releasing previous solenoid
int valve_inuse=0; // holds index of last valve driven
int valve_next; // variable used in valve control routine
float m_secs=0; // ~0.1 sec count see interrupt code
int secs=0; // sec count
int mins=0; // minute count
int hours=0; // hour count
int days=0; // day count
float t3_inc=0.1;
int crap;
int16 t3, t3_set;
//#include <drive_mode.c> // basic drive control code
//#include <int_code.c> // interupt code
#INT_TIMER3
void time3()
{
// occurs when Timer 3 rollover. Count rollovers to get time > rollover time
// timer 3 rollover
// clock is 8MHz, input to timer3 is 8/4=2MHz, div 4 divider =500000Hz or 2uS per tick
// pre load timer3 with 15535 so that it does a count of 50000 to rollover
// whihc is 0.1 Seconds between interupts
//timing test code
t3=get_timer3();
set_timer3(t3_set);
output_high(led0);
delay_ms(1);
output_low(led0);
//
m_secs=m_secs+t3_inc;
if (m_secs > 60)
{
m_secs=0;
secs=secs+1;
}
if (secs > 60)
{
secs=0;
mins=mins+1;
}
if (mins >60)
{
mins=0;
hours=hours+1;
}
if (hours > 24)
{
days=days+1;
hours=0;
}
}
main()
{
// clock is 8MHz
// timer input is 8/4 2MHz, div by 4 = 500kHz, each count 2uS, 16 bit max count 65535, pre // pre load with 15535 so that rollover occurs every 50000 * 2uS or 0.1 Sec
setup_timer_3 (T3_INTERNAL | T3_DIV_BY_4); //
// setup_timer_3(T3_disabled);
t3_set=15535;
set_timer3(t3_set);
enable_interrupts(INT_TIMER3); // basic clock
enable_interrupts(GLOBAL);
do {
delay_ms(5000);
}while(true);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19518
|
|
Posted: Thu Mar 18, 2010 4:56 am |
|
|
Your problem, is because you have a delay in the interrupt.
The PIC, does not have a hardware register stack, so it is very hard to write code that can safeley be called from inside itself (re-entrancy). CCS, for this reason, does not support this. Because of this, you should be getting a warning when you compile, saying:
"interrupts disabled during call to prevent re-entrancy. (delay_ms)".
Hence the interrupt is only being called in the few instructions when you loop back outside the delay. The actual time that then results wil depend on the duration of the delay, and the point when the counter wraps...
Now, a number of further comments:
1) If you have a timer 'tick', use _this_ to prvide your delays. Add something like:
Code: |
int16 tick;
//Then in the interrupt:
if(tick!=0) --tick;
//Then when you want a delay in the main
tick=50;
while(tick) {
//Nice thing is that you can still do other things here while waiting
}
|
2) Look at using timer2, and getting rid of settng the timer 'to' a value, or using a rolling counter to get your required timing. A search here will find why this is the way to go if you want anything approaching accuracy...
3) I you want to flash the LED, simply turn it on in one interrupt, and off in the next. Gives you the flash, without a delay. As a global 'rule' interrupt handlers should always be kept as short as possible, unless you really understand every implication of letting them be slower.
4) As a 'comment', what you post, is nothing near a 'bare minimum test code'. At least half the lines could be removed....
Best Wishes |
|
|
mbarrett
Joined: 19 Sep 2003 Posts: 8
|
problem with interupt timing and delay_ms function. |
Posted: Thu Mar 18, 2010 6:54 am |
|
|
Thanks for the helpful reply.
I had been testing the code without the delay_ms() in the interupt and still got the same problem....
I added the LED flash into the interupt to try to understand what was happening in the interrupt and didnt think about the affect that would have.
I understand what the problem with re-entrant code is. BTW I did not get any warning from the compiler.
I will go back and try it without the delay_ms() in the interrupt and see if I still get the same original timing error.
Your other points
1) yes much better simpler way of doing things..
2) will look at timer 2, but wanted to understand why it did not work with my method and timer3....
3) the LED flash code was just so I test with a scope I wanted the LED on for a known period of time hence the delay. Didnt think it would cause more problems .
4) yes agree, this was the part of a larger program that I was working on and stripped out most of the code. Yes it could have been smaller and simpler.......
Will go away and try some changes, and search the forum, before coming back...
cheers
mike |
|
|
|
|
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
|