Toggle a Single PORT Bit on / off ? alternative ?

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I tried to toggle port Bit On / off using

PORTD |= (1<<PORTD7);

PORTD |= (0<<PORTD7);

but its not working.

Searching more i found

PORTD |= _BV(PD7);

PORTD &= ~_BV(PD7);

Which seems to be working...

is there any other way out to toggle individual port ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Yes. Read the data sheet of your AVR.

Modern AVRs allow:

PIND = _BV(PD7);   //note this = and not |=

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you're after "toggle" as in "change the pin to the opposite state of what it is now" then you want the "exclusive-or" operator

PORTD ^= _BV(PD7); 

As david hints, modern AVRs have this implemented in hardware: You write a 1 to the appropriate bit in the PIND register when the pin is an output.

I would consider coding this with the C exclusive-or operation as I did above, and check that the compiler actually generates the single SBI opcode for it. This makes the code portable to non-modern AVRs, preventing a possibly very confusing bug hunt.

If by "toggle" you actually mean "set pin to 1" and "set pin to 0" then these (that you mentioned above) are good:

PORTD |= _BV(PD7);
PORTD &= ~_BV(PD7);

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

and check that the compiler actually generates the single SBI opcode for it.

Do you know of any AVR compiler that actually does that? That is it converts:

PORTB ^= (1 << 5);

to

PINB = (1 << 5);

I don't believe any of them perform that particular parlour trick. I would love to be proved wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Darn..

No, I never have investigated this myself.

If the compiler does not do this, then it will be necessary (at least in some cases) to take height for the non-atomicity of the operation. Now some of the point with keeping it tidy (C-wise)(IMO) is lost.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I don't believe any of them perform that particular parlour trick. I would love to be proved wrong.
That would tend to violate rules surrounding volatile access, wouldn't it? I'd guess that if you write:
PORTB ^= (1<<5)

... the compiler is obliged to touch PORTB. It is an feature of the hardware, not the language, which allows you to substitute PINx for PORTx.

Of course the same could be argued regarding the behaviour that replaces:

in  r24, PORTB
ori r24, (1<<5)
out PORTB, r24

... with:

sbi PORTB, 5

... in this case the result is exactly the same: bit 5 in PORTB is set. There are no side-effects to reading (or not reading) PORTB, nor in writing (or not writing) the adjacent bits.

In the case of toggling:

ldi r25, (1<<5)
in  r24, PORTB
eor r24, r25
out PORTB, r24

... replaced with:

ldi r24, (1<<5)
out PINB, r24

... again the result is exactly the same: bit 5 in PORTB is toggled. Again, there are no side-effects.

The difference in the latter case is that a different I/O register is accessed. I expect this is far enough outside the constraints of volatile access for it to be excluded. Given the lack of any side-effects however, it does seem a safe optimisation to make. Do you think a feature request will be well-received? ;)

JohanEkdahl wrote:
If the compiler does not do this, then it will be necessary (at least in some cases) to take height for the non-atomicity of the operation. Now some of the point with keeping it tidy (C-wise)(IMO) is lost.
True, but there is always atomic.h:
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { PORTB ^= (1<<5); }

The down-side is that the ATOMIC_BLOCK wrapper can be dropped if the body of the bock is reduced to a single machine instruction. Another feature request? ;)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Do you think a feature request will be well-received?

I'd have to hunt out the prior threads, but IMO/IME "much ado about nothing".

In any real app,how often do you actually toggle a port bit for important purposes? I tend to only use it for relatively trivial ends, such as toggling a port pin in an ISR for 'scope/LED indication.

IMO save the effort for more important things.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now some of the point with keeping it tidy (C-wise)(IMO) is lost.

IME as these operations come up rarely...

#define TOGGLE_STATUS_LED() (PINB=1<<5)

...looks like

   TOGGLE_STATUS_LED();

in the code. One can always make a more elaborate macro.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
IMO save the effort for more important things.
Agreed. I thought the smilie gave it away...

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
In any real app,how often do you actually toggle a port bit for important purposes? I tend to only use it for relatively trivial ends, such as toggling a port pin in an ISR for 'scope/LED indication.
I've done it, in assembly, for high-speed bit-banging.
That said, if one really wants:
#define toggle(p, mask) \
    if(HAS_PIN_TOGGLE) PIN ## p =(mask); else PORT ## p ^=(mask);

#define atomic_rs_toggle(p, mask) \
    if(HAS_PIN_TOGGLE) PIN ## p =(mask); \
    else { \
        char scratch; \
        asm (" IN __tmp_reg__, __SREG__\n" \
             " CLI\n" \
             " IN #[scratch], #[port]\n" \
             " XOR #[scratch], #[mask]\n" \
             " OUT __SREG__, __tmp_reg__\n" \
             " OUT #[port], #[scratch]" : [scratch] "=r"(scratch) : \
                 [port] "I" (_SFR_IO_ADDR( PORT ## p )), \
                 [mask] "r" ((char)(mask)) ); \
    }

#define atomic_fo_toggle(p, mask) \
    if(HAS_PIN_TOGGLE) PIN ## p =(mask); \
    else { \
        asm( " CLI\n" \
             " IN __tmp_reg__, #[port]\n" \
             " XOR __tmp_reg__, #[mask] \
             " SEI\n" \
             " OUT #[port], __tmp_reg__\n" : :
                 [port] "I" (_SFR_IO_ADDR( PORT ## p )), \
                 [mask] "r"((char)(mask)) ); \
    }

'Tis up to the user to #define HAS_PIN_TOGGLE .

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Asking - you need to read the bit manipulation tutorial methinks. See the tutorial section.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
'Tis up to the user to #define HAS_PIN_TOGGLE .
Now that might be worth a feature request. How hard can it be for AVR Libc to include a single macro in the header files for devices which support it?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

#define TOGGLE_STATUS_LED() (PINB=1<<5)

Arghhhh...! A MACRO! My eyes, my eyes...! :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I've done it, in assembly, for high-speed bit-banging.

Indeed. then in your bit-bang loop you are well aware of PINx writes for the feature. In fact, you probably tackled the whole thing with the knowlege of the single-instruction toggle. Just do it. (A corollary is bit-banging e.g. I2C -- the bit is turned on/off by manipulating the DDRx bit, not the PORTx bit.)

Quote:

How hard can it be for AVR Libc to include a single macro in the header files for devices which support it?

[flame bait] What AVR8 apps designed fresh in the last 10 years or so use an AVR8 model >>without<< that feature?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What would be the point of a one line macro anyway? What? Something like:

#define TOGGLE(p, n) PIN ## p = _BV(n)

or similar? Why wouldn't the programmer just write:

PIND = (1 << 3);

rather than:

TOGGLE(D, 3);

What does the macro actually gain you in this circumstance? It's a bit like these:

sbi(PORTD, 5);

macros that people use. How does that really help compared to:

PORTD |= (1 << 5);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
What does the macro actually gain you in this circumstance?
It can be changed to this for older devices:
#define TOGGLE(p, n) PORT ## p ^= _BV(n)

... or something similar to skeeve's suggestions for atomicity.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I over-simplified the macros for ease of exposition.
Instead of ## I would invoke the paste macro

#define xpaste(x, y) x ## y
#define paste(x, y) xpaste(x, y)

That way one could do something like

#define LCD_LETTER B
#define LCD_BIT 3
...
toggle(LCD_LETTER, 1<<LCD_BIT);

even

#define toggle2(x) toggle(paste(x, _LETTER, 1<<paste(x, _BIT))
...
toggle2(LCD)

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods