Académique Documents
Professionnel Documents
Culture Documents
// Iniciar o programa
// … (código que inicialize variáveis, LEDs, etc. aqui)
// Ligar interrupções particulares
// Ligar interrupções globais
// Definir ISR para lidar com as interrupções
particulares ligadas.
Para lidar com registos e interrupções, iremos precisar dos seguintes headers: <avr/io.h> e
<avr/interrupt.h> (já podemos também adicionar a função main(), e um loop eterno):
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
// … (código que inicialize variáveis, LEDs, etc. aqui)
// Ligar interrupções particulares
// Ligar interrupções globais
for(;;);
}
// Definir ISR para lidar com as interrupções
particulares ligadas.
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
// … (código que inicialize variáveis, LEDs, etc. aqui)
// Ligar interrupções particulares
sei();
for(;;);
}
// Definir ISR para lidar com as interrupções
particulares ligadas.
Neste tópico, vamos ignorar as interrupções individuais, pois ainda não falámos de nenhuma
interessante, e vamos concentrar-nos nas ISR.
A biblioteca do avr dá-nos uma macro muito útil para definir uma ISR, e tem o nome ISR()
(espertos, não são? xD), com um argumento: o nome do vector da interrupção. O vector da
interrupção é basicamente o que identifica qual a interrupção com que estamos a lidar. Esta
informação encontra-se na página 57 do datasheet que eu tenho (início do capítulo sobre
interrupções/capítulo 9), numa tabela, na coluna Source.
Por exemplo, no tópico a seguir, vamos lidar com a interrupção que ocorre no pino digital 2. Este
pino tem o nome de INT0. Ao olharmos para a tabela, vemos que a source da interrupção é INT0.
Para usarmos isto como argumento para a macro ISR, basta adicionar “_vect”. Assim, o vector é:
INT0_vect:
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
// … (código que inicialize variáveis, LEDs, etc. aqui)
// Ligar interrupções particulares
sei();
for(;;);
}
ISR(INT0_vect) {
// Definir o que fazer quando acontece esta interrupção
}
(para alguns, esta declaração da função ISR pode ser confusa, pois não tem tipo. No entanto,
lembrem-se que é uma macro, e por isso ISR não é realmente o que fica no código final).
Nota: Se notarem, alguns dos Vectores na datasheet têm espaços ou vírgulas no nome. Basta
substituir esses por _. Por exemplo, para a interrupção gerada quando se recebem dados por serial,
temos o seguinte Source: USART, RX. O argumento que usamos para a macro ISR é:
USART_RX_vect.
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
// … (código que inicialize variáveis, LEDs, etc. aqui)
// Ligar interrupções particulares
sei();
for(;;);
}
ISR(INT0_vect) {
// Definir o que fazer quando acontece esta interrupção
}
Vamos usar para este efeito o pino 2 (podia ser feito com o pino 3 também, com poucas
diferenças).
O objectivo deste código vai ser mudar o estado de um LED quando se toca num botão ligado ao
pino 2. O LED vai estar ligado ao pino digital 4 (PD4).
Vamos começar por criar uma variável global, com o estado do pino e inicializar esse pino como
output (vai começar desligado) (para mais informações sobre GPIOs, ler o tutorial que pus no
primeiro post), e já vamos colocar o código necessário para fazer toggle ao pino na ISR.
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
DDRD |= (1<<PD4); // Inicializar o pino digital 4 como
output.
PORTD &= ~(1<<PD4); // Inicializar o pino digital 4 como
desligado.
// Ligar interrupções particulares
sei();
for(;;);
}
ISR(INT0_vect) {
// Definir o que fazer quando acontece esta interrupção
output = ~output; // Alterar o estado
PORTD &= ~(1<<PD4); // Desligar o pino – isto é
necessário para quando o output é 0 se poder desligar.
PORTD |= ((output&1)<<PD4) // output&1 pois só nos
interessa o primeiro bit, assim evitamos mexer nos outros
pinos.
}
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void) {
DDRD |= (1<<PD4); // Inicializar o pino digital 4 como
output.
PORTD &= ~(1<<PD4); // Inicializar o pino digital 4 como
desligado.
EICRA |= ((1<<ISC00) | (1<<ISC01)); // Configurar
interrupção no pino INT0 para quando este transita para
HIGH
// Ligar interrupções particulares
sei();
for(;;);
}
ISR(INT0_vect) {
// Definir o que fazer quando acontece esta interrupção
output = ~output; // Alterar o estado
PORTD &= ~(1<<PD4); // Desligar o pino – isto é
necessário para quando o output é 0 se poder desligar.
PORTD |= ((output&1)<<PD4) // output&1 pois só nos
interessa o primeiro bit, assim evitamos mexer nos outros
pinos.
}
Agora só nos falta mesmo ligar a interrupção associada ao INT0. O bit que controla isto é o
INT0 no register EIMSK. Assim, é só modificar o código, e fica completo:
// Iniciar o programa
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(INT0_vect) {
// Definir o que fazer quando acontece esta interrupção
output = ~output; // Alterar o estado
PORTD &= ~(1<<PD4); // Desligar o pino – isto é
necessário para quando o output é 0 se poder desligar.
PORTD |= ((output&1)<<PD4) // output&1 pois só nos
interessa o primeiro bit, assim evitamos mexer nos outros
pinos.
}
Agora temos um código que, ao clicarmos num botão que liga o pino digital 2 e o pólo positivo,
faz toggle do pino digital 4 (nota: visto que não fizemos nada para tratar do bouncing, podem haver
resultados inesperados. No entanto, como isto é só para exemplo, não considerámos muito
importante).
Para experimentarem, podem montar o seguinte circuito:
//...
int i,j;
for(;;) {
cli();
j = i; // o i é alterado numa interrupção
sei();
// …
}
//...
E com isto acabamos a base das interrupções. Decidi começar com estas, pois nos próximos
tutoriais, explicarei as interrupções individuais de várias funcionalidades.