NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » How do/can you store boolean values in registers?

March 01, 2011
by BStory
BStory's Avatar

I've been searching and thinking about this for a while. Research tells me that a boolean value is 1 byte. If that byte = 0 then it is false, if byte != 0 then it's true.

Example 0b00101011,0b10000001, and 0b00000001 all compute to true or 1.

0b00000000 computes to false or 0.

My question... Could you store 8 boolean values into 1 byte/storage register/whatever? This way you could shrink 8 bytes down to 1 byte or 1 byte down to 1 bit. Are there registers in the 168 for general storage that can be directly accessed?

Would declaring uint8_t x assign x to it's own register in memory? I guess I'm assuming that memory is made up of 8 bit chunks. If so how could you check or change the individual bit configuration of x?

and please let me know if any of this info = 0... err, I mean is false.

March 01, 2011
by Ralphxyz
Ralphxyz's Avatar

"Are there registers in the 168 for general storage that can be directly accessed?"

Ahem, there is this wonderful resource called a specsheet. Weren't you the guy that said he had the Registers pined to the wall?

"Are there registers in the 168 for general storage that can be directly accessed?" Try looking for GPIOR (page 25).

7.5.1 General Purpose I/O Registers The ATmega48A/48PA/88A/88PA/168A/168PA/328/328P contains three General Purpose I/O Registers. These registers can be used for storing any information, and they are particularly use- ful for storing global variables and Status Flags. General Purpose I/O Registers within the address range 0x00 - 0x1F are directly bit-accessible using the SBI, CBI, SBIS, and SBIC instructions.

Maybe you could/would write-up a sketch of how you use the GPIO registers.

Ralph

March 01, 2011
by bretm
bretm's Avatar

You can do something like this to use the GPIORs (or any other byte) as boolean value storage. I'm typing this without access to the compiler so there may be some slight syntax problems, but it should be close.

typedef struct {
    uint8_t bit0 : 1;
    uint8_t bit1 : 1;
    uint8_t bit2 : 1;
    uint8_t bit3 : 1;
    uint8_t bit4 : 1;
    uint8_t bit5 : 1;
    uint8_t bit6 : 1;
    uint8_t bit7 : 1;
} uint8_bits;

#define myBool ((uint8_bits*)&GPIOR2)->bit4

myBool = 0;
myBool = 1;
if (myBool) ...;

I also use this to define my "pin" constants:

#define LED  ((uint8_bits*)&PORTC)->bit4

LED = 0; // turn off LED
LED = 1; // turn on LED

You can also do this to define sets of multiple pins, e.g. the four data pins for the LCD display:

typedef struct {
    uint8_t padding : 2;
    uint8_t data : 4;
} lcd_data_t;

#define lcdData ((lcd_data_t*)&PORTD)->data

lcdData = 3;
lcdData = 10;

The WriteByte function in lcd.c could be written along these lines:

typedef struct {
    uint8_t lo : 4;
    uint8_t hi : 4;
} nibbles;

void WriteByte(uint8_t byte)
{
    lcdData = ((nibbles*)&byte)->lo;
    RS = 1;
    delay_us(50);
    RS = 0;
    delay_us(50);
    lcdData = ((nibbles*)&byte)->hi;
    RS = 1;
    delay_us(50);
    RS = 0;
    delay_us(50);
}

It's ugly but the compiler actually generates really efficient code for it.

March 01, 2011
by bretm
bretm's Avatar

Important change: in order to use registers, you have to add "volatile", like so:

#define myBool ((volatile uint8_bits*)&GPIOR2)->bit4
March 02, 2011
by BStory
BStory's Avatar

Thanks for the replies. I think that cleared up a lot for me. Looking back at my post it does sorta look like I had a bunch of questions but it was really just 1 that I wasn't sure how to ask. I've been reading up on pointers and I'm thinking they might be the answer.

bretm: Thanks for the examples. They really help a lot. It looks like pointers are a pretty big necessity for direct memory access. It makes sense I think. I never really understood the use of pointers. Also typedef looks very useful. I'll definitely be looking more into that. Learning how to write efficient code is my ultimate goal here. You were spot on with your examples.

Ralph: I'm embarrassed that my question made me sound so hypocritical. I don't have all the registers on my wall. I'm just the guy with page 88, 89, and the pin-out on my wall. Though I guess that makes me guilty of knowing what a datasheet is. But thanks for mentioning it in case I didn't :D. I started to read through the AVR Memories section but it looked like it was just EEPROM stuff. Thanks for pointing me to 7.5.1. That looks right. I should have kept reading. I'll definitely be looking into it and try to post something about it when I figure it all out(best way to learn).. Specifically this part: "Purpose I/O Registers within the address range 0x00 - 0x1F are directly bit-accessible using the SBI, CBI, SBIS, and SBIC instructions". Looks like good stuff.

March 02, 2011
by Ralphxyz
Ralphxyz's Avatar

Well like I said you are forgiven as long as you post your final working code. bretm's sample code, as usual really helps.

I would love to understand the SBI, CBI SBIS and SBIC instructions. Well I sota kinda do a little but a nice simple explanation would be great.

Page 532 -535 has a nice table you could pin to your wall, it has the memory reference and a page reference for all of the Registers.

That is from the big specsheet it's also in the back of the smaller specsheets as well I believe.

Ralph

March 02, 2011
by bretm
bretm's Avatar

CBI, SBI, SBIS, SBIC aren't something that you would generally worry about when using C to program the AVR, but knowing about them lets you squeeze a few more bytes out of your program memory.

Normally, setting or clearing a bit in a memory location involves loading the byte from that location (unless it's already sitting around in an Rxx register), modifying it, and writing it back out. This takes three instructions (LD for load, ORI or ANDI to change bits, and ST for store) taking around 10 bytes, and 5 clock cycles.

If the memory location is in the first 32 bytes (0x00 to 0x1F) and you only need to set or clear a single bit, the compiler uses a shortcut instruction, either CBI to clear a bit, or SBI to set a bit. This takes only a single 2-byte instruction and 2 clock cycles.

You would think that that address range would be crammed full of the most important registers, but it's actually mostly wasted space. All the DDRx, PINx, and PORTx registers are there, the general-purpose register GPIOR0 is there, some timer and EEPROM stuff, but it's mostly empty, reserved space. GPIOR1 and GPIOR2 are outside that range.

(SBIS tells the microcontroller to skip the next machine instruction if a specific bit is set, and SBIC tells it to skip the next instruction if the bit is not set.)

So really GPIOR0 is the only general-purpose place where you can use SBI and CBI, so if you have 8 or fewer bits that you modify or test like crazy all throughout your program, each time you use it you can save a handful of bytes of program memory if you map those bits to GPIOR0.

Post a Reply

Please log in to post a reply.

Did you know that one NerdKits customer discovered that his hamster ran several miles in a night using his kit and a Hall-effect sensor? Learn more...