Réglez la fréquence PWM sur 25 kHz

12

Je peux actuellement définir quatre broches PWM à environ 31 kHz avec le code suivant:

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
}

J'ai trouvé cette configuration quelque part, mais je ne sais pas comment je peux régler ces quatre broches PWM à environ 25 kHz à la place. Comment est-ce possible?

user16307
la source
3
Comprenez-vous comment fonctionnent les minuteries AVR?
Ignacio Vazquez-Abrams
3
Voir ma page sur les minuteries .
Nick Gammon
1
@ IgnacioVazquez-Abrams Je ne suis pas familier et j'ai besoin de régler ces quatre broches à environ 25 kHz au début. J'ai hâte de terminer un projet et je serais heureux de toute aide. Le code que j'ai mis à 31 kHz. Puis-je le modifier à 25 kHz? Les moteurs à courant continu nécessitent cette fréquence.
user16307
1
@NickGammon Merci mais je n'ai vraiment pas assez de temps pour les étudier en ce moment. Pourriez-vous me fournir la partie code pour configurer 25 kHz. Je suis perdu
user16307
2
Je dois régler leur régime exact afin que leurs cycles de service soient légèrement différents. Comment est-il possible de régler 2 broches uniquement à 25 kHz?
user16307

Réponses:

10

Je poste cette deuxième réponse depuis que j'ai réalisé qu'il était possible d'avoir 4 canaux PWM à 25 kHz avec 161 pas sur un seul Arduino Uno. Cela implique de changer la fréquence d'horloge principale à 8 MHz , ce qui a certains effets secondaires puisque l'ensemble du programme s'exécutera deux fois moins vite. Elle implique également la reconfiguration des trois minuteries, ce qui signifie perdre les fonctions de synchronisation (Arduino millis(), micros(), delay()et delayMicroseconds()). Si ces compromis sont acceptables, voici comment cela se passe:

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
}

Contrairement à l' autre réponse , cela n'a pas besoin d'une version modifiée de analogWrite(): la version standard fonctionnera bien. Il ne faut veiller qu'à:

  1. La valeur écrite doit être comprise entre 0 (ce qui signifie toujours LOW) et 160 (toujours HIGH), inclus.
  2. Seules les broches 3, 5, 9 et 10 sont disponibles. Tenter de brancher sur analogWrite() les broches 6 ou 11 échouera non seulement à fournir une sortie PWM, mais changera également la fréquence sur les broches 5 ou 3 respectivement.
Edgar Bonet
la source
Cela fait très longtemps et maintenant je suis coincé avec la même chose avec Arduino Due qui utilise un autre processeur. Je serais heureux si vous avez des commentaires ici arduino.stackexchange.com/questions/67053/…
user16307
11

Vous pouvez configurer le temporisateur 1 pour qu'il cycle à 25 kHz en mode PWM à phase correcte, et utiliser ses deux sorties sur les broches 9 et 10 comme ceci:

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

L'écriture d'une valeur de 0 analogWrite25k()signifie que la broche sera toujours LOW, tandis que 320 signifie toujours HIGH. Le régulier analogWrite() devrait presque fonctionner, mais il interprétera 255 de la même manière que 320 (c'est-à-dire toujours HIGH).

Ce code suppose une carte Arduino Uno ou similaire (ATmega168 ou 328 @ 16 MHz). La méthode utilisée ici nécessite un temporisateur 16 bits, et donc elle utilise le temporisateur 1 car c'est le seul disponible sur l'Uno; c'est pourquoi seules deux sorties sont disponibles. La méthode pourrait être adaptée à d'autres cartes basées sur AVR avec une minuterie 16 bits. Comme Gerben l'a noté, ce temporisateur devrait avoir un registre ICRx correspondant. Il y a 4 minuteries de ce type sur l'Arduino Mega, chacune avec 3 sorties.

Edgar Bonet
la source
1
Il pourrait être utile d'expliquer que cette méthode ne fonctionne que pour timer1, car les autres timers n'ont pas de ICRxregistre. Tout au plus, vous ne pouvez avoir qu'une seule broche PWM par minuterie, pour les temporisateurs 0 et 2.
Gerben
1
@ Gerben: Tous les temporisateurs 16 bits n'ont-ils pas ce registre? Au moins sur le Mega, ils le font.
Edgar Bonet
1
Oui, mais seul timer1 est 16 bits sur l'ATMega328. Les autres sont 8 bits. Et l'OP veut 4 sorties PWM, et votre solution n'en fournit que 2. Ou je me trompe?
Gerben
1
@ Gerben: Non, vous avez raison. Je dis simplement qu'exiger ICRx semble être redondant avec l'exigence que le temporisateur soit de 16 bits. Pour les Uno et Mega au moins, je ne suis pas sûr des autres Arduinos basés sur AVR. L'OP comprend que cela ne fournit que 2 canaux PWM: voir mon commentaire sur sa question et sa réponse.
Edgar Bonet
2
@techniche: 1. Fonctionne pour moi. Peut - être que vous avez oublié de mettre COM4C1en TCCR4A? 2. Si ce n'est pas le problème, lisez Comment puis-je poser une bonne question? , puis mettez à jour votre question en incluant votre code source complet et en indiquant clairement ce que vous attendez du programme et ce qu'il fait à la place («Je ne vois aucun succès» n'est pas considéré comme une déclaration de problème valide).
Edgar Bonet