Reputation: 3337
Last night a friend and I came across an anomaly that we're trying to understand, but can't. At the time we were playing with an ATTiny2313 using AVR assembly, today I have replicated the exact same experience in Arduino on the ATMega328P.
I noticed he had an error in his code where he was setting a LED HIGH (and later LOW) using PINB,6
. I told him to use PORTB,6
and (for some other reason) that didn't work at all. That whole code is gone now, so I have replicated it in Ardunio C++ and got unexpected results being that when assigned correctly everything works as it should, yet when assigned incorrectly, the LED flashes at half the rate. It seems like a phasing issue.
Before I go into the code, I KNOW you can not assign output via PIN
, the issue is we tried (in error) and were trying to understand how it works.
void setup(){
DDRB=0xFF;
}
void loop(){
PORTB |= 1<<5; // turns on the LED
delay(500);
PORTB &= ~(1<<5); // turns off the LED
delay(500);
}
The above code is correct and the led toggles every 500ms
void setup(){
DDRB=0xFF;
}
void loop(){
PINB |= 1<<5; // turns on the LED
delay(500);
PINB &= ~(1<<5); // turns off the LED
delay(500);
}
Now the above code does in fact occasionally work. Instead of turning on and off every 500ms, it turns on and off every second!
I have inspected the circuit diagram for the chip (actually the ATMEga128—as I have a hard copy of the PDF here), pp66, Figure 30, General Digital I/O.
The circuit indicates PINx is read only, but you obviously can set it, although it seems to take every third set.
As an interesting aside, if you only SET PINx (as in the following code:)
void loop(){
PINB |= 1<<5; // turns on the LED
delay(500);
}
the LED TOGGLES! every 500ms. Trying the same with clearing the OUTPUT doesn't work, only setting it.
Again, I KNOW this isn't how the OUTPUT is set, I'm just trying to understand why it does what it does.
Upvotes: 2
Views: 371
Reputation: 743
You've encountered an often neglected feature of the ATmega line: setting a bit in a PINx register toggles the corresponding bit in PORTx.
From page 77 in the ATmega328P datasheet:
14.2.2 Toggling the Pin
Writing a logic one to PINxn toggles the value of PORTxn, independent on the value of DDRxn. Note that the SBI instruction can be used to toggle one single bit in a port.
Upvotes: 5