Pseudo CNA avec FastPWM de 8 à 16 bits

Bonjour à tourtes et à tous,

Peut-être que je suis en train de réinventer la roue, mais bon, je présente.

Il me faut un générateur de tension continue avec une résolution supérieure à 8 bits (attention, je n'ai pas dit précision). Pour cela, j'utilise le timer 2 associé aux sorties D3 et D11.

La sortie D3 va me générer un PWM sur les 8 bits de poids forts et la sortie D11, un PWM sur les N bits de poids faibles. N allant de 1 à 8 pour une résolution allant de 9 à 16 bits.

Comme le montre ce schéma
Schema_01

le courant généré vers l'ampli Op est la somme de ce qui passe dans R, connecté à D3 et ce qui passe dans kBitR connecté à D11. Le courant max dans kBitR valant 1 LSB de celui passant dans R.

Il y a deux manières de procéder :

  • soit sur D11 on utilise l'excursion max (0 à 256) et à ce moment, kBit vaut 256,

  • soit sur D11 on utilise une excursion limitée à 2^N LSB, auquel cas, kBit vaut 2^N.

Pour voir la forme des PWM sur chaque sortie, le sketch est le suivant :

#define frequencePWMde31372hz 0b00000001
#define frequencePWMde3921hz  0b00000010
#define frequencePWMde980hz   0b00000011
#define frequencePWMde490hz   0b00000100
#define frequencePWMde245hz   0b00000101
#define frequencePWMde122hz   0b00000110
#define frequencePWMde30hz    0b00000111
// Nota : ces fréquences sont celles obtenues avec un µC fonctionnant sur un quartz de 16MHz, tout en laissant le Prescaler sur "1" (pas de division de fréquence globale, donc)

int mode;
int NbBit = 16;
uint32_t kBit;

void setup()  
{ 
//  mode = 1; // Excursion partielle
  mode = 2; // Excursion totale
  kBit = pow(2, NbBit-8);

  // Déclaration de la broche d'E/S D3 en sortie
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);

  analogReference(DEFAULT); // Référence de 5 volts
  Serial.begin(9600);

  
  // Sélection du rapport de division de fréquence du timer 2
  
  TCCR2B &= 0b11111000;               // <===== à ne pas toucher
  TCCR2B |= frequencePWMde31372hz;    // <===== à changer, selon la fréquence que vous souhaitez en sortie

    // Nota 1 : l'opérateur "&=" constitue un "ET logique". Il applique le masque "0b11111000", afin de mettre à 0 les 3 derniers bits du registre TCCR2B, tout en laissant les autres bits intacts
    // Nota 2 : la fonction "|=" constitue un "OU logique". Il applique notre valeur à 8 bits, comme définie tout en haut, afin de modifier les 3 derniers bits du registre TCCR2B, précédemment mis à zéro

  bitClear(TCCR2B, WGM02);      // Mise de WGM02 à 0
  bitSet(TCCR2A, WGM01);        // Mise de WGM01 à 1
  bitSet(TCCR2A, WGM00);        // Mise de WGM00 à 1
}

void loop() { 
  switch(mode) {
    case 1: // Excursion partielle. La résistance vaut kBit*R
      for (uint32_t i = 112*kBit; i < 144*kBit; i++) {
        if (i%kBit == 0)
          analogWrite(3, i/kBit);
        analogWrite(11, i%kBit);
        delay(1024/kBit);
      }  
      break;
    case 2: // Excursion totale. Ici la résistance vaut toujours 256*R
      for (uint32_t i = 112*kBit; i < 144*kBit; i++) {
        if (i%kBit == 0)
          analogWrite(3, i/kBit);
        analogWrite(11, (256/kBit)*(i%kBit));      
        delay(1024/kBit);
      }
      break;
  }
}

Bon amusement pour ceux qui ont un oscilloscope.

Cordialement.

Pierre.

Je ne comprends pas très bien. Si tu veux un pwm 16 bits, pourquoi ne pas utiliser le pwm 16 bits du timer 2? C'est simple et il n'y a pas besoin d'AOP extérieur.

Par défaut le pwm sur le timer 3 est sur 8 bits et à 490Hz. Cela veut dire que TCCR2B contient
0b00000100. La première ligne ne sert à rien, la deuxième ne pas fonctionner car elle doit mettre 1 dans le bit de poids faible et on va se retrouver avec TCCR2B

Je n'ai pas précisé que j'utilise un Arduino Pro-Mini qui ne possède pas de timer 3.

Par ailleurs, peut-être que je n'ai pas bien cherché, mais je n'ai pas trouvé de Fast PWM 16 bits utilisant le timer 2.

Cordialement.

Pierre.

Tout est expliqué dans la datasheet du microcontroleur.
Il y a la valeur d'un ou deux registres à modifier.

La valeur max de la fréquence de récurence est
Fpwm = Fh/28 = 16 MHz/256 = 62,5 kHz

Sur un atmega328p

  • il y a 3 timers : le 0, le 1 , le 2
  • par défaut (sauce Arduino) leur prescaler est réglé sur 64 ce qui donne une fréquence de récurrence est 0,976 kHz (62,5 kHz / 64)
  • par défaut (sauce Arduino) le timer 0 et le timer 2 sont mis en mode "pwm fast"
  • par défaut (sauce Arduino) le timer 1 est mis en mode "pwm phase correct"

La conséquence est que la fréquence de récurence du mode phase corect est la moitié de celle en mode fast.

Je me m'engage pas à mettre ma main à couper, mais ce choix n'a rien à voir a voir avec le fait que les timers 0 et 2 soient 8 bits et le 1 soit 16 bits.
Il doit être modifiable, attention tous les timers n'ont pas les même propriétés.

Plus de renseignements dans la "mine aux trésors" de Nick Gammon

Bon, en consultant toute ces documentations, je ne suis pas arrivé à faire du Fast PWM de 16 bit. Du PWM, oui, mais pas du Fast PWM.

Pour autant 10 bits me suffisent. Dans ce cas, je suis arrivé à faire du Fast PWM à 7825 Hz (dixit mon oscillo) avec un Pro Mini cadencé à 8 MHz sur les sorties D9 et D10 avec le Timer 1 (embêtant car ça impacte la bibliothèque Servo) et sur les sorties D3 et D11 avec le Timer 2.

Voici les deux petits sketchs :

Avec le Timer 1 :

  // =================================
  // Timer 1 (sorties PWM : D9 et D10)
  //==================================

void setup()  
{ 

  TCCR1B &= 0b11111000;               // <===== à ne pas toucher
  TCCR1B |= 0b00000001;
  
  // Déclaration des pins D9 et D10 en sorties
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

// Sélection du mode "Fast PWM 10 bits"
  bitClear(TCCR1B, WGM13);      // Mise à 0 de WGM13
  bitSet(TCCR1B, WGM12);        // Mise à 1 de WGM12
  bitSet(TCCR1A, WGM11);        // Mise à 1 de WGM11
  bitSet(TCCR1A, WGM10);        // Mise à 1 de WGM10  
  bitSet(TCCR1A, COM1A1);
  bitClear(TCCR1A, COM1A0);      // Mise en marche du PWM sur la sortie D9, en mode "normal"
  bitSet(TCCR1A, COM1B1);
  bitClear(TCCR1A, COM1B0);      // Mise en marche du PWM sur la sortie D10, en mode "normal"  
}

void loop()  
{ 
  for (int i = 0; i < 1023; i++) {
    writePWM(9, i);
    writePWM(10, i);
    delay(10);
  }
}

void writePWM(uint8_t pin, uint16_t N) {
  switch(pin) {
    case 9:
      OCR1A = N;
      break;
    case 10:
      OCR1B = N;
      break;
  }
}

et avec le Timer 2 :

 // =================================
  // Timer 2 (sorties PWM : D3 et D11)
  //==================================

void setup()  
{ 

  TCCR2B &= 0b11111000; // <===== à ne pas toucher
  TCCR2B |= 0b00000001; // Prédiviseur à 1
  
  // Déclaration des pins D3 et D11 en sorties
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);

// Sélection du mode "Fast PWM 10 bits"
  bitClear(TCCR2B, WGM22);        // Mise à 1 de WGM22
  bitSet(TCCR2A, WGM21);        // Mise à 1 de WGM21
  bitSet(TCCR2A, WGM20);        // Mise à 1 de WGM20  
  bitSet(TCCR2A, COM2B1);
  bitClear(TCCR2A, COM2B0);      // Mise en marche du PWM sur la sortie D3, en mode "normal" 
  bitSet(TCCR2A, COM2A1);
  bitClear(TCCR2A, COM2A0);      // Mise en marche du PWM sur la sortie D11, en mode "normal"
}

void loop()  
{ 
  for (int i = 0; i < 1023; i++) {
    writePWM(3, i);
    writePWM(11, i);
    delay(10);
  }
}

void writePWM(uint8_t pin, uint16_t N) {
  switch(pin) {
    case 3:
      OCR2B = N;
      break;
    case 11: 
      OCR2A = N;
      break;
  }
}

Cordialement.

Pierre.

Bonjour ,
16 000 000 /1024 = 15 625 Hz : essaye encore !
pm. , 16MHz/256² = 244.14Hz , c'est du fast pwm , même si ça te paraît lent

Mon pro Mini fonctionne à 8 MHz et non pas 16 MHz

8MHz/256² = 122.07 Hz. Pour ce que je veux en faire, c'est lent.

Cordialement.

Pierre.

si tu as 1€12 , tu as : 32MHz

La fréquence de récurence max de la PWM est égale à la fréquence d'horloge divisée par 2n, n étant le nombre de bit de la PWM.
La fréquence théorique max en 10 bits et Fh = 8 MHz est
8 MHz/1024 = 7,81 kHz.

Avec le traitement de @ChPr pour faire un timer 10 bits avec des timers 8 bits il y aura obligatoirement de la perte.

PS pour moi c'est Fh/2(n) et non pas Fh/2(2*n)

Je comprends mieux pourquoi Microchip abandonne le 328p et Arduino fait un Uno R4.
Il me semble que ce concurrent a plus de flash (la doc est un peu beaucoup bord***lique) et surtout le 32 MHz est accessible à 3,3V alors que le 328p à 3,3V ne garanti que 12 MHz.
J'ai parcouru trop vite pour pouvoir comparer les consommations.

ATTENTION : Grosse erreur de ma part.

J'étais étonné d'avoir un PWM10 bits avec le timer 2, mais bon, ça avait l'air de fonctionner.

J'avais d'abord écrit le programme pour le Timer 1 et pour lequel, ça fonctionne.

J'ai fait un copier/coller des registres de 1 à 2 à quelques petits trucs près et j'ai obtenu des signaux ... comme ceux avec le Timer 1. Je n'ai pas pris garde que j'ai certainement modifié la fréquence de balayage de mon oscillo pour observer les signaux.

Tout me semblait fonctionner car je faisais un balayage de 0 à 1023 (2^10) et j'obtenais bien, de manière continu une modulation allant du min au max.

Incidemment, sur un sketch utilisant le Timer 2 en mode 10 bits, je regarde la fréquence sur mon oscillo et , Oh miracle, j'étais à 31.2 kHz. Oh la, Oh la, il y a un truc !

Le truc est que le Timer 2 avec le choix des registres que j'ai fait correspond à un PWM 8 bits.

Là où je me suis fait avoir, est que je régulais (c'est un asservissement) autour d'un valeur de 320, ce qui dépasse 256 ! En fait, le registre OCR2B prenait ma valeur, mais modulo 256.

J'ai modifié mon sketch pour réguler autour de 94, soit 320-256 et ... j'ai strictement le même fonctionnement.

Donc, mea culpa, mea maxima culpa, je me suis planté dans les grandes largeurs. Il n'y a que le Timer 1 qui permet un PWM sur 10 bits.

Cordialement.

Pierre.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.