| Author |
Message |
|
|
Posted: Sep 11, 2009 - 11:40 AM |
|

Joined: Aug 21, 2009
Posts: 57
|
|
In the pic world I often apply ExlusiveOR to an output port to "toggle" a pin. Doing it this way means I don't need to use a constant bit number for the pin. Handy for generic routines that don't know the pin that is being used and can be passed a bit mask.
To do this in AVR I seem to need to EOR a register and then OUT the register, am I doing it right, or have I missed a sneaky quicker way? |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 11:52 AM |
|

Joined: Dec 08, 2004
Posts: 4719
Location: Nova Scotia, Canada
|
|
In many newer AVRs (those released since about 2004) there is an unexpected 2nd function in the PINx registers.
Traditionally, the PINx register has been a read-only register, used to read the current logical state of the I/O pin's input buffer.
In newer devices, an unrelated write function has been added to the PINx register - writing a '1' to any bit in the register will have the effect of toggling the corresponding bit in the related PORTx register. Any '0's written to the register will have no effect.
You can access this feature using the bit-access SBI/CBI instructions if the PINx register is in range (with some pretty critical caveats on some devices!) or by sending a full byte to the register, with the bits you want to toggle set as '1's and the bits you want to stay the same cleared as '0's.
In the XMEGA series of AVRs, this feature has been made more distinct by the addition of unique OUTTGLx registers.
You'd have to check the data sheet for the particular AVR device you're working with to determine whether or not this feature is implemented in your device. |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 12:16 PM |
|

Joined: Sep 06, 2009
Posts: 29
Location: New Zealand
|
|
This is how I've done it in the past:
Code:
in R16,Portb ; Read current port state
ldi R17,0b00000100 ; Setup bit 2 to toggle
eor R16,R17 ; Do the magic stuff
out Portb,R16 ; Toggle the pin
|
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 12:20 PM |
|


Joined: Jun 15, 2008
Posts: 1762
Location: North Carolina USA
|
|
| Here is what AVR-gcc does with PORTB^= (1<<LED1) for the ATTiny85:
Code:
276: PORTB^= (1<<LED1);
+000000D6: B388 IN R24,0x18 In from I/O location
+000000D7: 258A EOR R24,R10 Exclusive OR
+000000D8: BB88 OUT 0x18,R24 Out to I/O location
|
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 12:42 PM |
|

Joined: Sep 06, 2009
Posts: 29
Location: New Zealand
|
|
Same thing, except their R10 needs to be set up with the mask.
If you have plenty of spare regs, this can remain setup so only needs to be initialised once. |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 01:10 PM |
|

Joined: Dec 08, 2004
Posts: 4719
Location: Nova Scotia, Canada
|
|
To be clear, if you've got an AVR with the alternate functionality of the PINx register, the following code:
Code:
PORTB ^= (1<<LED1);
which would typically be compiled to something like:
Code:
IN Rx, PORTB
LDI Ry, (1<<LED1)
EOR Rx, Ry
OUT PORTB, Rx
for a total of 4 words and 4 clock cycles, can be replaced with this totally functionally equivalent code:
Code:
PINB = (1<<LED1);
which would typically be compiled into something like:
Code:
LDI Rx, (1<<LED1)
OUT PINB, Rx
for at total of 2 words and 2 clock cycles. The average savings, in terms of both code size and execution time, is 50%.
The PINx formulation also happens to be more likely to be interrupt-safe, if you happen to have any interrupts in the system that might also be attempting to twiddle other bits in PORTx at the same time. |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 01:48 PM |
|


Joined: Jun 15, 2008
Posts: 1762
Location: North Carolina USA
|
|
| Hey, the '85s have it! From the datasheet:
Quote:
10.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
Now my program does
Code:
277: PINB=(1<<LED1);
+000000D6: BAA6 OUT 0x16,R10 Out to I/O location
or
Code:
278: PINB|=(1<<LED1);
+000000D6: 9AB0 SBI 0x16,0 Set bit in I/O register
|
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 01:59 PM |
|


Joined: Jul 23, 2001
Posts: 2440
Location: Osnabrueck, Germany
|
|
|
Code:
PINB|=(1<<LED1);
You need to be very careful with that. If the PIN register is not reachable by the SBI command, you will possibly toggle more than only one pin. |
_________________ Stefan Ernst
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 02:15 PM |
|

Joined: Sep 06, 2009
Posts: 29
Location: New Zealand
|
|
Thanks for that info lfmorrison.
I didn't know that about the newer devices. Learn something new every day! |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 02:52 PM |
|

Joined: Sep 05, 2001
Posts: 2499
|
|
|
sternst wrote:
Code:
PINB|=(1<<LED1);
You need to be very careful with that. If the PIN register is not reachable by the SBI command, you will possibly toggle more than only one pin.
Since SBI was a read-modify-write command, it works not as wanted on the PINx register.
The PORTx-bit, which was selected, toggle.
But all other PORTx-bits are cleared
And on using the CBI command, only the selected bit was not cleared.
A similiar behaviour was already known on the ADCSRA register:
Code:
ADSRA |=(1<<ADSC);
This compile also to the SBI command, which start a new conversion and clears the ADIF bit at the same time.
Peter |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 03:40 PM |
|

Joined: Aug 21, 2009
Posts: 57
|
|
| Works great, my boss is a happy man, thanks! |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 03:44 PM |
|


Joined: Jul 23, 2001
Posts: 2440
Location: Osnabrueck, Germany
|
|
|
danni wrote:
sternst wrote:
Code:
PINB|=(1<<LED1);
You need to be very careful with that. If the PIN register is not reachable by the SBI command, you will possibly toggle more than only one pin.
Since SBI was a read-modify-write command, it works not as wanted on the PINx register.
The data sheet has a different opinion:
Quote:
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.
|
_________________ Stefan Ernst
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 03:47 PM |
|


Joined: Feb 19, 2001
Posts: 25923
Location: Wisconsin USA
|
|
Peter--I don't think I agree with you on the PINx SBI to toggle a port bit. I've used it in many apps over the last few years without noticing any effects on other port bits.
Your ADSRA example, though, can indeed cause the side-effects on those registers that have an embedded xxIF.
Lee |
|
|
| |
|
|
|
|
|
Posted: Sep 11, 2009 - 04:37 PM |
|

Joined: Dec 08, 2004
Posts: 4719
Location: Nova Scotia, Canada
|
|
|
danni wrote:
sternst wrote:
Code:
PINB|=(1<<LED1);
You need to be very careful with that. If the PIN register is not reachable by the SBI command, you will possibly toggle more than only one pin.
Since SBI was a read-modify-write command, it works not as wanted on the PINx register.
The PORTx-bit, which was selected, toggle.
But all other PORTx-bits are cleared
And on using the CBI command, only the selected bit was not cleared.
A similiar behaviour was already known on the ADCSRA register:
Code:
ADSRA |=(1<<ADSC);
This compile also to the SBI command, which start a new conversion and clears the ADIF bit at the same time.
Peter
Note, firstly, that I did not recommend the
Code:
|=
formulation. I recommended the direct assignment
Code:
=
formulation. There is no possibility of read-modify-write atomicity problems with that formulation because there is no read-modify-write operation going on there. The OP couldn't have used the SBI method because he claimed he wanted a generic function that takes a variable btimask as an argument, and SBI cannot operate on a runtime variable bit. Anyway, the direct assignment to PINx is still smaller and faster than the OP's initial Exclusive OR method, thus preferable.
Note, secondly, that you're right, there is a bug in the SBI and CBI instructions on some generations of the AVR core - it actually reads the entire register in, modifies a single bit, then writes the entire register out again. This has potentially undesirable effects in clearing some status flag bits, and it might potentially have undesirable effects on toggling unintended bits in a PIN register. That's why I had said in my original reply that the SBI instruction might have some severe caveats.
However, many of the AVRs that have been released since about the same timeframe as the introduction of the alternate PIN register function, around 2004, have fixed the bug in the SBI and CBI instructions so that they truly operate only on the single bit specified in the instruction. There ought to be a footnote at the end of each AVR data sheet's register summary section that will indicate whether or not the bug is present in that particular part.
Of course, it would be an extremely trivial experiment to test to see whether or not the bug is relevant on any given part. Write this code:
Code:
.ORG 0
LDI R16, 0xFF
OUT DDRA, R16
OUT PORTA, R16
NOP
NOP
NOP
SBI PINA, PINA0
Loop:
RJMP Loop
And see what happens to port A. |
|
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 09:38 AM |
|

Joined: Sep 05, 2001
Posts: 2499
|
|
|
theusch wrote:
Peter--I don't think I agree with you on the PINx SBI to toggle a port bit. I've used it in many apps over the last few years without noticing any effects on other port bits.
Sorry.
It was only my assumption, that it works similar to registers with interrupt flags.
I have it never used until now.
Peter |
|
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 12:38 PM |
|


Joined: Jun 15, 2008
Posts: 1762
Location: North Carolina USA
|
|
|
lfmorrison wrote:
The OP couldn't have used the SBI method because he claimed he wanted a generic function that takes a variable btimask as an argument, and SBI cannot operate on a runtime variable bit. Anyway, the direct assignment to PINx is still smaller and faster than the OP's initial Exclusive OR method, thus preferable.
I had to use |= to get C to generate the SBI. It turned out to be the same number of instructions as the OUT since LED1=1 was already in a register. Is there some clean way to force the SBI in C?
Quote:
However, many of the AVRs that have been released since about the same timeframe as the introduction of the alternate PIN register function, around 2004, have fixed the bug in the SBI and CBI instructions so that they truly operate only on the single bit specified in the instruction. There ought to be a footnote at the end of each AVR data sheet's register summary section that will indicate whether or not the bug is present in that particular part.
A compiler warning would be nice too, might save a lot of hair-pulling. |
|
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 01:00 PM |
|


Joined: Jul 18, 2005
Posts: 62365
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
Quote:
Is there some clean way to force the SBI in C?
hat surely depends on your C compiler? I happen to know (with optimisation enabled) that GCC will always use SBI/CBI when it can (I/O 0x00..0x1F, single bit changing) but maybe not all C compilers do this? |
_________________
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 06:17 PM |
|

Joined: Nov 17, 2004
Posts: 13852
Location: Vancouver, BC
|
|
In CodeVision:
Code:
PINB.0 = 1;
would do it. |
_________________ Regards,
Steve A.
The Board helps those that help themselves.
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 09:16 PM |
|


Joined: Feb 19, 2001
Posts: 25923
Location: Wisconsin USA
|
|
As would PINB |= 1; -- >>if<< PINB is down low.
Even I am starting to get away from the CV bit notation, as the new generation has few registers down low and not all ports on large chips. |
|
|
| |
|
|
|
|
|
Posted: Sep 12, 2009 - 09:49 PM |
|


Joined: Mar 27, 2002
Posts: 18598
Location: Lund, Sweden
|
|
|
Quote:
Even I [Twisted Evil] am starting to get away from the CV bit notation
What is the world coming to..  |
|
|
| |
|
|
|
|
|