Ajuste la frecuencia PWM a 25 kHz

12

Actualmente puedo configurar cuatro pines PWM a alrededor de 31 kHz con el siguiente código:

void setup()
{
    TCCR1B = TCCR1B & B11111000 | B00000001; // Set PWM frequency for D9 & D10:
    pinMode(pwmPin9, OUTPUT); // Sets the pin as output
    pinMode(pwmPin10, OUTPUT); // Sets the pin as output


    TCCR2B = TCCR2B & B11111000 | B00000001; // Set PWM for D3 & D11
    pinMode(pwmPin3, OUTPUT); // Sets the pin as output
    pinMode(pwmPin11, OUTPUT); // Sets the pin as output
}

Encontré esta configuración en alguna parte, pero no sé cómo puedo configurar estos cuatro pines PWM a alrededor de 25 kHz. ¿Cómo es eso posible?

usuario16307
fuente
3
¿Entiendes cómo funcionan los temporizadores AVR?
Ignacio Vazquez-Abrams
3
Vea mi página sobre temporizadores .
Nick Gammon
1
@ IgnacioVazquez-Abrams No estoy familiarizado y necesito configurar esos cuatro pines a unos 25 kHz al principio. Tengo prisa por terminar un proyecto y me agradaría cualquier ayuda. El código que tengo establece a 31kHz. ¿Puedo modificarlo a 25kHz? Los motores de corriente continua requieren esa frecuencia.
usuario16307
1
@NickGammon Gracias, pero realmente no tengo tiempo suficiente para estudiarlos en este momento. ¿Podría proporcionarme la parte del código para configurar 25kHz? Estoy perdido
user16307
2
Necesito ajustar sus rpm exactas para que sus ciclos de trabajo sean ligeramente diferentes. ¿Qué tal es posible configurar 2 pines solo a 25kHz?
user16307

Respuestas:

10

Estoy publicando esta segunda respuesta ya que me di cuenta de que es posible tener 4 canales PWM a 25 kHz con 161 pasos en un solo Arduino Uno. Esto implica cambiar la frecuencia del reloj principal a 8 MHz , lo que tiene algunos efectos secundarios ya que todo el programa se ejecutará la mitad de rápido. También implica la reconfiguración de los tres contadores de tiempo, lo que significa que pierden las funciones de temporización Arduino ( millis(), micros(), delay()y delayMicroseconds()). Si estas compensaciones son aceptables, así es como funciona:

void setup()
{
    // Set the main system clock to 8 MHz.
    noInterrupts();
    CLKPR = _BV(CLKPCE);  // enable change of the clock prescaler
    CLKPR = _BV(CLKPS0);  // divide frequency by 2
    interrupts();

    // Configure Timer 0 for phase correct PWM @ 25 kHz.
    TCCR0A = 0;           // undo the configuration done by...
    TCCR0B = 0;           // ...the Arduino core library
    TCNT0  = 0;           // reset timer
    TCCR0A = _BV(COM0B1)  // non-inverted PWM on ch. B
        | _BV(WGM00);  // mode 5: ph. correct PWM, TOP = OCR0A
    TCCR0B = _BV(WGM02)   // ditto
        | _BV(CS00);   // prescaler = 1
    OCR0A  = 160;         // TOP = 160

    // Same for Timer 1.
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
        | _BV(COM1B1)  // same on ch. B
        | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
        | _BV(CS10);   // prescaler = 1
    ICR1   = 160;

    // Same for Timer 2.
    TCCR2A = 0;
    TCCR2B = 0;
    TCNT2  = 0;
    TCCR2A = _BV(COM2B1)  // non-inverted PWM on ch. B
        | _BV(WGM20);  // mode 5: ph. correct PWM, TOP = OCR2A
    TCCR2B = _BV(WGM22)   // ditto
        | _BV(CS20);   // prescaler = 1
    OCR2A  = 160;
}

void loop()
{
    analogWrite( 3,   1);  // duty cycle = 1/160
    analogWrite( 5,  53);  // ~ 1/3
    analogWrite( 9, 107);  // ~ 2/3
    analogWrite(10, 159);  // 159/160
}

A diferencia de la otra respuesta , esta no necesita una versión modificada de analogWrite(): la estándar funcionará bien. Solo se debe tener cuidado de que:

  1. El valor escrito debe estar entre 0 (que significa siempre BAJO) y 160 (siempre ALTO), inclusive.
  2. Solo los pines 3, 5, 9 y 10 están disponibles. Intentar con los analogWrite() pines 6 u 11 no solo fallará en entregar una salida PWM, sino que también cambiará la frecuencia en el pin 5 o 3 respectivamente.
Edgar Bonet
fuente
Ha pasado mucho tiempo y ahora estoy atascado con Arduino Due, que usa otro procesador. Me alegraría si tiene alguna entrada aquí arduino.stackexchange.com/questions/67053/…
user16307
11

Puede configurar el Temporizador 1 para realizar un ciclo a 25 kHz en el modo PWM de fase correcta, y usar sus dos salidas en los pines 9 y 10 de esta manera:

// PWM output @ 25 kHz, only on pins 9 and 10.
// Output value should be between 0 and 320, inclusive.
void analogWrite25k(int pin, int value)
{
    switch (pin) {
        case 9:
            OCR1A = value;
            break;
        case 10:
            OCR1B = value;
            break;
        default:
            // no other pin will work
            break;
    }
}

void setup()
{
    // Configure Timer 1 for PWM @ 25 kHz.
    TCCR1A = 0;           // undo the configuration done by...
    TCCR1B = 0;           // ...the Arduino core library
    TCNT1  = 0;           // reset timer
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
           | _BV(COM1B1)  // same on ch; B
           | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
           | _BV(CS10);   // prescaler = 1
    ICR1   = 320;         // TOP = 320

    // Set the PWM pins as output.
    pinMode( 9, OUTPUT);
    pinMode(10, OUTPUT);
}

void loop()
{
    // Just an example:
    analogWrite25k( 9, 110);
    analogWrite25k(10, 210);
    for (;;) ;  // infinite loop
}

Escribir un valor de 0 con analogWrite25k()significa que el pin siempre será BAJO, mientras que 320 significa siempre ALTO. El regular analogWrite() debería casi funcionar, pero interpretará 255 igual que 320 (es decir, siempre ALTO).

Este código supone una placa Arduino Uno o similar (ATmega168 o 328 @ 16 MHz). El método utilizado aquí requiere un temporizador de 16 bits y, por lo tanto, utiliza el temporizador 1, ya que es el único disponible en el Uno; Es por eso que solo hay dos salidas disponibles. El método podría adaptarse a otras placas basadas en AVR con un temporizador de 16 bits. Como señaló Gerben, ese temporizador debe tener un registro ICRx correspondiente. Hay 4 temporizadores de este tipo en el Arduino Mega, cada uno con 3 salidas.

Edgar Bonet
fuente
1
Puede ser útil explicar que este método solo funciona para el temporizador1, ya que los otros temporizadores no tienen un ICRxregistro. Como máximo, solo puede tener un pin PWM por temporizador, para los temporizadores 0 y 2.
Gerben
1
@ Gerben: ¿No tienen todos los temporizadores de 16 bits ese registro? Al menos en el Mega que hacen.
Edgar Bonet
1
Sí, pero solo el temporizador1 es de 16 bits en el ATMega328. El resto son de 8 bits. Y el OP quiere 4 salidas PWM, y su solución solo proporciona 2. ¿O me equivoco?
Gerben
1
@ Gerben: No, tienes razón. Solo digo que requerir ICRx parece ser redundante al requerir que el temporizador sea de 16 bits. Al menos para Uno y Mega, no estoy seguro acerca de otros Arduinos basados ​​en AVR. El OP entiende que esto solo proporciona 2 canales PWM: vea mi comentario sobre su pregunta y su respuesta.
Edgar Bonet
2
@techniche: 1. Funciona para mí. Tal vez se le olvidó preparar COM4C1en TCCR4A? 2. Si ese no es el problema, lea ¿Cómo hago una buena pregunta? , luego actualice su pregunta incluyendo su código fuente completo e indicando claramente lo que espera que haga el programa y lo que hace en su lugar ("No veo ningún éxito" no se considera una declaración de problema válida).
Edgar Bonet