Professional Documents
Culture Documents
Tutorial 2 "Special Function Registers (SFR) and Bitwise Operations"
Tutorial 2 "Special Function Registers (SFR) and Bitwise Operations"
Special Function Register (SFR) is a predefined register used for controlling peripherals in the
microcontroller. In general, each peripheral consists of 3 types of SFR:
Status register: a register that describes the status of the peripheral. Ex: Checking if
UART is currently busy transmitting or receiving the data
Control register: a register used to configure the parameters of the peripheral. Ex:
Setting Baudrate and parity for UART peripheral.
Data register: a register used for reading/writing data from/to the peripheral. Ex:
Reading the data from UART receive/transmit register.
However, for a less complex peripheral like PORT peripheral that used for controlling the
digital input and output of the microcontroller, you may find Status and Control register to
be merged into a single register. While in a more complex peripheral, you may find that there
are multiple registers for Status, Control and Data registers.
1
Figure 1. Peripherals in ATMEGA328P
1
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-
ATmega328P_Datasheet.pdf
2
There are 3 registers for controlling PORT peripheral:
DDRx : Data Direction Register used for setting port x as a digital input by writing
“0” or digital output by writing “1”2.
PORTx : Output data register at port x. Write a “0” will generate a logic low and “1”
is logic high.
PINx : Input data register at port x. Read a “0” will generate a logic low and “1” is
logic high.
Internal pull-up is very useful when working with button and I2C interface because these
applications requires a resistor to be connected to power rail. This effectively eliminate the
need for external resistor. When a button is connected as pull-up, it will be “logic high” when
button is released and “logic low” when the button is pushed. The transistor connected in
series with 𝑅 is responsible for enabling the PUD signal3 4.
2
x can be B, C or D for ATMEGA328P
3
You can see that there are 2 fast-switching diodes connected to every pin of the microcontroller. These are ESD (Electrostatic
Discharge) protection diodes used for preventing the pin from high voltage spikes.
4
𝐶 is just capacitance on microcontroller pin
3
1.2. Push button and LED example
Create a new GCC C Executable project named “Button_and_LED” and save it to your
preferred directory. In this example, want to toggle the LED on and off when pressing the
button. You will connect them to your Arduino as shown below:
If you take a look at the pin mapping below, you can see that the LED is connected to digital pin 9
and push button connected to analog pin 0.
Important note: Just because it said “analog pin” on Arduino, doesn’t mean it can only be used for
analog input! Most pin on ATMEGA328P are Multi-function, meaning that it can be configured to
connect to different types of peripheral, thus giving it different function. Refer to the datasheet for
which pin can be configured to which peripheral.
4
Figure 4. Schematic wiring on breadboard
5
#include <avr/io.h>
#include <stdbool.h>
int main(void)
{
/* Configure LED pin */
// Set PB1 as output by writing 1 bit 1 of DDRB
DDRB = 0b00000010; // B7 <----- B0
// Initialize logic high to PB1
PORTB = 0b00000010;
6
Note that in real-world test, it doesn’t go as smoothly as expected due to an effect known as
“contact bouncing” as shown in the figure below. This effect always happens on mechanical
switch during press because of imperfection of human and the limitation of mechanical switch
itself.
Therefore, during your testing, you will see that your LED will toggle a few times and sometimes it will
go back to its original state due to having odd number of bouncing at the contact point of your button.
In the future tutorial, you will learn how to “debounce” your button using hardware and
software in order to produce an accurate button press.
2. Bitwise operation
Table 1. Bitwise operator and its function
Operator Function
~ Bitwise NOT
& Bitwise AND
| Bitwise OR
^ Bitwise XOR (Exclusive OR)
>> Shift bit to the right
<< Shift bit to the left
In C language, there are 6 fundamental bitwise operators as shown in the table. In the last
section, you can see that there was no operator used at all in the source code. This can make
the code hard to read and cause a lot of confusion. Another disadvantage of the code in
previous section is that we are writing all 8 bits to SFR, not just one. This will overwrite
whatever in the registers with the new value, so we need to find a way of writing just one bit
to the position of interest. In this section, we will replicate the functionality of the LED
7
toggling code with the help of bitwise operators. You will see that these operators are very
powerful at manipulating bit in SFR and allows the code to be written in clean and concise
readable manner.
Bitwise NOT
Bitwise NOT is a unary operator, meaning that it only takes one argument and invert all its
bit. Assume an 8-bit value “A” below
A = 0100 1110
~A = 1011 0001
Bitwise AND
Assume two 8-bit value “A” and “B”
A = 0100 1110
B = 1111 0001
A & B = 0100 0000
Bitwise OR
Let’s use the same value from above, thus
A = 0100 1110
B = 1111 0001
A | B = 1111 1111
Bitwise XOR
A = 0100 1110
B = 1111 0001
A ^ B = 1011 1111
A = 0100 1110
A >> 3 = 0001 0011
8
A = 0100 1110
A << 3 = 0111 0000
Now that have all the understand the concept, we will use these operators to build 3 powerful
bit operation on SFR such that it does not affect other bits.
Set bit n
Set bit n of SFR to “1”
Clear bit n
Set bit n of SFR to “0”
Toggle bit n
Toggle bit n of SFR to “0” or “1”
9
SFR ^= (1 << n)
We are going to use the same initial value for PORTB and toggle on and off bit 3
New source code from the previous section. Note that in this new code, there is not need for
toggle statement anymore because we can toggle bit directly using bitwise operation, thus
reducing the code significantly.
#include <avr/io.h>
#include <stdbool.h>
int main(void)
{
/* Configure LED pin */
// Set PB1 as output by writing 1 to bit 1 of DDRB
DDRB |= (1 << PORTB1); // B7 <----- B0
// Initialize logic high to PB1
PORTB |= (1 << PORTB1);
10
PORTB ^= (1 << PORTB1);
}
button_previous = button_current; // Save the input port values
}
}
11