Hoy vamos a ver cómo cambiar la frecuencia de un pin de salida analógico en Arduino, para lo cuál nos va a tocar configurar los registros que controlan los Timers asociados a los pin PWM. Pero tranquilos, que vamos a dejarlo fácil en una única función.
El empleo de timers es algo normal en procesadores. Sin embargo, frecuentemente nos habréis visto recomendar evitar su manipulación en Arduino dado que puede tener efectos inesperados, sobre todo en librerías de terceros. Entonces, si normalmente recomendamos evitar manipular los Timers en Arduino ¿Para qué querremos modificar ahora su frecuencia? Pues, básicamente, porque esto permite mejorar el comportamiento de ciertos dispositivos cuando son controlados por una señal PWM.
Recordemos que al controlar un dispositivo mediante una señal PWM, realmente estamos encendiendo y apagando el dispositivo por completo múltiples veces por segundo. Eso no es lo mismo que aplicar una auténtica señal analógica y, como normal general, a muchos actuadores el control con PWM les sienta fatal.
Aumentar la frecuencia del PWM puede mejorar el funcionamiento de ciertos dispositivos, porque tienen menos tiempo para “darse cuenta” de que lo hemos apagado. Si la inercia del dispositivo es suficiente, y su frecuencia natural superior a la del PWM, el comportamiento será (casi) idéntico al alimentar con una tensión analógica.
El caso práctico más frecuente lo tenemos al controlar la velocidad de un motor, que al controlar con un PWM apagamos y encendemos el motor múltiples veces por segundo. El motor es una carga inductiva, y cada encendido supone un hermoso picos de arranque. Así lleva a la situación de que al aplicar una reducción de la velocidad, el motor consume más, se calienta más, hace más ruido… cuando, a priori, podríamos pensar que debería ser al revés porque está funcionado a menos velocidad.
Eso tampoco significa que podamos subir la frecuencia de forma indefinida como locos. No todos los drivers que estemos empleando van a soportar la frecuencia y si no la soportan… ¡cataplum!. Hay que mirar cada caso en particular pero, en general, subir la frecuencia del control PWM 4-10 Khz, resulta bastante razonable.
Así que aquí tenéis un chuletario con la frecuencia, prescalers, valores por defecto y consecuencias de cada uno de los pines PWM para procesadores Atmega328p y Atmega 32u2, así como una función para modificarlo de forma sencilla.
Atmega 328p (Arduino Uno, Nano)
Frecuencia
Pins | Timer | Frecuencia |
---|---|---|
5, 6 | Timer0 | 62500 Hz |
9, 10 | Timer1 | 31250 Hz |
3, 11 | Timer2 | 31250 Hz |
Valores por defecto
Pins | Timer | Prescalers | Frecuencia |
---|---|---|---|
5, 6 | Timer0 | 64 | 977Hz |
9, 10 | Timer1 | 64 | 490Hz |
3, 11 | Timer3 | 64 | 490Hz |
Prescalers
Pins | Timer | Prescalers |
---|---|---|
5, 6 | Timer0 | 1 8 64 256 1024 |
9, 10 | Timer1 | 1 8 64 256 1024 |
3, 11 | Timer2 | 1 8 32 64 128 256 1024 |
Consecuencias y efectos
Pins | Timer | Efecto |
---|---|---|
5, 6 | Timer0 | delay() y millis() |
9, 10 | Timer1 | Librería servo |
3, 11 | Timer2 |
Código
//Atmega 328p (Arduino Uno, Nano)
// Frecuencias
// 5, 6 Timer0 62500 Hz
// 9, 10 Timer1 31250 Hz
// 3, 11 Timer2 31250 Hz
// Prescalers
// 5, 6 Timer0 1 8 64 256 1024
// 9, 10 Timer1 1 8 64 256 1024
// 3, 11 Timer2 1 8 32 64 128 256 1024
// Valores por defecto
// 5, 6 Timer0 64 977Hz
// 9, 10 Timer1 64 490Hz
// 3, 11 Timer2 64 490Hz
// Consecuencias
// 5, 6 Timer0 delay() y millis()
// 9, 10 Timer1 Librería servo
// 3, 11 Timer2
void setPWMPrescaler(uint8_t pin, uint16_t prescale) {
byte mode;
if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
switch(prescale) {
case 1: mode = 0b001; break;
case 8: mode = 0b010; break;
case 64: mode = 0b011; break;
case 256: mode = 0b100; break;
case 1024: mode = 0b101; break;
default: return;
}
} else if(pin == 3 || pin == 11) {
switch(prescale) {
case 1: mode = 0b001; break;
case 8: mode = 0b010; break;
case 32: mode = 0b011; break;
case 64: mode = 0b100; break;
case 128: mode = 0b101; break;
case 256: mode = 0b110; break;
case 1024: mode = 0b111; break;
default: return;
}
}
if(pin==5 || pin==6) {
TCCR0B = TCCR0B & 0b11111000 | mode;
} else if (pin==9 || pin==10) {
TCCR1B = TCCR1B & 0b11111000 | mode;
} else if (pin==3 || pin==11) {
TCCR2B = TCCR2B & 0b11111000 | mode;
}
}
Atmega 32U (Micro y Leonardo)
Frecuencias
Pins | Timer | Frecuencia |
---|---|---|
3, 11 | Timer0 | 64500Hz |
9, 10 | Timer1 | 31250Hz |
5 | Timer3 | 31250Hz |
6, 13 | Timer4 | 31250Hz |
Valores por defecto
Pins | Timer | Prescalers | Frecuencia |
---|---|---|---|
3, 11 | Timer0 | 64 | 977Hz |
9, 10 | Timer1 | 64 | 490Hz |
5 | Timer3 | 64 | 490Hz |
6, 13 | Timer4 | 64 | 490Hz |
Prescalers
Pins | Timer | Prescalers |
---|---|---|
3, 11 | Timer0 | 1 8 64 256 1024 |
9, 10 | Timer1 | 1 8 64 256 1024 |
5 | Timer3 | 1 8 64 256 1024 |
6, 13 | Timer4 | 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 |
Consecuencias
Pins | Timer | Efecto |
---|---|---|
3, 11 | Timer0 | delay() y millis() |
9, 10 | Timer1 | |
5 | Timer3 | |
6, 13 | Timer4 |
Código
// Atmega 32U
// Frecuencias
// 3, 11 Timer0 64500Hz
// 9, 10 Timer1 31250Hz
// 5 Timer3 31250Hz
// 6, 13 Timer4 31250Hz
// Prescalers
// 3, 11 Timer0 64 1 8 64 256 1024
// 9, 10 Timer1 64 1 8 64 256 1024
// 5 Timer3 64 1 8 64 256 1024
// 6, 13 Timer4 64 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384
// Valores por defecto
// 3, 11 Timer0 64 977Hz
// 9, 10 Timer1 64 490Hz
// 5 Timer3 64 490Hz
// 6, 13 Timer4 64 490Hz
// Conescuencias
// 3, 11 millis(), delay()
// 9, 10
// 5
// 6, 13
void setPWMPrescaler(uint8_t pin, uint16_t prescale)
{
byte mode;
if(pin==3 || pin==5 || pin==9 || pin==10 || pin==11) {
switch(prescale) {
case 1: mode = 0b001; break;
case 8: mode = 0b010; break;
case 64: mode = 0b011; break;
case 256: mode = 0b100; break;
case 1024: mode = 0b101; break;
default: return;
}
} else if(pin==6 || pin==13) {
switch(prescale) {
case 1: mode = 0b0001; break;
case 2: mode = 0b0010; break;
case 4: mode = 0b0011; break;
case 8: mode = 0b0100; break;
case 16: mode = 0b0101; break;
case 32: mode = 0b0110; break;
case 64: mode = 0b0111; break;
case 128: mode = 0b1000; break;
case 256: mode = 0b1001; break;
case 512: mode = 0b1010; break;
case 1024: mode = 0b1011; break;
case 2048: mode = 0b1100; break;
case 4096: mode = 0b1101; break;
case 8192: mode = 0b1110; break;
case 16384: mode = 0b1111; break;
default: return;
}
}
if(pin==3 || pin==11) {
TCCR0B = TCCR1B & 0b11111000 | mode;
} else if (pin==9 || pin==10) {
TCCR1B = TCCR1B & 0b11111000 | mode;
} else if (pin==5) {
TCCR3B = TCCR3B & 0b11111000 | mode;
} else if (pin==6 || pin==13) {
TCCR4B = TCCR4B & 0b11110000 | mode;
}
}
Descarga el código
Todo el código de esta entrada está disponible para su descarga en Github.