sábado, 1 de setembro de 2018

Arduino Interrupção e Timer

Interrupção


No exemplo onde foi usado um botão para controlar a sequência de acionamento de 4 LEDs, tínhamos o problema de que o teste só era realizado uma vez durante o loop, dessa forma, só em alguns momentos era possível detectar que o botão foi pressionado.

Para resolver isso iremos usar interrupção, existem dos tipos de interrupções, interna e externar. A interrupção interna será usada mais a frente, quando timer foi explicado. Interrupção externa utiliza um pino do Arduino para acionar uma interrupção, que muda a sequência de execução das instruções do programa para uma rotina de interrupção determinada em alguma parte do programa.

Com uso de interrupção, não é preciso escrever uma função para testar a todo o momento o estado de um pino, além disso, ocorre a garantia de que uma determina instrução será executada no momento em que ocorrer o disparo da interrupção. Dessa forma, é possível criar sistemas mais robustos.

Ligue os LEDs e o botão no Arduino de acordo com a imagem a seguir:

Figura 1. LEDs sequenciais com interrupção

Conecte o Arduino ao computador e upload o seguinte código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
uint8_t pinButton = 3;   // Botão no pino 3
bool seq = 0;            // Sentido
uint16_t delay_ms = 150; // Delay entre LEDs, 16 bits -> max = 65535     

void interrupt()  // Rotina de interrupção
{
  seq = !seq;     
}
 
void setup() 
{
  DDRB = 0b00111100; // Configura PB5-PB2 como saída, (13,12,11,10) (Arduino)
  pinMode(pinButton, INPUT_PULLUP); // Ativa pullup do pino 3
  attachInterrupt(digitalPinToInterrupt(3), interrupt, LOW); //Configura a interrupção
}

void loop() 
{
  if(seq) // Se seq = 1, segue esse sentindo
  {
    PORTB = 0b00100000;  // Liga LED 13
    delay(delay_ms);
    PORTB = 0b00010000;  // Liga LED 12
    delay(delay_ms);
    PORTB = 0b00001000;  // Liga LED 11
    delay(delay_ms);
    PORTB = 0b00000100;  // Liga LED 10
    delay(delay_ms);
  }

  else  // Se seq = 0, segue esse sentido
  {
    PORTB = 0b00000100;  // Liga LED 10
    delay(delay_ms);
    PORTB = 0b00001000;  // Liga LED 11
    delay(delay_ms);
    PORTB = 0b00010000;  // Liga LED 12
    delay(delay_ms);
    PORTB = 0b00100000;  // Liga LED 13
    delay(delay_ms);
  }
}

Se você seguiu todos os passos correntemente, será possível mudar o sentido de acionamento dos LEDs com o botão, independente do momento em que você pressiona-lo. É possível utilizar os pinos de 2 e 3 do Arduino para gerar interrupções externas, para isso foi utilizado a função attachInterrupt() (attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)). Para mais detalhes acesse aqui.

Algumas funções não funcionaram muito bem dentro de uma rotina de interrupção, como a função delay().

Timer


O Timer é um hardware de contagem, que utiliza a frequência do clock como referência de contagem. Com o timer é possível criar temporizações, que são usadas em várias funções do Arduino, tais como, delay() (criação de atrasos), millis() (retorna o tempo em milissegundos desde o inicio da execução de um programa), micros() (retorna o tempo em  microssegundos desde o inicio da execução de um programa). Então, você já vinha utilizando o timer, mas provavelmente, não sabia disso.

Outras funcionalidades do timer são: Contagem de eventos externos, geração de sinais PWM, interrupções periódicas e medida de intervalos de pulsos.

O ATmega328P apresenta 3 Timers, o Timer0, Timer1 e Timer2. O Timer0 e Timer2 são contadores de 8 bits, dessa forma, é possível realizar uma contagem de 0-255 com eles. O Timer1 é contador de 16 bits, dessa forma, é possível realizar uma contagem de 0-65535 com ele.

O Timer0 é utilizado nas funções: delay(), millis(), micros(). O Timer1 é utilizado na biblioteca de controle de servos motores, o controle do ângulo de um servo motor é realizado com uso de um sinal PWM. O Timer2 é utilizado na função tone(), que gera um onda quadrada (PWM com duty cycle de 50%) na frequência desejada.

O próximo exemplo demonstrará o uso da biblioteca TimerOne para configuração de uma interrupção periódica, com isso, será possível mudar o estado de um LED no pino 13 sem uso da função delay() ( o uso da função delay não é aconselhável, pois ocorre uma pausa no fluxo de execução do programa, dessa forma, não é possível realizar outras tarefas).

Conecte o Arduino ao seu computador e upload o seguinte código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "TimerOne.h"

uint8_t pinLed = 13; // LED no pino 13
boolean state = 0;   // Variável de estado do LED

void timer1_ISR() // Função para ser chamada a cada interrupção do Timer1
{
  state = !state; // Muda o estado, 0 -> 1 e 1 -> 0 
  digitalWrite(pinLed, state); // LED = state
}

void setup()
{
  pinMode(pinLed, OUTPUT); // Configura pinLed como saída
  Timer1.initialize(500000); // Inicializa o Timer1 e configura para um período de 500 ms
  Timer1.attachInterrupt(timer1_ISR); // Associa timer1_ISR() a interrupção do Timer1
}

void loop()
{
  // Livre para executar outras tarefas
}

Após o upload, o LED do pino 13 deve está acendendo e apagando com um intervalo de 500 ms. A vantagem do uso do timer é a possibilidade da execução de múltiplas tarefas, como a função loop() não foi usado, poderíamos realizar outras tarefas nela, sem se preocupar com estado do LED, que seria controlado pela interrupção do timer.

A biblioteca TimerOne, além da interrupção periódica, permite a configuração de um sinal PWM de frequência desejada, para mais informações acesse aqui.

Além do uso da biblioteca TimerOne, é possível utilizar o timer com comandos para AVR, manipulando os registradores responsável por cada Timer, que seria um pouco mais complicado mas é bastante útil. O exemplo a seguir terá o mesmo objetivo do exemplo anterior, mas com uso do Timer2.

Conecte o Arduino ao seu computador e upload o seguinte código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <avr/interrupt.h>

uint8_t pinLed = 13; // LED no pino 13
boolean state = 0; // Variável de estado do LED
uint16_t contInterrupt = 0; // Conta o número de interrupções do timer2

                           // Interrupção do timer2, ocorre em aproximadamente 1.024 ms  
ISR(TIMER2_OVF_vect)       // t = prescaler*TCNT2/clock speed -> t = 64*256/16M -> t =  1.024ms
{
  contInterrupt++;         // contInterrupt = contInterrupt + 1 a cada interrupção
  if(contInterrupt == 500) // ocorre a cada 500*1.024 ms = 512ms =~ 0.5 s
  {
    state = !state; // Muda o estado, 0 -> 1 e 1 -> 0  
    digitalWrite(pinLed, state); // LED = state
    contInterrupt = 0; // Zera o contador de interrupções do timer2
  }
}

void setup() 
{
  TCCR2A = 0;             // Configura timer2 para operação normal
  TCCR2B = 0; 
  TCCR2B |= (1 << CS22);  // Configura prescaler para 64: CS22 = 1
  TCNT2 = 0;              // Inicia o contador com 0 (MAX = 2^8 -1= 255)
  TIMSK2 |= (1 << TOIE2); // Ativa interrupção temporal do timer2
  sei();                  // Ativa interrupções globais        
}

void loop()                   
{ 
  // Livre para executar outras tarefas
}

Após o upload, o LED do pino 13 deve está acendendo e apagando com um intervalo de 500 ms, como no exemplo anterior. A ideia foi só mostrar que existem diferentes soluções para o mesmo problema, escolha aquela em que você se sinta mais confortável de trabalhar. Nos exemplos utilizamos aplicações simples, mas os conceitos aprendidos podem ser utilizaados para exemplos mais complexos, o importante é aprender bem os conceitos básicos.

Os bits CS22, CS21 e CS20 controlam o clock do Timer2 (O cristal oscilador usado no Arduino gera um clock de 16 MHz) de acordo com a tabela a seguir:

Figura 2. Configuração clock Timer2
Para mais informaçoes de como configurar o Timer, consultar o datasheet do microcontrolador usado (ATmega328P).

As funções millis() e micros() também poderiam ser usados para se eliminar o uso da função delay(). Mais informações aqui.

Enfim, chegamos ao final dessa parte e ao fim do curso de Arduino nesse blog, parabéns por ter chegado até aqui. Além do curso, muitos projetos com Arduino estão (ou estarão) presentes nesse blog, servindo como uma fonte extra de informação sobre o Arduino, divirta-se com a implementação desses projetos.

<< Arduino PWM e ADC                                                                                                      Sumário >>

Nenhum comentário:

Postar um comentário