3 Blink an LED
This section demonstrates how to turn an LED on and off alternatively.
Although this use case is very simple, it is widely used in real life applications, and it helps in understanding how to achieve some of the very basic functions of the microcontroller. These functions are:
- Setting a pin direction
- Setting the output value of a pin
Setting a Pin Direction
The directions of the pins are stored in the PORTn.DIR register. For example, the
directions for the pins of PORTA are stored in PORTA.DIR. Each bit in the register
controls the direction of the corresponding pin, so PORTA.DIR bit 0 controls pin A0
(also called PA0). For a bit value of ‘1
’, the pin is configured as
output, while for ‘0
’, it is configured as input.
In most cases, it is only necessary to write one bit at a time. For this purpose,
there are eight bitmasks (PINx_bm) defined in the <avr/io.h>
header file, one for each bit. These macros represent bitmasks that have a
‘1
’ bit on x position and ‘0
’ bits in rest.
For example, PIN5_bm is 0b00100000
. Using these bitmasks in
conjunction with logic operations makes it possible to only write the corresponding
pin and make sure the others are unchanged.
To set (write to ‘1
’) pin x, use:
PORTn.DIR = PORTn.DIR | PINx_bm;
This because a logic OR
operation with ‘1
’ will
always result in ‘1
’, while logic OR
with
‘0
’ will leave the value unchanged (1|1 = 1, 1|0 = 1, 0|0 = 0).
Hence, the bitmask only has ‘1
’ on the position to be set.
To clear (write to ‘0
’) pin x, use:
PORTn.DIR = PORTn.DIR & ~PINx_bm;
This because a logic AND
operation with ‘0
’ will
always result in a ‘0
’, while logic AND
operation
with ‘1
’ will leave the value unchanged (1&1 = 1, 1&0 = 0,
0&0 = 0). Hence, the bitmask only has ‘0
’ on the position to
clear.
- The same approach, to only modify a single bit, can be used for all other registers as described in previous paragraphs.
- Logic operations with more bitmasks can be chained in an expression such as PORTA.DIR = PORTA.DIR | PIN0_bm | PIN1_bm.
- PINx_bp are similar macros that define the position of the bit in the register. For example, PIN2_bp has the value 2. The bitmask macros can be obtained using these macros and the shift operation.
Setting the Output Value of a Pin
The PORTn.OUT register controls the output values, so that ‘0
’ means
the pin will be pulled to GND and ‘1
’ means it will be pulled to
VDD.
0
’ and ‘1
’, can be inverted using the
PORTn.PINxCTRL register. See the PORTn.PINxCTRL register section in the data sheet
to understand its functionality.As previously described, the same approach to only modify a single bit can be applied to write to the PORTn.OUT register.
The following code toggles pin PB5 each 500 ms. An LED is attached to that pin on the ATmega 4809 Xplained Pro board.
#define F_CPU 3333333 #include <avr/io.h> #include <avr/delay.h> int main(void) { PORTB.DIR |= PIN5_bm; while (1) { PORTB.OUT |= PIN5_bm; _delay_ms(500); PORTB.OUT &= ~PIN5_bm; _delay_ms(500); } }
- To use the delay functions,
F_CPU must be defined before including
avr/delay.h
. F_CPU must match the CPU frequency. - The PORTn.OUTTGL register can also be used to toggle a pin.
- As an optimization, PORTn.OUTSET/PORTn.OUTCLR and PORTn.DIRSET/PORTn.DIRCLR registers can also be used to set the pin direction and output value. Their benefit is that instead of the read-modify-write operation, only a write operation is used.
An MCC generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here: