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
stu_san
PostPosted: Aug 06, 2008 - 07:22 PM
Raving lunatic


Joined: Dec 30, 2005
Posts: 2327
Location: Fort Collins, CO USA

The following is based on posts from Kevin Rosenberg and Johan Ekdahl. Thanks!

In some compilers, the compiler allows the programmer to directly refer to a single bit of an AVR port. For example, if we want to set the third bit of PORTB, the following works just fine:
Code:
PORTB.3 = 1;
Unfortunately, GCC rejects this. The reasons for this are obscure to a newbie, but is actually simple: The above syntax violates the rules of standard C. Since GCC itself supports more than just the AVR processor (and since the other processor users like the standard just fine, thank you Very Happy), we AVR users are stuck with the way the C standard (and GCC) as they are.

The most common suggestion to solve this problem is to learn how to use the bit manipulation operations in C. For a tutorial on this, check out [TUT] [C] Bit manipulation (AKA "Programming 101 For Embedded Code"). Whether you use the following method or not, we strongly suggest you read the "Programming 101" tutorial. You will end up using it, no matter what.

Okay, if you are stubborn and insist on defining a pin as a variable in GCC, there is a way. Suppose we set up a structure that defines the bits of a single byte:
Code:
typedef struct
{
  unsigned int bit0:1;
  unsigned int bit1:1;
  unsigned int bit2:1;
  unsigned int bit3:1;
  unsigned int bit4:1;
  unsigned int bit5:1;
  unsigned int bit6:1;
  unsigned int bit7:1;
} _io_reg;
Now we can define a macro that will allow us to define our single bit variable:
Code:
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt
The above macro works like this:

#define is the start of a macro definition

REGISTER_BIT is the name of the macro

(tg,bt) are the parameters to the macro

The C keyword volatile is accurately described in the AVR-libc Manual Frequently Asked Questions, in the FAQ item #1. It is also described in the article Introduction to the "Volatile" Keyword.

_io_reg is the struct type that is typedef'ed right above the macro, and thus
_io_reg* is a pointer to such a struct, and so
(volatile _io_reg*) is a typecast to "a volatile pointer to a _io_reg struct"

& is the "address of" operator, and thus
&rg is the address of rg, ie the address of the first parameter to the macro

The -> is the "member by pointer operator", in other words it "refers" to one of the fields in the struct that the pointer points to.

## is the token pasting operator (of the C preprocessor). The preprocessor concatenates the two operands together so that the compiler sees one token, in this case "bit" and whatever value the bt parameter to the macro has.

So, if you do
Code:
REGISTER_BIT(PORTB,4)
this is expanded by the C preprocessor to
Code:
((volatile _io_reg*)&PORTB)->bit4
By the way, PORTB is a macro, too. It is defined in a header file, based on your processor type. For example, in an ATmega128, PORTB is defined as
Code:
#define PORTB     _SFR_IO8(0x18)
You would be right if you guessed that _SFR_IO8 is also a macro, but that takes us a little far from the current discussion.

Let's use this in some example code:
Code:
#include <avr/io.h>

typedef struct
{
  unsigned int bit0:1;
  unsigned int bit1:1;
  unsigned int bit2:1;
  unsigned int bit3:1;
  unsigned int bit4:1;
  unsigned int bit5:1;
  unsigned int bit6:1;
  unsigned int bit7:1;
} _io_reg;

#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt


#define BUTTON_PIN  REGISTER_BIT(PINB,3)
#define LED_PORT    REGISTER_BIT(PORTB,4)

#define BUTTON_DIR  REGISTER_BIT(DDRB,3)
#define LED_DIR     REGISTER_BIT(DDRB,4)
 
main() {
  uint8_t is_button = BUTTON_PIN;
 
  LED_DIR = 1;
  BUTTON_DIR = 0;

  while (1) {
    LED_PORT = BUTTON_PIN;
  }
}
First off, we include the header file <avr/io.h>. This defines the PORT and PIN ports available on the selected AVR.

We then included our structure definition and the REGISTER_BIT macro.

We then define two register bits:
Code:
#define BUTTON_PIN  REGISTER_BIT(PINB,3)
#define LED_PORT    REGISTER_BIT(PORTB,4)
which in turn calls on the REGISTER_BIT macro, so that when we do
Code:
  uint8_t is_button = BUTTON_PIN;
this actually is expanded by the C preprocessor to
Code:
uint8_t is_button = ((volatile _io_reg*)&PINB)->bit3;
What does this mean? This: Take the address of the PINB register. Treat that address as a pointer to a _io_reg struct (where the different bits are conveniently declared). Dereference that pointer, and get the member with the name "bit3".

We have defined the "direction" bits for our register bits:
Code:
#define BUTTON_DIR  REGISTER_BIT(DDRB,3)
#define LED_DIR     REGISTER_BIT(DDRB,4)
This allows us to tell the processor what direction (input or output) the pin will be set.

In the main routine, set up the LED to be an output:
Code:
  LED_DIR = 1;
which after the preprocessor looks like:
Code:
  ((volatile _io_reg*)&DDRB)->bit4 = 1;
Similar to the description of is_button before, this translates to: Take the address of the DDRB register. Treat that address as a pointer to a _io_reg struct. Dereference that pointer, and get the member with the name "bit4". Set that bit to a 1.

We also set up the BUTTON to be an input:
Code:
  BUTTON_DIR = 0;
By now, the translation from the macro to pre-processed code should be apparent.

Inside the loop, we can actually set the output LED_PORT directly from the input BUTTON_PIN. How? Again, through the dereferencing of the structure, the compiler will set bit 4 of PORTB to the current value of bit 3 in PINB.

If you are confused now, then that is quite OK. We have dealt both with the C preprocessor and the compiler per se. In the preprocessor we have not only been involving straight-forward macros, but have used the token pasting operator (that is known to be confusing, and the source of more than one bug). (For more on macros, check out Macro tutorial and Macro Functions and the Wikipedia C Preprocessor entry.)

We have forced the compiler to look at a register as a struct, and when referencing the members we are using the token pasting operator to produce a correct member name.

You have at least three options now:

1) Not really understand why all this works, but accept that it does work and just use it.

2) Take a deep breath and start learning the ins and outs of the C Preprocesor, structs and typecasting to really understand what is going on here. This has very little to do with AVR-specific things and almost everything to do with standard C so any good book or other source of C knowledge will do.

3) Use the bit manipulation alternative presented when we started, using the bit manipulation operators to do the job.

You have this choice, but if you want my advice go with 3) for now until you gain proficiency in C, and then start on 2).


No matter what, be sure to learn more about C. Practically everything here is standard C. You can find online C tutorials at:

Programming Tutorial: C Made Easy

How C Programming Works

GNU C Programming Tutorial


And the following books are pretty good - in fact, the first should be required on every C programmer's desk!

The C Programming Language is almost a necessity.

Absolute Beginners Guide To C

Writing Solid Code I personally recommend this. Lots of good tips.

Book and Dev Kits: Smiley Micros - Smiley frequently posts on this forum!


Have fun!!

Stu

Edit 1: Fixed other compiler referenced. Did various typo and missing word fixes.

_________________
Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!


Last edited by stu_san on Oct 30, 2008 - 07:49 PM; edited 5 times in total
 
 View user's profile Send private message  
Reply with quote Back to top
glitch
PostPosted: Aug 06, 2008 - 10:38 PM
Raving lunatic


Joined: Jan 12, 2002
Posts: 7835
Location: Canada

stu_san wrote:

In compilers such as IAR, the compiler allows the programmer to directly refer to a single bit of an AVR port. For example, if we want to set the third bit of PORTB, the following works just fine:
Code:
PORTB.3 = 1;


Not quite. "PORTB.3" is not valid syntax... only Codevision supports this non-standard format (AFAIK). IAR does offer bitwise access, but adheres to the C language requirements, like GCC, of member names starting with an underscore, or alpha character.

For IAR it would be "PORTB.PORTB_Bit3"
 
 View user's profile Send private message  
Reply with quote Back to top
JohanEkdahl
PostPosted: Aug 06, 2008 - 11:08 PM
10k+ Postman


Joined: Mar 27, 2002
Posts: 22061
Location: Lund, Sweden

FYI: Original discussion was here: http://www.avrfreaks.net/index.php?name ... highlight= . Ive made some comments there on my contribution to the above tutorial.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
stu_san
PostPosted: Aug 07, 2008 - 05:23 PM
Raving lunatic


Joined: Dec 30, 2005
Posts: 2327
Location: Fort Collins, CO USA

glitch wrote:
stu_san wrote:

In compilers such as IAR, the compiler allows the programmer to directly refer to a single bit of an AVR port. For example, if we want to set the third bit of PORTB, the following works just fine:
Code:
PORTB.3 = 1;


Not quite. "PORTB.3" is not valid syntax... only Codevision supports this non-standard format (AFAIK).
Thanks for the heads-up. I've change the phrase to "some compilers", not naming them. Perhaps that's better?

Stu

_________________
Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!
 
 View user's profile Send private message  
Reply with quote Back to top
glitch
PostPosted: Aug 07, 2008 - 05:48 PM
Raving lunatic


Joined: Jan 12, 2002
Posts: 7835
Location: Canada

stu_san wrote:
Thanks for the heads-up. I've change the phrase to "some compilers", not naming them. Perhaps that's better?


Works for me Smile
 
 View user's profile Send private message  
Reply with quote Back to top
misterbenn
PostPosted: Aug 14, 2008 - 11:39 AM
Rookie


Joined: Apr 08, 2008
Posts: 20


Fantastic tutorial, very interesting and useful.

i'm fairly new to AVRs and this post looks like it could help me on the way to understanding it all a bit better. So i guess i'll have to take your advice and
Quote:
Take a deep breath and start learning the ins and outs of the C Preprocesor
 
 View user's profile Send private message  
Reply with quote Back to top
takashi_85
PostPosted: Aug 25, 2008 - 02:04 AM
Rookie


Joined: Nov 29, 2006
Posts: 23
Location: in the country of pyramids

Quote:
unsigned int bit0:1


i'm sorry for asking this question a little bit late.

i'm not getting the meaning of the colon and 1

Quote:
bit0:1

_________________
"An hour with a book would have brought to your mind the secret that took the whole year to find;
the facts that you learned at enormous expense,were all on a library shelf to commence"
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Aug 25, 2008 - 02:32 AM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

takashi_85 wrote:
i'm not getting the meaning of the colon and 1
It's standard C defining a bitfield one bit long.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
takashi_85
PostPosted: Aug 25, 2008 - 01:07 PM
Rookie


Joined: Nov 29, 2006
Posts: 23
Location: in the country of pyramids

thanks a lot the bitfield word was my key to understand.
i started using google to get the full picture

thank u again.

_________________
"An hour with a book would have brought to your mind the secret that took the whole year to find;
the facts that you learned at enormous expense,were all on a library shelf to commence"
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Aug 25, 2008 - 05:31 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

You're welcome. Yes, knowing the correct name for a construct can make all the difference in finding information about it.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
dircon
PostPosted: Sep 18, 2008 - 09:12 PM
Rookie


Joined: Aug 10, 2006
Posts: 43


Hi Stu,
thanks for this excellent tutorial. Unfortunately I am one of them who use it but don't understand in full depth how it works. Will you please introduce how can I check a particular bit of a read/write register by usink your tricky way?
Thanks,
Istvan
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Sep 18, 2008 - 09:18 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

dircon wrote:
Will you please introduce how can I check a particular bit of a read/write register by usink your tricky way?
Say that you defined an input pin B3 using
Code:
#define BUTTON_PIN  REGISTER_BIT(PINB,3)
, then'd you read the value of that bit by using something like
Code:
if (BUTTON_PIN) {
  do_whatever();
}

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
rearthur2003
PostPosted: Sep 18, 2008 - 10:58 PM
Newbie


Joined: Sep 18, 2008
Posts: 18


using portb.3 = 1 without the semicolon works fine for me i am using oshons avr sim but please remember to declear portb.3 as a bit
 
 View user's profile Send private message  
Reply with quote Back to top
dircon
PostPosted: Sep 19, 2008 - 11:46 AM
Rookie


Joined: Aug 10, 2006
Posts: 43


Hi,
its okay in case of an explicitly _input_ pin, but let me illustrate my problem:

#define LET_OC0 REGISTER_BIT(TCCR0,COM00)
#define GET_OC0 REGISTER_BIT(TCCR0,COM00)

To set as an output pin is ok:
LET_OC0=1;

But how can read if I want e.g. toggle it?

if ( GET_OC0==true)
{ do_something; }

Now GCC says:

../myproject.c:133: error: structure has no member named `bitCOM00'

Thanks,
Istvan
 
 View user's profile Send private message  
Reply with quote Back to top
dircon
PostPosted: Sep 19, 2008 - 01:51 PM
Rookie


Joined: Aug 10, 2006
Posts: 43


Hi folks, I got it. I changed symbolic bit references to numeric values and this way is ok.
Istvan
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Sep 19, 2008 - 01:56 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

dircon wrote:
#define LET_OC0 REGISTER_BIT(TCCR0,COM00)
#define GET_OC0 REGISTER_BIT(TCCR0,COM00)
...
./myproject.c:133: error: structure has no member named `bitCOM00'
Your two definitions expand to the exact same thing. Note that REGISTER_BIT requires a number for the second argument. Look again at the definition of REGISTER_BIT
Code:
#define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt
The ## concatenate the strings. So, your REGISTER_BIT expands to ((volatile _io_reg*)&TCCR0)->bitCOM00, but bitCOM00 is not a structure member, only bit0 to bit7.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
alex79
PostPosted: Oct 18, 2008 - 03:37 PM
Newbie


Joined: Oct 18, 2008
Posts: 1


Hi everyone, here goes my first post to AVRFREAKS:
I loved the tutorial, i´ve been looking for this answer for quite a few hours now. And I definitely love STU´s signature!

Oh, sorry, here goes:
Handling bits directly is a great way to go IMO, it makes program writing and mantaining quite easier. In fact, I´m also programming for 8051 on Keil C51 and they have a "sbit" definition which works just like that:
sbit LED = P0^1; // or better yet
sbit LED = LEDpORT^LEDpIN;
and there you go, no matter what changes on your board, you are covered with a few defines.
I came here trying to imitate this with winavr, but got no luck yet, I´ll try it your way for now. Have you looked for any code size or run time overhead of this? It may be even better than Keil C51 in terms of portability since it´s standard C, but bit-fields i think are implementation dependant so you could find yourself with your bits packed head-over-heels if you change compilers.
Anyway, that´s just me thinking after a few hours of googling, trying things and grumbling.

GREAT TUTORIAL STU!!!!
Regards,
Alex
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Oct 18, 2008 - 04:32 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

Alex, the code presented uses single opcodes (assuming the register location is low enough) with gcc and IAR. Imagecraft doesn't not optimize this and CodeVision has bit variables that seem similar to the sbit behavior you described.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
stu_san
PostPosted: Oct 20, 2008 - 04:26 PM
Raving lunatic


Joined: Dec 30, 2005
Posts: 2327
Location: Fort Collins, CO USA

alex79 wrote:
Have you looked for any code size or run time overhead of this?
It really depends on the efficiency of the compiler's "back end" (the code generator and the optimizer) as to how well these constructs will be used. For registers in the low address range (0x00 - 0x1F), the GCC compiler actually generates single bit set and clear instructions. Other compilers may vary.

alex79 wrote:
It may be even better than Keil C51 in terms of portability since it´s standard C, but bit-fields i think are implementation dependent so you could find yourself with your bits packed head-over-heels if you change compilers.
Since the bit-field definition is a C standard, I would not expect that the ordering of the bits would change between compilers, but hey, what do I know? Wink There are always crufty corners of C where one compiler decides to "make things easier" by not following the standard, or where the standard is intentionally ambiguous. I guess, "Your Mileage May Vary" is the best response I can give to this.

Stu

_________________
Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Oct 20, 2008 - 04:44 PM
10k+ Postman


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

Stu,

I'm guessing that for eight :1 fields in a uchar that almost all compilers will do the same even if it is "implementation dependent" (does any CPU put bit0 at the left and bit7 at the right??) but consider something like:
Code:
typedef struct {
   UINT16 segment_leds_intensity               :    4,     // intensity = 0 ..15:  0=min.  15=max intensity
UINT16 segment_leds_refresh_cycle_frequency      :    4,     // 70..310 Hz with 16Hz units: 0=70  15=310
UINT16 segment_leds_automation_select                  :    3,     //  0=spin leds – all with the same intensity
  //  1=spin leds – diminishing intensities from leed led
  //  2=segment direct mode: disables spin leds. In
  //       this mode the host may emulate spinning
  //       by writing to the field ‘segment_state_mask’
  //       in register OPERATE_SEGMENT_LEDS
   //   7=DEMO1 selected (???)
   keep_
  UINT16 led_positions_when_switching_direction      : 1           //  =1 to keep the lit spin leds in the same
  //  positions when the spin direction is
  //  changed. What is seen is the position of
  //  the leed led shifts in the group. If =0
  //  then the leds trail clockwise or anti-clock
  // wise from the spin leds so different leds are
  // lit when the spin led changes direction.
UINT16 segment_leds_param_1                :  3       //  = 0..7: for spin automations defines the
                                                                                                          //      number of spin leds  which are grouped
  //     behind the lead led for spin_leds
  //     automations
UINT16 enable_spin_leds_intensities_profile            :  1       //  = 1 to enable the use of four I2C bytes (ie 8
  //       nibbles which  define an  intensities profile
  //      weighting (4 bits) from the lead spin LED

} REG_CONFIGURE_SEGMENT_LEDS

care to predict exactly where those bits will be located?

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Oct 20, 2008 - 05:47 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

clawson wrote:
care to predict exactly where those bits will be located?
I wouldn't mind predicting, but since the answer is not specified by the C language specification, I'd rely instead on observation.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
Reply with quote Back to top
stu_san
PostPosted: Oct 21, 2008 - 03:49 PM
Raving lunatic


Joined: Dec 30, 2005
Posts: 2327
Location: Fort Collins, CO USA

Cliff wrote:
care to predict exactly where those bits will be located?
Exactly my point. Does the compiler take endian-ness into account? As you said, the bitfields in a uchar are likely to be pretty settled (as the bitfields in the original article are), but extending that to larger types is problematic.

Having written code that had to work on both a PowerPC and x86 processor, I understand the problems with endian-ness and bitfield ordering. I've come to the conclusion that, without testing, there is no such thing as "compiler independent" code, only "compiler compliant" code. A bold statement, but for non-trivial code an apparently true one.

Stu

_________________
Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!
 
 View user's profile Send private message  
Reply with quote Back to top
kmr
PostPosted: Oct 21, 2008 - 04:03 PM
Raving lunatic


Joined: Apr 01, 2004
Posts: 3812
Location: New Mexico

stu_san wrote:
I've come to the conclusion that, without testing, there is no such thing as "compiler independent" code, only "compiler compliant" code. A bold statement, but for non-trivial code an apparently true one.
There is "compiler independent" code. It is code whose function is guaranteed by the language specification, so that any conforming compiler will produce the specified results. As we all know, the C language specifications leaves many details unspecified and those areas are thus implementation-specific.

_________________
Kevin Rosenberg
http://b9.com
http://kevin.hypershots.com
 
 View user's profile Send private message Send e-mail Visit poster's website 
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