Impostare la frequenza PWM su 25 kHz


12

Al momento posso impostare quattro pin PWM a circa 31 kHz con il seguente codice:

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
}

Ho trovato questa configurazione da qualche parte, ma non so come impostare invece questi quattro pin PWM a circa 25 kHz. Come è possibile?


3
Capisci come funzionano i timer AVR?
Ignacio Vazquez-Abrams,


1
@ IgnacioVazquez-Abrams Non sono familiare e all'inizio devo impostare quei quattro pin a circa 25 kHz. Ho fretta di finire un progetto e sarei felice di ricevere aiuto. Il codice che ho impostato su 31kHz. Posso modificarlo a 25kHz? I motori CC richiedono tale frequenza.
user16307,

1
@NickGammon Grazie ma non ho davvero abbastanza tempo per studiarli al momento. Potresti fornirmi la parte di codice per impostare 25kHz. Sono perso
user16307

2
Devo mettere a punto il loro numero di giri esatto in modo che i loro cicli di lavoro siano leggermente diversi. Che ne dici di impostare 2 pin solo a 25kHz?
user16307,

Risposte:


10

Sto postando questa seconda risposta da quando ho capito che è possibile avere 4 canali PWM a 25 kHz con 161 passaggi su un singolo Arduino Uno. Ciò comporta la modifica della frequenza di clock principale su 8 MHz , il che ha alcuni effetti collaterali poiché l'intero programma verrà eseguito la metà della velocità. Esso comporta anche riconfigurare i tre timer, il che significa perdendo le funzioni di temporizzazione (Arduino millis(), micros(), delay()e delayMicroseconds()). Se questi compromessi sono accettabili, ecco come va:

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 differenza dell'altra risposta , questo non ha bisogno di una versione modificata di analogWrite(): quella standard funzionerà bene. Si deve solo fare attenzione che:

  1. Il valore scritto deve essere compreso tra 0 (che significa sempre BASSO) e 160 (sempre ALTO), incluso.
  2. Sono disponibili solo i pin 3, 5, 9 e 10. Il tentativo ai analogWrite() pin 6 o 11 non solo non riuscirà a fornire un'uscita PWM, ma cambierà anche la frequenza sul pin 5 o 3 rispettivamente.

È passato molto tempo e ora sono bloccato con la stessa cosa con Arduino Due che utilizza un altro processore. Sarei felice se hai qualche input qui arduino.stackexchange.com/questions/67053/…
user16307

11

È possibile configurare il Timer 1 per un ciclo a 25 kHz in modalità PWM a fase corretta e utilizzare le sue due uscite sui pin 9 e 10 in questo modo:

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

Scrivere un valore di 0 con analogWrite25k()significa che il pin sarà sempre BASSO, mentre 320 significa sempre ALTO. Il normale analogWrite() dovrebbe quasi funzionare, ma interpreterà 255 come 320 (ovvero sempre ALTO).

Questo codice presuppone un Arduino Uno o una scheda simile (ATmega168 o 328 a 16 MHz). Il metodo utilizzato qui richiede un timer a 16 bit, e quindi utilizza Timer 1 in quanto è l'unico disponibile su Uno; ecco perché sono disponibili solo due uscite. Il metodo potrebbe essere adattato ad altre schede basate su AVR con un timer a 16 bit. Come notato da Gerben, quel timer dovrebbe avere un registro ICRx corrispondente. Esistono 4 timer di questo tipo su Arduino Mega, ciascuno con 3 uscite.


1
Potrebbe essere utile spiegare che questo metodo funziona solo con timer1, poiché gli altri timer non hanno un ICRxregistro. Al massimo, puoi avere solo un pin PWM per timer, per i timer 0 e 2.
Gerben

1
@Gerben: Non tutti i timer a 16 bit hanno quel registro? Almeno sulla Mega lo fanno.
Edgar Bonet,

1
Sì, ma solo timer1 è a 16 bit su ATMega328. Il resto è a 8 bit. E l'OP vuole 4 output PWM, e la tua soluzione fornisce solo 2. O sbaglio?
Gerben,

1
@Gerben: No, hai ragione. Dico solo che richiedere ICRx sembra ridondante con la necessità che il timer sia a 16 bit. Almeno per Uno e Mega, non sono sicuro di altri Arduinos basati su AVR. L'OP capisce che fornisce solo 2 canali PWM: vedi il mio commento sulla sua domanda e la sua risposta.
Edgar Bonet,

2
@techniche: 1. Funziona per me. Forse ti sei dimenticato di impostare COM4C1in TCCR4A? 2. Se non è questo il problema, leggi Come posso fare una buona domanda? , quindi aggiorna la tua domanda includendo il tuo codice sorgente completo e indicando chiaramente cosa ti aspetti che faccia il programma e cosa fa invece ("Non vedo alcun successo" non è considerato una dichiarazione di problema valida).
Edgar Bonet,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.