Início
/
Projetos Arduino
/
Projetos Squids
/
Básico
/
Projeto 80 - Gerando sinal PWM através de interrupção por timer - Arduino
Projeto 80 - Gerando sinal PWM através de interrupção por timer - Arduino
Angelo Luis Ferreira | 31/03/2020
Acessos: 12.921
Básico - Projeto 80
Interrupção por timer no Arduino - gerando sinal de saída PWM
Objetivo
Neste projeto vamos mostrar como gerar um sinal de saída PWM (Pulse Width Modulation) diretamente em um pino do Arduino através de interrupção por timer. No exemplo, ao tocarmos em um sensor de toque (touch), acionaremos por interrupção externa um led que irá piscar e um buzzer que irá emitir som em um determinado tempo definido por uma onda PWM gerada por uma interrupção por timer. Desta forma, podemos receber o sinal PWM diretamente dos pinos de saída do Arduino sem a necessidade da função anogWrite() - referência Arduino - função analogWrite().
PWM (Pulse Width Modulation) - Modulação por Largura de Pulso
Refere-se ao conceito de pulsar rapidamente um sinal digital em um condutor. Quando geramos a modulação através da largura do pulso em uma onda quadrada podemos controlar a potência ou a frequência de um circuito.
A técnica de PWM é empregada em diversas áreas da eletrônica e, por meio da largura do pulso de uma onda quadrada, é possível o controle de potência ou velocidade nas aplicações de motores elétricos, aquecedores elétricos, leds e luzes nas diferentes intensidades e frequências. O Projeto 14 - Led com efeito dimmer usando potenciômetro é um ótimo exemplo de como utilizar o PWM para controlar a intensidade luminosa de um led utilizando a função analogWrite(). Veja abaixo um gráfico que mostra alguns exemplos de PWM de acordo com a razão cíclica (duty cycle) entre 0% e 100%.
Interrupção externa (External Interrupts)
É quando o programa do Arduino está instruído para reagir à mudança de um sinal externo, por exemplo, mudança do nível lógico de um sinal gerado por um botão ou por um sensor qualquer (Referência Arduino). Faça o projeto Projeto 78 - Como realizar interrupção externa no Arduino usando sensor de toque para entender melhor como funciona a interrupção externa.
- A ideia de utilizar a interrupção externa no Arduino em certos projetos, é quando precisamos realizar duas ou mais tarefas ao mesmo tempo.
Interrupção por timer (Timer Interrupts)
Semelhante à interrupção externa, a interrupção por timer ocorre através do tempo e não por uma ação externa do hardware, como um toque no sensor, por exemplo. Assim, a cada período podemos executar uma função independente, sem que a contagem do tempo seja interferida pelos demais comandos do programa. A interrupção por timer substitui as funções: delay(), millis() e o controle de loop for, gerando um intervalo de tempo independente. Faça o projeto Projeto 79 - Interrupção por timer no Arduino - disparo de alarme com sensor de toque e conhecer o recurso de interrupção por timer.
Obs.: Para gerarmos uma onda PWM e utilizarmos o recurso da interrupção por timer no Arduino precisaremos instalar a biblioteca TimerOne, conforme mostraremos durante o tutorial do projeto.
Aplicação
Para fins didáticos e projetos gerais.
Componentes necessários
Referência
|
Componente
|
Quantidade
|
Imagem
|
Observação
|
Protoboard |
Protoboard 830 pontos |
1 |
|
No mínimo utilizar protoboard com 400 pontos
|
Jumpers |
Kit cabos ligação macho / macho |
1 |
|
|
Sensor de toque capacitivo |
Sensor touch TP223B |
1 |
|
Tensão de Operação: 2 a 5V
Saída estado Alto (HIGH): 0,8V
Saída estado Baixo (LOW): 0,3V
Tempo de resposta: 220ms (LOW) e 60ms (HIGH)
(datasheet)
|
Led Difuso 5mm |
LEDs 5mm |
1 |
|
1 LED alto brilho ou difuso de qualquer cor
Você poderá utilizar também LEDs de 3 mm na cor que desejar.
|
Resistor |
Resistor
|
1 |
|
1 Resistor 150Ω
Se precisar usar outros valores, calcule o resistor apropriado para o led utilizado
|
Arduino UNO R3 |
Arduino UNO |
1 |
|
Você poderá utilizar uma placa Arduino UNO original ou similar
|
Montagem do Circuito
Conecte os componentes no Protoboard como mostra a figura abaixo. Verifique cuidadosamente os cabos de ligação antes de ligar seu Arduino. Lembre-se que o Arduino deve estar totalmente desconectado da força enquanto você monta o circuito.
Atenção
1. Antes de montarmos o led e o buzzer é importante entendermos que ao usarmos o recurso de interrupção por timer, através da biblioteca TimerOne, a função analogWrite() não funcionará corretamente. Para resolver isto, podemos substituir a função analogWrite() pela método pwm() da biblioteca TimeOne, desde que utilizarmos os seguintes pinos:
1.1. Como vimos na tabela acima, podemos utilizar o pinos 9 e 10 do Arduino Uno, por exemplo, para gerarmos um sinal PWM através do recurso de interrupção por timer. No nosso exemplo vamos conectar o led no pino 9 e o buzzer no pino 10, garantindo-se assim a funcionalidade do método pwm() da biblioteca TimeOne.
2. Lembre-se que o LED tem polaridade: O terminal maior tem polaridade positiva e o lado do chanfro tem polaridade negativa.
2.1. Portanto, faça a conexão do Arduino no terminal positivo do led (anodo) e o GND no terminal negativo (catodo).
2.2. Para evitar danos ao led é necessário a inclusão de um resistor no circuito. Como o resistor é um limitador da corrente elétrica, ele poderá estar conectado no anodo (terminal maior) ou no catodo (terminal menor) do led, tanto faz.
3. Determinamos o valor do resistor através da tabela prática: Tabela prática de utilização de leds 3mm e 5mm. Entretanto, o mais correto é sempre verificar o datasheet do fabricante do LED para você ter os exatos valores de tensão e corrente do mesmo - leia Como calcular o resistor adequado para o led e Leds ligados em série e em paralelo.
3.1. Valores utilizados para nossos projetos: LEDs difusos ou de alto brilho: Vermelho, Laranja e Amarelo: 150 Ω | Led Verde e Azul: 100 Ω
4. O buzzer tem polaridade. Portando, cuidado para não ligar o buzzer invertido. Se você retirar o adesivo superior do buzzzer poderá ver um sinal de positivo (+). Este sinal mostra onde está o pino positivo do componente que deverá estar conectado ao potenciômetro (neste projeto) ou a uma porta digital do Arduino e o polo negativo ao GND.
5. Antes de montarmos o nosso sensor touch, temos que observar que os microcontroladores Arduino possuem pinos específicos para desempenhar a função de entrada de sinal para a interrupção externa. No Arduino UNO, por exemplo, as portas digitais 2 e 3 têm esta função, sendo nomeadas como int = 0 e int = 1, respectivamente. Veja a tabela a seguir que mostra os pinos das principais placas Arduino que possuem esta função:
Board |
int.0 |
int.1 |
int.2 |
int.3 |
int.4 |
int.5 |
Uno, Nano, Mini, other 328-based |
pino 2 |
pino 3 |
|
|
|
|
Mega, Mega2560, MegaADK |
pino 2 |
pino 3 |
pino 21 |
pino 20 |
pino 19 |
pino 18 |
Micro, Leonardo, other 32u4-based |
pino 3 |
pino 2 |
pino 0 |
pino 1 |
pino 7 |
|
Due |
número da interrupção = todos pinos digitais = número do pino |
Zero |
todos pinos digitais, exceto 4 = número do pino
|
Veja abaixo a configuração dos pinos do Arduino Uno R3:
5.1 Conforme vimos na tabela anterior, definiremos o pino digital 2 do Arduino UNO para conectarmos o pino do sensor de toque e obtermos a funcionalidade da interrupção externa no nosso projeto.
Obs.: Se desejar, você também pode utilizar o pino 3 do Arduino para obter a mesma funcionalidade da interrupção externa, lembrando de nomeá-lo como int = 1.
6. Monte o sensor touch TP223B conforme descrito a seguir.
6.1. Montando o componente como mostra a imagem abaixo, quando o sensor for tocado, o Arduino retornará "HIGH" ou "1".
6.2. Lembre-se, para obtermos a funcionalidade da interrupção externa, devemos ligar o pino do sensor na porta digital 2 ou 3 do Arduino. No nosso projeto, conectamos no pino digital 2.
6.3. Os sensores de toque, assim como os botões de pressão (push button ou chaves tácteis) apenas mudam seu estado enquanto estamos pressionando ou tocando, voltando ao seu estado original quando liberados. Neste projeto, teremos uma rotina para atribuir a um só sensor duas funções: ligar e desligar um componente eletrônico qualquer através de uma interrupção externa.
6. Veja abaixo como realizamos a montagem do nosso projeto utilizando dois protoboard de 430 pontos.
Usando o comando de interrupção externa do Arduino
1. Para que o Arduino leia uma interrupção externa, é necessário utilizarmos a função attachInterrupt() - Leia Referência Arduino - attachInterrupt().
2. Esta função irá verificar se a condição aconteceu e qual função especial (ISR) será executada caso a interrupção ocorra. Veja a sintaxe abaixo:
Obs.: Como já explicamos acima, IRS (Interrupt Service Routine, ou Rotina de Serviço de Interrupções em português) nada mais é do que uma função especial executada quando ocorre uma interrupção externa:
Sintaxe
attachInterrupt(INT,IRS,MODO); //Configurando a interrupção
Onde:
INT: Número da porta usada para a interrupção. No Arduino UNO podemos utilizar INT = 0 que corresponde à porta digital 2 e INT = 1 corresponde à porta digital 3.
Observação: Podemos utilizar no lugar de INT o comando digitalPinToInterrupt(pino) que já faz a correspondência do pino conectado no Arduino e a fonte da interrupção. Portanto, para o Arduino UNO podemos utilizar digitalPinToInterrupt(2) para INT = 0 ou digitalPinToInterrupt(3) para INT = 1.
IRS: Nome da função especial que será chamada e executada quando ocorrer a interrupção.
Atenção: A função IRS tem algumas limitações importantes conforme mencionamos no início deste tutorial, como sendo:
- Uma ISR não recebe argumentos e não retorna nada.
- Uma ISR deverá ser a mais curta e rápida possível. Se o seu programa utiliza múltiplas ISRs, apenas uma poderá ser executada de cada vez.
- A função delay() não funcionara em uma IRS.
- A função millis() não poderá ser incrementada dentro de uma IRS.
- A função delayMicroseconds() ao não utilizar nenhum contador, funcionará normalmente em uma ISR.
MODO: Define em qual tipo de variação do sinal a interrupção será disparada. As opções são:
- LOW: Dispara a interrupção quando o estado no pino (2 ou 3) for LOW.
- HIGH: Dispara a interrupção quando o estado no pino for HIGH (Apenas as Placas Due, UNO, Zero e MKR1000 suportam este modo)
- CHANGE: Dispara sempre que o sinal no pino muda de estado, borda 0V (0) para 5V(1) ou vice-versa;
- RISING: Dispara somente na borda de subida, 0v (0) para 5V (1);
- FALLING: Dispara somente na borda de descida, 5V (1) para 0V (0)
Obs.: É importante que você teste todos os modos para entender bem a funcionalidade de cada um.
Incluindo a biblioteca TimerOne
Para realizarmos a interrupção por tempo (tmier) devemos incluir a biblioteca TimerOne.h que facilita a utilização do timer interno do Arduino via software. Assim, podemos configurar o Arduino através de métodos pré-definidos na biblioteca, para medir repetidamente um período de tempo em microssegundos. No final de cada período, uma função de interrupção poderá ser executada.
Download dos arquivos da biblioteca TimerOne.h
DOWNLOAD - TimerOne-master.zip
Para saber detalhes desta biblioteca clique aqui.
Instalando a biblioteca pelo IDE do Arduino
Após fazer o download do arquivo Arduino_Led_matrix.zip com todos os arquivos da biblioteca compactados no formato zip, abra o IDE do Arduino e siga o tutorial: Como incluir uma biblioteca no IDE do Arduino.
Comandos básicos da biblioteca TimerOne - Interrupção por timer
1. Usando o timer
1.1. Um timer nada mais é do que um contador que é incrementado a cada intervalo de tempo (em alguns microcontroladores intervalo pode ser configurado, o Arduino é um deles). Os timers funcionam como um relógio que pode ser usado para contar o tempo, medir a duração de certos eventos, entre outras aplicações.
1.2. O Arduino vem equipado com um microcontrolador ATmega168 ou ATmega328 (que diferem apenas na quantidade de memória interna). Esses microcontroladores possuem 3 timers: timer0, timer1 e timer2, timer0 e timer2 são contadores de 8bits, ou seja, contam de 0 a 255, e o timer1 é um contador de 16bits, conta de 0 a 65535.
1.3. O Arduino Mega vem equipado com o ATmega1280 ou ATmega2560 (que diferem apenas na quantidade de memória). Eles possuem 6 timers: timer0, timer1, timer2, timer3, timer4, timer5. Os timers 0, 1 e 2 são idênticos aos do ATmega168/328, e os timers 3, 4 e 5 são de 16bits.
- O timer0 é utilizado pelo Arduino para funções como delay(), millis() e micros(). Então não se deve utilizar esse timer para evitar comprometer essas funções.
- O timer1 no Arduino UNO esse é o timer utilizado pela biblioteca de controle de servos. Caso você não esteja utilizando essa biblioteca, esse timer está livre para ser utilizado para outros propósitos. No Arduino Mega esse timer só será utilizado para controlar os servos se você estiver usando mais de 12 servos. (timer utilizado no projeto)
- O timer2 é utilizado pela função tone(). Então se você não precisar da função tone() esse timer está livre para outras aplicações.
2. Utilização da biblioteca
2.1. O timer (cronômetro) está configurado para medir repetidamente um período de tempo, em microssegundos, portanto 1.000.000 = 1 segundo.
Obs1: Os pinos PWM também podem ser configurados para alternar durante uma parte do período de acordo com a tabela por tipo de microcontrolador:
Obs2.: Se estiver usando o TimerThree, substitua "Timer1" por "Timer3".
3. Configuração
Inicialização do timer
Timer1.initialize(microseconds);
Esta função deve ser chamada primeiro. Microssegundos é o período de tempo que o timer leva.
Define novo período
Timer1.setPeriod(microseconds);
Define um novo período após a biblioteca já ter sido inicializada.
4. Função de interrupção
Chama uma função IRS (Interrupt Service Routine)
Timer1.attachInterrupt(function);
Executa uma função sempre que o período do timer terminar. A função é executada como uma interrupção, portanto, é necessário um cuidado especial para compartilhar quaisquer variáveis entre a função de interrupção e seu programa principal. Esta função é uma IRS (Interrupt Service Routine, ou Rotina de Serviço de Interrupções em português) e possui limitações como já mencionamos anteriormente.
Desativa a interrupção
Timer1.detachInterrupt();
Desativa a interrupção para que a função não seja mais executada.
5. Sinal de saída PWM
Configura um dos pinos PWM (ex.: Arduino Uno - pinos 9 ou 10)
Timer1.pwm(pin, duty);
Configura um dos pinos PWM, definindo a razão "duty" de 0 a 1023, onde 0 faz o pino permanecer LOW constante e 1023 faz o pino permanecer sempre HIGH.
Obs.: Enquanto você estiver usando o método pwm(), os pinos não poderão ser controlados por digitalWrite().
Desative o PWM
Timer1.pwm(pin, duty);
Interrompa o método pwm(). Os pinos agora poderão ser controlados por digitalWriet().
6. Referências
6.1. Eletronics Componentes: https://www.pjrc.com/teensy/td_libs_TimerOne.html
6.2. Laboratório de Garagem: http://labdegaragem.com/profiles/blogs/tutorial-executando-fun-es-em-intervalos-de-tempo-fixos-timers
6.3. Playground Arduino: https://playground.arduino.cc/Code/Timer1/
6.4. Como usar temporizadores no Arduino
Entenda como pwm() funciona na prática
Para compreender melhor a utilização do método pwm() em uma interrupção por timer, faça os 3 exemplos abaixo utilizando a montagem do circuito original do projeto principal que mostramos no início do artigo.
Exemplo1: Quando usamos uma interrupção por timer, podemos criar uma função que é acionada por um determinado período de tempo. No exemplo a seguir, vamos utilizar uma interrupção por timer a cada segundo (1.000.000 microssegundos), para acender e apagar um led, infinitamente:
Obs1: Veja que o código do exemplo faz com que o led fique 1 segundo ligado e 1 segundo desligado, pois utilizamos a função digialWrtie(ledPin, !digitalRead(ledPin)); , que troca o nível lógico do led cada vez que a função é chamada. Veja como podemos observar este efeito no monitor serial:
Obs.: Como usamos o circuito original, o led foi conectado no pino 9 do Arduino. Entretanto, você poderá utilizar qualquer pino digital do Arduino que o código funcionará corretamente.
/*************************************************
*
* Exemplo 1: Fazendo o led piscar - 1 seg ligado, 1 seg desligado
*
***********************************************/
#include <TimerOne.h>
const byte ledPin = 9;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Timer1.initialize(1000000); // initialize timer1, and set a 1 second period
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
}
void callback() {
digitalWrite(ledPin, !digitalRead(ledPin));
Serial.println(digitalRead(ledPin));
}
void loop(){
}
Exemplo2: Usando um sinal PWM junto com uma interrupção por timer, podemos modular o tempo que o led fica ligado e apagado. No exemplo a seguir vamos acender um led a cada segundo na razão de 50%, ou seja, a cada segundo o led ficará 50% aceso e 50% apagado.
Obs.1: Como já mencionamos antes, para gerarmos um sinal PWM em uma interrupção por timer, precisamos conectar o led nos pinos específicos (pinos 9 ou pino 10 do Arduino Uno, por exemplo).
Obs.2: Para gerarmos um sinal PWM precisamos utilizar o método pwm(), pois a função analogWrite() não funciona normalmente em uma interrupção por timer nos pinos específicos (pinos 9 e 10 do Arduino Uno).
Obs3: No método pwm() a razão "duty" varia de 0 a 1023 e não de 0 a 255. Para uma razão de 50%, utilizaremos o valor 512.
Obs4: Ao utilizar o método pwm() nos pinos específicos, as funções digitalWrite() e digitalRead() ficam bloqueadas.
/*************************************************
*
* Exemplo 2: Fazendo o led piscar razão 50%
*
***********************************************/
#include <TimerOne.h>
const byte ledPin = 9;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Timer1.initialize(1000000); // initialize timer1, and set a 1 second period
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
}
void callback() {
Timer1.pwm(ledPin, 512); // setup pwm on pin 9, 50% duty cycle
//analogWrite(ledPin, 127);
}
void loop() {
}
Exemplo3: Neste exemplo, vamos mostrar que a função analgogWrite() em uma interrupção por timer não funciona apenas para os pinos específicos (pinos 9 ou pino 10 do Arduino Uno, por exemplo).
Obs1: Para fazer este exercício, altere a conexão do led do pino 9 para o pino 5, por exemplo (também pode ser os pinos 3, 5, 6 e 11 do Arduino Uno, ou seja, pinos com saídas PWM nativas).
Obs2: Veja que o resultado será bem diferente, pois a função analogWrite(64) irá ligar e desligar o led na razão de 25%, mas em uma frequência muito alta (entre 490HZ e 98HZ nos pinos 5 e 6), tão rapidamente que para os nossos olhos que parece estar sempre ligado, e o que muda é a intensidade da luz. Para saber mais, veja o projeto 14 e a Referência Arduino - analogWrite().
/*************************************************
*
* Exemplo 3: Alterando a intensidade da luz na razão de 25%
*
***********************************************/
#include <TimerOne.h>
const byte ledPin = 5;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Timer1.initialize(1000000); // initialize timer1, and set a 1 second period
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
}
void callback() {
analogWrite(ledPin, 64); // altera o brilho do led
}
void loop() {
}
Agora que já aprendemos um pouco mais sobre interrupção por timer e o método pwm(), vamos ao que interessa que é o nosso projeto principal.
Código do projeto (Sketch)
1. Faça o dowload e abra o arquivo projeto80.ino no IDE do Arduino: DOWNLOAD - projeto80.ino
Ou se preferir, copie e cole o código abaixo no IDE do Arduino:
/*******************************************************************************
**
* Projeto 80 - Saída PWM com Interrupção por timer
* Autor: Angelo Luis Ferreira
* Data: 02/04/2020 (dd/mm/AA)
* http://squids.com.br/arduino
*
*******************************************************************************/
#include <TimerOne.h>
// hardware
const byte pinLed = 9;
const byte pinBuzzer = 10;
const byte pinSensor = 2;
byte pinPWM; // define pwm no pino 9 ou pino 10
// variáveis
int duty = 0;
const byte increment = 20;
byte control = 0;
boolean state = 0;
void setup(){
pinMode(pinLed, OUTPUT);
pinMode(pinBuzzer, OUTPUT);
pinMode(pinSensor, INPUT);
Timer1.initialize(500000); // Inicializa o timer1 com período de 0,5 segundos
Timer1.attachInterrupt(setTimer); // Chama a função setTimer a cada período do timer (a cada 0,5 seg)
attachInterrupt(digitalPinToInterrupt(2), setStop, RISING); // // Chama função "setStop", quando houver o toque no sensor
Serial.begin(9600);
}
void loop() {
// liga ou desliga o led laranja
if(state){
Timer1.resume(); // retoma o timer
for (byte i =9; i< 11; i++) {
pinPWM = i;
Timer1.pwm(pinPWM, duty); // setup pwm no pino 9 e 10, variando de 0% to 100% duty cicle
}
} else {
Timer1.stop(); // desarma o timer
Timer1.disablePwm(pinPWM); // desarma o pwm no pino 10 e libera o digitalWrite() e analogWrite()
digitalWrite(pinLed, LOW);
noTone(pinBuzzer);
}
Serial.println(duty);
}
void setTimer() {
if (duty > 1024) control = 1;
if (duty == 0) control = 0;
switch (control) {
case 0:
duty += increment;
break;
case 1:
duty -= increment;
break;
}
}
void setStop() {
state = !state;
}
Vídeo
Como o projeto deve funcionar
1. Ao iniciar o programa, todo os leds estarão apagados.
MODO RISING (dispara a função na borda de subida de 0V para 5V)
2. Ao tocar o sensor o led laranja piscará e o buzzer irá emitir som no tempo de acordo com a modulação PWM gerada por uma interrupção por timer.
3. A modulação PWM será incrementada até atingir o valor máximo de 1023. A partir daí, a modulação é reduzida até zero, começando um novo ciclo.
4. Ao tocar novamente o sensor o led e o buzzer são desligados.
5. Ao tocar mais uma vez, o ciclo é retomado a partir de onde foi interrompido.
6. Ao abrir o monitor serial, aparecerá na tela os valores da razão cíclica (duty cycle) do sinal PWM.
O anúncio abaixo ajuda a manter o Squids Arduino funcionando
Comentários