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
abcminiuser
PostPosted: Apr 22, 2006 - 02:26 PM
Moderator


Joined: Jan 23, 2004
Posts: 10215
Location: Melbourne, Australia

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:
Code:
unsigned char foo = 0;


To set bit 0 in foo and then store the result back into foo:
Code:
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. Wink

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

Code:
foo |= 0x01;


This is equivalent to the statement above.

To clear bit 0 in foo requires 2 bit operators:

Code:
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:

Code:
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:

Code:
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:

Code:
foo = foo ^ 0x01;


Or the shorter statement:

Code:
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:

Code:
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:

Code:
(0x01 << 2)


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

Code:
(0x01 << 7)


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

Code:
(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:

Code:
#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:
Code:
bit_set(foo, 0x01);


To set bit number 5:
Code:
bit_set(foo, BIT(5));


To clear bit number 6 with a bit mask:
Code:
bit_clear(foo, 0x40);


To flip bit number 0:
Code:
bit_flip(foo, BIT(0));


To check bit number 3:
Code:
if(bit_get(foo, BIT(3)))
{
}


To set or clear a bit based on bit number 4:
Code:
if(bit_get(foo, BIT(4)))
{
    bit_set(bar, BIT(0));
}
else
{
    bit_clear(bar, BIT(0));
}



To do it with a macro:
Code:
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.[/list]

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


Last edited by abcminiuser on Apr 25, 2006 - 12:23 AM; edited 1 time in total
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
smileymicros
PostPosted: Apr 22, 2006 - 02:59 PM
Raving lunatic


Joined: Nov 17, 2004
Posts: 6496
Location: Great Smokey Mountains.

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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
cell
PostPosted: May 04, 2006 - 10:58 PM
Newbie


Joined: Apr 21, 2006
Posts: 9


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:

Code:

#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
 
 View user's profile Send private message  
Reply with quote Back to top
abcminiuser
PostPosted: May 05, 2006 - 06:30 AM
Moderator


Joined: Jan 23, 2004
Posts: 10215
Location: Melbourne, Australia

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:
Code:
#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:
Code:
#define Status_LED  PORTA, 3

C_SETBIT(Status_LED);
C_CLEARBIT(Status_LED);


- Dean Twisted Evil

_________________
Make Atmel Studio better with my free extensions. Open source and feedback welcome!
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
BenG
PostPosted: Mar 19, 2007 - 03:37 PM
Hangaround


Joined: May 03, 2006
Posts: 155
Location: Manchester, UK

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

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

_________________
Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101
 
 View user's profile Send private message  
Reply with quote Back to top
donblake
PostPosted: Mar 20, 2007 - 02:49 AM
Posting Freak


Joined: Jan 03, 2004
Posts: 1680
Location: Apalachin, NY, USA

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
 
 View user's profile Send private message  
Reply with quote Back to top
Bingo600
PostPosted: Mar 24, 2007 - 01:37 PM
Raving lunatic


Joined: Apr 25, 2004
Posts: 3930
Location: Denmark

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
 
 View user's profile Send private message  
Reply with quote Back to top
danni
PostPosted: May 11, 2007 - 11:01 AM
Raving lunatic


Joined: Sep 05, 2001
Posts: 2612


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:


Code:

#include <io.h>
#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
 
 View user's profile Send private message  
Reply with quote Back to top
Adam_Y
PostPosted: Jun 28, 2007 - 02:50 PM
Hangaround


Joined: Dec 24, 2006
Posts: 201


Code:
 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.
 
 View user's profile Send private message  
Reply with quote Back to top
bloody-orc
PostPosted: Jun 28, 2007 - 03:18 PM
Posting Freak


Joined: Dec 17, 2005
Posts: 1601
Location: Europe- Estonia- Tallinn

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 Wink
 
 View user's profile Send private message Send e-mail  
Reply with quote Back to top
clawson
PostPosted: Jun 28, 2007 - 03:44 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71171
Location: (using avr-gcc in) Finchingfield, Essex, England

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
Code:
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):
Code:
#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:
Code:
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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
AllN
PostPosted: Jun 29, 2007 - 02:52 AM
Posting Freak


Joined: Feb 14, 2007
Posts: 1858
Location: San Diego California

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!
 
 View user's profile Send private message  
Reply with quote Back to top
Adam_Y
PostPosted: Jun 29, 2007 - 05:32 PM
Hangaround


Joined: Dec 24, 2006
Posts: 201


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
Code:
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):
Code:
#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.
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jun 29, 2007 - 05:34 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71171
Location: (using avr-gcc in) Finchingfield, Essex, England

Yup in GCC all your programs include <avr/io.h> 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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Adam_Y
PostPosted: Jun 29, 2007 - 05:53 PM
Hangaround


Joined: Dec 24, 2006
Posts: 201


clawson wrote:
Yup in GCC all your programs include <avr/io.h> 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?
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jun 29, 2007 - 06:03 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71171
Location: (using avr-gcc in) Finchingfield, Essex, England

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 !?!

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Adam_Y
PostPosted: Jun 29, 2007 - 06:51 PM
Hangaround


Joined: Dec 24, 2006
Posts: 201


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.
 
 View user's profile Send private message  
Reply with quote Back to top
fleemy
PostPosted: Jul 03, 2007 - 08:03 PM
Hangaround


Joined: May 10, 2007
Posts: 426
Location: Brussels, Belgium

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?
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Jul 03, 2007 - 08:42 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71171
Location: (using avr-gcc in) Finchingfield, Essex, England

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

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
robinsm
PostPosted: Nov 06, 2007 - 08:19 PM
Hangaround


Joined: Aug 14, 2006
Posts: 118


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...
Code:


if (!( foo & 0x80 ))
{
  ...
}
 
 View user's profile Send private message  
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