FPWM

Me ha hecho falta trastear un poco con el PWM de Arduino. La verdad es que la función analogWrite( ) es fantástica y seguramente nos permite cubrir casi todas nuestras necesidades. Yo he querido ir un poco más allá y ver qué nos ofrece el ATmega2560 que viene con el Arduino Mega.

Como siempre, el documento base es el ATmega2560, que es la documentación de la familia de los mega. En el apartado 17.9 nos habla de los «Modos de operación» y más específicamente en el apartado 17.9.3 nos habla de el «Fast PWM Mode».

La tabla 17-2 nos indica la enorme variedad de modos PWM en los que podemos trabajar (opino que es imprescindible leerse todo el apartado 17 para una mejor comprensión).

Nosotros vamos a experimentar con el FPWM, que nos ofrece 5 modos diferentes de trabajo:

  1. FPWM 8bits (modo 5)
  2. FPWM 9bits (modo 6)
  3. FPWM 10bits (modo 7)
  4. FPWM 16bits (modo 14)
  5. FPWM 16bits (modo 15)

Si no recuerdo mal, la función de Arduino, analogWrite( ), utiliza el modo 5 -teniendo en cuenta 2 casos particulares-, para más info consulta el fichero «wiring_analog.c» (ya sé que soy un pesado, pero para estas cosas de buscar dónde están las definiciones de una función, utiliza herramientas como find y grep).

Además de los modos citados, podemos controlar la salida gracias a los registros COM (Compare Output Mode) que vienen explicados en la tabla 17-4, que haciendo un rápido resumen son:

  • No hacer nada
  • Toggle on compare match
  • Clear on compare match (no invertido)
  • Set on compare match (invertido)

La primera de las opciones útiles (el toggle) sólo está presente para los modos 14 y 15; hablaremos de ella más adelante. Veamos ahora por ejemplo el modo 5 en modo no invertido e invertido.

FPWM

Figura 1

El «triangulito» indica la evolución del valor del temporizador TCNTn, y las OCnx son las diferentes salidas, donde «n» va de 1 a 5 y la «x» va de A a C.

La rallita que intersecta con el temporizador, será el valor de nuestro OCRnx.

Vale, pero… ¿qué frecuencia tiene la onda y cuál es el valor TOP? Respondiendo a la segunda parte de la pregunta, en estos modos simples los valores de TOP ya vienen predeterminados:

  • 8bits: 255
  • 9bits: 511
  • 10bits: 1023

El valor de nuestro OCRnx hará que el duty cycle sea mayor o menor.

Finalmente la frecuencia es una de las 5 posibles (con el cristal ya incluido en la placa) teniendo en cuenta la fórmula dada en la documenación (pág 152):

  • Sin preescalado: 16Mhz –> 62500Hz
  • Con preescalado de 8: 2Mhz –> 7812.5Hz
  • Preescalado de 64: 250Khz –> 976.56Hz
  • Preescalado de 256: 62.5Khz –> 244.14Hz
  • Preescalado de 1024: 15625hz –> 61.03Hz

Esto es, para el modo 5. Aquí tienes un resumen de todas las frecuencias disponibles en estos modos de frecuencias predeterminadas:

Frecuencias

Figura 2

En general, para motores paso a paso, con frecuencias de hasta 600Hz, tendríamos 6 velocidades distintas.

Podría ser que esta variedad de frecuencias no fuera adecuada para nosotros. Aquí es donde los modos 14 y 15 vienen al rescate. Esta vez, seremos nosotros los que escojamos el valor de TOP variando así a nuestro gusto la frecuencia (sí, ya sé que también el los modos anteriores podríamos utilizar la interrupción de overflow para variar el valor del TCNTn y variar así también la frecuencia pero lo he omitido voluntariamente… hasta ahora).

A mi parecer, estos modos son los que más confusión causan al leer el manual de Atmel y es por ello por lo que escribo este documento con la esperanza de que sea de utilidad para alguien. Resumiendo, diré que el modo 14 sirve para crear una frecuencia fija a nuestro gusto y el modo 15 se utilizará para crear frecuencias variables a nuestro gusto (esto es debido al doble buffer del registro).

Aquí además, disponemos del modo toggle (en el modo 15), que cambiará por nosotros el estado anterior de la salida (notemos que así podemos crear frecuencias la mitad de lentas). En este caso, simplemente indicamos el valor del TOP en el registro OCRnA. No habrá comparación con otros registros, cuando llegue a TOP simplemente cambiará su estado. Nuestra onda aparecerá en el pin OCnA.

Antes de continuar. Necesitas bajarte el esquemático de Arduino Mega para saber qué pines son de los que estamos hablando. Por ejemplo, si estamos utilizando el toggle del temporizador 5 tenemos que buscar el OC5A. Veremos que tiene la etiqueta PL3, así que buscaremos PL3 y veremos finalmente que está conectado a la patilla 46 de nuestro Arduino Mega.

Si utilizamos alguno de los otros modos: set, clear. Utilizaremos el registro OCRnA para indicar el valor del TOP y cualquier otro de los registros que nos quedan OCRnB/C para realizar el compare match (la rallita de la Figura 1 que intersecta con el TCNTn). Por tanto, y esto es importante, la salida de nuestra onda la encontraremos en este otro registro B/C. La salida A queda inutilizada puesto que hemos utilizado su registro para indicar el valor del TOP (esto no es así en el modo 14).

Veamos ahora unos ejemplos prácticos:

Utilización del FPWM de 8bits.

/**
* Utilizaremos el Timer5, salida A, tren de pulsos de 7812.5Hz
* y duty-cycle del 25%
*
*/
static const int pinFPWM = 46;
static const unsigned int MAX = 255;

void setup() {
	// Activar el pin como salida
	pinMode(pinFPWM, OUTPUT);
	// FPWM modo 5, no-invertido, escalado 8
	TCCR5A = _BV(COM5A1) | _BV(WGM50);
	TCCR5B = _BV(WGM52) | _BV(CS51);
	// Duty-cycle
	OCR5A = MAX / 4;
	// Iniciar
	TCNT5 = 0;
}

void loop() {
}

Utilización del FPWM de 16bits modo toggle.

/**
* Utilizaremos el Timer5, salida A en modo toggle, tren de 15.25Hz
*
*/
static const int pinFPWM = 45;
static const unsigned int MAX = 65535;

void setup() {
	// Activar el pin como salida
	
	pinMode(pinFPWM, OUTPUT);
	// FPWM modo 15, toggle, escalado 8
	TCCR5A = _BV(COM5A0) | _BV(WGM51) | _BV(WGM50);
	TCCR5B = _BV(WGM53) | _BV(WGM52) | _BV(CS51);
	// Duty-cycle
	OCR5A = MAX;
	// Iniciar
	TCNT5 = 0;
}

void loop() {
}

Utilización del FPWM de 16bits con OCRnA como TOP y OCRnB como compare match.

/**
* Utilizaremos el Timer5, salida B
*
*/
static const int pinFPWM = 45;
static const unsigned int MAX = 65535;
void setup() {
	// Activar el pin como salida
	pinMode(pinFPWM, OUTPUT);
	// FPWM modo 15, no-invertido, escalado 8
	TCCR5A = _BV(COM5B1) | _BV(WGM51) | _BV(WGM50);
	TCCR5B = _BV(WGM53) | _BV(WGM52) | _BV(CS51);
	// Duty-cycle
	OCR5A = MAX / 2; // Valor de TOP
	OCR5B = 5000;   // Valor para el Compare Match
	// Iniciar
	TCNT5 = 0;
}

void loop() {
}