Defina a frequência PWM para 25 kHz

12

Atualmente, posso definir quatro pinos PWM para cerca de 31 kHz com o seguinte 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
}

Encontrei essa configuração em algum lugar, mas não sei como definir esses quatro pinos PWM para cerca de 25 kHz. Como isso é possível?

user16307
fonte
3
Você entende como os temporizadores do AVR funcionam?
Ignacio Vazquez-Abrams
3
Veja minha página sobre temporizadores .
Nick Gammon
11
@ IgnacioVazquez-Abrams Não estou familiarizado e preciso definir esses quatro pinos para cerca de 25kHz no início. Tenho pressa de terminar um projeto e ficaria feliz em qualquer ajuda. O código que tenho definido para 31kHz. Posso modificá-lo para 25kHz? Motores de corrente contínua requerem essa freq.
user16307
11
@ NickGammon Obrigado, mas eu realmente não tenho tempo suficiente para estudá-los no momento. Você poderia me fornecer a parte do código para configurar 25kHz. Estou perdido
user16307
2
Preciso ajustar a rotação exata para que seus ciclos de tarefas sejam ligeiramente diferentes. Que tal é possível definir 2 pinos apenas para 25kHz?
user16307

Respostas:

10

Estou postando esta segunda resposta desde que percebi que é possível ter 4 canais PWM a 25 kHz com 161 etapas em um único Arduino Uno. Isso envolve alterar a freqüência do relógio principal para 8 MHz , o que tem alguns efeitos colaterais, já que todo o programa será executado com metade da velocidade. Também envolve a reconfigurar os três temporizadores, o que significa que afrouxam as funções de temporização (Arduino millis(), micros(), delay()e delayMicroseconds()). Se esses trade-offs são aceitáveis, eis como segue:

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
}

Diferente da outra resposta , isso não precisa de uma versão modificada de analogWrite(): a padrão funcionará bem. Apenas deve-se tomar cuidado para que:

  1. O valor escrito deve estar entre 0 (significando sempre BAIXO) e 160 (sempre ALTO), inclusive.
  2. Apenas os pinos 3, 5, 9 e 10 estão disponíveis. Tentar os analogWrite() pinos 6 ou 11 não apenas falhará na saída de uma saída PWM, mas também mudará a frequência nos pinos 5 ou 3, respectivamente.
Edgar Bonet
fonte
Faz muito tempo e agora estou com a mesma coisa no Arduino Due, que usa outro processador. Eu ficaria feliz se hae qualquer entrada aqui arduino.stackexchange.com/questions/67053/...
user16307
11

Você pode configurar o Timer 1 para pedalar a 25 kHz no modo PWM de fase correta e usar suas duas saídas nos pinos 9 e 10 da seguinte maneira:

// 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
}

Escrever um valor de 0 com analogWrite25k()significa que o pino será sempre BAIXO, enquanto 320 significa sempre ALTO. O regular analogWrite() deve quase funcionar, mas interpretará 255 o mesmo que 320 (ou seja, sempre ALTO).

Este código pressupõe uma placa Arduino Uno ou similar (ATmega168 ou 328 a 16 MHz). O método usado aqui requer um timer de 16 bits e, portanto, usa o Timer 1, pois é o único disponível no Uno; é por isso que apenas duas saídas estão disponíveis. O método pode ser adaptado a outras placas baseadas em AVR com um temporizador de 16 bits. Como Gerben observou, esse timer deve ter um registro ICRx correspondente. Existem 4 desses temporizadores no Arduino Mega, cada um com 3 saídas.

Edgar Bonet
fonte
11
Pode ser útil explicar que esse método funciona apenas para o timer1, pois os outros timers não possuem um ICRxregistro. No máximo, você só pode ter um pino PWM por timer, para temporizadores 0 e 2.
Gerben
11
@ Gerben: Todos os temporizadores de 16 bits não têm esse registro? Pelo menos no Mega eles fazem.
Edgar Bonet
11
Sim, mas apenas o timer1 é de 16 bits no ATMega328. O restante é de 8 bits. E o OP quer 4 saídas PWM, e sua solução fornece apenas 2. Ou estou enganado?
Gerben
11
@ Gerben: Não, você está certo. Eu apenas digo que exigir ICRx parece ser redundante com exigir que o timer seja de 16 bits. Para o Uno e Mega, pelo menos, não tenho certeza sobre outros Arduinos baseados em AVR. O OP entende que isso fornece apenas 2 canais PWM: veja meu comentário sobre sua pergunta e sua resposta.
Edgar Bonet
2
@techniche: 1. Funciona para mim. Talvez você se esqueceu de definir COM4C1em TCCR4A? 2. Se esse não é o problema, leia Como faço uma boa pergunta? , atualize sua pergunta incluindo seu código-fonte completo e afirmando claramente o que você espera que o programa faça e o que ele faz ("Não vejo sucesso" não é considerado uma declaração de problema válida).
Edgar Bonet