This is exactly why the volatile keyword exists. It really doesn't matter if you "transfer" one-byte or a hundred, you need to ensure that:
1) your ISR isn't going to get called again (disabling interrupts is correct in almost all cases)
2) the compiler is aware that the storage you are using may change due to external factors, so it can adjust optimization and access schemes appropriately
I'd ask what the difference is between the following two pieces of code:
Code:
// Assume char is 8 bits
volatile unsigned char rx_flag;
volatile unsigned char ready_flag;
volatile unsigned char coffee_time;
void interrupt() {
// disable interrupts
// check interrupt source etc.
rx_flag = 1;
ready_flag = 1;
coffee_time = 1;
}
Code:
// Assume int on this platform is 16 bits
volatile unsigned int status;
void interrupt() {
// disable interrupts
// check interrupt source etc.
status = 0x0003;
}
As long as you mark the variable as volatile, I don't see how the second example is any different. The compiler should ensure any action to modify it is atomic.
Am I wrong?
YUP, you're wrong! ;-) This is a common misunderstanding.
volatile does NOTHING to resolve an interrupt jumping in during the write of a variable and thus the ISR may read an incoherent mix of bytes, or may write it while the main() is reading or writing it, resulting in an incoherent value in the variable.
volatile is a directive to prevent optimization from assuming a variable has the same value it used to have. e.g.:
a=5;
b=a+5;
Will prevent any optimization from making this into b=10 (constant) in asm. No telling if your compiler would have done that in the first place... its exact implications are vague. But no, it does not attempt to resolve ISR conflicts at all, ever.
To prevent the ISR conflict, you MUST disable the relevant interrupt (or all interrupts) while reading variables which are written by the ISR, or while writing variables which may be read or written by an ISR. Then immediately reenable them. Or find some sort of guarantee in the program flow that this ISR simply can't occur while working with that variable.
Sometimes you want to be certain the ISR responds quickly, but the math equation to modify that variable is long. In this case you might calculate the value to a temp variable, disable the ISR, copy to the intended variable, and reenable the ISR. As such the ISR is disabled for only a few cycles.
Warning: in some cases, you might have ISRs getting enabled/disabled regularly for other reasons. Be careful you are not in a period where the interrupt was already disabled and needs to stay disabled, yet blindly disable, then reenable it as part of the main() math.
Also, note this problem is NOT limited to multi-byte variables. Consider:
In main():
volatile char a=50; //volatile will NOT help you at all
a+=2;
And the ISR clears "a" around about the same time.
Now depending on your compiler and PIC instruction set, it MIGHT use at atomic instruction to increment "a" in a file register. The PIC24 instruction set can do that if the compiler's smart enough. In that case, the code will either give a 2 or a 0, depending on exactly what cycle that ISR occurred on. But, if it's NOT atomic (and it probably won't be), you could get 52, and the ISR's clearing it will be lost forever. Because the main() will mov the value from the file register to wx, add 2, ISR clears the file register, then main(), unaware that this just happened, writes the a+2 value it's working on back to the file register, clobbering the value. Again, the point here is that this is NOT a multi-byte variable problem. Char.
When you go through it, there's no way what you assume is happening would be possible to implement.
There's no atomic-write or reads for multi-bytes in the instruction set. All it could do is copy the current ISR enable bit, clear the interrupt enable, then copy the current enable back, and that'd certainly cause problems for many people. Interrupts shouldn't spontaneously disable themselves like that.
And it wouldn't be guaranteed to fix things:
volatile a=50;
volatile b;
b=a;
b+=2;
;<<ISR clears "a" here
a=b;
Well, a simple, "dumb" automatic disabling of interrupts for the line where "a" is written will not fix this. The value from "a" was temporarily preserved elsewhere and will come back to clobber the ISR's clear, but there's no way the compiler could see that.