Changing the state of only one bit in C

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

We were tasked with writing a code in C for a scenario where a toggle button was connected to the PortD Pin 0 and an LED was connected to the PortB Pin 7. The microcontroller is internally pulled up, if the toggle button is pressed once, the LED turns off or on depending what its previous state was. The code I wrote for this in C is as follows:

#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h> 
main()
{
    while(1)
    {
        DDRD& = 0b11111110;
        DDRB| = 0b00000001;
        a = PIND;
        switch(a&0b00000001)
            case 0: //if the switch satement is 0, toggle the LED
                PORTB = ~(PORTB);
            case 1: //if the switch statement is 1, do nothing
                PORTB &= 0b11111111;
    }
}

Two things elude me from this task that was given to us. First, how do I toggle only the one bit. In my cases, I seem to be toggling all the pins instead of just Pin 7. Second, the frequency of the microcontroller is 16MHz which means that it will run this over and over again. How can I get its state to remain the same until the button is pressed again?

This is my first time posting in a forum such as this, please guide me and pardon me if I accidentally break any rules. I tried to make it as formal and as compliant as I could.

 

Attachment(s): 

This topic has a solution.
Last Edited: Thu. Nov 1, 2018 - 06:14 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
PORTB |= (1 << 5);        // set only bit 5 - all others remain unchanged
PORTB &= ~(1 << 5);       // clear only bit 5 - all others remain unchanged
PORTB ^= (1 << 5);        // toggle only bit 5 - all others remain unchanged

 

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

Cliff,

 

 Is the ^= operator GCC specific?

 

A.

 

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

andrew99 wrote:

Cliff,

 

 Is the ^= operator GCC specific?

 

A.

 

Standard C bitwise operator.

#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

andrew99 wrote:
Is the ^= operator GCC specific?

I'm surprised that you haven't pulled out your favourite C reference manual and looked up the chapter on operators when you had this question.

 

Also see https://www.avrfreaks.net/forum/...

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

No, it's not gcc specific. It's just "exclusive or", a standard binary operation.

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

Aithorusa wrote:
The microcontroller is internally pulled up,

 

Not the way you have it programmed.  You need to activate the pull up resistors by writing to the PORT register.

 

Also, you will want to make some sort of one shot(state machine) so as not to activate the toggle when teh button is pressed.  Keep in mind that teh AVR is running faster than your finger can press/release the button so its possible that your LED may not remain in the state you think it should.

 

While I am at it, and before someone mentions the "D" word, regarding your pushbutton, (Google debounce).  Since your project is very simple, one button setup you can sit in a loop until the button is pressed, then wait a few milliseconds, then read the port/pin and act accordingly.

 

HINT:  Use a WHILE() to wait for the button to be pressed.  Use _delay_ms() for the delay.  Use Cliffs suggestion of the XOR to toggle the bit.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

To reliably toggle the LED only once each time the button is pressed, you need to introduce the concepts of time and previous button state into the program.  To look for changes in the button state, do something like this

 

bool button_was_pressed = FALSE;
...
if (button_pressed && !button_was_pressed)
{
    // button just pressed code here
}
button_was_pressed = button_pressed;

I've deliberately left this vague, and there are plenty of other ways to write it, but it gets the concept across.

 

Now, when the button is pressed, your LED toggle code will execute so fast that you might see the button mechanical bounces as new presses, but they are not new presses.  The solution is to let the button state stabilize before you resume looking at the button input.  A typical stabilization time for most buttons might be 10 or 20 ms.  Then you have something like

 

bool button_was_pressed = FALSE;
...
if (button_pressed && !button_was_pressed)
{
    delay_10_ms();  // let button finish bouncing
    // you can optionally check again here to be sure button is pressed
    // button just pressed code here
}
button_was_pressed = button_pressed;

There can be a lot more involved, in particular finding ways of not wasting 10 ms doing nothing in a delay when the processor could execute tens of thousands of instructions to do useful work in that time, but that's a more advanced chapter.

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

theusch wrote:

andrew99 wrote:
Is the ^= operator GCC specific?

I'm surprised that you haven't pulled out your favourite C reference manual and looked up the chapter on operators when you had this question.

 

Also see https://www.avrfreaks.net/forum/...

As this more looks like a school project, I am more shocked that not even these simple basic things are not longer thought before the students are thrown in to the deep end to have a go at the real thing.

Or in the other case like I had in my classes the OP decided that it was more fun sitting in the pub, rather than attending classes as questions could ( back then) also be asked during the practical hours.......

 

advice to the OP buy a good reference book on C and let that be your first line of questions, specially on these basic things. It will for sure come in handy.

I graduated 20 years ago and still use the school books as a reference. Or books I bought long long time ago, like the C bible from K&R.

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

meslomp wrote:
As this more looks like a school project,
Eh? It was andrew99 not OP that was asking?

 

BTW I will make the usual point that for "old" AVRs the only way to toggle was using ^. This does a read-modify-write on the register where the "modify" is an "exclusive or" (aka "flip the bis") but modern AVRs have a facility in hardware where a write to the PIN register of a port that is output will toggle the bits. So one could actually use:

PORTB ^= (1 << 5); // old style AVR
PINB = (1 << 5); // new style AVR

"new style AVR" basically means anything delivered from about 2005 onwards.

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

Lee,

 

 Typically, I would have reached for my K&R before asking. Currently

it sits in a storage unit 10 miles away. ( In the middle of a business move. )

 

 I Googled it as well. Maybe I should have spent longer than two minutes looking.

 

My bad.

 

A.

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

clawson wrote:

meslomp wrote:
As this more looks like a school project,
Eh? It was andrew99 not OP that was asking?

 

BTW I will make the usual point that for "old" AVRs the only way to toggle was using ^. This does a read-modify-write on the register where the "modify" is an "exclusive or" (aka "flip the bis") but modern AVRs have a facility in hardware where a write to the PIN register of a port that is output will toggle the bits. So one could actually use:

PORTB ^= (1 << 5); // old style AVR
PINB = (1 << 5); // new style AVR

"new style AVR" basically means anything delivered from about 2005 onwards.

Sorry about that. I read the first couple of posts and was interrupted multiple times while reading the rest. Still think the OP has a school project on his hands though and they started without the minimum needed knowledge to even start the task.

Wonder if teachers start being more lazy, students getting dumber ( that is a for sure), or things are going totally wrong in the schools at all.

 

Did not know about the PINB feature being present. Is that something that will have been written in the datasheet if it is a posibility?

It is a section that I normally do not bother about, but it might be interesting.

Also then wonder if when this is used on a processor that does not have the feature, will the compiler warn you about it? (I do know that will be an interesting check to add to the compilation process, but as PINn is an output register using it as input could trigger a warning.

For instance you take some new code written recently using the PINB write and then all of the sudden you have to fall back to an ancient processor and re-use that piece of code. This would be something easily missed during checks and will cause a hell of a search in trying to find what is going wrong

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

meslomp wrote:
Did not know about the PINB feature being present. Is that something that will have been written in the datasheet if it is a posibility?
Unless Atmel thought the engineers using AVrs were psychic they'd have to mention something wouldn't they? cheeky

 

An old school AVR (mega16) has this:

 

 

A new style (about 2005- onwards) AVR has this:

 

 

So if the next section after "Configuring the Pin" is "Toggling the Pin" then the AVR has that feature.

meslomp wrote:
Also then wonder if when this is used on a processor that does not have the feature, will the compiler warn you about it?
How could the compiler possibly know? If you look at the register/bit definitions in any compiler for mega16 versus mega328p I don't think you will see any difference so there's no clue that the hardware might have this "hidden" feature. It's true the datasheet for "old school" has:

So in theory the bits in PINx are "read only" but there's nothing in the register definition to impose this. Actually for "PIN" they could have added "const" to the ones that cannot be written then the compiler would have thrown an error but I don't think any compilers define things this ways - certainly not in the headers for avr-gcc. 

 

To make code that could work both ways you'd need something like:

#ifdef __AVR_ATmega16__
    // old school
    PORTB ^= (1 << 5);
#elif defined(__AVR_ATmega328P__)
    // new style
    PINB = (1 << 5);
#endif

But the whole point of using "PIN" is to get atomicity (rather than an RMW) and these lines represent one of each - so not good really as behaviour is potentially different.

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

Cliff,

thanks for the explanation.

I would not have been surprised if it would have been sneaked in somewhere in a single sentence in a broader description. But this is a clear indicator that it is well thought off.

 

Well if the compiler would throw an error when this readonly register is written, that would at least indicate to the programmer that something is wrong. 

In the new case indeed you can toggle the pin anatomically, but in the old situation it is just simply impossible, so you get the programmer to think about it I guess by atleast warning in that case such that he can take action up front rather than after a days debuggin and complaining that a pin is not doing what it is supposed to do ( as the feature is no longer there on the older processor.  

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

meslomp wrote:
In the new case indeed you can toggle the pin anatomically, but in the old situation it is just simply impossible, so you get the programmer to think about it I guess by atleast warning in that case such that he can take action up front rather than after a days debuggin and complaining that a pin is not doing what it is supposed to do ( as the feature is no longer there on the older processor.

Moderation in all things. -- ancient proverb

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

Anyone heard from the OP?

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

Jim,

 

I can not recall that the post had a solution mark yesterday when I looked.

Now it has Cliffs thread marked as the solution. Might have missed it, but it might indicate that he at least take the time to mark the thread, but has not made any new replies.