Best way to set or clear a single bit in a control register

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

I'd like to manually reset the TOV0 bit for timer0 in an ATMega6450.  The datasheet says to write a one to it.  It seems to me the way to do this without affecting other bits is 

 

TIFR0 |= 0x01

or

TIFR0 |= (1 << TOV0)

Not

TIFR0 = 0x01

since the latter would overwrite other bits in the register.  This discussion, however, maintains the opposite.  Which approach is preferred, and why?  Thanks.

 

* Title adjusted for clarity. *

 

Last Edited: Mon. Mar 13, 2017 - 11:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lautman wrote:
TIFR0 |= (1 << TOV0)
  This will read the register and OR the value with the TOV0 bit, and write it back. Any other bits in the register that had been set will be cleared since writing 1 to them clears them...

 

Edit: writing 0 to the other bits has no effect in this register so TIFR0 = (1 << TOV0) works correctly.

David

Last Edited: Wed. Mar 8, 2017 - 07:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lautman wrote:

I'd like to manually reset the TOV0 bit for timer0 in an ATMega6450.

 

T0VO is in the register at address 0x15 so it can be reached with SBI and CBI. Is your compiler clever enough to use that?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

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

lautman wrote:
the latter would overwrite other bits in the register.

Read the register description more carefully.

 

This particular register does not work like a memory location - where you "store" a value.

 

With this particular register, it is the act of writing a '1' to a bit in the register which clears the associated flag.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr:  > Any other bits in the register that had been set will be cleared since writing 1 to them clears them

 

But the OR function would only OR a 1 to the specified bit, and 0s everywhere else, leaving 1s as 1s and 0s as 0s.  Particularly since it takes writing a 1 to clear a 1, why would any other set bits be cleared.

 

awneil:  > it is the act of writing a '1' to a bit in the register which clears the associated flag.

 

Aah, that's right.  But why does that make = better than |= ?  Seems to me the result would be the same.

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

lautman wrote:
But why does that make = better than |= ?  

Because you do not want to "accidentally" write any ones to any other bits.

 

You are not writing values to bits - you are triggering the hardware to clear a bit.

 

Remember: writing a '1' clears the corresponding flag;  so, with this register, you are not trying to read current values & write them back - which is what |= does.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lautman wrote:
Aah, that's right. But why does that make = better than |= ?

Your question/thread is one of the oldest recurring themes on AVRfreaks.  ;)  [I'll guess that with enough digging I can find a 2001 thread...]

 

The sparkies have provided the reasons why the design is done this way.  I forget the reasoning.  Remember that early AVR generations had RMW concerns with even "atomic" operations such as SBI and CBI.

 

 

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

Think about what |= actually does.  It first reads the register, performs an OR operation of the value with the bit you're trying to clear (0x01 in this case), then writes it back.  Everything's fine if only the one bit is set, but what happens if you have, say, bit 2 set as well?  First it reads 0b00000011, performs the OR with 0x01 which doesn't change anything, then writes back 0b00000011, clearing both bits.  Whoops, you just cleared the output compare flag too.

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

The translation of IOreg|=onebitmask and IOreg&=~onebitmask
into SBI and CBI instructions is of debatable validity.
My side of the debate is that it is not valid
(unless covered by the as-if rule),
but in most cases, the compiler should do it anyway.
"Most cases" excludes PINx and interrupt flag registers.
I'm not convinced that the compiler should translate such cases at all.
The as-if rule does not apply.

 

The semantics of PINx and interrupt flag registers
presents logical difficulties for compilers.
Some things cannot be done at all, e.g. flags := 0xFF.
Some things can be done, e.g. flags := 0,
but would require mechanisms different  from that of nearby targets.
avr-gcc (and I suspect other compilers) "deal" with the issue by ignoring it.
The "method" fails on IOreg|=onebitmask and IOreg&=~onebitmask .
The result depends on the level of optimization.
RMW |= will clear all the interrupt flags in a register.
On recent processors, SBI will clear just one.
RMW &=~ will clear all the interrupt flags except the one in the mask.
On recent processors, CBI will have no effect.
To me, that is proof the compiler does it wrong.
The semantics of PINx registers presents similar problems.

 

In conclusion, do not use |= or &= to affect
single bits of interrupt flag or PINx registers.

Using them to affect multiple bits will probably not do what you want either,

but the result is predictable.

 

BTW just to make things more interesting,
some registers have both ordinary bits and interrupt flags.

 

 

 

Iluvatar is the better part of Valar.

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

The OP should be aware that interrupt flag bits are SPECIAL. There are only a few cases (timers, I think) where a whole register is devoted to interrupt flags. The particular register you are referring to is one where all of the bits are interrupt flag bits. That means that the register is managed in a very different way from generic I/O registers.

 

For generic I/O registers, during operation, you set a bit using REG |= bitmask;, where bitmask MIGHT be (1<<bitnumber)|(1<<bitnumber)|...  and you similarly clear a bit with REG &= ~bitmask;. When you are doing initial configuration of a register, then you usually want to explicitly set or clear all of the bits in the register, so you do REG = bitmask;. Its not a matter of which is "better". It is a matter of which operation does what you need.

 

For interrupt flag registers, you CLEAR interrupt flags by writing a ONE to the corresponding bit. Writing a ZERO to an interrupt flag bit does nothing. That is just the way it works. So, for this special kind of register, you CAN do REG = bitmask; That writes ones to the bits in the bitmask and zeros to all other bits. Since these are interrupt flag bits, writing those zeros does not do anything. Again, it is not a matter of which is "better" - its a matter of which does what you want to do.

 

There is a third case: when there is an interrupt flag bit in a register occupied by various other control bits. UART is a peripheral that does this. In this case, you must treat the register like a generic I/O register, taking care NOT to alter those other bits when you clear an interrupt flag bit. In this case, you do the basic REG |= bitmask; For interrupt flag bits, you NEVER need to do REG &= ~bitmask because you set that bit to clear it. 

 

Now, there are some additional comments about how those operations work, "under the hood". Those comments can be useful but tend to obscure this particular case of dealing with interrupt flag bits.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Fri. Mar 10, 2017 - 05:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve and I and others have been here not too long ago...

https://www.avrfreaks.net/comment...

 

skeeve wrote:
In conclusion, do not use |= or &= to affect single bits of interrupt flag or PINx registers.

Right (IMO).  I'd think straight assignment is what the chip designers had in mind.

 

[the other thread goes into AVR C compilers generating an SBI if it is a single-bit assignment.  On old models, that is a RMW with side effects.  I'd think that is fairly moot as those models would rairly be used in new development.]

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

Correct, PIN registers are another case of registers with special behavior. You need to read up on them before you start writing to them!

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

This should look familiar to some:

// will fail at assembly time if reg or bit are out of range
// or bit and &reg are not compile-time constants
#define sbi1(reg, bit)  asm(" sbi %0, %1" :: "I"(_SF_IO_ADDR(reg)), "I"(bit))

// will fail at assembly time or run time [1]
// if the condition is not evaluated at compile time
// needs optimization
#define writeonetospecial(reg, bit)  do {         \
        if(_SF_IO_ADDR(reg)<=31) sbi1(reg, bit) ; \
        else                     reg=1<<(bit);    \
    } while(0)

// [1] Failure at run time means that the code,
// *including calculation of the condition*, will run.
// Not exactly a success.
// That the else code is there does not help matters

 

How should the compiler translate "PORTC=0x80" on an atmega168?

Iluvatar is the better part of Valar.

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

skeeve wrote:
How should the compiler translate "PORTC=0x80" on an atmega168?

Do you mean PINC?

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:
skeeve wrote:
How should the compiler translate "PORTC=0x80" on an atmega168?

Do you mean PINC?

I mean PORTC, though PINC and DDRC present the same issue.  See the atmega168 datasheet.

Iluvatar is the better part of Valar.

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

I think you've stumped me on "today's puzzle".  It is an undefined bit, right? So there is no PC7.  The datasheet indicates read-only.  (probably the same for nearly all "undefined"?  I recall something about "always reads as zero"?)

 

But I can't see that would be the compiler's problem.

 

 

 

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:
I think you've stumped me on "today's puzzle".  It is an undefined bit, right? So there is no PC7.  The datasheet indicates read-only.  (probably the same for nearly all "undefined"?  I recall something about "always reads as zero"?)

 

But I can't see that would be the compiler's problem.

Really?

The assignment says to do something the hardware cannot do at all.

Not only that, the attempt could result in the hardware's version of nasal demons.

I think that technically that makes it invalid C,

but not invalid C that requires a diagnostic.

It seems to me the compiler has three options:

Pretend C7 exists and live with the possibility of nasal demons.

Refuse to compile the assignment.

Compile it as PORTC=0.

I expect avr-gcc and others select the first.

It is the course of least resistance from a situation that ought not arise.

Iluvatar is the better part of Valar.

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

skeeve wrote:
I think that technically that makes it invalid C,

I don't know.  Is the compiler expected to know what lies under the hood?

 

There are many many other unused bits in AVR8 I/O space.  Some are specifically broken out as "reserved".

 

Sometimes one must just "trust the programmer".  This ain't Pascal or Ada.  My two cents.

 

 

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

skeeve wrote:

theusch wrote:
I think you've stumped me on "today's puzzle".  It is an undefined bit, right? So there is no PC7.  The datasheet indicates read-only.  (probably the same for nearly all "undefined"?  I recall something about "always reads as zero"?)

 

But I can't see that would be the compiler's problem.

Really?

The assignment says to do something the hardware cannot do at all.

Not only that, the attempt could result in the hardware's version of nasal demons.

I think that technically that makes it invalid C,

but not invalid C that requires a diagnostic.

It seems to me the compiler has three options:

Pretend C7 exists and live with the possibility of nasal demons.

Refuse to compile the assignment.

Compile it as PORTC=0.

I expect avr-gcc and others select the first.

It is the course of least resistance from a situation that ought not arise.

I think you might be reading too much into the datasheet...After all, if the bit is read only it's just as invalid to write a 0 as it is to write a 1, but obviously it's impossible to avoid doing one or the other. Read only just means that bit is ignored.

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

I think you might be reading too much into the datasheet...After all, if the bit is read only it's just as invalid to write a 0 as it is to write a 1, but obviously it's impossible to avoid doing one or the other. Read only just means that bit is ignored.

Uh...:

"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

skeeve wrote:
Not only that, the attempt could result in the hardware's version of nasal demons.

Any uproar feels a little overdone to me.  See the quote from the last post -- indeed, future models might use that bit.  (in skeeve's example of PC7, that would be e.g. Tiny48 family right?)  But nothing about releasing the nasal demons.

 

Reserved addresses -- perhaps.  I suppose an "undefined" register could unleash some type of test-sequence response.

 

If you think it might be a problem, THEN DON'T WRITE YOUR CODE TO DO THAT.  An oft-used excerpt from the bible:

And if your eye offend you, pluck it out, and cast it from you: it is better for you to enter into life with one eye, rather than having two eyes to be cast into hell fire.

In C, you can create all kinds of uproar when you use a null pointer.  And you should code with proper precautions.  That doesn't mean the compiler should stop you.

In C, you can create all kinds of uproar when you index outside of array limits.  And you should code with proper precautions.  That doesn't mean the compiler should stop you.

...

 

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

As noted, the described situation ought not occur,

but that alone is not what makes the discussion a bit silly.

What makes it silly is that the situation is unlikely to occur,

even to careless programmers.

Unlike the the PINx and interrupt flag issues,

whether "PINC=0x80;" is valid C does not really need answering.

Iluvatar is the better part of Valar.

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

I don't understand why you're linking that datasheet with that highlighted? It specifically says "for compatibility with future devices". In other words, it might have unintended effects in a future device, but it's meaningless on this device.

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

?

I don't understand why you're linking that datasheet with that highlighted? It specifically says "for compatibility with future devices". In other words, it might have unintended effects in a future device, but it's meaningless on this device.

if a future device with things added you could enable something, if you want this program to work on org. and new devices it will work if you write 0's as #20 show.

 

 

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

I don't understand why you're linking that datasheet with that highlighted? It specifically says "for compatibility with future devices". In other words, it might have unintended effects in a future device, but it's meaningless on this device.

That is not what it says.

 

There are two points made, which is why I highlighted them with different colours.

 

1) When writing to an address containing reserved bits, always ensure those bits are written as zero.  This is 'for compatibility with future devices'.

2) Never write to reserved addresses.  Period.  No qualification.

"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 don't know if I missed something in another post, but in the post of mine you quoted I wasn't referring to reserved addresses at all. I was specifically referring to the unused bit 7 in PORTC on an atmega168. Reserved addresses don't come into play there, or at least I should hope PORTC isn't broken out as a reserved address; makes the use of PORTC rather difficult I should think!

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

I don't know if I missed something in another post, but in the post of mine you quoted I wasn't referring to reserved addresses at all. I was specifically referring to the unused bit 7 in PORTC on an atmega168. Reserved addresses don't come into play there, or at least I should hope PORTC isn't broken out as a reserved address; makes the use of PORTC rather difficult I should think!

 

You said:

After all, if the bit is read only it's just as invalid to write a 0 as it is to write a 1

Which is not true, because:

 

 

The datasheet is quite explicit.  Reserved bits can be written, and when doing so they should be written as zero.

 

The remaining sentence in that paragraph applies to reserved addresses, not reserved bits in occupied addresses, which is why I highlighted it separately.  I understand that you weren't referring to reserved addresses, but it bears mentioning.

 

"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

By the way, the title for this thread is somewhat confusing.

Four legs good, two legs bad, three legs stable.

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

John_A_Brown wrote:

By the way, the title for this thread is somewhat confusing.

 

Suggest a "perfect" one and I will change it... with the OP's agreement.

 

Ross McKenzie ValuSoft Melbourne Australia

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

If we read an "only" into the beginning of "For compatibility with future devices",

it settles the matter of whether "PORTC=0x80;" is valid C.

It is.

It might be unwise, but it will not cause an atmega168 to emit nasal demons.

 

I really hadn't thought so much energy would have been devoted to it.

Iluvatar is the better part of Valar.

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

valusoft wrote:

John_A_Brown wrote:

By the way, the title for this thread is somewhat confusing.

 

Suggest a "perfect" one and I will change it... with the OP's agreement.

 

How about changing "set" to "reset" or "clear"?

 

Four legs good, two legs bad, three legs stable.