|
|
View previous topic :: View next topic |
Author |
Message |
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
16f877a interrupt issue |
Posted: Sun Nov 21, 2010 10:07 am |
|
|
Hi,
i am going to use the interrupt in PIC16f877a.
But anyone can explain methods to use timer and few register that will be used to me?
i am using 20Mhz crystal, but i dun know the pre-scaler is what for?
from what i know is put a number inside the register, then when the register count to 0xff, then the interrupt flag will be set. is it indicates when the flag be set, then the instruction will go to do the interrupt work???
For example,
if i want my program every 10 seconds go to do the interrupt service routine. that what should i set and how i calculate?
*note: i really do not understand the datasheet.
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Sun Nov 21, 2010 10:42 am |
|
|
You won't get 10 seconds, unless you use an external clock at a much lower frequency. However this 'doesn't matter'.
Key is to think about machines like the PC. Here you have an internal 'tick' interrupt running at quite a high frequency (on the original PC, 18.2 times per second), but you can setup a 'task' to be called 10 seconds in the future, by simply 'counting ticks', and calling the code you want (in this case) after 182 ticks.
Simplest timer to use, is timer2. This is fed from the master clock/4. Then you have a choice of 'prescalers' (dividers), /1 /4, or /16. Then a divider of /1 to /256, and finally another 'postscaler', of /1 to /16 (Phew).
What you want is a nice 'high' count, that gives a easy to count 'tick'. So:
20000000/(4*4*250*10), gives '50', which is a nice number to work with.
So, you then want something like:
Code: |
#define TicksPerEvent (500)
int1 do_operation=FALSE;
#int_timer2
void tick(void) {
static int16 counts=TicksPerEvent;
//Arrive here 50*/second
if (--counts==0) {
//500/50 = 10 so here every ten seconds
counts=TicksPerEvent;
do_operation=TRUE;
}
}
//Then in your main
//Early on
setup_timer2(T2_DIV_BY_14,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
//Then in your main 'loop'
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
//Code here that you want to execute every ten seconds
}
//Your other code
}
|
Now, important things. Read here about why it is important to keep interrupt handlers quick. Hence the code you want, unless it is very simple, should be in the main loop as shown, rather than in the interrupt.
There is an application note about the timers, on the MicroChip site, and this is _much_ more informative than the data sheet.
The prescalers, and postscalers, are just 'dividers'.
Best Wishes |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Sun Nov 21, 2010 11:03 am |
|
|
Thx for the information
But some of the thing i am not clear,
may i know
"20000000/(4*4*250*10), gives '50', which is a nice number to work with. "
20000000 --> my crystal value
4 -->?
4 -->?
250 --> ?
10--> 10second?
may i know those number indicates what? why i need use this equation?
the final answer 50 is indicates50 ticks in a second?
sorry, juz how to know to use this equation for counting?
thanks. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Sun Nov 21, 2010 11:28 am |
|
|
I do explain these numbers.
"This is fed from master clock/4" - Hence the first 4
"Then you have a choice of 'prescalers' (dividers), /1 /4, or /16"
You can use whatever you want, but I was looking for a set up numbers that gives a nice decimal division of your clock.
4*250*10, is 10000 nice decimal number, and gives 50 ticks per second.
You have a choice of:
1,4,16
1 to 256
1 to 16
Trying to find values that multiplied together, gives a high number that divides evenly into 5000000 (20000000/4), gives 10000 as one possibility. Another is:
16*125*10, which then gives 20000 (25 ticks per second).
Trying to go above this, leads to results that give 'odd' results, making the divisions harder.
You can equally well work with 'near enough' binary numbers, and go for a less frequent tick, _but_ then the accuracy disappears.
Best Wishes |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Sun Nov 21, 2010 12:32 pm |
|
|
Thanks alot.
But still wanna asking...
Hmmm...I try to run your program...
in my main program,
Code: |
void main()
{
setup_timer_2(T2_DIV_BY_4,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
output_high(PIN_A0) //to turn on the LED
}
lcd_putc("\ftesting...") // display something on LCD
delay_ms(3000)
output_low(PIN_A0) // to turn off the LED
}
}
|
My problem is, after I wait few seconds, the LED turn on, so I assume the interrupt work. But, the LCD never display anything and LED never turn off again after this... may I know why?
Or can I don't put the isr in the main function, but it still can be call every 10 seconds? |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Sun Nov 21, 2010 12:56 pm |
|
|
May i know how come the main program never call the tick() function?
is it in the if(do_operation) {... }, then the tick() already be called?
sorry for a lot of question, i really very weak in this. but i wish to learn and understand
Code: |
#define TicksPerEvent (500)
int1 do_operation=FALSE;
#int_timer2
void tick(void) {
static int16 counts=TicksPerEvent;
//Arrive here 50*/second
if (--counts==0) {
//500/50 = 10 so here every ten seconds
counts=TicksPerEvent;
do_operation=TRUE;
}
}
//Then in your main
//Early on
setup_timer2(T2_DIV_BY_14,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
//Then in your main 'loop'
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
//Code here that you want to execute every ten seconds
}
//Your other code
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Sun Nov 21, 2010 1:03 pm |
|
|
The main loop is being executed all the time. You want this to be as fast as possible, wen nothing is eing done. So:
Code: |
void main()
{
setup_timer_2(T2_DIV_BY_4,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
output_high(PIN_A0); //to turn on the LED
lcd_putc("\ftesting...");// display something on LCD
delay_ms(100);
lcd_putc("\ffinished");
output_low(PIN_A0); // to turn off the LED
}
}
|
Your lines are all 'illegitimate' - no terminaing ';'. Probably nothing executes after the first output statement.....
Best Wishes |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Sun Nov 21, 2010 1:09 pm |
|
|
That you mean all the things need inside the if {}?
But if like that, does it mean all action will just do once for every 10 seconds?
If not, can you explain a bit? sorry...sorry..sorry...
*Note: What I mean is I want to put those actions outside the if() because those actions are normal in while(1) loop. I simply write that for testing purpose, because I want to make sure my other actions can work as well when using interrupt.
Sorry for the mistake. |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Sun Nov 21, 2010 9:05 pm |
|
|
Hi, I am actually rushing a project.
Can anyone give me some idea about interrupt?
I hope can make my program work fine.
Thanks.
Regards,
sysysy |
|
|
gpsmikey
Joined: 16 Nov 2010 Posts: 588 Location: Kirkland, WA
|
|
Posted: Sun Nov 21, 2010 11:48 pm |
|
|
In the code shown, the timer triggers an interrupt 50 times / second. Inside the ISR, the "counts variable gets decremented each time through. When it reaches 0, the "do_operation" gets set to TRUE (which your "Main" sees) and the counts gets reset to 500 again to start the next 500 counts down at 50 times/ second. The end result is that every 10 seconds, the "do_operation" flag will get set to true. When it does, the code running in main() sees it go true and does whatever you want resulting in something happening every 10 seconds. Hope that helps.
mikey _________________ mikey
-- you can't have too many gadgets or too much disk space !
old engineering saying: 1+1 = 3 for sufficiently large values of 1 or small values of 3 |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Nov 22, 2010 3:12 am |
|
|
The key is to keep things 'moving'. Delays (unless short), are your enemy. While a delay is executing, nothing else in your main code is running. You can extend the 'tick' idea, to do other jobs. For example:
Code: |
#define TicksPerEvent (500)
#define TicksPerSecond (50)
int1 do_operation=FALSE;
int8 second_counter=0;
#int_timer2
void tick(void) {
static int16 counts=TicksPerEvent;
static int16 ticks=TicksPerSecond;
//Arrive here 50*/second
if (--counts==0) {
//500/50 = 10 so here every ten seconds
counts=TicksPerEvent;
do_operation=TRUE;
}
if (--ticks==0) {
//Here every second
ticks=TicksPerSecond;
if (second_counter) --second_counter;
}
}
//Then in your main
int8 old_seconds=0;
//Early on
setup_timer2(T2_DIV_BY_14,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
//Then in your main 'loop'
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
//Code here that you want to execute every ten seconds
output_high(PIN_A0); //Turn on LED
second_counter=3;
}
if (old_seconds!=second_counter) {
//You will get here once per second, while 'second_counter' <>0
old_seconds=second_counter;
lcd_putc("`fcount %d",old_seconds;
if (old_seconds==0) {
lcd_putc("\f"); //Clear screen
output_low(PIN_A0); //drop pin
}
}
}
|
Now you have the every ten seconds, uring the LED on for 3 seconds, and displaying a countdown, then when it gets to zero, clearing the screen and turning the LED off.
However the 'loop' is still executing quickly, allowing fast response to other things.
At any time you want to wait for a number of seconds, you can just set the second_counter to a value, and carry on doing other things till it gets to zero.
Best Wiishes |
|
|
sysysy
Joined: 17 Nov 2010 Posts: 38 Location: 121
|
|
Posted: Mon Nov 22, 2010 6:59 am |
|
|
Code: |
//Then in your main
int8 old_seconds=0;
//Early on
setup_timer2(T2_DIV_BY_14,249,10); /(4*4*250*10)
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
//Then in your main 'loop'
while (TRUE) {
if (do_operation) {
do_operation=FALSE;
//Code here that you want to execute every ten seconds
output_high(PIN_A0); //Turn on LED
second_counter=3;
}
if (old_seconds!=second_counter) {
//You will get here once per second, while 'second_counter' <>0
old_seconds=second_counter;
lcd_putc("`fcount %d",old_seconds;
if (old_seconds==0) {
lcd_putc("\f"); //Clear screen
output_low(PIN_A0); //drop pin
}
}
}
|
Ok thx alot... 1 last thing i wanna confirm.
in the above code,
1. if (do_operation){... } is do the task every 10 seconds --->OK
2. if (old_seconds!=second_counter), is do the other routine, and we set the desired time we want. But my problem is without this if (old_seconds!=second_counter), we direct write our program inside the while(true) loop, is it still correct? becoz all the things still inside the while loop. Then i assume, it will always loop.
So, can take out the second the if check condition one?
or my concept wrong? |
|
|
|
|
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
|