Augmentez la résolution des bits PWM

9

Je voudrais augmenter la résolution bit PWM de l'Arduino Uno. En ce moment, c'est 8 bits que je considère comme trop bas. Est-ce possible sans perdre la possibilité d'interruptions et de retards?

Koen

EDIT Cette configuration fournit une résultance 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
la source

Réponses:

15

L'Arduino Uno est basé sur un microcontrôleur ATmega382P. Cette puce possède deux temporisateurs 8 bits, pilotant chacun deux canaux PWM, et un temporisateur 16 bits, pilotant les deux derniers canaux.

Vous ne pouvez pas augmenter la résolution des minuteries 8 bits. Vous pouvez cependant mettre le minuteur 16 bits en mode 16 bits, au lieu du mode 8 bits utilisé par la bibliothèque principale Arduino. Cela vous donnera deux canaux PWM 16 bits, avec une fréquence réduite de 244 Hz (maximum). Vous devrez probablement configurer la minuterie vous-même et ne bénéficierez pas de la fonction facile à utiliser analogWrite(). Pour plus de détails, consultez la section sur le temporisateur 1 dans la fiche technique ATmega328P .

Mise à jour : Voici une implémentation d'un 16 bits analogWrite(). Il ne fonctionne que sur les broches 9 et 10, car ce sont les seules broches connectées au temporisateur 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;
    }
}

Vous pouvez remarquer que le haut de la séquence de compteur est configuré explicitement. Vous pouvez changer cela à une valeur plus petite pour rendre le PWM plus rapide, au prix d'une résolution réduite.

Et voici un exemple de croquis illustrant son utilisation:

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
la source
Wow merci beaucoup, c'est exactement ce dont j'ai besoin. Je veux que ma résultat PWM soit le même que ma résolution de capteur. Si je change votre code pour <regarder ma modification>, serait-ce une résultante de 13 bits? Si oui, quelle serait la fréquence? Je conduirai un moteur à courant continu avec lui, donc 244 Hz sera un peu moins je suppose
KoenR
@KoenR: Non, le prescaler n'a aucun effet sur la résolution, son but est de ralentir le comptage. Régler le prescaler sur 8 vous donnera une fréquence PWM de 30,5 Hz. Si vous voulez une résolution de 13 bits, réglée ICR1sur 0x1fff, alors votre fréquence sera 1953 Hz (F_CPU / (TOP + 1)) avec un pré-échelle à 1.
Edgar Bonet
Merci pour l'explication. Modification de ma question afin qu'elle couvre ces erreurs. Ainsi, d'autres personnes peuvent le voir directement. Je vous remercie!
KoenR
1
@Edgar Bonet C'est génial, mais je n'arrive pas à éteindre complètement une LED. J'utilise ICR1 = 0x03FFet à 0 je vois une petite impulsion sur la lunette suffisamment pour allumer la led. Des idées?
davivid
1
@davivid: Oui, vous ne pouvez pas avoir un rapport cyclique nul. analogWrite16(pin, val)donne un rapport cyclique de (val + 1) / ICR1. Comme solution de contournement, Arduino le analogWrite()fait if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);. Mais alors vous ne pouvez pas obtenir un cycle d'utilisation de 1 / ICR1 ...
Edgar Bonet
3

Avec un certain étalonnage, vous pouvez additionner les sorties de deux canaux PWM avec des résistances de pondération différentes. À l'extrême, vous pouvez théoriquement utiliser une sortie pour fournir 8 bits de résolution et mettre à l'échelle l'autre au 1 / 256ème du niveau et les ajouter de sorte que le 2ème canal couvre un bit de plage et vous (encore une fois théoriquement) obtenez 16 bits de résolution. Sans grand soin et ajustement, tout ce que vous obtiendriez serait un gâchis.
Cependant, en divisant le 2e canal par 16 ou 32, vous pouvez ajouter plusieurs bits supplémentaires de résolution PWM. En ajoutant simplement 2 sorties PWM filtrées analogiques, vous ajoutez un bit supplémentaire (car la plage de potentiel est doublée pour un mV / bit inchangé).
Théoriquement (à nouveau) pour chaque division supplémentaire par 2, vous obtenez un bit de résolution supplémentaire, mais cela ne peut être effectué que pour peut-être 4 ou 5 ou 6 bits supplémentaires, avec des exigences de précision croissantes des résistances de mise à l'échelle et un étalonnage et une propension aux erreurs plus difficiles .

Bref exemple.
Si un PWM est réduit pour donner par exemple 0 - 255 mV par pas de 1 mV, la somme de deux PWM avec une amplitude égale donnerait une plage de 0 - 510 mV par pas de 1 mV.
Si un PWM est réduit d'un facteur 32, au lieu d'ajouter 255 mV à la plage PWM initiale, il n'ajouterait que 8 mV à l'extrémité supérieure (0,256,32 = 8 mV, mais la résolution serait de 0,03125 (1/32 e). ) Pas en mV.

Bien que cela puisse peut-être être réalisé uniquement avec la sommation des résistances et le filtrage RC, l'utilisation d'un ampli op été améliorerait considérablement les résultats.

De plus, l'ondulation PWM pourrait être filtrée avec un simple filtre RC, mais l'utilisation d'un ampli op comme tampon (ou même juste un seul transistor comme suiveur d'émetteur) vous donnerait 3 ou 5 pôles de filtrage passe-bas et de bien meilleures chances d'obtenir un PWM supplémentaire résolution. Je n'ai pas inspecté la "cohérence de phase" des sorties PWM mais je m'attends à ce qu'elles se déplacent en cadence relative afin que vous n'obteniez pas l'avantage de lissage d'ajouter deux formes d'onde non corrélées.

Plus de commentaires peuvent être faits si nécessaire. Demandez si vous êtes intéressé.

Russell McMahon
la source
C'est intelligent! Il semble que la bibliothèque de synthèse sonore Mozzi utilise cette astuce car elle est appelée mode "HIFI".
Edgar Bonet
C'est un grand nous du PWM. Mais cela ne lisserait-il pas la forme d'onde? Je pose cette question puisque vous utilisez un filtre RC. Je n'ai pas mentionné cela dans ma question, mais je conduis un moteur à courant continu avec <honte>. Merci de votre contribution!
KoenR
@KoenR (fwiw: Je ne vois rien à avoir honte.) Je ne sais pas quelle réponse en fréquence / taux de changement vous voulez dans votre sortie ADC. Ou pourquoi vous voulez N bits ou quelle taille est suffisante. Les moteurs ne seront généralement pas utilement contrôlés par plus de 8 bits - cela dépend de la précision d'une application. Le moteur fait partie d'un filtre de lissage en raison de l'inductance. Vous devez dire quel type de moteur et comment il est entraîné. Et un schéma de circuit est essentiel. Sauf si le moteur est minuscule, vous avez un pilote. Un moteur à balais alimenté en PWM doit avoir une diode de capture pour faire passer le courant du moteur lorsque le PWM est éteint. Ajout de deux ...
Russell McMahon
... Les PWM ici sont entièrement réalisables mais les détails du circuit doivent être connus.
Russell McMahon
Il faut se méfier! Dans certains cas, le lissage PWM avec un RC passe-bas n'est pas souhaitable. Par exemple, si vous branchez la sortie Arduino dans la porte d'un MOSFET, le MOSFET restera froid tant qu'il est piloté par un PWM propre. Mais si vous le lissez, le MOSFET commencera à dissiper beaucoup plus de chaleur. Parfois, ce n'est pas une bonne chose.
Florin Andrei