[TUT] [C] Bit manipulation (AKA "Programming 101")

Last post
225 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The following text is a slightly reformatted version of a post made by Eric quite a while ago. It has been referred to by so many users (it's now the post for bit-manipulations), and has helped so many that I have reposted it here so it is more easily found.

Programming 101 - By Eric Weddington

To really understand what's going, it's best to learn C languages bit operators and about truth tables.

    | bit OR & bit AND
    ~ bit NOT
    ^ bit EXLUSIVE OR (XOR)
    << bit LEFT SHIFT
    >> bit RIGHT SHIFT

These operators work on bits and not logical values. Take two 8 bit bytes, combine with any of these operators, and you will get another 8 bit byte according to the operator's function. These operators work on the individual bits inside the byte.

A truth table helps to explain each operation. In a truth table, a 1 bit stands for true, and a 0 stands for false.

The OR operation truth table:

    0 OR 0 = 0 0 OR 1 = 1
    1 OR 0 = 1
    1 OR 1 = 1

The AND operation truth table:

    0 AND 0 = 0 0 AND 1 = 0
    1 AND 0 = 0
    1 AND 1 = 1

The XOR operation truth table:

    0 XOR 0 = 0 0 XOR 1 = 1
    1 XOR 0 = 1
    1 XOR 1 = 0

The NOT operator inverts the sense of the bit, so a 1 becomes a 0, and a 0 becomes a 1.

So let's say I have a byte foo that is initialized to 0:

unsigned char foo = 0;

To set bit 0 in foo and then store the result back into foo:

foo = foo | 0x01;

The OR operation is used between the variable that we want to change and a constant which is called a BIT MASK or simply the MASK. The mask is used to identify the bit that we want changed.

Remember that we write the constants in hexadecimal because it's shorter than writing it in binary. It is assumed that the reader knows how to convert back and forth between hex and binary. ;-)

Usually, though the statement is made shorter in real programming practice to take advantage of C's compound assignment:

foo |= 0x01;

This is equivalent to the statement above.

To clear bit 0 in foo requires 2 bit operators:

foo = foo & ~0x01;

This uses the AND operator and the NOT operator. Why do we use the NOT operator? Most programmers find it easier to specify a mask wherein the bit that they are interested in changing, is set. However, this kind of mask can only be used in setting a bit (using the OR operator). To clear a bit, the mask must be inverted and then ANDed with the variable in question. It is up to the reader to do the math to show why this works in clearing the desired bit.

Again, the statement is made shorter with a compound assignment:

foo &= ~0x01;

To see if a bit is set or clear just requires the AND operator, but with no assignment. To see if bit 7 is set in the variable foo:

if(foo & 0x80)
{
}

The condition will be zero if the bit is clear, and the condition will be non-zero if the bit is set. NOTE! The condition will be NON-ZERO when the bit is set. But the condition will not NECESSARILY BE ONE. It is left to the reader to calculate the value of the condition to understand why this is the case.

There is another useful tool that is not often seen, and that is when you want to flip a bit, but you don't know and you don't care what state the bit is currently in. Then you would use the XOR operator:

foo = foo ^ 0x01;

Or the shorter statement:

foo ^= 0x01;

A lot of times the bit mask is built up dynamically in other statements and stored in a variable to be used in the assignment statement:

foo |= bar;

Sometimes, a programmer wants to specify the bit NUMBER that they want to change and not the bit MASK. The bit number always starts at 0 and increases by 1 for each bit. An 8 bit byte has bit numbers 0-7 inclusive. The way to build a bit mask with only a bit number is to LEFT SHIFT a bit by the bit number. To build a bit mask that has bit number 2 set:

(0x01 << 2)

To build a bit mask that has bit number 7 set:

(0x01 << 7)

To build a bit mask that has bit number 0 set:

(0x01 << 0)

Which ends up shifting the constant 0 bytes to the left, leaving it at 0x01.

MACROS

Because there are a number of programmers who don't seem to have a familiarity with bit flipping (because they weren't taught it at school, or they don't need to know it because of working on PCs), most programmers usually write macros for all of these operations. Also, it provides a fast way of understanding what is happening when reading the code, or it provides additional functionality.

Below is a set of macros that works with ANSI C to do bit operations:

#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))

To set a bit:

bit_set(foo, 0x01);

To set bit number 5:

bit_set(foo, BIT(5));

To clear bit number 6 with a bit mask:

bit_clear(foo, 0x40);

To flip bit number 0:

bit_flip(foo, BIT(0));

To check bit number 3:

if(bit_get(foo, BIT(3)))
{
}

To set or clear a bit based on bit number 4:

if(bit_get(foo, BIT(4)))
{
    bit_set(bar, BIT(0));
}
else
{
    bit_clear(bar, BIT(0));
}

To do it with a macro:

bit_write(bit_get(foo, BIT(4)), bar, BIT(0));

If you are using an unsigned long (32 bit) variable foo, and have to change a bit, use the macro LONGBIT which creates un unsigned long mask. Otherwise, using the BIT() macro, the compiler will truncate the value to 16-bits.[/]

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

This was also the basis for excellent an article in Circuit Cellar http://www.dtweed.com/circuitcellar/caj00180.htm#3120
Unfortunately I think you have to pay to get a copy, but it is really worth it.

Smiley

FREE TUTORIAL: 'Quick Start Guide for Using the WinAVR C Compiler with ATMEL's AVR Butterfly' AVAILABLE AT: http://www.smileymicros.com

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

this is also touched on in "AVR035: Efficient C Coding for AVR" http://www.atmel.com/dyn/resources/prod_documents/doc1497.pdf

I based a few macros off of that app note, and created avr035.h:

#ifndef _AVR035_H_
#define _AVR035_H_

// from AVR035: Efficient C Coding for AVR

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

#define SETBITMASK(x,y) (x |= (y))
#define CLEARBITMASK(x,y) (x &= (~y))
#define FLIPBITMASK(x,y) (x ^= (y))
#define CHECKBITMASK(x,y) (x & (y))

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

A recent thread had a very nice solution which extends on the basic bit-manipulation macros. IIRC it went something along the lines of:

Defines:

#ifndef _AVR035_H_
#define _AVR035_H_

// from AVR035: Efficient C Coding for AVR

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

#define SETBITMASK(x,y) (x |= (y))
#define CLEARBITMASK(x,y) (x &= (~y))
#define FLIPBITMASK(x,y) (x ^= (y))
#define CHECKBITMASK(x,y) (x & (y))

#define VARFROMCOMB(x, y) x
#define BITFROMCOMB(x, y) y

#define C_SETBIT(comb) SETBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
#define C_CLEARBIT(comb) CLEARBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
#define C_FLIPBIT(comb) FLIPBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
#define C_CHECKBIT(comb) CHECKBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))

#endif

Use:

#define Status_LED  PORTA, 3

C_SETBIT(Status_LED);
C_CLEARBIT(Status_LED);

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

As an additional item to check if a bit is clear:

if(~(foo) & 0x80)
{
}

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

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

BenG wrote:
As an additional item to check if a bit is clear:

if(~(foo) & 0x80)
{
}

My 1st choice would be for the following which, IMHO, is easier to "read":
if ( ( foo & 0x80 ) == 0 )
{
  ...
}

should result in the same compiler generated code.

Don

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

With regards to the above i'd use

if(!CHECKBITMASK(foo,0x80))

or

if(!CHECKBIT(foo,7))

But i agree Don's code is more readable

/Bingo

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

Another approach:

I like it to access bit variables like any other variables and then I can write:

if(i == 1)
if(i == 0)
i = 0;
i = 1;

which looks easy readable for me.

This can easy be done by casting a portbit as a member of a bit field.

On the attachment there is the definition of the macro SBIT.

Following an example code:

#include 
#include "sbit.h"


#define KEY0            SBIT( PINB, 0 )
#define KEY0_PULLUP     SBIT( PORTB, 0 )

#define LED0            SBIT( PORTB, 1 )
#define LED0_DDR        SBIT( DDRB, 1 )


int main( void )
{
  LED0_DDR = 1;         // output
  KEY0_PULLUP = 1;      // pull up on

  for(;;){

    if( KEY0 == 0 )     // if key pressed (low)
      LED0 = 0;         // LED on (low)
    else
      LED0 = 1;         // LED off (high)
  }
}

Naturally this macro can also be used for internal flag variables, not only for IO registers.

Peter

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 TCCR1B |= (1 << CS10); 

You might want to explain what this means. I didn't realize you could actually left shift a bit to a specific place using this actual name of the bit. It drove me crazy because the timer tutorial was directing me here and your tutorial makes no mention of this.

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

That means, that nr 1 is shifted left as many times as it is needed to reach bit named CS10. Where does the compiler know that CS10 is that? well you give him the AVR's name and it's smart enough to know such things thanks to some smart programmers on the GCC side ;)

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

I think this tutorial is trying to be as generic as possible. Not all the AVR C compilers have all the bit names defined in the header files for each AVR part so

TCCR1B |= (1 << CS10);

won't necessarily work on all compilers.

There's no "magic" to it anyway, a typical part definition file that does include the bit positions simply has something like (for mega16):

#define CS10    0
#define CS11    1
#define CS12    2
#define WGM12   3
#define WGM13   4
#define ICES1   6
#define ICNC1   7

For those C compilers that don't have the bit definitions in their .h collection the AVR Studio file \Program Files\Atmel\AVR Tools\AvrStudio4\xmlconvert.exe will probably prove useful to generate .h files from Atmel's own XML part definition files:

C:\Program Files\Atmel\AVR Tools\AvrStudio4>xmlconvert
xmlconvert: No source file specified

Usage: xmlconvert [-f output-format] [-o outdir] [-1nbclV] infile ...
        Output formats: a[vrasm] | g[cc] | i[ar] | c[c] (generic c)
Options:
        -1  = Don't generate AVRASM2 #pragma's
        -n  = Don't warn about bad names
        -b  = use DISPLAY_BITS attribute to limit bit definitions
        -c  = Add some definitions for compatibility with old files
        -l  = Produce linker file (IAR only)
        -q  = Allow linked register quadruple (32-bit)
        -V  = print xmlconvert version number

Cliff

 

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

Thanks all; I'm going to use most of the above for next yr's class. And another lesson you just taught. Show Dons, BenG, and then Bingos to show writing style and how they accomplish the same thing.
I couldn’t write (copy) the curriculum without this site.
Thanks for the help and making the world a better place,
John

Resistance is futile…… You will be compiled!

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

clawson wrote:
I think this tutorial is trying to be as generic as possible. Not all the AVR C compilers have all the bit names defined in the header files for each AVR part so

TCCR1B |= (1 << CS10);

won't necessarily work on all compilers.

There's no "magic" to it anyway, a typical part definition file that does include the bit positions simply has something like (for mega16):

#define CS10    0
#define CS11    1
#define CS12    2
#define WGM12   3
#define WGM13   4
#define ICES1   6
#define ICNC1   7


Where are the definitions in AVR GCC? I am guessing in the IO header file or each header has every single part defined for only the related registers.

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

Yup in GCC all your programs include and your Makefile will define a MCU=. As a consequence of these two things it will lead to io.h choosing to pull in one of the io????.h files in \winavr\avr\include\avr\

Cliff

 

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

clawson wrote:
Yup in GCC all your programs include and your Makefile will define a MCU=. As a consequence of these two things it will lead to io.h choosing to pull in one of the io????.h files in \winavr\avr\include\avr\

Cliff


I am guessing Avr Studio is handling the making the Makefile when I compile my program. Oddly enough I don't even think I defined the correct MCU when I first created the project. I defined it as a Mega 168 and only figured out that it was a Mega 48 when my Dragon complained that the parts didn't match up. Is this because the parts are in the same data sheet?

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

There's several MAJOR difference between 48 and 168. For example one uses RJMPs for its interrupt vectors and one uses JMPs (because the entire memory is no longer reachable with an RJMP). Far more worryingly the 168 has 1K of SRAM while the 48 has 512 bytes. So the RAMEND used to initialise the stack pointer will be different between the two. I'm therefore astonished that you found that code built for a 168 worked in a 48 !?!

 

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

clawson wrote:
There's several MAJOR difference between 48 and 168. For example one uses RJMPs for its interrupt vectors and one uses JMPs (because the entire memory is no longer reachable with an RJMP). Far more worryingly the 168 has 1K of SRAM while the 48 has 512 bytes. So the RAMEND used to initialise the stack pointer will be different between the two. I'm therefore astonished that you found that code built for a 168 worked in a 48 !?!

Well the code I wrote was only five lines long and didn't use interrupts. Though I might want to make sure that the I actually did program for the 168. I don't have access to the computer I wrote the code in at the moment.

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

Another thing I've just learned, you can toggle the PORTxn pins by writhing to the PINxn register.

So instead of toggling the bits with the XOR operator, you can write to the PINxn address.

Do not know if the compiler optimization already does that?

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

But that only works on the recent AVRs, not all of them.

 

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

Quote:
BenG wrote:
As an additional item to check if a bit is clear:

Code:
if(~(foo) & 0x80)
{
}
My 1st choice would be for the following which, IMHO, is easier to "read":
Code:
if ( ( foo & 0x80 ) == 0 )
{
...
}
should result in the same compiler generated code.

Don

and another way...


if (!( foo & 0x80 )) 
{ 
  ... 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know this is compiler generic, but I do have a related question:

I've been using (1<<PIN_NAME) for some time in my code, but I am working on a project now where the developer is using _BV(PIN_NAME) all over. Is there a best practice on which to use?

Jim

Jim Brain

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

Will this blink an LED?

#inculde 
#inculde 


DDRB = 0b11111111;

while(1)
{
PORTB |= (0x01 << 7);
_delay_ms(250);
PORTB &= (0x00 << 7);
_delay_ms(250);
}

Life Is Like A Bucket Of Chicken.

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

Quote:
Will this blink an LED?

Well, yes, but it isn't doing what you really want.

PORTB |= (0x01 << 7); 

This sets bit 7 (and only bit 7) to 1. This is because it translates into:

PORTB = PORTB | 0b10000000;

So bit 7 is changed, and the rest of the bits remain what they were.

PORTB &= (0x00 << 7);

This sets all bits to 0, not just bit 7.
This is because this translates into:

PORTB = PORTB & 0b00000000;

since 0 shifted by any amount is still going to be 0, and 0 ANDed with anything is 0. You want:

PORTB &= ~(0x01 << 7);

Regards,
Steve A.

The Board helps those that help themselves.

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

Thank You Koshchi,
That is exactly what I wanted. I was going to ask this next:

Quote:

This sets all bits to 0, not just bit 7.
This is because this translates into:

PORTB = PORTB & 0b00000000;

since 0 shifted by any amount is still going to be 0, and 0 ANDed with anything is 0. You want:
Code:

PORTB &= ~(0x01 << 7);

Thanks again,

Life Is Like A Bucket Of Chicken.

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

Taco_Bell wrote:
Will this blink an LED?

#inculde 
#inculde 


DDRB = 0b11111111;

while(1)
{
PORTB |= (0x01 << 7);
_delay_ms(250);
PORTB &= (0x00 << 7);
_delay_ms(250);
}


It would blink a led, but it would be a inefficient way to do so.

This is how I would solve it:

#include 
#include 

int main(void) {
	DDRB = 0xFF;
	
	while(1) {
		PORTB ^= (1 << 7);
		_delay_ms(250);
	}
}

Here I use the XOR operator which is ideal for flipping a bit. Using the 1 XOR as opposed to 1 OR and 1 AND also reduced the code size from 443 bytes (your example) to 398 bytes (my example)

The logic behind it is that on the first run the 8th bit of PORTB, which is 0, is XOR'ed with a 1 which results in a 1.
0 XOR 1 = 1
On the next loop iteration the 8th bit of PORTB, which is now 1, will be XOR'ed with a 1 which will result in a 0.
1 XOR 1 = 0

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

Hello ,
Please excuse my dumb question :
how do I do octet/byte rotation in avrgcc ?
I mean how do I wrote in C the equivalent of asm rol cmd ?
Kind regards,
Daniel

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

I guess the first question is WHY you'd want to. But there's no easy way, you'd need to use something like

topbit = (bytevar & 0x80) ? 0 : 1;
bytevar <<= 1;
bytevar |= topbit;

 

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

First of all thanks for answer.
I will answer why I need to do this : I writing an dmx receiver and I need to read the address of that receiver from a dipswitch pack. Because I was restricted to single side pcb I have inverted the traces to get the routing done.

I will abuse of your kindness and I would like to ask if it is possible to wrote an inline asm macro like :
http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

something like this would work ?
asm volatile("rol %0" : "=r" (value) : "0" (value));

Thanks
Daniel

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

those are my definitions, i find them more user-friendly, tell me what you think.

#define LOW 0
#define HIGH 1

#define INPUT(port,pin) DDR ## port &= ~(1<<pin)
#define OUTPUT(port,pin) DDR ## port |= (1<<pin)
#define CLEAR(port,pin) PORT ## port &= ~(1<<pin)
#define SET(port,pin) PORT ## port |= (1<<pin)
#define TOGGLE(port,pin) PORT ## port ^= (1<<pin)
#define READ(port,pin) (PIN ## port & (1<<pin))

usage:

OUTPUT(D, 3); // port D, pin 3 as output
SET(D, 3); // set port D pin 3 to HIGH
CLEAR(D, 3); // set it to LOW

INPUT(B, 5);
if (READ(B, 5) == HIGH)
...

feel free to add them to the tutorial :)
[updated]

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

Tigrezno,
I am been C ing with AVR for a few months. I am a novice. I got an A/D working with interrupts and putting the data to RS232 to RealTerm on a PC working!
Big accomplishment for me, lots of hours.

I have tried a few of the examples on this topic posted here and ran into problems. Mostly with the fact that PORTs need indirection methods to do bit fiddling? I think?
I just tried your "toggle" on a mega128 PORTA Bit 1 and it compiled and the hardware worked! Thanks, I wanted PORT bit setting and flipping, not just variables. This seems to be the best method discussed here yet? I will try the rest of thems soon.
Where is the #Define documentation for AVR C, or is it per standard C practice and I find it in a C book?
In the DOC directory in my AVR C installation there are many many HTML files. They seem to just compare features to standard C, with no examples? Not much help for a beginner?
Am I missing a manual for this AVR C somewhere?
I didn't know you could use # for parameter subsitution.
Thanks, I put your defines in my first AVR learning C project!
Mike

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

#define is just one of the C Pre Processor functions. The facilities available are standard to most c compilers so this manual for the GCC variant should be as good as any:

http://gcc.gnu.org/onlinedocs/cpp/

 

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

Thanks!

I am looking at it now. Lots there.

The use of the # # to pass those port parameters still confuses me? I'll keep thinkin on it!

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

## is just the concatenation operator in macros. #define is just a text replacement tool, not a C programming construct. So something like:

Quote:
READ(B, 5)

simply gets replaced by:
Quote:
PORTB & (1<<5)

Regards,
Steve A.

The Board helps those that help themselves.

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

Got it! :D

Thanks, this if from the manual Clawson sent the link too! "I wooda never known!"

"This is called token pasting or token concatenation. The `##' preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each `##' operator are combined into a single token, which then replaces the `##' and the two original tokens in the macro expansion."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define READ(port,pin) PORT ## port & (1<<pin) 

tigrezno, did you actually use this? To read you have to use the PIN register. Additionally you can not compare the & result with 1 as you do here:

if (READ(B, 5) == HIGH) 

This would work if you need to compare:

#define READ(port,pin) ((PIN ## port & (1<<pin)) != 0)

or just

#define READ(port,pin) ((PIN ## port & (1<<pin))

if you are ok with the simpler test condition:

if (READ(B,5)) {

/Lars

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

ouch that was a typo!

You're correct, it should be PIN and not PORT, sorry, i'll change it.

The comparation issue is true too, me bad, very very bad.

I wrote them without using the "input" defines, sorry.
I hope you found the other methods usefull.

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

I'm a beginner in embedded development and for small applications I really enjoy a simple mode to access a port pin directly ( like PORTA.1 or PORTA_1).
I have found this example a while ago and I would like to hear your comments on it :

Quote:
#include

// Define the bits in the port
typedef struct
{
unsigned char bit0 : 1,
bit1 : 1,
bit2 : 1,
bit3 : 1,
bit4 : 1,
bit5 : 1,
bit6 : 1,
bit7 : 1;
} bit_field;

// Define macro to get the value of each bit
#define GET_BIT(port) (*(volatile bit_field *) (_SFR_ADDR(port)))

// Define functions for each bit of the I/O ports in the program
#define SIG GET_BIT(PINB).bit0
#define LED GET_BIT(PORTD).bit0

int main (void)
{
for (;;)
{
if (SIG) LED = 1;
else LED = 0;
}
}


I would really like to understood how this code is working ... especially part with setting a bit.

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

Well break it down as the pre-processor will be doing for you. Let's say you use:

LED = 1;

firstly that becomes:

GET_BIT(PORTD).bit0;

which in turn is:

(*(volatile bit_field *) (_SFR_ADDR(PORTD))).bit0;

and from the GCC header files the _SFR_ADDR() and PORTD macros further break this down to be:

 (*(volatile bit_field *) (((uint16_t) &((*(volatile uint8_t *)((0x12) + 0x20)))))).bit0 = 1;

The 0x12 in there will vary depending on your AVR - the above is correct for mega16.

So ultimately it's casting a bitfield structure onto memory address 0x32 and then setting 1 into "bit0" of that struct.

When you see:

struct {
 :1
 :1
}

it's effectively assigning one bit for each element. So this is just allowing individual bit access to the 8 bits being held in memory location 0x32.

Cliff

 

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

Thank you for the tutorial. I found it very useful coming from beeing used to only sbi(), cbi() macros.

Somewhere else on the net i found these two macros beeing called obsolete... Any idea why is it so? Is it indeed more justified to acces the bits as shown in the tutorial?

This article is helping a lot in understanding many other articles around here.
Thanks once more.

Take care.

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

Quote:
Somewhere else on the net i found these two macros beeing called obsolete... Any idea why is it so? Is it indeed more justified to acces the bits as shown in the tutorial?

There are three ways to change a single bit using the AVR. If the register in question is in IO space locations 0x00..0x1F then SBI/CBI can be used to set/clear individual bits. Those opcodes occupy 16 bits and execute in 1 cycle. If the register is between 0x20..0x3F then it is no longer reachable using SBI/CBI but it is using IN/OUT but in this case to set a bit it wil require IN/OR/OUT and to clear a bit it will require IN/AND/OUT which is 3 16 bit opcodes and takes 3 cycles. If the register is beyond IO address 0x3F (that is beyond RAM address 0x5F) then the only access is LDS/STS and to set it wil be LDS/OR/STS and to clear it will be LDS/AND/STS. In this case it's five 16 bit opcodes (LDS and STS are 2 each) and takes 5 cycles to execute.

Now when the early versions of the GCC compiler appeared it did not have an optimisation to spot when LDS/OR/STS could be reduced to IN/OR/OUT or even SBI so a macro sbi() was defined to use inline assembler so the programer could force SBI to be used even though the compiler didn't realise it.

In later compiler versions the code generator was improved so that it would always generate the most optimal code (as long as -O0 was not used!) so the need for the programmer to take control and force when SBI should be used was no longer required.

In the latest GCC (and the other AVR C compilers) the totally portable, totally standard PORTB |= (1<<PB5) will always generate just an SBI PORTB,5 as long as PORTB is between 0x00 and 0x1F and the optimiser is enabled.

The joy of using that standard construct is that you don't need the code to rely on some extra .h file that provides sbi()/cbi() so it makes it more portable between copilers and other architectures.

If using GCC then (for the time being) there is still and this now includes:

#define sbi(port, bit) (port) |= (1 << (bit))
#define cbi(port, bit) (port) &= ~(1 << (bit))

so, as you can see, this is just supporting "old" code and turning the macro into the "standard" construct that's been explained above anyway.

As the file is deprecated there's a chance it may be withdrawn from future distributions so it's unwise to write code these days to use it because next time you upgrade compiler you may find that the file has gone.

Cliff

 

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

Thanks a lot.
Thins makes it clear. You'v definitely convinced me to get used to the current stnadard notation.

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

I just have small query regarding syntax:

Is this syntax valid:
PORTC = (0<<PC0); eg. driving the PC0 pin low.
or are you only able to do bit shift operations with "1"
PORTC = (1<<PC0)

I have tried this out on ATMEGA8 and it seems to work but many ppl have told me that that it is not wise to use the bit shift operation with 0 as it can lead to bugs. Could someone please clarify this for me?

Thanks in Advance.

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

Zero shifted left or right is always zero, so your code there just clears the entire PORTC register by setting it to zero.

You should never need to shift 0 at all, other than to indicate clearly that a bit is not set.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
Could someone please clarify this for me?

You did read the whole of this thread didn't you?

Hopefully it should have been obvious to you that to achieve what you think:

PORTC = (0<<PC0);

might do (assuming the intention is only to set bit PC0 to zero) you would actually use:

PORTC &= ~(1<<PC0);

Remeber you can only set bits with | and you can only clear them with &. Though you can obviously set/clear all 8 bits in a register in one go with a simple =, but then you cannot affect just single bits.

Cliff

 

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

Can somebody help me out pls, I just cant access the register definitions because it says undeclared after compiling. Im using atmega324p chip. Thanks.

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

Quote:

Can somebody help me out pls, I just cant access the register definitions because it says undeclared after compiling. Im using atmega324p chip. Thanks

Any particular language or variant thereof ?

 

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

I'm working on a project with an external 16-bits adc using SPI. The first 5 bits are for settling of the adc and can be discarded, I am however receiving them.
I'm reading the following bytes in 3 chars (where x is either 1 or 0):
0-7
msb|0|0|0|0|0|x|x|x|
8-15
|x|x|x|x|x|x|x|x|
& 16-23
|x|x|x|x|x|0|0|0|lsb

I want to put 0-7 in a long and shift it 5+16 to the left, put 8-15 in a long and shift it 5+8 to the left and add it to 0-7, put 16-23 in a long and shift it 5 to the left and also add it. Then convert it to a double.

so:
|0|0|0|0|0|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|0|0|
<<5
|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|0|0|0|0|0|0|0|0|
typecast
|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|
how can I shift all bits to the left in a long?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
unsigned int result = (byte1 << 13) | (byte2 << 5) ( (byte3 >> 3);

at a rough guess.

 

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

I would use a union.

Regards,
Steve A.

The Board helps those that help themselves.

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

When I was a boy... I learned to fix "mainframes" for the Navy. Understand that was 35 years ago so my memory of the details is questionable at best but I seem to remember that a shift took X clocks per shift position. For example to shift one position left took one clock, 2 positions took 2 cycles etc.

Is that true in the Atmega processors, or does it shift any number of positions in a single clock?

JWColby
AVRNubee
www.ColbyConsulting.com

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

The ROL/ROR/LSL/LSR instructions are all shown as being 1 cycle but to shift 5 places might take 5 of them and cost 5 cycles. Though an intelligent optimiser will spot that a shift of 4 can probably be achieved with a SWAP, an AND mask and one shift for the cost of 3 cycles

Cliff

 

Pages