Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
joandunning
PostPosted: Sep 11, 2009 - 11:40 AM
Wannabe


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?
 
 View user's profile Send private message  
Reply with quote Back to top
lfmorrison
PostPosted: Sep 11, 2009 - 11:52 AM
Raving lunatic


Joined: Dec 08, 2004
Posts: 4722
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.
 
 View user's profile Send private message  
Reply with quote Back to top
ADI8421
PostPosted: Sep 11, 2009 - 12:16 PM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
dak664
PostPosted: Sep 11, 2009 - 12:20 PM
Posting Freak


Joined: Jun 15, 2008
Posts: 1977
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
 
 View user's profile Send private message  
Reply with quote Back to top
ADI8421
PostPosted: Sep 11, 2009 - 12:42 PM
Rookie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
lfmorrison
PostPosted: Sep 11, 2009 - 01:10 PM
Raving lunatic


Joined: Dec 08, 2004
Posts: 4722
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.
 
 View user's profile Send private message  
Reply with quote Back to top
dak664
PostPosted: Sep 11, 2009 - 01:48 PM
Posting Freak


Joined: Jun 15, 2008
Posts: 1977
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
 
 View user's profile Send private message  
Reply with quote Back to top
sternst
PostPosted: Sep 11, 2009 - 01:59 PM
Raving lunatic


Joined: Jul 23, 2001
Posts: 2728
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
 
 View user's profile Send private message  
Reply with quote Back to top
ADI8421
PostPosted: Sep 11, 2009 - 02:15 PM
Rookie


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!
 
 View user's profile Send private message  
Reply with quote Back to top
danni
PostPosted: Sep 11, 2009 - 02:52 PM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2616


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 Exclamation

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
 
 View user's profile Send private message  
Reply with quote Back to top
joandunning
PostPosted: Sep 11, 2009 - 03:40 PM
Wannabe


Joined: Aug 21, 2009
Posts: 57


Works great, my boss is a happy man, thanks!
 
 View user's profile Send private message  
Reply with quote Back to top
sternst
PostPosted: Sep 11, 2009 - 03:44 PM
Raving lunatic


Joined: Jul 23, 2001
Posts: 2728
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
 
 View user's profile Send private message  
Reply with quote Back to top
theusch
PostPosted: Sep 11, 2009 - 03:47 PM
10k+ Postman


Joined: Feb 19, 2001
Posts: 29245
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
 
 View user's profile Send private message  
Reply with quote Back to top
lfmorrison
PostPosted: Sep 11, 2009 - 04:37 PM
Raving lunatic


Joined: Dec 08, 2004
Posts: 4722
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 Exclamation

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.
 
 View user's profile Send private message  
Reply with quote Back to top
danni
PostPosted: Sep 12, 2009 - 09:38 AM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2616


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. Embarassed
It was only my assumption, that it works similar to registers with interrupt flags.

I have it never used until now.


Peter
 
 View user's profile Send private message  
Reply with quote Back to top
dak664
PostPosted: Sep 12, 2009 - 12:38 PM
Posting Freak


Joined: Jun 15, 2008
Posts: 1977
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.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Sep 12, 2009 - 01:00 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71821
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?

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Koshchi
PostPosted: Sep 12, 2009 - 06:17 PM
10k+ Postman


Joined: Nov 17, 2004
Posts: 15123
Location: Vancouver, BC

In CodeVision:
Code:
PINB.0 = 1;

would do it.

_________________
Regards,
Steve A.

The Board helps those that help themselves.
 
 View user's profile Send private message  
Reply with quote Back to top
theusch
PostPosted: Sep 12, 2009 - 09:16 PM
10k+ Postman


Joined: Feb 19, 2001
Posts: 29245
Location: Wisconsin USA

As would PINB |= 1; -- >>if<< PINB is down low.

Even I Twisted Evil 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.
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Sep 12, 2009 - 09:49 PM
10k+ Postman


Joined: Mar 27, 2002
Posts: 22264
Location: Lund, Sweden

Quote:

Even I [Twisted Evil] am starting to get away from the CV bit notation

What is the world coming to.. Wink
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits