Patch Level Update - HI-TECH C® PRO for the PIC18 MCU Family v9.63PL1
New HI-TECH C PRO for the Silicon Labs 8051 MCU Family - Now Available
HI-TECH announces support for Microchip's new Enhanced PIC12/16 MCUs
Omniscient Code Generation: as featured in EDN Hot 100 Products of 2007.
|
If you have ever uttered the above statement, there are a couple things you should check in your code:
These two checks, which look
similar, will actually tell you if you have tripped over one of two different
issues - the first related to the abstract "virtual machine" modeled by
the C language, and the second related to the much more concrete machine
provided by your chip.
unsigned char x;
interrupt void isr(void) {
if (condition) x++;
}
main(void) {
while (!x) /* wait for x to become non-zero */ ;
/* code here */
}
The compiler is allowed to assume that, since x is never changed by code inside main(), that it will never change inside main(), and thus (since x is initialized to zero) main()' is equivalent to:
main(void) {
while (1) /* wait forever */ ;
/* code here has all been removed because it can't be reached */
}
int x;
interrupt void isr(void) {
if (x == 256) do_something();
}
main(void) {
while (1) {
do_something_else();
x++;
}
}
If x held the value 255, then the x++ could increment the low byte (at which point the value stored in memory is 0), an interrupt occurs which sees that x is still less than the threshold, and then the main routine gets to carry the increment into the high byte; if the next interrupt does not happen until after the next time x++ begins then the fact that x reached 256 would have been missed. (This same problem can also occur with bitfields even though they may be small enough for the chip to fetch or store in a single instruction, due to the multiple instructions which may be necessary to separate them from the other bitfields stored within the same byte). Never Fear! We have the Technology to Solve these Problems.
volatile unsigned char x;
interrupt void isr(void) {
if (condition) x++;
}
main(void) {
while (!x) /* wait for x to become non-zero */ ;
/* code here */
}
And the compiler is required to not assume that x will never change within main() - it must generate a loop that tests the variable on each iteration because the volatile qualifier tells it that the variable may change at any time.
#include <htc.h>
int x;
interrupt void isr(void) {
if (x == 256) do_something();
}
main(void) {
while (1) {
do_something_else();
di(); /* protect the integrity of 'x' */
x++;
ei(); /* OK, 'x' is consistent again */
}
}
Note: You must put such protection around any access to such a variable outside an ISR, including anywhere that it's involved in the condition of a branch or loop; if testing other than a single variable, or in a loop whose body is very small, you may want to use a non-interrupt-accessed temporary to reduce the complexity. Tying all of the above together:#include <htc.h> /* defines 'di()' and 'ei()' macros */
volatile unsigned int a; /* modified inside ISR */
unsigned int b; /* modified outside ISR but not inside */
volatile unsigned char c; /* modified inside ISR */
unsigned char d; /* modified outside ISR but not inside */
unsigned int t; /* temporary, used outside ISR only */
main(void) {
while(1) {
di(); /* protect multi-byte 'a' */
if (a) {
ei(); /* done with 'a' */
/* do stuff */
/* no precautions need be taken here
because access to 'd' is atomic */
d++;
/* do more stuff */
}
else ei();
/* remember to re-enable after a loop or conditional
as well as inside it */
di(); /* protect test */
while (a) {
ei(); /* done with 'a' */
/* do inside-loop stuff */
di(); /* protect next test */
}
ei();
/* remember to re-enable after a loop or conditional
as well as inside it */
/* stretch the window for the interrupt to do its job
when the loop body is very small */
di();
t = a;
ei();
while (t) {
/* do tiny bit of inside-loop stuff */
di();
t = a;
ei();
}
/* no precautions need be taken here
because access to 'c' is atomic */
if (c) {
/* do before-b stuff */
di(); /* protect multi-byte 'b' */
b++;
ei(); /* done with 'b' */
/* do after-b stuff */
}
}
}
interrupt void ISR(void) {
/* no need for further protection here
as this ISR can't be interrupted;
a 'low_priority' ISR would need it, though, if it accessed
a multi-byte quantity also accessed here */
a = b * 23 + 1;
c = d + 47;
}
|
Copyright © 2008 HI-TECH Software • Trademarks • Forum
Site Map