Aumentar la resolución de bits PWM

9

Me gustaría aumentar la resolución de bits PWM del Arduino Uno. En este momento es de 8 bits, lo que considero demasiado bajo. ¿Es esto posible sin perder la capacidad de interrupciones y retrasos?

Koen

EDITAR Esta configuración ofrece un resultado de 16 bits

void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS11);                    /* prescaler: clock / 8 */
    ICR1 = 0xffff;                      /* TOP counter value (freeing OCR1A*/
}
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> (16000000/8)/(65535+1)=30.5175Hz
*/

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}
KoenR
fuente

Respuestas:

15

El Arduino Uno está basado en un microcontrolador ATmega382P. Este chip tiene dos temporizadores de 8 bits, que controlan dos canales PWM cada uno, y un temporizador de 16 bits, que controla los dos últimos canales.

No puede aumentar la resolución de los temporizadores de 8 bits. Sin embargo, puede poner el temporizador de 16 bits en modo de 16 bits, en lugar del modo de 8 bits utilizado por la biblioteca principal de Arduino. Esto le dará dos canales PWM de 16 bits, con una frecuencia reducida de 244 Hz (máximo). Probablemente tendrá que configurar el temporizador usted mismo y no se beneficiará de la función fácil de usar analogWrite(). Para obtener detalles, consulte la sección sobre el Temporizador 1 en la hoja de datos ATmega328P .

Actualización : Aquí hay una implementación de 16 bits analogWrite(). Solo funciona en los pines 9 y 10, ya que estos son los únicos pines conectados al temporizador de 16 bits.

/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                    /* no prescaling */
    ICR1 = 0xffff;                      /* TOP counter value */
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

Puede notar que la parte superior de la secuencia del contador está configurada explícitamente. Puede cambiar esto a un valor más pequeño para que el PWM sea más rápido, a costa de una resolución disminuida.

Y aquí hay un boceto de ejemplo que ilustra su uso:

void setup() {
    setupPWM16();
}

/* Test: send very slow sawtooth waves. */
void loop() {
    static uint16_t i;
    analogWrite16(9, i);
    analogWrite16(10, 0xffff - i);
    i++;
    delay(1);
}
Edgar Bonet
fuente
Wow muchas gracias, esto es exactamente lo que necesito. Quiero que mi resultado PWM sea el mismo que la resolución de mi sensor. Si cambio su código a <mirar mi edición>, ¿sería un resultado de 13 bits? Si es así, ¿cuál sería la frecuencia? Conduciré un motor de CC con él, por lo que 244Hz será un poco menos, supongo
KoenR
@KoenR: No, el preescalador no tiene ningún efecto en la resolución, su objetivo es ralentizar el conteo. Configurar el preescalador a 8 le dará una frecuencia PWM de 30.5 Hz. Si desea una resolución de 13 bits, juego ICR1para 0x1fff, a continuación, su frecuencia será 1953 Hz (F_CPU / (TOP + 1)) con un pre-escalador en 1.
Edgar Bonet
Gracias por la explicación. Edité mi pregunta para que cubra estos errores. Para que otras personas puedan verlo directamente. ¡Gracias!
KoenR
1
@Edgar Bonet Esto es genial, sin embargo, parece que no puedo apagar completamente un LED. Estoy usando ICR1 = 0x03FFy en 0 veo un pequeño pulso en el visor lo suficiente como para encender el led. ¿Algunas ideas?
Davivid
1
@davivid: Sí, no puede tener un ciclo de trabajo cero. analogWrite16(pin, val)da un ciclo de trabajo de (val + 1) / ICR1. Como solución alternativa, Arduino lo analogWrite()hace if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. Pero entonces no puede obtener un ciclo de trabajo de 1 / ICR1 ...
Edgar Bonet
3

Con alguna calibración, podría sumar las salidas de dos canales PWM con diferentes resistencias de ponderación. En el extremo, podría usar una salida para proporcionar 8 bits de resolución y escalar la otra a 1/256 del nivel y agregarlos para que el segundo canal cubra un bit de rango y usted (nuevamente nocionalmente) obtenga 16 bits de resolución. Sin un cuidado y ajuste inmenso, todo lo que obtendrías sería un desastre.
Sin embargo, al dividir el segundo canal por 16 o 32, puede agregar varios bits adicionales de resolución PWM. Simplemente agregando 2 canales PWM con salidas analógicas filtradas, agrega un bit adicional (ya que el rango potencial se duplica para mV / bit sin cambios).
Nocionalmente (de nuevo) por cada división adicional entre 2 obtienes un bit adicional de resolución, pero esto solo puede llevarse a cabo para quizás 4 o 5 o 6 bits adicionales, con requisitos de precisión cada vez mayores de resistencias de escala y calibración más difícil y propensión a errores .

Breve ejemplo
Si un PWM se reduce para dar 0 - 255 mV en un paso de 1 mV, entonces sumar dos PWM con igual amplitud daría un rango de 0 - 510 mV en pasos de 1 mV.
Si un PWM se reduce por un factor de 32, en lugar de agregar 255 mV al rango inicial de PWM, agregaría solo 8 mV al extremo superior (0.256.32 = 8 mV, pero la resolución estaría en 0.03125 (1/32) ) mV pasos.

Si bien esto podría lograrse simplemente con la suma de resistencias y el filtrado RC, el uso de un amplificador operacional de verano mejoraría enormemente los resultados.

Además, la ondulación PWM podría filtrarse con un filtro RC simple, pero el uso de un opamp como amortiguador (o incluso un solo transistor como seguidor de emisor) le daría 3 o 5 polos de filtrado de paso bajo y muchas más posibilidades de lograr PWM adicional resolución. No he inspeccionado la "coherencia de fase" de las salidas PWM, pero espero que se muevan en un bloqueo relativo para que no tenga la ventaja de suavizar la adición de dos formas de onda no correlacionadas.

Se pueden hacer más comentarios si es necesario. Pregunte si está interesado.

Russell McMahon
fuente
Esto es inteligente! Parece que la biblioteca de síntesis de sonido Mozzi utiliza este truco porque se llama modo "HIFI".
Edgar Bonet
Ese es un gran nosotros de la PWM. ¿Pero no suavizaría esto la forma de onda? Te pregunto esto porque estás usando un filtro RC. No he mencionado esto en mi pregunta, pero estoy conduciendo un motor de CC con él <sintiéndome avergonzado>. ¡Gracias por la aportación!
KoenR
@KoenR (fwiw: no veo nada de qué avergonzarse). No sé qué respuesta de frecuencia / tasa de cambio desea en su salida de ADC. O por qué quieres N bits o qué tan grande es suficiente. Los motores generalmente no serán controlados de manera útil por más de 8 bits, depende de la precisión que tenga una aplicación. El motor actúa como parte de un filtro de suavizado debido a la inductancia. Debe decir qué tipo de motor y cómo se conduce. Y un diagrama de circuito es esencial. A menos que el motor sea pequeño, tiene un controlador. Un motor cepillado alimentado con PWM debe tener un diodo de captura para pasar la corriente del motor cuando el PWM está apagado. Agregando dos ...
Russell McMahon
... Los PWM aquí son totalmente factibles, pero los detalles del circuito deben conocerse.
Russell McMahon
¡Tener cuidado! En algunos casos, no es deseable suavizar PWM con un RC de paso bajo. Por ejemplo, si conecta la salida Arduino a la puerta de un MOSFET, el MOSFET se mantendrá frío siempre que sea impulsado por un PWM limpio. Pero si lo suaviza, el MOSFET comenzará a disipar mucho más calor. A veces eso no es bueno.
Florin Andrei