Gracias por leer este Tutorial para generar señales PWM con Arduino, una señal PWM o Pulso con modulación puede ser implementado en arduino de diferentes maneras. En este artículo se explicará una simple técnica para generar un PWM directamente desde los registros, de esta manera se obtiene un mayor control de la frecuencia y el Duty cicle.
con modulación (PWM) puede ser implementado en arduino de diferentes maneras. En este artículo se explicará una simple técnica para generar un PWM directamente desde los registros, de esta manera se obtiene un mayor control de la frecuencia y el Duty cicle.
Pulso con modulación (PWM) puede ser implementado en arduino de diferentes maneras. En este artículo se explicará una simple técnica para generar un PWM directamente desde los registros, de esta manera se obtiene un mayor control de la frecuencia y el Duty cicle.
Para este ejemplo nos vamos a centrar en la placa de desarrollo Arduino nano, la cual incorpora un microcontrolador ATmega328
Que es un señal PWM?
Antes de continuar, vamos a ver que es una señal PWM y algunos posibles use cases. Una señal PWM consta de dos parámetros fundamentales, que son: la frecuencia PWM y el ciclo de trabajo o duty cycle. Es posible generar señales PWM modificando cualquiera de estos parámetros, podemos mantener un Duty cicle fijo y variar la frecuencia entre en una frecuencia PWM mínima y una frecuencia PWM máxima o bien mantener una frecuencia fija e ir variando el Duty cicle de esta frecuencia.
En este Tutorial generar señales PWM con Arduino, vamos a mantener una frecuencia fija e iremos variando el Duty cicle según nuestra necesidad, con esta técnica de generación de señales PWM podemos dimear un simple LED, variar la velocidad de un motor, general señales de audio, etc… El objetivo final de esta técnica es aplicar una frecuencia de trabajo al elemento a controlar (por ejemplo un LED) y variando el ciclo de trabajo conseguimos variar el voltaje que se le suministra al LED en este caso. Consiguiendo un efecto dimmer en el LED.
Generación simple de señales PWM en Arduino
Arduino dispone de una función para la generación de una señal PWM de manera muy simple, esta funcion nos puede ser muy util si no necesitamos grandes requisitos, para ello se puede utilizar la función analogWrite (pin, dutuCycle). En el parámetro pin le pasaremos el pin de arduino donde queremos generar la señal y el parámetro dutyCycle le pasaremos un valor entre 0 y 255 el cual corresponde con el porcentaje del duty cicle.
Los pines disponibles para generar una señal PWM en arduino nano son (3, 5, 6, 9,10 y 11).
Siguiendo con el ejemplo del LED, vamos a hacer un dimmer, para ello conectamos un potenciómetro en el pin A0 y conectamos un led en el pin D3, mediante el potenciómetro podremos variar la intensidad del led.
Como es habitual, se definen los pines de entrada y salida, y utilizamos la función analogRead () para generar nuestra señal PWM
int potenciometerPin = A0; // select the input pin for the potentiometer
int ledPin = 3; // select the pin for the LED
int potenciometerValue = 0; // variable to store the value coming from the potenciometer
void setup() {
pinMode(ledPin, OUTPUT); // declare the ledPin as an OUTPUT:
}
void loop() {
potenciometerValue = analogRead(potenciometerPin); // read the value from the sensor
analogWrite(ledPin, potenciometerValue / 4); // Set PWM dutyCicle acording Potenciometer
}
Puedes descargar el codigo de este tutorial para generar señales PWM con Arduino desde mi repositorio
Con esta función es forma muy simple de generar una señal PWM, hay remarcar que no podemos variar la frecuencia PWM que ya viene pre-configurada en Arduino, en la tabla están las frecuencias PWM por defecto, si estas frecuencias no son adecuadas para nuestro propósito, existe una manera de variar la frecuencia a nuestra necesidad, se trata de manipular directamente los registros de configuración de Arduino para obtener la frecuencia y modo de funcionamiento que nos interese.
Timer | Default PWM Freq |
---|---|
Timer0 | 490.20 Hz |
Timer1 | 976.56 Hz |
Timer2 | 490.20 Hz |
Generar señales PWM mediante los registros de ATmega328 – ArduinoNano
Sigue leyendo este Tutorial para generar señales PWM con Arduino, para generar señales PWM, lo primero que se necesitan son los Timers, el Atmega328P dispone de 3 timers para generar hasta 6 salidas PWM. Cada uno de los 3 timers está asociado a dos pines de salida
Timer | Pin | Bits | Utilizacion |
---|---|---|---|
Timer0 | 6 y 5 | 8 | micros() milis() delay() ... |
Timer1 | 9 y 10 | 16 | Servos |
Timer2 | 11 y 3 | 8 | Tone() |
Cada Timer dispone de dos registros Output Compare, estos registros son los encargados de controlar las salidas PWM. Cuando el valor del Timer alcanza el valor del registro Output compare, su pin asociado cambiará de estado. Las dos salidas asociadas a cada Timer tendrán la misma frecuencia, pero el duty cicle podrá ser diferente.
Los timers utilizan la señal de reloj (Clock) de arduino y esta señal se pasa por un prescaler (1, 8, 64,256 o 1024) para los timers 0 y 1, y (1, 8, 32, 64, 128,256 o 1024) para el Timer 2.
La tabla siguiente muestra las salidas y pines asociados a los 6 registros Output Compare
Timer | Timer Output | Arduino Pin |
---|---|---|
Timer0 | OC0A | 6 |
OC0B | 5 | |
Timer1 | OC1A | 9 |
OC1B | 10 | |
Timer2 | OC2A | 11 |
OC2B | 3 |
Timer 2, Registros asociados a la generación de señales PWM
Aunque los 3 timers que dispone el Atmega328 tienen algunas diferencias, el funcionamiento es prácticamente el mismo, en este apartado entraré en detalle en el timer2 y haremos el mismo ejemplo pero modificando los registros directamente, para los timer0 y timer1 mirar el datasheet para ver sus particularidades.
Una de los principales motivos por los cuales necesitaríamos modificar los registros es para poder ajuntar la frecuencia PWM. De manera predeterminada, Arduino ya la trae prefijada según el Timer o salida PWM que utilicemos.
Para poder generar una señal PWM, primero debemos configurar el modulo, en este caso el Timer 0, la configuración de este Timer se realiza mediante los registros TCCR2A y TCCR2B.
Es este articulo no trataremos la gestion de las interrupciones, si quieres saber mas, mira esta entrada, para generar señales PWM, bajo mi punto de vista no es necesario añadir interrupciones, aunque cada uno es libre de hacer lo que crea oportuno.
TCCR2A | COM2A1 | COM2A0 | COM2B1 | COM2B0 | - | - | WGM21 | WGM20 |
TCCR2B | FOC2A | FOC2B | - | - | WGM22 | CS22 | CS21 | CS20 |
COMA2:1 –> Configura la salida A (pin 11), invertida o no invertida
COMB2:1 –> Configura la salida B (Pin 3), invertida o no invertida
WGM20:2 –> Selecciona el modo de funcionamiento (Waveform Generation mode bit)
CS20:1 –> Selección del prescaler para la señal Timer Clock
FOC2A: B –> No utilizado en modo PWM, poner a 0
Una vez configurado, se deben cargar los valores correspondientes a los duty cicle para que empiece la fiesta… los registros son:
OCR2A y OCR2B.
Configuración de las salidas – Registros COM2A0:1 – COM2B0:1
La configuración de las salidas, depende del modo de funcionamiento que seleccionemos, básicamente podemos diferenciar entre 2 modos, Fast PWM y Phase Correct PWM, lo que hace esta configuración es seleccionar la polaridad de las salidas es decir, poner 0 o 1 cuando se alcanzan los valores de duty cicle, o dicho de otra manera, seleccionar el modo no invertido o modo invertido.
TCCR0A- Compare Output Mode, Fast PWM Mode | ||
---|---|---|
COM0A1 | COM0A0 | |
0 | 0 | Normal port operation, OC0A disconnected. |
0 | 1 | WGM02 = 0: Normal port operation, OC0A disconnected. WGM02 = 1: Toggle OC0A on compare match. |
1 | 0 | Clear OC0A on compare match (non-inverting mode). |
1 | 1 | Set OC0A on compare match (inverting mode). |
COM0B1 | COM0B0 | |
0 | 0 | Normal port operation, OC0B disconnected. |
0 | 1 | Reserved |
1 | 0 | Clear OC0B on compare match, set OC0B at BOTTOM, (non-inverting mode). |
1 | 1 | Set OC0B on compare match, clear OC0B at BOTTOM, (inverting mode). |
TCCR0A- Compare Output Mode, Phase Correct PWM Mode | ||
---|---|---|
COM0A1 | COM0A0 | |
0 | 0 | Normal port operation, OC0A disconnected. |
0 | 1 | WGM02 = 0: Normal port operation, OC0A disconnected. WGM02 = 1: Toggle OC0A on compare match. |
1 | 0 | Clear OC0A on compare match when up-counting. Set OC0A on compare match when down-counting. |
1 | 1 | Set OC0A on compare match when up-counting. Clear OC0A on compare match when down-counting. |
COM0B1 | COM0B0 | |
0 | 0 | Normal port operation, OC0B disconnected. |
0 | 1 | Reserved |
1 | 0 | Clear OC0B on compare match when up-counting. Set OC0B on compare match when down-counting. |
1 | 1 | Set OC0B on compare match when up-counting. Clear OC0B on compare match when down-counting. |
Modos de funcionamiento – Registros WGM20:2
Normal Mode – Non PWM:
Este modo de funcionamiento requiere de interrupciones para un mejor control de la señal, el funcionamiento básico de este modo es que el registro contador de Timer (TCNT2) se incrementa con cada Timer Clock, cuando el valor de TCNT2 alcanza 0xFF se genera una interrupción por overflow (TOV2), esta interrupción se ha de tratar por SW en su rutina correspondiente. El contador del Timer TCNT2 vuelve a iniciar la cuenta desde 0x00.
Este modo de funcionamiento no se considera una operación PWM, ya que el Timer se utiliza como un mero contador
Clear Timer on Compare Mach (CTC) – Non PWM
Este modo es una evolución del normal mode, en el registro OCR2A se carga un valor de 8 bits que corresponde al porcentaje del duty cicle de la señal, el Contador del Timer (TCNT2) se incrementa con cada Timer Clock, una vez que TCNT2 alcanza el valor cargado en OCR2A, TCNT2 se resetea y comienza a contar de nuevo desde 0x00.
Este modo de funcionamiento no se considera una operación PWM, ya que este modo es un Timer o temporizador.
Fast PWM mode
Es el modo más simple de generar una señal PWM, el contador del Timer TCNT2 cuenta repetidamente desde 0x00 hasta 0xFF, en el registro OCR2x se cargan los valores correspondientes al porcentaje del duty cicle, cuando el contador TCNT2 alcanza el valor cargado en OCR2x su salida cambia de estado en función si tenemos configurado el modulo como una salida no invertida o una salida invertida. Una vez el contador TCNT2 alcanza el valor 0xFF, este se resetea a 0x00 y la salida vuelve a cambiar de estado.
La fórmula para calcular la Frecuencia PWM para el mode 3 es la siguiente:
FPWM = Fosc / (N x (TOP +1))
Fosc –> Frecuencia de oscilación (16Mhz en Arduino)
N –> Valor prescaler
TOP –> 0xFF
La fórmula para calcular la Frecuencia PWM para el mode 7 es la siguiente:
FPWM = (Fosc / (N x (TOP +1)))/2
Fosc –> Frecuencia de oscilación (16Mhz en Arduino)
N –> Valor prescaler
TOP –> Valor del duty cicle (OCR2A)
Phase Correct PWM Mode
En este modo PWM, el contador TCNT2 se incrementa desde 0x00 hasta 0xFF con cada Timer Clock, una vez ha alcanzado 0xFF, el contador se decrementa hasta alcanzar 0x00 y vuelve a empezar repetidamente.
Durante la cuenta ascendente, la salida cambiará de estado cuando TCNT2 sea igual al valor de OCR2x y volverá a cambiar el estado de la salida cuando en la cuenta atrás los valores de TCNT2 y OCR2x vuelvan a coincidir.
La fórmula para calcular la Frecuencia PWM para el mode 1 es la siguiente:
FPWM = Fosc / (N x TOP)
Fosc –> Frecuencia de oscilación (16Mhz en Arduino)
N –> Valor prescaler
TOP –> (0xFF * 2) –> 0x1FE –> 510 dec.
La fórmula para calcular la Frecuencia PWM para el mode 5 es la siguiente:
FPWM = (Fosc / (N x (TOP*2)))/2
Fosc –> Frecuencia de oscilación (16Mhz en Arduino)
N –> Valor prescaler
TOP –> Valor del duty cicle (OCR2A)
Los modos de funcionamiento se pueden ver resumidos en la siguiente tabla:
Mode | WGM22 | WGM21 | WGM20 | Modo de Operación | Valor TOP |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | Normal (Contador) | 0xFF |
1 | 0 | 0 | 1 | PWM Fase correct | 0XFF |
2 | 0 | 1 | 0 | CTC (Timer) | OCRA |
3 | 0 | 1 | 1 | FAST PWM | OxFF |
4 | 1 | 0 | 0 | Reserved | - |
5 | 1 | 0 | 1 | PWM Fase correct | OCRA |
6 | 1 | 1 | 0 | Reserved | - |
7 | 1 | 1 | FAST PWM | OCRA |
Selección del prescaler – CS20:1
En la siguiente tabla se muestra los posibles valores de prescaler para generar la señal Timer Clock, recordar que el Timer 2 tiene valores de prescaler diferentes.
CS22 | CS21 | CS20 | Descripcion |
---|---|---|---|
0 | 0 | 0 | Sin fuente de reloj - Timer stop |
0 | 0 | 1 | Clk sin prescaler |
0 | 1 | 0 | Clk prescaler 8 |
0 | 1 | 1 | Clk prescaler 32 |
1 | 0 | 0 | Clk prescaler 64 |
1 | 0 | 1 | Clk prescaler 128 |
1 | 1 | 0 | Clk prescaler 256 |
1 | 1 | 1 | Clk prescaler 1024 |
Manos a la obra!!!
Manteniendo el circuito con el potenciómetro y el led, vamos a modificar los registros de TMR2 para configurarlo de tal manera que genere una señal PWM y con el potenciómetro podamos dimear el Led.
Vamos a seleccionar el modo FAST PWM Mode 3, seleccionamos un valor de preescaler N=8 y configuramos las salidas como no invertidas. Para esto se deben modificar los registros TCCR2A y TCCR2B con los siguientes Valores:
TCCR2A | COM2A1 | COM2A0 | COM2B1 | COM2B0 | - | - | WGM21 | WGM20 |
0 | 0 | 1 | 0 | 1 | 1 | |||
TCCR2B | FOC2A | FOC2B | - | - | WGM22 | CS22 | CS21 | CS20 |
0 | 0 | 0 | 0 | 1 | 0 |
Y en el registro OCR2B cargaremos el valor del duty cicle, los bits COM2A0-1 y el registro OCR2A, lo dejamos a cero ya son los que controlan el pin 11, y nosotros tenemos conectado el led al pin 3.
int potenciometerPin = A0; // select the input pin for the potentiometer
int ledPin = 3; // select the pin for the LED
int potenciometerValue = 0; // variable to store the value coming from the potenciometer
void setup() {
pinMode(ledPin, OUTPUT); // declare the ledPin as an OUTPUT
TCCR2A = 0;
TCCR2B = 0;
TCCR2A |= ( 0 << COM2A1);
TCCR2A |= ( 0 << COM2A0);
TCCR2A |= ( 1 << COM2B1);
TCCR2A |= ( 0 << COM2B0);
TCCR2A |= ( 1 << WGM21);
TCCR2A |= ( 1 << WGM20);
TCCR2B |= (0 << FOC2A);
TCCR2B |= (0 << FOC2B);
TCCR2B |= (0 << WGM22);
TCCR2B |= (0 << CS22);
TCCR2B |= (1 << CS21);
TCCR2B |= (0 << CS20);
TCNT2 = 0;
OCR2A = 0;
}
void loop() {
potenciometerValue = analogRead(potenciometerPin); // read the value from the sensor
OCR2A = potenciometerValue/4;
}
Descargate el codigo en este link.