I have an LED matrix that I row scanning to display an arbitrary image. To get a decent frame rate I am writing to the PORT registers directly rather than calling digitalWrite.
I have seen several people talk about disabling interrupts around setting PORT registers using code something like this:
The reason is that code using digitalWrite will not interact safely with such code in the interrupt routine - if its using the same port.
The code in digitalWrite will do something like
PORTD |= bit ;
or
PORTD &= bit ;
where bit is a variable. This compiles to more than one instruction, so that the interrupt routine can run between these instructions and change PORTD, not knowing the the relevant bit is supposed to change - net effect is that randomly digitalWrite will fail (or the changes the interrupt routine intends will fail).
If you know that only the interrupt routine touches that port, you don't have to worry about this.
I am a little perplexed by the use of cli/sei around a line of the form
PORTD |= 0b00010000 ;
Since that should compile to a single instruction though... Certainly there are assembler instructions to set or clear individual bits of the ports (which are suffiently low in the register space for the single-instruction bit-set and bit-clear ops)
MarkT:
The reason is that code using digitalWrite will not interact safely with such code in the interrupt routine - if its using the same port.
The code in digitalWrite will do something like
PORTD |= bit ;
or
PORTD &= bit ;
where bit is a variable. This compiles to more than one instruction, so that the interrupt routine can run between these instructions and change PORTD, not knowing the the relevant bit is supposed to change - net effect is that randomly digitalWrite will fail (or the changes the interrupt routine intends will fail).
If you know that only the interrupt routine touches that port, you don't have to worry about this.
That and the fact that you are sending constant data type to the port, you shouldn't suffer any 'atomic' problem?
Thanks for the explanations, that makes a lot of sense.
As for digitalWrite not being fast enough...
I am actually trying to see what the limits are of using an arduino to power LED matricies. And playing around with my own version of shiftPWM. I am not interested in just using the library that someone else has written because there is no challenge in that. Using digitalWrite I was getting flickering effects on a 24x8 RG matrix with only a few levels of PWM.
retrolefty:
That and the fact that you are sending constant data type to the port, you shouldn't suffer any 'atomic' problem?
In the general case, Whatever&=value; (or Whatever|=value;) compiles to at least three machine instructions. Load, modify, then store. It may be easier to see when written "long hand": Whatever = Whatever & value;. Whatever is loaded from memory, Whatever is bitwise-anded with value, and the result is stored in Whatever. In this case an interrupt could occur between the load and modify or between the modify and store which could lead to problems.
If it had been a simple assignment (Whatever=value;) then you are correct. There is no possibility of a problem because there is essentially just a store.
The AVR processor has instructions for manipulating a single bit in a "low register" (which PORTD is). The GCC compiler is smart enough to recognize that the examples posted here only manipulate a single bit so the result is a single machine instruction which is safe.
As an additional example, this manipulates more than one bit so it compiles to at least three machine instructions and is not safe...