Вы находитесь на странице: 1из 11

Tutorial 2

“Special Function Registers (SFR) and


Bitwise Operations”
Instructed by TEP Sovichea

1. Special Function Registers

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.1. Using PORT peripheral


The PORT peripheral is used to control the digital IO of the microcontroller. In most 8-bit
microcontroller, the IOs are control as a group of 8. For example, in ATMEGA328P there are
3 ports B, C and D. Port B and D consist of 8 bits, whiles Port C only contain 7 bits. So, you
should always refer to the datasheet1 of a specific microcontroller that you are using for more
information.

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.

Figure 2. Using pull-up button configuration

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:

Figure 3. Push button and LED schematic

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

Source code for reading button and toggling the LED

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;

/* Configure push button pin */


// Enable pull-up by first configuring it as output and write 1 to Port
DDRC = 0b00000001;
PORTC = 0b00000001;
// Set PC0 as input
DDRC = 0b00000000;

// Initialize button state


uint8_t button_current, button_previous = 0b00000001;
bool button_state = 0;
while (1)
{
// Read input data register
button_current = PINC;
if (button_current == 0b00000000 && button_previous == 0b00000001) //
Push button active low
{
button_state = !button_state; // Flip the button state every push
// Toggling the LED state
if (button_state == 1)
{
PORTB = 0b00000000; // Turn on LED on logic low
}
else
{
PORTB = 0b00000010; // Turn off LED on logic high
}
}
button_previous = button_current; // Save the input port value
}
}

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.

Figure 5. Button bouncing when changing state from high to low

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

Right Shift (>>)


This operator will shift the value to right by a number of bit and fill the empty bit by “0”

A = 0100 1110
A >> 3 = 0001 0011

Left Shift (<<)


This operator is the same as the previous except that it shifts the value to the left

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”

SFR = SFR | (1 << n)


or
SFR |= (1 << n)
This operation will take the previous value of SFR OR with 1 at n position and write the new
value back to the SFR. Ex: we have the PORTB register with initial values below and we
want to write 1 to bit 2.

PORTB = 0001 1000


PORTB = PORTB | (1 << 2)
= 0001 1000 | 0000 0100
PORTB = 0001 1100

Clear bit n
Set bit n of SFR to “0”

SFR = SFR & ~(1 << n)


or
SFR &= ~(1 << n)
Using the above example, we want to clear bit 4 on PORTB

PORTB = 0001 1000


PORTB = PORTB & ~(1 << 4)
= 0001 1000 & ~(0001 0000)
= 0001 1000 & 1110 1111
PORTB = 0000 1000

Toggle bit n
Toggle bit n of SFR to “0” or “1”

SFR = SFR ^ (1 << n)


or

9
SFR ^= (1 << n)
We are going to use the same initial value for PORTB and toggle on and off bit 3

PORTB = 0001 1000


PORTB = PORTB ^ (1 << 3)
= 0001 1000 ^ 0000 1000
PORTB = 0001 0000
PORTB = PORTB ^ (1 << 3)
= 0001 0000 ^ 0000 1000
PORTB = 0001 1000

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);

/* Configure push button pin */


// Enable pull-up by first configuring it as output and write 1 to Port
DDRC |= (1 << PORTC0);
PORTC |= (1 << PORTC0);
// Set PC0 as input
DDRC &= ~(1 << PORTC0);

// Initialize button state


uint8_t button_current, button_previous = 1;
while (1)
{
// Read input data register
button_current = PINC & (1 << PORTC0); // Read only C0
if (button_current == 0 && button_previous == 1) // Push button active low
{
// Toggling the LED state

10
PORTB ^= (1 << PORTB1);
}
button_previous = button_current; // Save the input port values
}
}

11

Вам также может понравиться