Arduino

# Introduction to Bare-metal Programming for Arduino

April 03, 2020 by Brett Garberman
Share

Learn about bit-wise operations for register level programming for Arduino to increase your board’s potential!

To transition away from the more beginner-friendly Arduino IDE framework and begin programming microcontrollers at the register level (also referred to as bare metal), itâ€™s vital to know how to use the C language to manipulate the 1â€™s and 0â€™s that make up these spaces in memory. In this article, Iâ€™ll introduce how to do exactly that. This article shows how to perform the bitwise operations set, clear, toggle, and read.

## Setting a Bit

Setting a bit refers to placing a 1 at a given position in a register, without affecting the other values in the same register. This is done using the bitwise OR operator â€œ | â€œ. Hereâ€™s the truth table for the OR operator:

A | B = Y

A
B
Y
0
0
0
1
0
1
0
1
1
1
1
1

When comparing two bits with the OR operator, the output will be 1 if only one of the inputs is 1. If both inputs are the same, 1 or 0, the output will be 0.

To set the bit at position N (remember that C is 0 indexed, so the first position is position 0) in a register to a 1, we can do this:

REGISTER |= (1<<N)

The â€œ1<<Nâ€ term is called the mask - itâ€™s used to select the specific bit that we are interested in modifying.

The left shift operator, <<, pushes a number to the left the specified number of times, so 1<<N pushes a 1 to the Nth position of the mask. The register and the mask are ORd ( |= ) to obtain the result.

For example: setting the bit at position 5 in a register called PORTB.

``````PORTA = 00010001
PORTA |= (1<<5)

PORTA:              00010001
PORTA |= (1<<5):    00110001``````

The bit at position 5 in the register has been set to 1. The other bits in the number are unchanged - no matter if they were 1 or 0.

## Clearing a Bit

Clearing a bit refers to placing a 1 at a given position in a register, without affecting the other values in the same register. This is done using the bitwise AND operator â€œ & â€œ. Hereâ€™s the truth table for the AND operator:

A & B = Y

A
B
Y
0
0
0
1
0
0
0
1
0
1
1
1

When comparing two bits with the AND operator, the output will be 1 if both inputs are 1. Otherwise, the output will be 0.

To clear the bit at position N in a register (set it to 0), we can do this:

REGISTER &= ~(1<<N)

Take note of the fact that when clearing a bit, we NOT (~) the mask. The NOT operator inverts every bit in a number - the 1â€™s become 0â€™s, the 0â€™s become 1â€™s.

For example: clearing the bit at position 2 in a register called PORTB.

``````PORTB = 00011111
PORTB &= ~(1<<2)

PORTB:               00011111
PORTB &= ~(1<<2):    00011011``````

The bit at position 2 in the register has been set to 1. The other bits in the number are unchanged - no matter if they were 1 or 0.

## Toggling a Bit

Toggling a bit refers to flipping the value of a bit - if itâ€™s a 0, make it a 1. If itâ€™s a 1, make it a 0. This is done using the bitwise XOR operator â€œ ^ â€œ. Hereâ€™s the truth table for the XOR operator:

A ^ B = Y

A
B
Y
0
0
0
1
0
1
0
1
1
1
1
0

For example: toggling the bit at position 5 in a register called PORTC.

``````PORTC = 00000001
PORTC ^= (1<<6)

PORTC:              00000001
PORTA |= (1<<6):    01000001``````

The bit at position 6 in the register has been flipped from a 0 to 1. The other bits in the number are unchanged - no matter if they were 1 or 0.

That changed a 0 to 1, but the same operation works to change a 1 to 0.

``````PORTC = 00100001
PORTC ^= (1<<6)

PORTC:              01000001
PORTA |= (1<<6):    00000001``````

Registers are not only written to, but they are also read from. To read if the value of a certain bit in a register is a 0 or a 1, we mask all of the other bits to a 0 (leaving the desired bit unaltered), and then check the resulting number. If the result is all 0â€™s, we know that the desired bit was a 0. If the result is anything else, we know that there was a 1 in the desired position.

This explores the concept of bitwise TRUE and FALSE. A false is a 0, and a true is anything greater.

For example: reading the bit at position 7 in register PORTD. We can check the result with an IF statement in the C language.

``````PORTD = 10000000

if (PORTD & (1<<7) ) {
return 1;
else{
return 0;
}
}

PORTD:              10000000
PORTA &= (1<<7):    10000000

The function will return 1, as the result isn’t all 0’s.``````

Another example: reading the bit at position 0 in register PORTD.

``````PORTD = 10000000

if (PORTD & (1<<0) ) {
return 1;
else{
return 0;
}
}

PORTD:              10000000
PORTA &= (1<<7):    00000000

The function will return 0, as the result is all 0’s.``````

A microcontrollerâ€™s datasheet (or hardware reference document), maps out the registers for all of its peripherals, like GPIOâ€™s, ADCâ€™s, and communication buses. When using a framework like Arduino, a hardware abstraction layer, or any other microcontroller library, the registers are mostly hidden inside of more user-friendly functions. However, knowing how to manipulate registers is essential to understanding the deviceâ€™s operation, and the key to full control over its features.

### Author

Brett Garberman

I'm an electrical engineer. I want to help hobbyists and engineers conquer obstacles and learn design practices.