unit-05-avr
unit-05-avr
Computer Structure
E.T.S.I. Informática
Universidad de Sevilla
Contents
●
Introduction
– What is a microcontroller?
– The Atmel AVR family
●
The ATmega328P
– Block diagram and CPU core
– Registers
– Program memory
– Data memory
– Digital I/O ports
– Development boards
Contents
●
AVR assembly programming
– Requierements
– AVR instruction set
– Toolchains, platforms, frameworks and IDE’s
– The GNU AVR toolchain
– C pre-processor macros
– GNU assembler directives and expressions
– Assembling, uploading and testing
– AVR programming (uploading) alternatives
– Debugging programs
– AVR IDEs and PlatformIO
– Assembly subroutines
– Reading and writing program memory
– Working with data other than 8 bits
Contents
●
AVR C programming
– C program example
– Code optimization
– Compiling and disassembling
– C language functions
– Wait: why learning assembly?
– Where and why is assembly used?
– AVR C calling convention
– Calling assembly subroutines from C
– Calling C functions from assembly.
Contents
●
Interrupts
– Interrupt vectors
●
Timers
– AVR timers
– Timer/Counter 1
– Example: blink with interrupts.
●
Other peripherals
– Analog inputs
– SPI
– TWI (I2C)
Objectives
●
Understand the structure of a real computer.
●
Understand typical peripherals present in real coputers.
●
Understand the importance of small computers in today’s computer
technology (from sensor networks to the IoT).
●
Learn to program a computer at the lowest level (assembly).
●
Understand the low-level programming conventions.
●
Learn the basic tools involved in computer programming.
●
Understand the role of programming frameworks and Integrated
Development Environments (IDE’s).
Bibliography
●
ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet – Technical
information about ATmegaX8 chips. Use as reference.
●
AVR Instruction Set Manual – Detailed description of AVR assembly
instructions. Use as reference.
●
AVR microcontroller programming with avr-libc and the GNU toolchai
n
– AKA the “avr-bare” repo. Must read.
●
AVR Libc Home Page and documentation – Definitions for C and
assembly programming. Use as reference.
●
AVR Libc source code – Use as reference.
What is a microcontroller?
●
Typical characteristics
– Low cost.
– Robust.
– Small size.
– Low power consumption.
– Low processing power.
– Harvard architecture.
– Non-volatile program memory (EEPROM/Flash).
●
Typical applications.
– Embedded systems in general.
– Sensor networks.
– Robotics.
– Control systems: small appliances, automotive, remotes, heating, etc.
Typical applications
https://iot-analytics.com/state-of-the-iot-2020-12-billion-iot-connections-surpassing-non-iot-for-the-first-time/
ATmega328P
Quick exercise:
1) Find 3 things that are similar to the YASAC.
2) Find 3 things that are different from the YASAC.
Pointer registers
Program memory
●
16-bit wide.
●
All instruction codes are 16 bit.
– Some instructions use an
additional 16-bit immediate
value in the next position.
●
Also byte-addressable by
program memory load and
store instructions (will see
later).
●
ATmega328P memory is:
– 16k x 16 (word addressable)
– 32k x 8 (byte addressable)
●
Byte addressable.
●
All general-purpose registers and I/O registers are mapped to data
memory.
– First 64 I/O registers can also be accessed by specific input/output
instructions (faster).
●
Real usable data memory starts at 0x100
– ATmega328P data memory goes from 0x100 to 0x8FF (2kB).
PORTC
PORTD
Development boards
●
Microcontrollers are autonomous
– Only need a supply voltage and a connection to a programmer to be
used.
●
Development boards simplify doing projects with microcontrollers by
including additional circuitry and devices:
– Supply voltage connections and regulation.
– Easy to use connection pins.
– Easy to use programming pins.
– Simple peripherals: LED’s, switches, push buttons, etc.
– Programming interface (e.g. USB): no need of an external programmer.
– BUT: not all pins of the MCU may be available, some MCU resources
may be used by the board’s hardware or software, etc.
●
Arduino boards are the most popular boards built around the AVR
platform.
Arduino UNO
AVR programming
●
AVR microcontrollers are programmed in AVR assembly. There are
tools that translate assembly code to machine code (assemblers).
●
Most AVR microcontrollers can also be programmed in C and C++.
There are programs that translate C/C++ to machine code
(compilers) and a library of standard C functions.
●
All YASAC assembly instructions can be used with AVR’s (we copied
from them :)
– AVR assembly has many more instructions.
– AVR instructions have different restrictions.
– AVR instruction format is different (not binary compatible).
– AVR peripherals are different.
You do not need to know all the details from the beginning.
Start with a basic set and learn more as you need.
Branch instructions
●
YASAC-like:
– BRBS, BRBC, and derivatives.
●
Relative jump and call:
– RJMP, RCALL.
– Faster and uses less program memory.
●
Indirect call (to address in Z):
– ICALL.
●
Skip and bit test instructions: easy decisions on single bits.
– Compare and skip if equal: CPSE.
– Skip if bit in register is cleared/set: SBRC/SBRS
– Skip if bit in I/O register is cleared/set: SBIC/SBIS
●
Return from interrupt:
– RETI.
AVR toolchains
Tools for embedded systems are cross-
●
Atmel’s AVR assembler tools: the tools run in a different
architecture than the code they produce:
– Free licensed assembler.
●
Cross-compiler
– Supports basic assembly programming.
●
Cross-assembler
●
AVR GNU toolchain ●
Etc.
– The toolchain that drives GNU/Linux and thousands of Free Software
projects around the world (including Arduino).
– Complete set of tools to program AVR MCU’s in assembly, C and C++.
●
Compiler, assembler, linker, etc.
– Complete programming platform together with external tools:
●
Programmer (avrdude), in-hardware debugger (avarice), simulators (simavr), etc.
– Most AVR projects use this.
●
MPLAB XC compiler
– Non-free C compiler from Microchip.
– Supports AVR MCU’s since Microchip acquired Atmel.
Assembly code
assembler main.o
C source code Object (compiled)
code
delay.o
main.elf Executable
Linker program
libc.a
Programmer
Compiled
library Loaded
program
C source main.c
code
Object wait.o
code Executable
program
pre-
compiler assembler linker
processor
C pre-processor macros
●
Every assembly or C AVR source file should include the avr/io.h
header file that defines standard pre-processor macros for AVR.
●
The appropriate macros for the MCU in use will be loaded
automatically (from the -mmcu paramter to avr-gcc).
●
All peripheral registers are defines, like PORTB, DDRB, PINB, PB5,
etc.
●
Some convenient macro functions are also defined:
– _SRF_IO_ADDR(x): converts x from memory to I/O address.
●
_SRF_IO_ADDR(x) = x - 32 = x - 0x20.
– _BV(x): Bit Value for number x.
●
_BV(x) = (1 << x)
Pre-processor macros are text transformations run before actual
compilation/assembly.
●
Useful to define names for constants.
●
Some macros may take a parameter making them look like functions.
Example 1
a) Write an assembly program for the ATmega328P in an Arduino UNO board that reads
PORTD7 (UNO D7) and write its value to PORTB5 (UNO D13 -LED-). Use load and
store instructions to access port data and logic instructions to alter their bits.
b) Modify the program to use the more efficient bit and bit-branch instructions.
c) Modify the program to activates PORTB5 only when PORTD6 and PORTD7 are one
(emulates an AND gate).
d) Estimate the delay from an input change to an output change if the MCU runs at
16MHz. Compare with the delay of a 74HC08 logic gate.
Load/store instructions do the job but the AVR has more powerful instructions
for the task: bit set/clear instructions and bit test and skip instructions.
avrdude: AVR device initialized and ready to accept instructions Upload to the
microcontroller
Reading | ################################################## | 100% 0.00s
debugWIRE
Single pin protocol (connected to RESET) that
allows programming and in-chip debugging.
See the AVR block diagram Useful for MCU's that have a few pins only.
Needs an external control board.
Debugging programs
●
Programmers (humans) are not perfect, thus programs have errors
(bugs).
●
Finding and correcting errors in programs (debugging) can be really
tough.
– Just executing and watching the program failing does not help very
much.
●
Debuggers can execute programs in a controlled way:
– Single instruction execution.
– Execution until a break point.
– Stop the program when a condition is met.
– Inspect registers/variables as the program runs.
– Change the value of registers/variables as the program runs.
– Etc.
Debugging programs
●
Native vs non-native debuggers
– Native: the debugger runs in the same computer than the program being
debugged.
– Non-native: the debugger runs in a different computer than the program
being debugged.
●
The program being debugged runs in the target system.
●
Non-native debuggers
– The target system may be a real computer or a simulated one.
– Microcontroller development uses non-native debuggers (the target system
is not powerful enough to run the debugger).
– MCU's with a debug unit can be used as target systems: in-chip debugging.
●
Debug symbols
– The compiler can include debug symbols in the object code with links to the
original source code (-g option in gcc).
– It makes debugging much more powerful and easy.
See the avr-bare repo for a quick introduction to avr-gdb and simavr simulation.
Departamento de Tecnología Electrónica – Universidad de Sevilla 51
Contents
Integrated Development Environments
for AVR microcontrollers
●
PlatformIO
– Free Software IDE integrated with VSCode editor.
– Multi-platform.
– Supports many embedded systems platforms, including the AVR.
– Uses the GNU toolchain, avrdude and simavr as back-end.
– Supports the Arduino Framework.
●
Can be used to develop Arduino projects (with Arduino libraries).
●
Microchip Studio (former Atmel Studio)
– Official IDE from the AVR foundry.
– Supports other MCUs from Microchip (PIC, etc.).
– Supported toolchains:
●
Basic Atmel assembler.
●
GNU toolchain.
●
Microchip's own C compiler and toolchain.
– Based on MS Visual Studio (MS Windows only).
– Nice peripheral interface within the debugger.
Example 1
c) Modify the program to activate PORTB5 only when PORTD6 and PORTD7 are one
(emulates an AND gate).
Example 1
d) Estimate the delay from an input change to an output change if the MCU runs at
16MHz. Compare with the delay of a 74HC08 logic gate.
●
Worst case is when the input changes right after it has been checked:
the whole loop have to be executed before activating the output.
●
Input changes from 10 to 11 (output changes from 0 to 1)
– rjmp (2), cbi (2), rjmp (2), sbis-true (3), sbis-true (3), sbi (2), input sync
(1), output sync (1)
– 16 cycles at 16MHz = 1 microsecond.
●
Input changes from 11 to 10 (output changes from 1 to 0)
– Fewer cycles because one sbis instruction is false (1 cycle only).
●
Typical 74HC delay: 10ns = 0,01us (100 times faster).
– NOTE: the actual gate is much faster. This delay includes the input and
output buffers in the chip.
Assembly subroutines
●
Programs in high-level languages use functions, objects and
methods extensively. Assembly language programs use subroutines
for the same purpose:
– Code re-usability, program modularization, easier development, easier
debugging, etc.
●
Arguments can be passed to subroutines using registers or the
stack.
– Which option is better?
– Which registers may be used?
●
Subroutines will use registers for their calculations.
– Will a subroutine clobber my used registers?
– Who is responsible for saving register values, caller or callee?
Assembly subroutines
Example 2
Write a subroutine in assembly that adds up a list of 8-bit number in data memory. The
subroutine should add all the numbers up to the first one that is zero.
The memory address where the list starts is passed to the subroutine in registers r25:r24.
The sum should be left in register r24 before returning from the subroutine.
Leave the subroutine in a separate file so that it can be re-used in other projects, and write
a simple program to test it.
#include <avr/io.h>
; check the return value (should be 19) and activate PB5 if correct
cpi r24, 19
brne finish ; not correct. finish the program
sbi _SFR_IO_ADDR(PORTB), PB5 ; activate PB5
finish: ; just stay here for a while
rjmp finish
Departamento de Tecnología Electrónica – Universidad de Sevilla 61
Contents
Assembly subroutines
Add list subroutine
; File: add_list.S
; Description: Subroutine to add a list of bytes in memory up to the first
; byte that is zero.
; Input: list address in r25:r24.
; Return: list sum in r24
; Author: Jorge Juan-Chico
; Date: 2021-05-27
; main loop: read data from the list, return if zero or accumulate and
; read the next value.
loop:
ld r0, z+
tst r0 ; test r0 (equivalent to and r0,r0)
breq finish
add r24, r0
rjmp loop
finish:
ret
Quick exercise
Take a look at the main loop. Can it be improved to run faster?
Example 3
a) Write a test program for the add list subroutine of example 2 that creates the list in data
memory by copying a constant list stored in program memory.
b) Write a similar subroutine to the one in example 2 but operating on constant data stored
in program memory instead of data memory. Modify the test program to test it.
.global main
main:
; initialize the output pin
sbi _SFR_IO_ADDR(DDRB), PB5 ; set PB5 as output
cbi _SFR_IO_ADDR(PORTB), PB5 ; set PB5 to zero
; check the return value (should be 19) and activate PB5 if correct
cpi r24, 19
brne finish ; not correct. finish the program
sbi _SFR_IO_ADDR(PORTB), PB5 ; activate PB5
finish: ; just stay here for a while
Departamento de Tecnología Electrónica – Universidad de Sevilla
rjmp finish 66
Contents
; check the return value (should be 19) and activate PB5 if correct
cpi r24, 19
brne finish ; not correct. finish the program
sbi _SFR_IO_ADDR(PORTB), PB5 ; activate PB5
finish: ; just stay here for a while
rjmp finish
; pre-calculated result
.equ RESULT, 6081
Controlling time
●
Controlling time is important in many MCU applications:
– Wait an amount of time before taking an action.
●
E.g. triggering an alarm only when the sensor is active for at least 3
seconds.
– Do something regularly with a fixed time lapse.
●
E.g. blinking a LED or incrementing a counter.
– Waiting some time after a button press to avoid bounces.
●
Two ways to control time:
– Waiting loops
●
Execute a loop a number of times.
●
The processor does not stop (bad for energy consumption).
●
Elapsed time can be calculated counting the number of cycles and using the
processor’s clock frequency.
– Using a timer peripheral and possible interrupts (will see later).
Controlling time
Example 5
Write a subroutine in assembly that waits for a number of milliseconds. The number of
milliseconds to wait is passed in registers r25:r24. Returns r25:r24=0.
Using the subroutine, write an assembly program that makes an LED to blink once every
second: it is 500ms on and 500ms off. The LED is connected to pin PB5.
Subroutine ; Define the board's CPU frequency in Hz. Depends on the board and chip
; configuration.
.equ F_CLK, 16000000
; Code segment
;
.text
;
; Subroutine: delay_ms
;
; Input: wait time in ms (r25:r24)
; Return: 0 (r25:r24 = 0)
;
.global delay_ms
delay_ms:
ldi r27,hi8(NPASS_1MS) ; initialize 1ms loop
1ms loop. ldi r26,lo8(NPASS_1MS)
wait_1ms_loop: ; 1ms loop (see AVR Instruction Set Manual)
sbiw r26,1 ; substract 1 (2 cycles)
brne wait_1ms_loop ; compare (1 cycle if false, 2 if true)
Repeat r25:r24 sbiw r24,1 ; main loop check
times. brne delay_ms
ret
Controlling time
Test program ;
;
Commutes
blink in
bit 5 in PORTB (PB5) every 500ms. It makes the built-in LED to
the Arduino UNO board that uses an ATmega328p. Can be easily
; modified to us a different output bit or blinking frequency.
; Uses the delay_ms subroutine in delay_ms.S.
; AVR i/o symbols definitions. Defines port and memory names for easier
; programming. Do not write AVR programs without it
#include <avr/io.h>
;
; Code segment
;
.text ; text (code) segment starts here
;
; Main program
;
.global main
main: ; our program starts here
; Initialization code
ldi r16, _BV(PB5) ; set bit 5 in PORTB as output
out _SFR_IO_ADDR(DDRB), r16 ; (the built-in LED in Arduino UNO is here)
cbi _SFR_IO_ADDR(PORTB), PB5 ; clear PB5
ldi r16, lo8(DELAY) ; preload delay
ldi r17, hi8(DELAY)
; Main loop
loop:
movw r24, r16 ; argument to the delay_ms subroutine
In output mode, call delay_ms ; wait
sbi _SFR_IO_ADDR(PINB), PB5 ; toggle the LED
setting PINB toggles rjmp loop ; repeat forever
the corresponding pin.
C program example
Example 6
a) Write a C program for the ATmega328P in an Arduino UNO board that reads PORTD7
(UNO D7) and write its value to PORTB5 (UNO D13 -LED-).
b) Debug the program using a debugger and simulator. Test the program in the board if
you have one.
C program example
// Trivial AVR C program
// Read bit 7 of PORTD and write bit 5 of PORTB
Example 7
a) Compile the program in example 5 to assembly language and compare the generated
assembly code with that of example 1. Try different options for compiler optimization:
●
-Os, -Og, etc.
b) Imagine you do not have the original C code anymore. You can still list the assembly
code by disassembling the executable file. Disassemble the code compiled with size
optimization “-Os” and compare with the assembly code generated in (a). Pay attention
to the extra code introduced by the linker.
.file "main.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0 ; Read macros with port numbers
__zero_reg__ = 1 #include <avr/io.h>
.section .text.startup,"ax",@progbits
.global main .text
.type main, @function
main: .global main
/* prologue: function */ main:
/* frame size = 0 */ sbi _SFR_IO_ADDR(DDRB), DDB5
/* stack size = 0 */ ldi r16, 0
.L__stack_usage = 0 out _SFR_IO_ADDR(DDRD), r16
ldi r24,lo8(32) sbi _SFR_IO_ADDR(PORTD), PD7
out 0x4,r24 loop:
out 0xa,__zero_reg__ sbis _SFR_IO_ADDR(PIND), PIND7
ldi r24,lo8(-128) rjmp is_zero
out 0xb,r24 sbi _SFR_IO_ADDR(PORTB), PB5
.L2: rjmp continue
sbis 0x9,7 is_zero:
rjmp .L3 cbi _SFR_IO_ADDR(PORTB), PB5
sbi 0x5,5 continue:
rjmp .L2 rjmp loop
.L3:
cbi 0x5,5
rjmp .L2
.size main, .-main
.ident "GCC: (GNU) 5.4.0"
Disassembling
main.elf: file format elf32-avr
avr-objdump -dr main.elf > main.dis
Disassembly of section .text:
.file "main.c" 00000000 <__vectors>:
__SP_H__ = 0x3e 0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
__SP_L__ = 0x3d 4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
__SREG__ = 0x3f 8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
__tmp_reg__ = 0 [...]
__zero_reg__ = 1 64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
.section .text.startup,"ax",@progbits
.global main 00000068 <__ctors_end>:
.type main, @function 68: 11 24 eor r1, r1
main: 6a: 1f be out 0x3f, r1 ; 63
/* prologue: function */ Linker 6c: cf ef ldi r28, 0xFF ; 255
/* frame size = 0 */ 6e: d8 e0 ldi r29, 0x08 ; 8
/* stack size = 0 */ 70: de bf out 0x3e, r29 ; 62
.L__stack_usage = 0 72: cd bf out 0x3d, r28 ; 61
ldi r24,lo8(32) 74: 0e 94 40 00 call 0x80 ; 0x80 <main>
out 0x4,r24 78: 0c 94 4b 00 jmp 0x96 ; 0x96 <_exit>
out 0xa,__zero_reg__
ldi r24,lo8(-128) 0000007c <__bad_interrupt>:
out 0xb,r24 7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
.L2:
sbis 0x9,7 00000080 <main>:
rjmp .L3 80: 80 e2 ldi r24, 0x20 ; 32
sbi 0x5,5 82: 84 b9 out 0x04, r24 ; 4
rjmp .L2 84: 1a b8 out 0x0a, r1 ; 10
.L3: 86: 80 e8 ldi r24, 0x80 ; 128
cbi 0x5,5 88: 8b b9 out 0x0b, r24 ; 11
rjmp .L2 8a: 4f 9b sbis 0x09, 7 ; 9
.size main, .-main 8c: 02 c0 rjmp .+4 ; 0x92 <main+0x12>
.ident "GCC: (GNU) 5.4.0" 8e: 2d 9a sbi 0x05, 5 ; 5
90: fc cf rjmp .-8 ; 0x8a <main+0xa>
92: 2d 98 cbi 0x05, 5 ; 5
94: fa cf rjmp .-12 ; 0x8a <main+0xa>
Our code is here.
00000096 <_exit>:
96: f8 94 cli
00000098 <__stop_program>:
Departamento de Tecnología Electrónica – Universidad
98: ff cf rjmp .-2 de Sevilla
; 0x98 84
<__stop_program>
Contents
Structure of an executable program
(generated by the GNU toolchain)
main.elf: file format elf32-avr
00000096 <_exit>:
96: f8 94 cli Exit code: disable
interrupts and block
00000098 <__stop_program>: the processor.
98: ff cf rjmp .-2 ; 0x98 <__stop_program>
Departamento de Tecnología Electrónica – Universidad de Sevilla 85
Contents
Wait!
Why learning assembly?
●
If we have C and C compilers, why learning assembly?
– The academic reason: Software Engineers supposed to fully
understand how software works.
– The obvious reason: someone have to write the compilers.
●
Not only C, but any system-level language compiler: C++, Rust, etc.
– The practical reason: some operating system code and C library
functions are actually written in assembly (see _delay_ms() in avr-libc
for example).
●
Is assembly and low-level programming that useful?
– Embedded systems and the IoT.
– Operating systems.
– Device drivers.
– Game engines.
– It’s fun!
C language functions
Example 8
a) Write a C language function that adds up a list of 8-bit number in data memory. The
function should add all the numbers up to the first one that is zero. The sum should be
returned by the function as a 16-bit integer (type int).
b) Write a simple C program to test the function.
c) Take a look at the assembly code generated by the compiler and compare with the add
list assembly version in example 4.
C language functions
// File: add_list.c
// Add bytes in a list up to the first zero
// File: main.c
// Test add_list
#include <avr/io.h>
int main(void)
{
char data_list[] = {1, 2, 3, 5, 8, 0}; // test list to add
int sum; // store the sum here
DDRB = _BV(PB5); // set PB5 as output
C language functions
avr-gcc -mmcu=atmega328p -Os -S -o add_list_Os.s add_list.c
Example 9
a) Check if the add list subroutine in example 4 adheres to the C language calling
convention. If not, modify the subroutine to make it compliant with the convention.
b) Write a simple C program to test that calling the subroutine from C actually works. You
may adapt the test program in example 8.
Solution:
a)Yes, the add_list_w subroutine adheres to the C calling convention:
●
Argument in r25:r24.
●
Return value in r25:r24
●
Use registers correctly: r0 (scratch register), r24, r25, z (r31:r30).
b)Just use add_list_w() instead of add_list().
Example 10
We want to make an LED to blink for 0.1s every second. Write a C program for the AVR to do the
task.
a) Use the _delay_ms() function from the C library to control the time.
b) Write another version that uses the delay_ms assembly subroutine of example 5 to control the
time.
c) (Optional) Disassemble both versions and compare the generated code.
d) (Optional) Find the source code of the _delay_ms() function in the avr-libc distribution. Compare
to the delay_ms assembly subroutine.
Example 11
Write a subroutine that adds the absolute values of a list of 16-bit words in data memory.
Use the abs function in the standard C library to calculate the absolute value. Write a test
program for the subroutine.
; Subroutine: add_list_abs
;
; Arguments: r25:r24 - address of the list.
; r22 - number of elements.
; Return: r25:r24 - result of the sum (16 bits). GCC will automatically
; Description: Adds the absolute value of all the
; elements in the list. link to the C library and
; Returns 0 if the number of elements is zero.
; Uses the abs(int) function from the C library. find the “abs” function.
; Read macros with port numbers (and more)
#include <avr/io.h>
.text
; main loop: read data from the list,
.global add_list_abs ; calculate abs, accumulate and repeat.
add_list_abs: loop:
tst r22 ld r24, z+
brne init ld r25, z+
clr r25 call abs
clr r24 add r18, r24
ret adc r19, r25
init: subi r22, 1
; use Z as pointer and brne loop
; r19:r18 as accumulator
mov zl, r24 ; save result and return
mov zh, r25 movw r24, r18
clr r19 ret
clr r18
Interrupts
●
Interrupts are activated by peripherals or external signals.
●
Interrupts cause the processor to jump to a service routine depending on
the interrupt line activated.
– Similar to a “call” but triggered by hardware.
– Service routines return with “reti” instruction.
●
Interrupts allow doing things “just in time” and avoid continuous “polling”.
– An input bit in a digital port changes.
– Data arrives to a serial port.
– A timer has finished, etc.
●
While not “interrupted” the processor can (should) be in a “sleep” mode,
not executing instructions, and saving power.
– Most real-life programs execute most or all of their functions in interrupt
service routines.
– Essential in battery-powered systems.
Interrupts
●
Interrupts can be selectively enabled or disabled.
●
The “I” flag in the status register enables (1) or disables (0) interrupts
globally. Use SEI instruction to enable and CLI instruction to disable.
●
Most peripherals may generate interrupts and there are also external
interrupt lines.
●
A peripheral may generate an interrupt when configured to do so.
– Check the peripheral’s control registers for bits that enable interrupts.
Interrupt vectors
●
An interrupt activation will make the processor to jump to an interrupt
vector.
– The specific executed interrupt vector depends on the source of the
interrupt .
●
Interrupt vectors are located at the beginning of program memory.
●
Each vector uses two word addresses in the ATmega328P.
●
Each vector will typically contain a “jmp” instruction to the interrupt
services routine. This way:
– Interrupt service routines can be easily defined.
– Interrupts can be easily disabled by software.
●
The GNU GCC compiler will create the interrupt vector in program
memory automatically.
– The programmer just have to define the interrupt vector destination
routines using standard labels: “<interrupt_source_name>_vect:”
Interrupt vectors
Sleep modes
●
The processor core is the most energy consuming device in the
MCU.
●
Ideally, the processor core should be stopped while there is nothing
useful to do (just wait for interrupts).
– It is essential in battery-powered devices.
●
The SLEEP instruction will stop the processor and it will be waken up
when necessary (an interrupt occurs).
●
Sleep mode have to be enabled for the SLEEP instruction to do
something useful.
●
The AVR has different sleep modes, with different power savings and
wake up conditions.
●
In addition, unused peripherals can be shut down to save even more
power by programming the Power Reduction Register (PRR).
Sleep modes
Typical “sleepy”
main loop
; Main program
sei ; enable interrupts
ldi r16, _BV(SE) ; enable "idle"
out _SFR_IO_ADDR(SMCR), r16 ; sleep mode
loop:
sleep ; wait for interrupts
rjmp loop
Sleep modes
Typical “sleepy”
main loop
#include <avr/io.h> ;
.text ; PCINT0 interrupt handler
.global main ;
main: .global PCINT0_vect
; PCINT0 configuration: PCINT0_vect:
ldi r16, _BV(PCIE0) ; enable PCINT0 interrupt ; set PB5 to PB0
sts PCICR, r16 sbis _SFR_IO_ADDR(PINB), PB0
ldi r16, _BV(PCINT0) ; enable interrupt on rjmp int_clear_output
sts PCMSK0, r16 ; PCINT0 (PB0) change sbi _SFR_IO_ADDR(PORTB), PB5
rjmp int_continue
; PORTB configuration int_clear_output:
ldi r16, _BV(PB5) | _BV(PB1) ; PB1 and PB5 as output cbi _SFR_IO_ADDR(PORTB), PB5
out _SFR_IO_ADDR(DDRB), r16 ; rest of PORTB as input int_continue:
ldi r16, ~_BV(PB5) ; activate pull-ups reti
out _SFR_IO_ADDR(PORTB), r16
; Main program
sei ; enable interrupts globally
ldi r16, _BV(SM1) | _BV(SE) ; enable "power-down"
out _SFR_IO_ADDR(SMCR), r16 ; sleep mode
loop:
cbi _SFR_IO_ADDR(PORTB), PB1 ; clear PB1 before sleep
sleep ; wait for interrupts “Power-down” mode is good
sbi _SFR_IO_ADDR(PORTB), PB1 ; set PB1 at wake-up here because it switches off
rjmp loop
most devices.
AVR Timers
●
AVR timers applications:
– Measure time.
– PWM generation.
– Execute periodic tasks (using interrupts).
– ...
●
Timer/Counter1 in the AVR has the following registers:
– Control registers: TCCR1A, TCCR1B, TCCR1C.
– Timer/Counter registers: TCNT1(H/L).
– Output compare registers: OCR1A(H/L), OCR1B(H/L).
– Input capture register: ICR1(H/L).
– Interrupt mask register: TIMSK1.
– Interrupt flag register: TIFR1
Timer/Counter 1
Timer/Counter 1
Timer/Counter 1
Timer/Counter 1
Example 13
Blink a LED every 250ms using interrupts.
Modify it to blink only when a button connected to PB0 is pressed (PB0 = 0).
Other peripherals