Temporizadores
PWM y servos
PWM con un Timer, el periférico PWM1 y el control de un servo
Idea clave. Una PWM es una señal de periodo fijo en la que solo cambia cuánto tiempo está en alto. Con eso se regula el brillo de una pantalla o se le dice a un servo a qué ángulo ir. Se puede generar con un Timer normal (gastando interrupciones) o con el periférico PWM1, que lo hace todo en hardware.
Qué es una PWM
es el periodo (fijo) y el tiempo en alto. En cuentas del timer: , y .
PWM con un Timer y toggle
Es el método de las varias señales del tema 2, pero sumando una cantidad distinta según el nivel del pin. Con EMCx = 11 (conmutar) y sin reset:
void TIMER0_IRQHandler(void) {
LPC_TIM0->IR = (1 << 0);
if (LPC_TIM0->EMR & (1 << 0)) // EM0: el pin acaba de pasar a 1
LPC_TIM0->MR0 += N_H; // la próxima conmutación, dentro de T_H
else
LPC_TIM0->MR0 += N_L;
}
El bit EM0 de EMR dice el nivel actual de MAT0.0, así que no hace falta leer el pin. El límite: si o son más cortos que latencia + ejecución de la ISR, cuando se suma el Match ya ha pasado y se pierde un flanco. Los ciclos de trabajo muy pequeños o muy grandes (D ≈ 0,001 o D ≈ 1) a frecuencia alta no salen.
PWM con un Timer y dos Match
Una alternativa con una sola interrupción por periodo:
- MR0 marca el periodo: reset del TC (MR0R) y, con EMC0 = 10, pone MATn.0 a 1 al empezar cada periodo.
- MR1 marca el ancho: interrumpe (MR1I) y la ISR baja el pin a mano.
void TIMER0_IRQHandler(void) {
LPC_TIM0->IR = (1 << 1); // flag de MR1
LPC_TIM0->EMR &= ~(1 << 0); // MAT0.0 a 0
}
Con MR2 y MR3 se pueden sacar más PWM con el mismo periodo (cada una en su pin y con su parte de código en la ISR). Con periodos distintos, no: el periodo lo marca MR0 para todo el timer.
El periférico PWM1
Práctica 2 (PWM para el brillo del LCD) · conviene dominarlo
Para no gastar CPU, el LPC1768 tiene un periférico dedicado: PWM1. Es un Timer con 7 Match (MR0–MR6) y 6 salidas (PWM1.1–PWM1.6):
- MR0 marca el periodo de todas las salidas: misma frecuencia para las 6.
- MR1–MR6 marcan el ancho de cada salida.
En flanco único (single edge, el de reset), la salida sube al empezar el periodo y baja cuando TC = MRx:
Ojo con el −1. Estas fórmulas son las de clase, que cuentan de 0 a MR0 como un Timer. El UM10360 dice que en modo PWM el contador vuelve a 1 (no a 0), y su ejemplo pone MR0 = 100 para un periodo de 100 cuentas; con esa lectura, . Con valores grandes (24 999 o 25 000) la diferencia es de una cuenta; en el examen, usa la convención de clase.
En doble flanco (bits PWMSEL de PCR), la subida y la bajada las marcan dos Match. Así se pueden desplazar los pulsos, pero solo salen 3 salidas.
| Bits | Campo | Qué hace |
|---|---|---|
| 0 | Counter Enable | 1 = contar |
| 1 | Counter Reset | 1 = TC y PC a 0 |
| 3 | PWM Enable | 1 = modo PWM (activa los registros sombra) |
| Bits | Campo | Qué hace |
|---|---|---|
| 0, 1, 2 | PWMMR0I, PWMMR0R, PWMMR0S | Interrumpir, poner el TC a 0 o parar en MR0. Para una PWM: PWMMR0R |
| 3–20 | MR1–MR6 | Lo mismo para cada Match, 3 bits por Match (para PWM se dejan a 0) |
| Bits | Campo | Qué hace |
|---|---|---|
| 2–6 | PWMSEL2–6 | 1 = doble flanco en esa salida; 0 = flanco único |
| 9–14 | PWMENA1–6 | 1 = salida PWM1.1…PWM1.6 habilitada |
| Bits | Campo | Qué hace |
|---|---|---|
| 0–6 | Enable PWM Match 0–6 Latch | El valor escrito en MRx se usa a partir del siguiente MR0 |
Por qué hay registros sombra. Si se cambiase MRx a mitad de periodo, podría salir un pulso cortado o doble. Con LER, el valor nuevo espera al siguiente MR0 y el cambio siempre empieza en un periodo limpio.
void set_ciclo(float ciclo) { // ciclo en %, de 0 a 100
LPC_PWM1->MR5 = ciclo * LPC_PWM1->MR0 / 100;
LPC_PWM1->LER = (1 << 5); // se aplica en el próximo periodo
}
La configuración inicial: alimentar (PWM1 ya está encendido tras el reset), llevar el pin a su función PWM1.x con PINSEL, escribir MR0 con MR0R en MCR, habilitar la salida en PCR, poner los bits de LER y arrancar con Counter Enable + PWM Enable en TCR.
Servos
Práctica 3 (Servomotor con un Timer) · conviene dominarlo
Un servo de radiocontrol lleva dentro un motor, una reductora y un control de lazo cerrado: compara su posición con la que le pide la señal y se mueve hasta igualarlas. Tiene tres cables (alimentación, masa y control), y por el de control recibe una PWM: el ancho del pulso dice el ángulo.
- Nominalmente, de 1 ms (0°) a 2 ms (180°), con 1,5 ms en el centro, repitiendo cada 20 ms.
- En clase se usó un servo con un rango más amplio, ms y:
void set_servo(float grados) {
LPC_PWM1->MR5 = Ftick * 0.4e-3 + Ftick * 2e-3 * grados / 180;
LPC_PWM1->LER = (1 << 5);
}
Cada servo tiene sus extremos reales algo distintos, así que se calibran los anchos mínimo y máximo antes de fijar la fórmula.
Preguntas de examen
Genera con PWM1 una señal de 1 kHz con D = 30 % (F_PCLK = 25 MHz). Da MR0, MRx y los registros que tocas.
. . MCR: MR0R (bit 1). PCR: PWMENAx. LER: bits 0 y x. TCR: bits 0 y 3. Y el pin en su función PWM1.x con PINSEL. (Respuesta redactada a partir de los apuntes.)
¿Qué ventajas tiene PWM1 frente a generar la PWM con un Timer e interrupciones?
No gasta CPU (no hay ISR por flanco), no tiene el límite de latencia (D muy pequeños o muy grandes salen bien), da hasta 6 salidas con la misma frecuencia y, gracias a los registros sombra, cambiar el ciclo de trabajo no corta ningún periodo. La pega: todas sus salidas comparten frecuencia. (Respuesta redactada a partir de los apuntes.)
Escribe set_servo(grados) para un servo de 0,4–2,4 ms y explica cada término.
MR5 = Ftick*0.4e-3 + Ftick*2e-3*grados/180: el primer término son las cuentas del ancho mínimo (0,4 ms, 0°); el segundo añade la parte proporcional al ángulo, hasta 2 ms más a 180°. Luego LER = (1 << 5) para que se aplique en el siguiente periodo. (Respuesta redactada a partir de los apuntes.)
Material del curso usado: Apuntes de Miguel · Temporizadores del LPC17xx (diapositivas de clase) · UM10360 (manual del LPC17xx), cap. 21 y 24.