D'autres exemples de codes Arduino, avec schémas associés

Premiers exemples, dans une page précédente.

° ex0400_timer_2_interrupt.ino
Un petit exemple d'utilisation du timer 2 du ATmega328 de l'Arduino, pour qu'il génère des interruptions, qui accèdent régulièrement à une fonction.
° ex0401_timer_2_interrupt.ino
Exemples d'utilisations du timer du ATmega328 de l'Arduino, pour modifier les fréquences et temps à l'état haut des 6 pins PWM (Pulse Width Modulated) liés à la commande "analogWrite(...)".
° ex0405_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.
La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.
° ex0406_timer_2_interrupt.ino
Exemples d'utilisations du timer 2 du ATmega328 de l'Arduino, pour générer une rampe en fréquence sur la pin 13.
° ex0440_Menu_LiquidCrystal_vitesses_tests.ino
Mesures de temps d'exécution de diverses instructions.
La mesure n'est pas précise, car elle utilise la fonction "micros()".
Les exemples suivantes, "ex0441" et "ex448", feront des mesures beaucoup plus précises, car elles utilisent le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Utilise l'afficheur LCD 1602 avec 5 boutons, pour afficher les résultats et choisir la mesure désirée.
Permet également de voir à quelle valeur de l'ADC0 est attaché chaque bouton.
° ex0441_DigitalWrite_speed.ino
Mesures de temps d'exécution de la fonction "DigitalWrite".
La mesure est précise, car elle utilise le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Affichage de résultats des mesures dans le moniteur série.
° ex0448_Menu_LCD_vitesses_tests.ino
Mesures de temps d'exécution de diverses instructions.
Les mesures sont précises, car elles utilisent le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Utilisation de l'afficheur LCD 1602 avec 5 boutons. Utilisation d'un menu pour sélectionner le test à faire.
Indique également la valeur de l'ADC0 associé à chaque bouton.
° Commentaires et informations sur le Timer 1, utile pour la suite.
Ce sont des commentaires généraux, valident pour les exemples ex0450 à ex0464 qui suivent.
° ex0450_timer_1_interrupt.ino
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilise un Arduino Mega.
L'exemple suivant est meilleur. La méthode utilisée dans cet exemple n'est pas conseillée.
° ex0451_timer_1_interrupt_OCR1A.ino
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilisation du registre OCR1A pour bien contrôler les intervalles de temps entre deux interruptions.
La fréquence d'oscillation de la pin 13 va augmenter avec le temps, puis diminuer et recommencer.
La mémoire des variables est presque entièrement utilisée.
C.f. l'ex0452 suivant, qui utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.
° ex0452_timer_1_interrupt_OCR1A_PROGMEM.ino
Similaire à l'ex0451, qui a le défaut d'utiliser presque toute la mémoire RAM pour stocker un tableau.
Cet exemple utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.
Cela libère la RAM de stockage des variables.
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilisation du registre OCR1A pour bien contrôler les intervalles de temps entre deux interruptions.
La fréquence d'oscillation de la pin 13 va augmenter avec le temps, puis diminuer et recommencer.
° ex0453_timer_1_interrupt_OCR1A_PROGMEM_LED.ino
Similaire à l'ex0452.
Utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.
Utilisation de l'afficheur LCD1602 pour indiquer la fréquence générée.
° ex0460_timer_1_interrupt_OCR1A_LCD.ino
Utilisation du timer 1 pour générer des fréquences sur les pins 2 et 3 de l'arduino.
Utilisation du timer 0 pour des mesures de temps.
Utilisation de l'afficheur LCD 1602 avec les 5 boutons, pour avoir un menu qui sélectionne la fréquence désirée.
Un menu permet d'augmenter la fréquence par 24e d'octave.
Utilise des instructions de bas niveau, pour accéder plus rapidement aux ports de l'Arduino et pour lire la valeur de l'ADC0, sans attendre les 100 micro-secondes de temps de conversion.
° ex0462_timer_1_interrupt_LCD_sinus.ino
Utilisation des mêmes techniques que dans l'ex0460, avec comme objectif de générer un signal sinusoïdal, juste en chargeant - déchargeant un condensateur ou en maintenant constant sa tension en mettant sous haute impédence la pin 3 qui lui est reliée.
Les commentaires du code explique comment cela est obtenu.
° ex0463_timer_1_interrupt_digital_oscillo_LCD.ino
Utilisation du timer 1 pour générer des fréquences sur les pins 11 et 12 de l'arduino.
Utilisation du timer 0 pour des mesures de temps.
Utilisation de l'afficheur LCD 1602 avec les 5 boutons, pour avoir un menu qui sélectionne les fréquences désirées.
Utilise des instructions de bas niveau, pour accéder plus rapidement aux ports de l'Arduino et pour lire la valeur de l'ADC0, sans attendre les 100 micro-secondes de temps de conversion.
Voir les commentaire de l'ex0464, qui est une version plus complète et mieux aboutie de génération d'une paire de fréquence ajustable.
° ex0464_timer_1_interrupt_digital_oscillo_LCD.ino
Extension de l'ex0463, avec des améliorations, version plus aboutie.
Si on utilise le schéma de l'ex0460 avec le haut-parleur et les bons branchements, deux cas limites sont intéressants à étudier.
1) Vers les basses fréquences, entre 10 et 80 [Hz], expérimenter à partir de quelle fréquence on endent un son continu et non des TACs distincts.
2) En augmentant la fréquence de 1'000 à 20'000 [Hz], à partir de quelle fréquence on entent plus rien.
Cela dépend beaucoup de l'âge de la personne qui écoute.
Les jeunes entendent jusqu'à entre 17'000 et 20'000 [Hz], des personnes âgées n'entendent plus rien au delà de 10'000 [Hz].
Par défaut, les fréquences ne sont pas affichées, car l'utilisation est destinées à des élèves qui apprennent à manier un oscilloscope.
En introduisant le bon code dans le bon menu, les fréquences apparaîssent.

° Supplément, sur la génération de sons de fréquences variables.
Accès à 4 fichiers au format ".wav" de sons de fréquences variables.
Accès au programme écrit dans l'environnement Scilab pour générer les fichiers de sons.

Avec la colorisation syntaxique   ...   Sans la colorisation syntaxique

    - Suivant, ex0401
ex0400_timer_2_interrupt.ino   TOP
Un petit exemple d'utilisation du timer 2 du ATmega328 de l'Arduino, pour qu'il génère des interruptions, qui accèdent régulièrement à une fonction.

/*
ex0400_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.

plusieurs registres définissent la manière de générer des imulsions sur les pin 3, 5, 6, 9, 10 et 11.
Il y a trois groupes de 2 : 
Timer pin1 pin2 registres
  0    6    5    TCCR0B  TCCR0A OCR0A OCR0B TCNT0
  1    9   10    TCCR1B  TCCR1A OCR1A OCR1B TCNT1
  2   11    3    TCCR2B  TCCR2A OCR2A OCR2B TCNT2

Ici, on se limitera au Timer 2.

"TCCR" signifie Timer Counter Control Register

Structure des registres 8-bits de contrôle :
TCCR2A - [COM2A1, COM2A0, COM2B1,   COM2B0,   reserved, reserved, WGM21, WGM20]
TCCR2B - [FOC2A,  FOC2B,  reserved, reserved, WGM22,    CS22,     CS21,  CS20]
bit           7       6        5         4        3        2         1      0

COM2Ax bits == Compare Output Mode bits
OCR2A == Output Compare Register A
WGM2x bits == Waveform Generation Mode bits

La valeur du registre se trouve dans TCNT2, en mode R/W (écriture et lecture)
Le compteur compte de TCNT2 à 255 ou de TCNT2 à 0, puis génère une interruption.

(COMP2A1, COMP2A0)  C.f. p. 153
(0, 0) => OC2A est déconnecté, 
(0, 1) => Toggle OC2A on compare Match
(1, 0) => Clear OC2A  on Compare Match (Set output to low level)
(1, 1) => Clear OC2A  on Compare Match (Set output to high level)

(COMP2B1, COMP2B0)  C.f. p. 153
(0, 0) => OC2B est déconnecté, compte de 0 à 255 ou de 255 à 0
(0, 1) => Toggle OC1B on compare Match
(1, 0) => Clear OC2B  on Compare Match (Set output to low level)
(1, 1) => Clear OC2B  on Compare Match (Set output to high level)

(WGM22, WGM21, WGM20)   C.f. page 146  et page 155
(0, 0, 0) => le compteur  TCNT2  est incrémenté chaque  T * xxx [us]. Pour  xxx  c.f. ci-dessous
             Lorsque le compteur passe de 255 à 0, une interruption est générée.
             Lors de cette interruption, une nouvelle valeur de départ peut être assignée à  TCNT2.
             À n'importe quelle moment, une nouvelle valeur peut être assignée à TCNT2.
(0, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(0, 1, 0) => Mode CTC "Clear Timer on Compare".  C.f. page 146
             Le registre  OCR2A  est utilisé ici.
             Lorsque le compteur  TCNT2 == OCR2A,  le compteur  TCNT2  est mis à 0 et
             une interruption est générée si les interruptions sont autorisée (enabled)             
(0, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à 255.
(1, 0, 0) => reserved 
(1, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(1, 1, 0) => reserved
(1, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à OCR2A

(FOC2A, FOC2B) C.f. page 156   (Force Output Compare A) et (Force Output Compare B)
Ils sont mis à 0 ou 1  selon le mode défini par (WGM22, WGM21, WGM20) et
selon la comparaison de  TCNT2  avec  OCR2A  et  OCR2B. 


Gère le temps entre deux incréments du compteur.
(CS22, CS21, CS20)  C.f. page 156
(0, 0, 0) => No clock source, Timer/Counter stopped
(0, 0, 1) => T * 1    =  0,0625 [us]
(0, 1, 0) => T * 8    =  0,500  [us], division par 8 de la fréquence
(0, 1, 1) => T * 32   =  2,000  [us], division par 32 de la fréquence
(1, 0, 0) => T * 64   =  4,000  [us]
(1, 0, 1) => T * 128  =  8,000  [us]
(1, 1, 0) => T * 256  = 16,000  [us]
(1, 1, 1) => T * 1024 = 64,000  [us] = 0,064 [ms]



Ce qui suit est utile pour le PWM "Pulse Width Modulated"
OCR2A = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2A est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2A1, COMP2A0) = (0, 0)

OCR2B = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2B est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2B1, COMP2B0) = (0, 0)

WGM21 = 1 => mode "Fast PWM"  ;  WGM21 = 0 => mode "Phase-Correct PWM"
C'est utile pour le PWM, par pour les interruptions.

==========================================================*/

byte volatile bEtat = 0;
byte volatile bCompteur = 0;  //Compteur pour interruption

void setup() {
//============
// Initialise le Timer 2 pour déclencher les interruptions à intervalle régulier

  cli(); // Désactive l'interruption globale
  //bitClear (TCCR2A, WGM20); // WGM20 = 0   TCCR2A = 0; //default 
  //bitClear (TCCR2A, WGM21); // WGM21 = 0 
  TCCR2A = 0b00000000; // TCCR2A = 0; //default 
  TCCR2B = 0b00000110; // Clock * 256 soit 16 micro-s et WGM22 = 0.  Clock = T = 0.0625 [us]
  //TCCR2B = 0b00000011; // Clock * 32 soit 2 micro-s et WGM22 = 0.  Clock = T = 0.0625 [us]
  TIMSK2 = 0b00000001; // Interruption locale autorisée par TOIE2
  sei(); // Active l'interruption globale

pinMode(13, OUTPUT); 
} // setup

void loop() {
//===========
// Boucle principale
// Elle ne fait rien, tout se passe dans le timer  
} // loop

// routine d'interruption du timer
ISR (TIMER2_OVF_vect) {
//=====================
// TCNT2 = valeur du compteur, qui est compté de sa valeur initiale à 255.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT2 = 0
// Le compteur  TCNT2  est décrémenté de 1 chaque 0.0625 [us] * facteur défini dans le setup.

// Recharge la valeur de départ du compteur pour que la prochaine interruption se déclenche dans ... [ms]
  TCNT2 =   6; // => 250 X 16 [us] = 4.000  [ms]
//TCNT2 = 131; // => 125 x 16 [us] = 2.000 [ms]
//TCNT2 = 194; // =>  62 x 16 [us] = 0.992 [ms]

// Chaque temps ci-dessus est multiplié par 250  
if (bCompteur++ == 250) {
  // Change l'état de la LED
  bCompteur=0;  
  bEtat = 1 - bEtat;
  digitalWrite(13, bEtat);
  }
} // ISR
ex0400_timer_2_interrupt.ino
Il n'y a pas d'images associée, car le but est juste de tester les timers, sans connexions hardware.
On peut voir la LED intrégrée à l'Arduino, celle connectée à la pin 13, qui clignote.

    - Suivant, ex0405   ;   Précédent, ex0400
ex0401_timer_2_interrupt.ino   TOP
Exemples d'utilisations du timer 2 du ATmega328 de l'Arduino, pour modifier les fréquences et temps à l'état haut des 6 pins PWM (Pulse Width Modulated) liés à la commande "analogWrite(...)".

/*
ex0401_timer_2_interrupt-ino
Exemple d'utilisation du timer 2 pour changer des fréquences d'interruptions.
Cela peut influencer les fonctions "delay()" et "millis()"

Plus d'information est donné dans les programmes : "ex045x_....ino"
En particulier, à partir de : "ex0451_timer_1_interrupt_OCR1A.ino",
le registre  OCR1A  est utilisé pour mieux contrôler les interruptions.
Je recommande cette manière de faire, plutôt que celle utilisée dans cet exemple.

Ce programme de tests ne fait rien d'autre que de changer le timer 2.
Il est destiné à être utilisé avec un oscilloscope, pour voir les effets.
La variable local  "int nMode" défini le test que l'on désire exécuter.
Les fréquences que l'on peut obtenir varient de 30 Hz à 8'000'000 Hz.

plusieurs registres définissent la manière de générer des imulsions sur les pin 3, 5, 6, 9, 10 et 11.
Il y a trois groupes de 2 : 
Timer pin1 pin2 registres
  0    6    5    TCCR0B  TCCR0A OCR1A OCR0B TCNT0
  1    9   10    TCCR1B  TCCR1A OCR1A OCR1B TCNT1
  2   11    3    TCCR2B  TCCR2A OCR2A OCR2B TCNT2

"PWM" signifie Pulse Width Modulated
"TCCR" signifie Timer Counter Control Register

The TCCR2A and TCCR2B 8-bit registers have the following structures:

TCCR2A - [COM2A1, COM2A0, COM2B1, COM2B0, reserved, reserved, WGM21, WGM20]
TCCR2B - [FOC2A, FOC2B, reserved, reserved, WGM22, CS22, CS21, CS20]

COM2Ax bits == Compare Output Mode bits
OCR2A == Output Compare Register A
WGM2x bits == Waveform Generation Mode bits

COM2A1 == 1 => OCR2A/255 ou OCR2A/256 définit la proportion de temps que passe la pin  3 à l'état haut.
            => OCR2B/255 ou OCR2B/256 définit la proportion de temps que passe la pin 11 à l'état haut.
COM2A1 == 0 => OCR2A définit jusqu'où le compteur compte avant de passer à 0 (Fast PWM) ou redescendre (Phase-Correct PWM)
               c.f. plus bas.

COM2A0 == 0 => comptage jusqu'à 256 (Fast PWM) ou 255 (Phase-Correct PWM).
COM2A0 == 1 => comptage jusqu'à  OCR2A+1 (Fast PWM) ou OCR2A (Phase-Correct PWM), avant de recompter

Un des COM2Ax  doit être à 1 pour avoir un signal.

WGM21 = 1 => mode "Fast PWM"  ;  WGM21 = 0 => mode "Phase-Correct PWM"

CS22, CS21, CS20  définissent le facteur de division de la fréquence de 16'000'000 Hz de base de l'Arduino.

En mode "Fast PWM" :
Compte depuis 0 jusqu'à 256 ou ( OCR2A+1 si COM2A0==1) avant de repasser à 0.
OCR2A/ 256 défini la proportion de temps à l'état haut de la pin 11
OCR2B/ 256 défini la proportion de temps à l'état haut de la pin  3


En mode "Phase-Correct PWM" :
Compte depuis 0 jusqu'à 255 ou ( OCR2A si COM2A0==1) avant de redescendre à 0.
OCR2A/ 255 défini la proportion de temps à l'état haut de la pin 11
OCR2B/ 255 défini la proportion de temps à l'état haut de la pin  3

À l'initialisation des timers, tous les clocks sont divisés par 64.
Timer 0 est mis en mode "Fast PWM", donc sa fréquence = 16'000'000 Hz / 64 / 256 = 976.5625 Hz.  Période = 1.024 ms
Timer 1 et 2 sont mis en mode "Phase-Correct PWM", donc sa fréquence = 16'000'000 Hz / 64 / 256 / 2 = 488.2815 Hz.  Période = 2.048 ms

Références :
https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
http://www.righto.com/2009/07/secrets-of-arduino-pwm.html
http://www.fiz-ix.com/2012/01/how-to-configure-arduino-timer-2-registers-to-drive-an-ultrasonic-transducer-with-a-square-wave/
http://forum.arduino.cc/index.php?topic=16612#msg121031

La documentation de Atmega328 se trouve ici.
http://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf

I est possible de lire la valeur du compteur des timers dans TCNT0  ;  TCNT1  et  TCNT2,  c.f. p95  et  116.
*/

/*
De : // http://forum.arduino.cc/index.php?topic=16612#msg121031

 If you change TCCR0B, it affects millis() and delay().  
 They will count time faster or slower than normal if you change the TCCR0B settings.  
 Below is the adjustment factor to maintain consistent behavior of these functions:

Default: delay(1000) or 1000 millis() ~ 1 second

0x01: delay(64000) or 64000 millis() ~ 1 second
0x02: delay(8000) or 8000 millis() ~ 1 second
0x03: is the default
0x04: delay(250) or 250 millis() ~ 1 second
0x05: delay(62) or 62 millis() ~ 1 second
(Or 63 if you need to round up.  The number is actually 62.5)

Also, the default settings for the other timers are:
TCCR1B: 0x03
TCCR2B: 0x04

There may be other side effects from changing TCCR0B.  For example my project would not properly run with TCCR0B set to 0x02 or 0x01. 
But it worked fine at 0x03 and higher.  YMMV
*/

void stopTransducer() {
//=====================
// Arrêt des times.
TCCR2A = 0;
TCCR2B = 0;
} // stopTransducer

void setPwmFrequency(int pin, int divisor) {
//==========================================
// Facteurs de division des compteurs, pour varier la fréquence des pins
byte mode;
if (pin == 5 || pin == 6 || pin == 9 || pin == 10) {
  switch(divisor) {
    case 1: mode = 0x01; break;
    case 8: mode = 0x02; break;
    case 64: mode = 0x03; break;
    case 256: mode = 0x04; break;
    case 1024: mode = 0x05; break;
    default: return;
    }
  if(pin == 5 || pin == 6) {
    TCCR0B = TCCR0B & 0b11111000 | mode;
    } 
  else {
    TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  }
else if(pin == 3 || pin == 11) {
  switch(divisor) {
    case 1: mode = 0x01; break;
    case 8: mode = 0x02; break;
    case 32: mode = 0x03; break;
    case 64: mode = 0x04; break;
    case 128: mode = 0x05; break;
    case 256: mode = 0x06; break;
    case 1024: mode = 0x07; break;
    default: return;
    }
  TCCR2B = TCCR2B & 0b11111000 | mode;
  }
} // setPwmFrequency

void setup() {
//============
// Timer 0
pinMode( 5, OUTPUT);
analogWrite( 5, 64);
pinMode( 6, OUTPUT);
analogWrite( 6, 48);
// Timer 1
pinMode( 9, OUTPUT);
analogWrite( 9, 64);
pinMode(11, OUTPUT);
analogWrite(10, 48);
// Timer 2
pinMode( 3, OUTPUT);
analogWrite( 3, 64);
pinMode(11, OUTPUT);
analogWrite(11, 48);

// Pins 3 et 11, car dans TCCR2A  il y a un "2", pour Timer 2, correspondant aux pins 3 et 11.
//============================================================================================
int nMode = 1; // Impair => "Fast PWM", Pair => "Phase-Correct PWM".

if (nMode == 1) {
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // _BV(WGM21) = 2  ;  _BV(WGM20) = 1
  TCCR2B = 7; // = setPwmFrequency(3,1024);
  OCR2A = 64;  // défini la proportion relativement à 256 de l'état haut de la pin 11
  OCR2B = 128; // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024);
  // Période = 33 ms ; Fréquence = 30 Hz ; Fréqu = 16 MHz / 1024 / 256 / 2 = 30.517 [Hz]
  // C'est la plus basse fréquence possible.
  }
else if (nMode == 2) {
  // Le mode est "Phase-Correct PWM". La fréquence est la moitié de "Fast PWM"
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20); //  | 2 = _BV(WGM21) C'est lui qui défini le mode "Fast PWM"
  TCCR2B = 7; // = setPwmFrequency(3,1024);
  OCR2A = 64;  // défini la proportion relativement à 256 de l'état haut de la pin 11
  OCR2B = 128; // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // Période = 16.5 ms ; Fréquence = 60 Hz ; Fréqu = 16 MHz / 1024 / 256 = 61.035 [Hz]
  }
else if (nMode == 3) {
  // Contrôle de la fréquence dans OCR2A
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | 7;
  OCR2A = 180; // Fréqu = 16 MHz / 1024 / 181 = 86.326 [Hz], période = 11.584 ms
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 4) {
  // Contrôle de la fréquence dans OCR2A
  // Le mode est "Phase-Correct PWM". La fréquence est la moitié de "Fast PWM"
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); //  | 2 = _BV(WGM21) C'est lui qui défini le mode "Fast PWM"
  TCCR2B = _BV(WGM22) | 7;
  OCR2A = 180; // Fréqu = 16 MHz / 1024 / 180 / 2 = 43.403 [Hz], période = 23.04 ms
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 5) {
  // Contrôle de la fréquence dans OCR2A
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // _BV(WGM21) = 2  ;  _BV(WGM20) = 1
  TCCR2B =  7;
  OCR2A = 180; // Fréqu = 16 MHz / 1024 / 181 = 86.326 [Hz], période = 11.584 ms
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 7) {
  // Les deux signaux sont à 0, _BV(COM2A1) ou _BV(COM2A0)  doit être à 1.
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = 0 | 0 | _BV(WGM21) | _BV(WGM20); // _BV(WGM21) = 2  ;  _BV(WGM20) = 1
  TCCR2B =  7;
  OCR2A = 180; // Fréqu = 16 MHz / 1024 / 181 = 86.326 [Hz], période = 11.584 ms
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 9) {
  // Comme le nMode == 1
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = 128 | 64 | 32 | 2 | 1; // _BV(WGM21) = 2  ;  _BV(WGM20) = 1
  TCCR2B =  7;
  OCR2A = 180; // Fréqu = 16 MHz / 1024 / 181 = 86.326 [Hz], période = 11.584 ms
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1024); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 11) {
  // Contrôle de la fréquence dans OCR2A
  // Mode haute fréquence
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | 1;
  OCR2A = 180; // Fréqu = 16 MHz / 1 / 181 = 88.398 [kHz], période = 11.3125 micro-secondes
  OCR2B = 50;  // défini la proportion relativement à 256 de l'état haut de la pin  3
  //setPwmFrequency(3,1); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  }
else if (nMode == 13) {
  // Contrôle de la fréquence dans OCR2A
  // Mode haute fréquence
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | 1;
  OCR2A = 1; // Fréqu = 16 MHz / 1 / 2 = 8 [MHz], période = 0.125 micro-secondes
  OCR2B = 0;  // (OCR2B+1) / (OCR2A+1) défini la proportion relativement de l'état haut de la pin  3
  //setPwmFrequency(3,1); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La fréquence de la pin 11 est la moitié de celle de la pin 3.
  // C'est la fréquence la plus rapide pour la pin 3.
  // C'est le double de la fréquence la plus rapide pour la pin 11
  }
else if (nMode == 15) {
  // Contrôle de la fréquence dans OCR2A
  // Mode haute fréquence
  // Le mode "Fast PWM". La fréquence est le double de "Phase-Correct PWM"
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | 1;
  OCR2A = 0; 
  OCR2B = 0;
  //setPwmFrequency(3,1); 
  // La pin 11 est 50% du temps haut et 50% du temps bas.
  // La pin 3 reste à l'état haut
  // C'est la fréquence la plus rapide pour la pin 11, soit 8 MHz.
  }


// Il reste plusieurs points obscures, sur les combinaisons possibles de bits.
} // setup

void loop() {
//===========
// Boucle principale
// Elle ne fait rien, on peut mesurer les fréquences générés 
// sur les différentes pins avec un oscilloscope.
} // loop
ex0401_timer_2_interrupt.ino
Il n'y a pas d'images associée, car le but est juste de tester les timers, sans connexions hardware.


    - Suivant, ex0406   ;   Précédent, ex0401
ex0405_timer_2_interrupt.ino   TOP
Exemple d'utilisation du timer 2 pour générer des interruptions.
La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.

/*
ex0405_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.
La fréquence varie de 1 [Hz] à 25'000 [Hz], puis redescent à 1 [Hz], pour recommencer à croitre.

Un haut-parleur ou un oscilloscope est utile pour percevoir l'effet.
c.f. "ex0451.." et suite pour des améliorations imporantes.
En particulier, le timer 1 sur 16 bits sera utilisé, ainsi que le registre OCR1A,
permettant un meilleur contrôle des temps d'interruptions.

Suite de ex0404.

plusieurs registres définissent la manière de générer des imulsions sur les pin 3, 5, 6, 9, 10 et 11.
Il y a trois groupes de 2 : 
Timer pin1 pin2 registres
  0    6    5    TCCR0B  TCCR0A OCR1A OCR0B TCNT0
  1    9   10    TCCR1B  TCCR1A OCR1A OCR1B TCNT1
  2   11    3    TCCR2B  TCCR2A OCR2A OCR2B TCNT2

Ici, on se limitera au Timer 2.

"TCCR" signifie Timer Counter Control Register

Structure des registres 8-bits de contrôle :
TCCR2A - [COM2A1, COM2A0, COM2B1,   COM2B0,   reserved, reserved, WGM21, WGM20]
TCCR2B - [FOC2A,  FOC2B,  reserved, reserved, WGM22,    CS22,     CS21,  CS20]
bit           7       6        5         4        3        2         1      0

COM2Ax bits == Compare Output Mode bits
OCR2A == Output Compare Register A
WGM2x bits == Waveform Generation Mode bits

La valeur du registre se trouve dans TCNT2, en mode R/W (écriture et lecture)
Le compteur compte de TCNT2 à 255 ou de TCNT2 à 0, puis génère une interruption.

(COMP2A1, COMP2A0)  C.f. p. 153
(0, 0) => OC2A est déconnecté, 
(0, 1) => Toggle OC2A on compare Match
(1, 0) => Clear OC2A  on Compare Match (Set output to low level)
(1, 1) => Clear OC2A  on Compare Match (Set output to high level)

(COMP2B1, COMP2B0)  C.f. p. 153
(0, 0) => OC2B est déconnecté, compte de 0 à 255 ou de 255 à 0
(0, 1) => Toggle OC2B on compare Match
(1, 0) => Clear OC2B  on Compare Match (Set output to low level)
(1, 1) => Clear OC2B  on Compare Match (Set output to high level)

(WGM22, WGM21, WGM20)   C.f. page 146  et page 155
(0, 0, 0) => le compteur  TCNT2  est incrémenté chaque  T * xxx [us]. Pour  xxx  c.f. ci-dessous
             Lorsque le compteur passe de 255 à 0, une interruption est générée.
             Lors de cette interruption, une nouvelle valeur de départ peut être assignée à  TCNT2.
             À n'importe quelle moment, une nouvelle valeur peut être assignée à TCNT2.
(0, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(0, 1, 0) => Mode CTC "Clear Timer on Compare".  C.f. page 146
             Le registre  OCR2A  est utilisé ici.
             Lorsque le compteur  TCNT2 == OCR2A,  le compteur  TCNT2  est mis à 0 et
             une interruption est générée si les interruptions sont autorisée (enabled)             
(0, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à 255.
(1, 0, 0) => reserved 
(1, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(1, 1, 0) => reserved
(1, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à OCR2A

(FOC2A, FOC2B) C.f. page 156   (Force Output Compare A) et (Force Output Compare B)
Ils sont mis à 0 ou 1  selon le mode défini par (WGM22, WGM21, WGM20) et
selon la comparaison de  TCNT2  avec  OCR2A  et  OCR2B. 


Gère le temps entre deux incréments du compteur.
(CS22, CS21, CS20)  C.f. page 156
(0, 0, 0) => No clock source, Timer/Counter stopped
(0, 0, 1) => T * 1    =  0,0625 [us]
(0, 1, 0) => T * 8    =  0,500  [us], division par 8 de la fréquence
(0, 1, 1) => T * 32   =  2,000  [us], division par 32 de la fréquence
(1, 0, 0) => T * 64   =  4,000  [us]
(1, 0, 1) => T * 128  =  8,000  [us]
(1, 1, 0) => T * 256  = 16,000  [us]
(1, 1, 1) => T * 1024 = 64,000  [us] = 0,064 [ms]



Ce qui suit est utile pour le PWM "Pulse Width Modulated"
OCR2A = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2A est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2A1, COMP2A0) = (0, 0)

OCR2B = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2B est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2B1, COMP2B0) = (0, 0)

WGM21 = 1 => mode "Fast PWM"  ;  WGM21 = 0 => mode "Phase-Correct PWM"
C'est utile pour le PWM, par pour les interruptions.

==========================================================*/

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat =      0;
byte volatile bT0H_max =  61;  // Temps entre deux transitions, partie haute
byte volatile bT0L_max =   9;  // Temps entre deux transitions, partie basse
byte volatile bT0H  =      0;  // Compteur de temps entre deux transitions
byte volatile bT0L  =      0;  // == bT0L_max  ou  bT0L_max - 128
//byte volatile bFac  = 1024;  // Facteur de multiplication de 0.0625 [us]   [us] = micro-seconds

float vFreq = 1.0; // [Hz]
float vTemps = 0.0; // Temps écoulé depuis le début, en secondes.
float vTemps_next_print = 0.0; // Temps auquels on affiche le temps
float vTemps_delta = 0.5; // Pour accroissement du temps et décroissement du temps.
float vTmp = 1.0; // Pour des calculs temporaires
byte volatile bSet  = 0; // Est mis à 1 pour mettre à jours les nouvelles valeurs de bT0H_max, bT0L_max et TCCR2B
byte volatile bChanged = 0; // Compte le nombre de changements d'état depuis le dernier incrément du compteur.
byte volatile bT0H_max_new = 255; // Calculé à partir de vFrequ, puis mis à jours dans la gestion du Timer
byte volatile bT0L_max_new = 255;
byte volatile bTCCR2B_new  =   7;
byte volatile TCCR2B_old   =   7;
byte volatile bFac = 128; 
byte volatile bChanged_last = 0; // Juste pour informations dans le Serial.print

// (256           ) * bFac * 0.0625 [us] = temps entre deux générations d'interruption du timer, sauf le dernier
// (bT0L_max) * bFac * 0.0625 [us] = temps entre deux générations d'interruption du timer avant une transition
// Si bT0L_max < 128, alors
// L'avant dernière interruption avant une transition se fait après
// (256 - 128) * bFac * 0.0625 [us]
// La dernière interruption avant une transition se fait après
// (bT0L_max + 128) ) * bFac * 0.0625 [us]
// Ainsi, il n'y a pas un trop petit temps entre deux générations d'interruption.

// bFac = 1024 => detla_T_max = 256 * 1024 + 0.0625 [us] = 16.384 [ms] 
// bFac = 1024 => detla_T_min =       1024 + 0.0625 [us] =  0.064 [ms] 

// (bT0H * 256 + bT0L) * bFac * 0.0625 [us] = temps entre deux transitions

// (bT0H, bT0L, bFac) = 
// (  0,   1, 1024) => temps = (  0 * 256 +   1) * 1024 * 0.0625 =  64.000000 [us]
// (255, 255, 1024) => temps = (255 * 256 + 255) * 1024 * 0.0625 =   4.194240 [s]
// (122,  18, 1024) => temps = (122 * 256 +  18) * 1024 * 0.0625 =   2.000000 [s] <=>  0.25 Hz
// ( 61,   9, 1024) => temps = ( 61 * 256 +   9) * 1024 * 0.0625 =   1.000000 [s] <=>  0.5 Hz
// ( 30, 133, 1024) => temps = ( 30 * 256 + 133) * 1024 * 0.0625 =   0.500032 [s] <=>  ~1 Hz

// (  0,   1,   32) => temps = (  0 * 256 +   1) *   32 * 0.0625 =   2.00000 [us]
// (255, 255,   32) => temps = (255 * 256 + 255) *   32 * 0.0625 =   0.31250 [s]
// (195,  80,   32) => temps = (195 * 256 +  80) *   32 * 0.0625 =   0.10000 [s]  <=>  5  Hz
// ( 97, 168,   32) => temps = ( 97 * 256 + 168) *   32 * 0.0625 =   0.05000 [s]  <=> 10  Hz
// (  1, 244,   32) => temps = (  1 * 256 + 244) *   32 * 0.0625 =   1.00000 [ms] <=>  2 kHz
// (  0, 250,   32) => temps = (  0 * 256 + 250) *   32 * 0.0625 =   0.50000 [ms] <=>  1 kHz

// (  0,   1,    8) => temps = (  0 * 256 +   1) *    8 * 0.0625 =   0.50000 [us]
// (  0, 100,    8) => temps = (  0 * 256 + 100) *    8 * 0.0625 =   0.05000 [ms] <=> 10 kHz
// (  0,  50,    8) => temps = (  0 * 256 +  50) *    8 * 0.0625 =   0.02500 [ms] <=> 20 kHz

// Formules de conversions
//========================
// Selon la valeur de TCCR2B, bFac varie selon le tableau suivant :
// TCCR2B  2  3  4   5   6    7
// bFac    1  4  8  16  32  128
// dT [s]    = (bT0H * 256 + bT0L) * 2 * 8 * bFac * 62.5 * 10^(-9) 
// Fréquence = 16'000'000 / ((bT0H * 256 + bT0L) * 2 * 8 * bFac)
// Fréquence =  1'000'000 / ((bT0H * 256 + bT0L) * bFac)
//
// Pour Fréquence = Freq  donnée :
// Freq [Hz] < 0.5  < 1  < 2  < 4  < 16  >=16
// bFac       128    32   16   8    4     1
// bT0H = Tronc(1000000 / (256 * Freq * bFac)
// bT0L = Tronc(1000000 / (256 * Freq * bFac - bT0H * 256)
// bT0H et bT0L <=  255  pour  Freq < 0.12 [Hz].

void setup() {
//============
// Initialise le Timer 2 pour déclencher les interruptions après un certain temps

cli(); // Désactive l'interruption globale
//bitClear (TCCR2A, WGM20); // WGM20 = 0   TCCR2A = 0; //default 
//bitClear (TCCR2A, WGM21); // WGM21 = 0 
TCCR2A = 0b00000000; // TCCR2A = 0; //default   
//TCCR2B = 0b00000111; // Clock * 1024 soit 64  micro-s et WGM22 = 0
//TCCR2B = 0b00000110; // Clock *  256 soit 16  micro-s et WGM22 = 0
//TCCR2B = 0b00000011; // Clock *   32 soit  2  micro-s et WGM22 = 0
//TCCR2B = 0b00000010; // Clock *    8 soit 0.5 micro-s et WGM22 = 0

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive le Timer/Counter0   C.f. p. 109

//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
TIMSK1 = 0b00000000; // Désactive le Timer/Counter1  C.f. p. 135

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
//TIMSK2 = 0b00000000; // Désactive le Timer/Counter2  C.f. p. 157
TIMSK2 = 0b00000001; // Active le Timer/Counter 2. 
                     // Une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

TCCR2B = 0b00000111; // Clock * 1024 soit 64  micro-s et WGM22 = 0

TCCR2B_old = TCCR2B;
vFreq  =    1.0; // [Hz]
vTemps =    0.0; // [s]
vTemps_next_print = vTemps;
bSet  = 0; // Est mis à 1 pour mettre à jours les nouvelles valeurs de bT0H_max, bT0L_max et TCCR2B

// 1 Hz
bT0H_max = 122;
bT0L_max =  18;
bT0H = bT0H_max;
bT0L = bT0L_max;

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);

//Serial.begin(9600);  // Initialise la communication série à une haute vitesse.
//Serial.begin(115200);  // Initialise la communication série à une haute vitesse.
} // setup

void loop() {
//===========
// Boucle principale
// Augmentation progressive de la fréquence

// L'augmentation progressive de la fréquence se fait par à-coups.
// Cela s'entend aux hautes fréquences !!!  Est-ce vrai ???

if ( (!bSet) && (bChanged) ) {
    // Il y a eu un changement d'état
    vTemps   += vTemps_delta * float(bChanged) / vFreq; // Mesure du temps en secondes.
    bChanged_last = bChanged; // Juste pour information dans le Serial.print
    bChanged = 0; // Attendra le prochaine changement d'état pour changer la valeur du compteur
    
    if ( (vFreq > 25000.0) && (vTemps_delta > 0) ) { 
      // Fréquences en descendant.
      vTemps_delta = -vTemps_delta; 
      }
    else if ( (vFreq < 1.0) && (vTemps_delta < 0) ) { 
      // Fréquences montantes.
      vTemps_delta = -vTemps_delta; 
      }
 
    vFreq = pow(10.0, vTemps / 10.0); // [Hz] Elle est multipliée par 10 toutes les 10 secondes.
     
    // Fixe le diviseur de fréquence d'horloge, qui change la vitesse d'incrémentation du compteur TCNT2
    if (vFreq >= 16) {
      bTCCR2B_new = 2;
      bFac = 1;
      }
    else if (vFreq >= 4) {
      bTCCR2B_new = 3;
      bFac = 4;
      }
    else if (vFreq >= 2.0) {
      bTCCR2B_new = 4;
      bFac = 8;
      }
    else if (vFreq >= 1.0) {
      bTCCR2B_new = 5;
      bFac = 16;
      }
    else if (vFreq >= 0.5) {
      bTCCR2B_new = 6;
      bFac = 32;
      }
    else {
      bTCCR2B_new = 7;
      bFac = 128;
      }
    
      vTmp = 1.0e6 / (vFreq * bFac);
      bT0H_max_new = byte(vTmp / 256.0);
      bT0L_max_new = byte(vTmp - 256.0 * bT0H_max_new);
      
      bSet = 1; // Indique de mettre à jours les nouvelles valeurs de bT0H_max, bT0L_max et TCCR2B
/*
      if ( ( (vTemps_delta > 0) && (vTemps >= vTemps_next_print) ) || 
           ( (vTemps_delta < 0) && (vTemps <= vTemps_next_print) ) ) {
        vTemps_next_print += vTemps_delta * 2.0; // Affichage toutes les ... secondes.
        Serial.print(vTemps); Serial.print(" : ");
        Serial.print(vFreq); Serial.print(" ");
        Serial.print(bT0H_max_new); Serial.print(" "); Serial.print(bT0L_max_new); Serial.print(" "); 
        Serial.print(bChanged_last); 
        Serial.println(" ");
        } /*  */
    } // if (bSet == 0) && (bChanged)
} // loop

// routine d'interruption du timer
ISR (TIMER2_OVF_vect) {
//=====================
// TCNT2 = valeur du compteur, qui est compté de sa valeur initiale à 255, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT2 = 0
// Le compteur  TCNT2  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR2B.

bT0H--;

if (bT0H == 255) {  
  // Transition de l'état 
  if (bSet) {
    // Modifie les paramètres fixant la prochaine interruption
    bSet = 0;
    if (TCCR2B_old != bTCCR2B_new) { 
      // Lire la valeur de TCCR2B perturbe.
      TCCR2B     = bTCCR2B_new;
      TCCR2B_old = bTCCR2B_new;
      }
    bT0H_max = bT0H_max_new;
    bT0L_max = bT0L_max_new;
    }

  bChanged += 1;
  bT0H = bT0H_max; // Recharge la valeur de départ du compteur pour que la prochaine interruption se déclenche dans ... [ms]
  bT0L = bT0L_max;
  bEtat = 1 - bEtat;
  digitalWrite(13, bEtat);  // 4 [us], on verra dans d'autres exemples comment accéder aux pins plus rapidement.
  }
if (bT0H == 0) {
  // La transition d'état se fera à la prochaine interruption
  TCNT2 -= bT0L; // TCNT2 += 256 - bT0L;
  }
if (bT0H == 1) {
  // La transition d'état se fera dans deux interruptions
  if (bT0L_max < 128) {
    // Pour ne pas avoir deux interruptions trop approchées
    TCNT2 += 128; // TCNT2 += 256 - 128;
    bT0L = bT0L_max + 128;
    }
  }
  
} // ISR
ex0405 blink
ex0405_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.
La fréquence de clignotement de la LED pin 13 augmente avec le temps.


    - Suivant, ex0440   ;   Précédent, ex0405
ex0406_timer_2_interrupt.ino   TOP
Exemples d'utilisations du timer 2 du ATmega328 de l'Arduino, pour générer une rampe en fréquence sur la pin 13.

/*
ex0406_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.
La fréquence va varier de ~4 kHz à ~25 kHz
Elle double en 10 secondes.

Suite de ex0405.
Autre approche, à chaque passage dans la fonction de gestion d'interruption, on change l'état de la pin 13

plusieurs registres définissent la manière de générer des imulsions sur les pin 3, 5, 6, 9, 10 et 11.
Il y a trois groupes de 2 : 
Timer pin1 pin2 registres
  0    6    5    TCCR0B  TCCR0A OCR1A OCR0B TCNT0
  1    9   10    TCCR1B  TCCR1A OCR1A OCR1B TCNT1
  2   11    3    TCCR2B  TCCR2A OCR2A OCR2B TCNT2

Ici, on se limitera au Timer 2.

"TCCR" signifie Timer Counter Control Register

Structure des registres 8-bits de contrôle :
TCCR2A - [COM2A1, COM2A0, COM2B1,   COM2B0,   reserved, reserved, WGM21, WGM20]
TCCR2B - [FOC2A,  FOC2B,  reserved, reserved, WGM22,    CS22,     CS21,  CS20]
bit           7       6        5         4        3        2         1      0

COM2Ax bits == Compare Output Mode bits
OCR2A == Output Compare Register A
WGM2x bits == Waveform Generation Mode bits

La valeur du registre se trouve dans TCNT2, en mode R/W (écriture et lecture)
Le compteur compte de TCNT2 à 255 ou de TCNT2 à 0, puis génère une interruption.

(COMP2A1, COMP2A0)  C.f. p. 153
(0, 0) => OC2A est déconnecté, 
(0, 1) => Toggle OC2A on compare Match
(1, 0) => Clear OC2A  on Compare Match (Set output to low level)
(1, 1) => Clear OC2A  on Compare Match (Set output to high level)

(COMP2B1, COMP2B0)  C.f. p. 153
(0, 0) => OC2B est déconnecté, compte de 0 à 255 ou de 255 à 0
(0, 1) => Toggle OC2B on compare Match
(1, 0) => Clear OC2B  on Compare Match (Set output to low level)
(1, 1) => Clear OC2B  on Compare Match (Set output to high level)

(WGM22, WGM21, WGM20)   C.f. page 146  et page 155
(0, 0, 0) => le compteur  TCNT2  est incrémenté chaque  T * xxx [us]. Pour  xxx  c.f. ci-dessous
             Lorsque le compteur passe de 255 à 0, une interruption est générée.
             Lors de cette interruption, une nouvelle valeur de départ peut être assignée à  TCNT2.
             À n'importe quelle moment, une nouvelle valeur peut être assignée à TCNT2.
(0, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(0, 1, 0) => Mode CTC "Clear Timer on Compare".  C.f. page 146
             Le registre  OCR2A  est utilisé ici.
             Lorsque le compteur  TCNT2 == OCR2A,  le compteur  TCNT2  est mis à 0 et
             une interruption est générée si les interruptions sont autorisée (enabled)             
(0, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à 255.
(1, 0, 0) => reserved 
(1, 0, 1) => Pour PWM, phase mode.  C.f 148
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(1, 1, 0) => reserved
(1, 1, 1) => Pour PWM, fast mode, compte de  TCNT2  à OCR2A

(FOC2A, FOC2B) C.f. page 156   (Force Output Compare A) et (Force Output Compare B)
Ils sont mis à 0 ou 1  selon le mode défini par (WGM22, WGM21, WGM20) et
selon la comparaison de  TCNT2  avec  OCR2A  et  OCR2B. 


Gère le temps entre deux incréments du compteur.
(CS22, CS21, CS20)  C.f. page 156
(0, 0, 0) => No clock source, Timer/Counter stopped
(0, 0, 1) => T * 1    =  0,0625 [us]
(0, 1, 0) => T * 8    =  0,500  [us], division par 8 de la fréquence
(0, 1, 1) => T * 32   =  2,000  [us], division par 32 de la fréquence
(1, 0, 0) => T * 64   =  4,000  [us]
(1, 0, 1) => T * 128  =  8,000  [us]
(1, 1, 0) => T * 256  = 16,000  [us]
(1, 1, 1) => T * 1024 = 64,000  [us] = 0,064 [ms]



Ce qui suit est utile pour le PWM "Pulse Width Modulated"
OCR2A = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2A est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2A1, COMP2A0) = (0, 0)

OCR2B = registre 8 bits
Si le compteur atteint la valeur de ce registre, 
le Flag (bit) OCF2B est mis à 1 au prochain cyle d'horloge.
N'est pas utilisé si (COMP2B1, COMP2B0) = (0, 0)

WGM21 = 1 => mode "Fast PWM"  ;  WGM21 = 0 => mode "Phase-Correct PWM"
C'est utile pour le PWM, par pour les interruptions.

==========================================================*/

// Nombre de fois qu'il faut répéter la même fréquence
word volatile awRepete[] = {
 218,  219,  222,  223,  224,  227,  228,  231,  232,  234, 
 236,  238,  240,  241,  244,  246,  248,  250,  252,  254, 
 257,  258,  261,  263,  266,  267,  270,  272,  275,  277, 
 280,  282,  285,  287,  289,  293,  295,  298,  300,  303, 
 306,  309,  312,  315,  318,  321,  323,  327,  330,  334, 
 336,  340,  343,  347,  349,  354,  357,  360,  365,  368, 
 371,  376,  379,  384,  387,  391,  396,  399,  404,  409, 
 412,  417,  422,  426,  431,  436,  441,  445,  451,  455, 
 461,  466,  472,  477,  482,  488,  494,  500,  505,  512, 
 518,  524,  530,  537,  544,  551,  557,  564,  572,  578, 
 587,  593,  602,  609,  617,  626,  634,  642,  651,  660, 
 669,  678,  687,  698,  707,  717,  727,  738,  748,  759, 
 771,  782,  793,  806,  817,  830,  843,  856,  870,  883, 
 897,  911,  926,  941,  957,  972,  988, 1005, 1023, 1039, 
1057, 1076, 1095, 1114, 1134, 1154, 1175, 1197, 1219, 1242, 
1265, 1289, 1314, 1339, 1365, 1393, 1420, 1449, 1479, 1508, 
1541, 1572, 1606, 1641, 1676, 1713, 1751, 1790, 1830, 1873, 
1916, 1961, 2008, 2056, 2106, 2157, 2212, 2267, 2326, 2385, 
2449, 2513, 2581, 2651, 2725, 2802, 2881, 2965, 3051, 3142, 
3237, 3337, 3440, 3549, 3663, 3783, 3908, 4040, 4179, 4325, 
4479, 4641, 4812, 4993, 5184, 5386, 5601, 5828, 6070, 6327,
6601, 6892, 7204, 7536, 7894, 8277, 8687, 9131, 0};
// Le zéro à la fin indique que c'est la fin du tableau

word volatile wCompte = 0; // Compte le nombre de répétitions d'une fréquence
byte volatile bInd = 0; // Indice de compteur de répétitions d'une fréquence

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat =      0;
byte volatile bT0L  =    255;  // Fixe le temps entre deux transitions
byte volatile bUp   = 1; // Indique si la fréquence va en augmentant ou diminuant

// (256 - bT0L) * bFac * 0.0625 [us] = temps entre deux générations d'interruption du timer avant une transition

// bFac = 1024 => detla_T_max = 256 * 1024 + 0.0625 [us] = 16.384 [ms] 
// bFac = 1024 => detla_T_min =       1024 + 0.0625 [us] =  0.064 [ms] 

// bT0L * bFac * 0.0625 [us] = temps entre deux transitions

// (bT0L, bFac) = 
// (  0,    8) => temps = (256 -   0) *    8 * 0.0625 = 0.000128 [s] <=> ~ 3'906.25 [Hz]
// (127,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] <=> ~ 7'751.94 [Hz]
// (206,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] <=> ~20'000.00 [Hz]
// (127,    8) => temps = (256 - 216) *    8 * 0.0625 = 0.000020 [s] <=> ~25'000.00 [Hz]

// Formules de conversions
//========================
// Selon la valeur de TCCR2B, bFac varie selon le tableau suivant :
// TCCR2B  2  3  4   5   6    7
// bFac    1  4  8  16  32  128
// dT [s]    = bT0L * 2 * 8 * bFac * 62.5 * 10^(-9)  // 62.5e-9 = 1 / 16'000'000
// Fréquence = 16'000'000 / (bT0L * 2 * 8 * bFac)
// Fréquence =  1'000'000 / (bT0L * bFac)
//
// Pour Fréquence = Freq  donnée :
// bFac       128    32   16   8    4     1
// bT0L = Tronc(1000000 / (Freq * bFac)

void setup() {
//============
// Initialise le Timer 2 pour déclencher les interruptions après un certain temps

cli(); // Désactive l'interruption globale
//bitClear (TCCR2A, WGM20); // WGM20 = 0   TCCR2A = 0; //default 
//bitClear (TCCR2A, WGM21); // WGM21 = 0 
TCCR2A = 0b00000000; // TCCR2A = 0; //default   
//TCCR2B = 0b00000111; // Clock * 1024 soit 64  micro-s et WGM22 = 0
//TCCR2B = 0b00000110; // Clock *  256 soit 16  micro-s et WGM22 = 0
//TCCR2B = 0b00000011; // Clock *   32 soit  2  micro-s et WGM22 = 0
//TCCR2B = 0b00000010; // Clock *    8 soit 0.5 micro-s et WGM22 = 0

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive le Timer/Counter0   C.f. p. 109

//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
TIMSK1 = 0b00000000; // Désactive le Timer/Counter1  C.f. p. 135

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
//TIMSK2 = 0b00000000; // Désactive le Timer/Counter2  C.f. p. 157
TIMSK2 = 0b00000001; // Active le Timer/Counter 2. 
                     // Une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

TCCR2B = 0b00000010; // Clock *    8 soit 0.5 micro-s et WGM22 = 0
bT0L   = 0; // Freq = Tronc(1000000 / Freq
bInd   = 0;
wCompte = awRepete[bInd];

// La fréquence va varier de ~4 kHz à ~25 kHz
// Elle double en 10 secondes.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);

//Serial.begin(9600);  // Initialise la communication série à une haute vitesse.
//Serial.begin(115200);  // Initialise la communication série à une haute vitesse.
} // setup

void loop() {
//===========
// Boucle principale
// Augmentation progressive de la fréquence

/*
      if ( ( (vTemps_delta > 0) && (vTemps >= vTemps_next_print) ) || 
           ( (vTemps_delta < 0) && (vTemps <= vTemps_next_print) ) ) {
        vTemps_next_print += vTemps_delta * 2.0; // Affichage toutes les ... secondes.
        Serial.print(vTemps); Serial.print(" : ");
        Serial.print(vFreq); Serial.print(" ");
        Serial.print(bT0H_max_new); Serial.print(" "); Serial.print(bT0L_max_new); Serial.print(" "); 
        Serial.print(bChanged_last); 
        Serial.println(" ");
        } /*  */
} // loop

// routine d'interruption du timer
ISR (TIMER2_OVF_vect) {
//=====================
// TCNT2 = valeur du compteur, qui est compté de sa valeur initiale à 255, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT2 = 0
// Le compteur  TCNT2  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR2B.

// Transition de l'état
// Étrangement, TCNT2 n'est pas toujours nul lorsqu'on entre dans cette fonction.
TCNT2 += bT0L; // Fréquence = 1'000'000 / ( (256 - bT0L))

bEtat = 1 - bEtat;
digitalWrite(13, bEtat);  // 4 [us]

if (bUp) {
  // Fréquence montante
  wCompte -= 1;
  if (wCompte == 0) {
    // Augmentation de la fréquence.
    bT0L++; // Passage à la fréquence suivante.
    bInd++;
    wCompte = awRepete[bInd];
  
    if (wCompte == 0) {
      // Passe en fréquence descendante
      bUp = 0;
      bT0L--;
      bInd--;
      wCompte = awRepete[bInd];
      }
    }
  }
else {
  // Fréquence descendante
  wCompte -= 1;
  if (wCompte == 0) {
    // Diminution de la fréquence.
    bT0L--; // Passage à la fréquence précédente.
    bInd--;
    wCompte = awRepete[bInd];
  
    if (bT0L == 0) {
      // Passe en fréquence montante
      bUp = 1;
      bT0L = 0;
      bInd = 0;
      wCompte = awRepete[bInd];
      }
    }
  }
} // ISR
ex0405 blink
ex0406_timer_2_interrupt.ino
Exemple d'utilisation du timer 2 pour générer des interruptions.
La fréquence de clignotement de la LED pin 13 augmente avec le temps.
Autre approche que l'exemple ex0405, avec un effet similaire.


    - Suivant, ex0441   ;   Précédent, ex0406
ex0440_Menu_LiquidCrystal_vitesses_tests.ino   TOP
Mesures de temps d'exécution de diverses instructions.
La mesure n'est pas précise, car elle utilise la fonction "micros()".
Les exemples suivantes, "ex0441" et "ex448", feront des mesures beaucoup plus précises, car elles utilisent le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Utilise l'afficheur LCD 1602 avec 5 boutons, pour afficher les résultats et choisir la mesure désirée.
Permet également de voir à quelle valeur de l'ADC0 est attaché chaque bouton.

/*
ex0440_Menu_LiquidCrystal_vitesses_tests.ino

Pour faire des mesures de temps de diverses instructions, telles que :
° l'écriture sur l'afficheur, c'est long, environ 5 [ms] pour afficher une ligne.
° la lecture des boutons
° la conversion d'un float en string

Ces mesures utilisent un oscilloscope.

La pin 2 sera la référence pour le trigger
la pin 3 changera d'état après chaque boucle d'instruction

À partir d'un menu, permet aussi d'afficher le nom du bouton pressé et
la valeur de l'ADC 0 correspondant.

C.f. ex0448_Menu_LCD_vitesses_tests.ino
qui fait des mesures de temps beaucoup plus précis.

Connections :
LCD   Arduino (pin) ou tension
 1 - Vss = 0 V
 2 - Vcc = 5 V
 3 - V0 = Tension variable réglée par une résistance variable, pour la lumineusité de l'affichage
 4 - RS = pin 8 (Register Select) 
 5 - R/W = 0 V donc uniquement en Write
 6 - E = pin 9  Enable, mise à haut pour accepter les données.
 7 .. 10 = connecté à la commande de rétro-éclairage "back light"
11 - D4 = pin 4, données sur 4 bits 
12 - D5 = pin 5, données sur 4 bits 
13 - D6 = pin 6, données sur 4 bits 
14 - D7 = pin 7, données sur 4 bits 
15 - LED + = 220 Ohm - +5V
16 - LED - = 0V

Autres connexions :
pin  2 et 3 pour le signal
pin 13 pour indiquer si le HP est on ou off
pin  0, pin 1, utilisées pour la transmission par USB,
ADC  0 pour les lecture les boutons.

c.f. : http://www.arduino.cc/en/Tutorial/LiquidCrystal
c.f. : https://arduino-info.wikispaces.com/LCD-Pushbuttons

Quelques constantes :
=====================
Elles sont définies dans le fichier : Arduino.h
Sous Linux KUbuntu, ce fichier se trouve dans le dossier : /usr/share/arduino/hardware/arduino/core/arduino

#define HIGH 0x1
#define LOW  0x0

#define INPUT 0x0
#define OUTPUT 0x1
#define INPUT_PULLUP 0x2

#define true 0x1
#define false 0x0

#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105

#define SERIAL  0x0
#define DISPLAY 0x1

#define LSBFIRST 0
#define MSBFIRST 1
======================================================== */

// include the library code:
#include <LiquidCrystal.h>

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Dernier numéro de menu. Non utilisé
#define MENUMAX  8

#define pinTrigger 2 // pour le signal du trigger
#define pinSignal  3 // pour le signal de mesure de temps de l'instruction

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String strS = ""; 
// c.f. : https://www.arduino.cc/en/Reference/StringObject

char acStr[20]; // Pour conversion de nombre en un string.

int  nButtonPush = btnNONE; // Bouton pressé
byte bMenu1 = 5;  // indique le menu sélectionné
byte bMenu1_Last = 0;  // indique le dernier menu sélectionné
                         // que lorsque adc_key_in a suffisemment changé.
String strCode = ""; // pour indiquer l'état du code

byte bEtat1 = 0; // Etat du signal 1,  LOW=0 ou HIGH=1

unsigned long lwTemps1 = 0; // temps en micro secondes
unsigned long lwTemps2 = 0; // temps en micro secondes
unsigned long lwTempsActionLast = 0; // temps en micro secondes de la dernière commande 

byte bNbCycle = 0; // Nombre de boucle dans loop() avant la lecture de boutons

void setup() {
//============  
// set up the LCD's number of columns and rows: 
lcd.begin(16, 2);

// Affichage du type de programme en cours.
lcd.print("Oscillo, mes. T ");
lcd.setCursor(0, 1);
lcd.print("ex0440 20200626 ");

delay(1200); // Attente pour afficher le programme en cours

 // efface le texte
lcd.setCursor(0, 0);
lcd.print("                ");
lcd.setCursor(0, 1);
lcd.print("                ");
//lcd.autoscroll();

lwTemps1 = micros();
lwTemps2 = lwTemps1;
lwTempsActionLast   = lwTemps1;

pinMode(13, OUTPUT);
pinMode( pinSignal, OUTPUT); 
pinMode( pinTrigger, OUTPUT); 

digitalWrite( pinSignal, LOW); 
digitalWrite( pinTrigger, LOW);
bNbCycle = 0;

AfficheEtat();
} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Button pressed according to the value of adc_key_in
static byte bButtonLast = btnNONE; // Last button
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// La lecture des boutons sont centrée autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

if (nAdc_key_in > 790) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
  }
else if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.

  if (bButtonCount == 3) {
    bButtonCount = 0;
    return bButton;  // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 1;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons

void AfficheEtat() {
//==================
// Affichage du menu et d'information

if (bMenu1 == 1) {
  lwTemps1 = micros();
  lcd.setCursor(0, 0);
  lcd.print("1) affich. LCD  ");
  lwTemps2 = micros();

  // Temps d'affichage d'une ligne sur le LCD : 4850 us ~= 5 [ms] !!!
  sprintf(acStr, "T=%9d [us]", lwTemps2 - lwTemps1);
 
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 2) {
  // Temps entre deux passages ~= 10 [ms]
  // C'est le temps d'affichage sur l'écran LCD qui est long
  lwTemps1 = lwTemps2;
  lwTemps2 = micros();
  lcd.setCursor(0, 0);
  lcd.print("2. T une boucle ");

  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
 
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 3) {
  // Mesure le temps du "sprintf". T ≃ 160 [us]
  lcd.setCursor(0, 0);
  lcd.print("3. T sprintf    ");

  lwTemps1 = micros();  
  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
  lwTemps2 = micros();

  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }


if (bMenu1 == 4) {
  // Mesure le temps du "sprintf" avec conversion en float. T ≃ 270 [us]
  lcd.setCursor(0, 0);
  lcd.print("4. T sprintf + f");

  lwTemps1 = micros();  
  float f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT = %3d.%03d :", (int)f, (int)(f*1000)%1000);
  lwTemps2 = micros();

  f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT= %3d.%03d [ms]", (int)f, (int)(f*1000)%1000);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 5) {
  // Mesure le temps du "digitalWrite". T ≃ 4 [us]
  // Temps de lecture de micros() : T = 4 [us].
  lcd.setCursor(0, 0);
  lcd.print("5. T digitalWrit");

  lwTemps1 = micros();
  bEtat1 = 1 - bEtat1;
  //pinMode(pinSignal, OUTPUT);  // 4 [us]
  digitalWrite( pinSignal, bEtat1); // 4 [us]
  //lwTemps2 = micros();  // 4 [us]
  lwTemps2 = micros();  // 4 [us]

  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 6) {
  // Mesure le temps du "dtostrf.  T ≃ 160 [us]
  lcd.setCursor(0, 0);
  lcd.print("6. T dtostrf    ");

  lwTemps1 = micros();
  dtostrf( (lwTemps2 - lwTemps1) / 1000.0, 8, 3, acStr);
  // dtostrf fonctionne bien, alors qu'il a un bug dans tinkercad
  lwTemps2 = micros();

  float f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT= %3d.%03d [ms]", (int)f, (int)(f*1000)%1000);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 7) {
  // Vérifie qu'un delay(10) dure bien 10 [ms]
  lcd.setCursor(0, 0);
  lcd.print("7. T delay(10)  ");

  lwTemps1 = micros();
  delay(10);
  lwTemps2 = micros();

  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
 
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

if (bMenu1 == 8) {
  // Boucle sans affichage de menu
  // Temps d'une boucle est d'environ 16 [us]
  lwTemps2 = micros();

  if (bMenu1_Last != bMenu1) {
    lcd.setCursor(0, 0);
    lcd.print("8. T une boucle ");
    sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
    lcd.setCursor(0, 1);
    lcd.print(acStr);
    bMenu1_Last = bMenu1;
    }
  else {
    bMenu1_Last = bMenu1 - 1; // différent de bMenu1
    }
    
  lwTemps1 = micros();
  }

if (bMenu1 > 8) {
  // Pour afficher des informations sur le bouton pressé  
  // Pour afficher des informations sur le bouton pressé  
  // Indique le bouton pressé
  String astrS[6];  // Indices vont de astrS[0] à astrS[5],  l'indice  6  n'est pas valide !

  // Texte qui peut etre affiché
  astrS[btnNONE]   = "None  ";
  astrS[btnSELECT] = "Select";
  astrS[btnLEFT]   = "Left  ";
  astrS[btnDOWN]   = "Down  ";
  astrS[btnUP]     = "Up    ";
  astrS[btnRIGHT]  = "Right ";

  int nAdc_key_in = analogRead(0);

  sprintf(acStr, "Menu=%2d         ", bMenu1);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "  adc=%4d", nAdc_key_in);
  String strS = astrS[nButtonPush] + acStr;
  lcd.setCursor(0, 1);
  lcd.print(strS);
  }
} // AfficheEtat

void MenuTreat() {
//================
// Pour afficher des informations sur le bouton pressé  
AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1

if (micros() - lwTempsActionLast < 200000ul)  return; // Pas deux changements de menu en moins de 0,2 [s]

lwTempsActionLast = micros(); // Mémorise quand le dernier changement de menu a eu lieu.

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = MENUMAX;  // boucle
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
   // if (bMenu1 > MENUMAX) bMenu1 = 1;  // boucle
  }

AfficheEtat();

bMenu1_Last = bMenu1; // Mémorise le dernier menu
} // Menu1_Change

void loop() {
//===========
// Boucle principale, pour tester le temps d'exécution d'instructions.

// Ne lit l'état des boutons que tous les ... cycles
bNbCycle++;
if (bNbCycle == 4) {
   // Lecture de l'état des boutons
   bNbCycle = 0;
   digitalWrite( pinTrigger, 1); // 4.6 à 5.0 us de décalage entre les deux "digitalWrite"

   nButtonPush = read_LCD_buttons();

   if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Changement de menu
   if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
   digitalWrite( pinTrigger, 0);
   }

if (bMenu1 <= 9) AfficheEtat(); // Mesure de certain temps d'affichage

bEtat1 = 1 - bEtat1;
digitalWrite( pinSignal, bEtat1); 
} // loop

//FIN
ex0440 blink
ex0440_Menu_LiquidCrystal_vitesses_tests.ino
Mesure de vitesse d'exécution de diverses instructions, affichées sur l'écran LCD 1602.


    - Suivant, ex0448   ;   Précédent, ex0440
ex0441_DigitalWrite_speed.ino   TOP
Mesures de temps d'exécution de la fonction "DigitalWrite".
La mesure est précise, car elle utilise le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Affichage de résultats des mesures dans le moniteur série.

/*
ex0441_digitalWrite_speed.ino
Test la vitesse d'écriture sur un port digital
c.f.
https://arduino.stackexchange.com/questions/36871/is-the-digitalwritefast-library-still-needed

Un cycle = 0.0625 micro-seconde.   On note [us] l'unité des micro-secondes.
un cycle = 1 / 16'000'000 [Hz]
La fréquence de base d'un Arduino est de 16'000'000 [Hz].

Ce test montre qu'un accès direct à un port pour changer son état est 47/2 = 23 fois plus rapide
que l'utilisation de la fonction "digitalWrite(byte, byte)".

Voir aussi : ex0448_Menu_LCD_vitesses_tests.ino
Certaines mesures diffèrent dans cet autre programme. Pourquoi ???
Une différence est que "ex0448_Menu_LCD_vitesses_tests.ino" utilise la librairie : <iquidCrystal.h>

***********************************************************************/

void setup() {
//============
uint16_t overhead, start, end1;

pinMode( 2, OUTPUT); 
pinMode(13, OUTPUT); 

Serial.begin(9600);
TCCR1A = 0;
TCCR1B = 1;

overhead = 4; // Nombre de cycles utilisés pour la lecture du compteur TCNT1

// Time digitalWrite().
cli();
start = TCNT1;
digitalWrite(2, HIGH);  // 47 cycles
end1 = TCNT1;
sei();
Serial.print("digitalWrite(): ");
Serial.print(end1 - start - overhead);
Serial.println(" cycles");

// Time digitalWrite().
cli();
start = TCNT1;
digitalWrite(2, HIGH);
digitalWrite(2, HIGH); // Deux fois 47 cycles.
end1 = TCNT1;
sei();
Serial.print("digitalWrite(): ");
Serial.print(end1 - start - overhead);
Serial.println(" cycles");

// Time direct port access.
cli();
start = TCNT1;
//PORTD &= ~_BV(PD2);  // _BV(PD2) = 0b00000100    ~_BV(PD2) = 0b11111011   2 cycles.
//PORTD &= 0b11111011;   // mise à 0   équivalent à : PORTD = PORTD & 0b11111011;    2 cycles
PORTD |= 0b00000100; // mise à 1   équivalent à : PORTD = PORTD | 0b00000100;    2 cycles
end1 = TCNT1;
sei();
Serial.print("direct port access: ");
Serial.print(end1 - start - overhead);
Serial.println(" cycles");
} // setup()

void loop() {
//===========
// Juste comme exemple de manipulation de port.
// Fait clignoter une LED branchée sur la pin n° 2 et la LED de la pin 13.
PORTD &= 0b11111011; // mise à 0
PORTB &= 0b11011111; // mise à 0
delay(500);
PORTD |= 0b00000100; // mise à 1
PORTB |= 0b00100000; // mise à 1
delay(500);
} // loop

/*
PORTD
bit 0 => pin 0
bit 1 => pin 1
bit 2 => pin 2
...
bit 7 => pin 7

PORTB
bit 0 => pin  8
bit 1 => pin  9
bit 2 => pin 10
bit 3 => pin 11
bit 4 => pin 12
bit 5 => pin 13
bit 6 => non utilisable
bit 7 => non utilisable 

PORTC Analog input pins
c.f.
https://www.arduino.cc/en/Reference/PortManipulation
*/
ex0441_DigitalWrite_speed.ino
Il n'y a pas d'images associée, car le but est juste de mesurer des temps d'exécutions de diverses instructions, sans connexions hardware. L'affichage se fait dans le moniteur série.


    - Suivant, ex0450   ;   Précédent, ex0441
ex0448_Menu_LCD_vitesses_tests.ino   TOP
Mesures de temps d'exécution de diverses instructions.
Les mesures sont précises, car elles utilisent le registre TCNT1 qui compte le temps au cycle d'horloge près.
Il y a 16'000'000 cycles d'horloge par seconde.
Utilisation de l'afficheur LCD 1602 avec 5 boutons. Utilisation d'un menu pour sélectionner le test à faire.
Indique également la valeur de l'ADC0 associé à chaque bouton.

/*
ex0448_Menu_LCD_vitesses_tests.ino

Pour faire des mesures de temps de diverses instructions, telles que :
° l'écriture sur l'afficheur, c'est long, environ 5 [ms] pour afficher une ligne.
° la lecture des boutons
° la conversion d'un float en string

Ces mesures utilisent un oscilloscope.
Elles affichent également le résultat des mesures sur l'afficheur LCD 1602 avec les 5 boutons et
utilisent une menu pour sélectionner le test à faire.
Indique également la valeur de l'ADC0 associé à chaque bouton.

La pin 2 sera la référence pour le trigger
la pin 3 changera d'état après chaque boucle d'instruction

À partir d'un menu, permet aussi d'afficher le nom du bouton pressé et
la valeur de l'ADC 0 correspondant.

C.f. ex0149_Menu_LiquidCrystal_vitesses_tests.ino
qui fait aussi des mesures de temps, mais moins précisément.

C.f. ex0440_DigitalWrite_speed.ino
Fait aussi des mesures, avec la mesure du "DigitalWrite" différente.
Une différence entre ces programmes est que "ex0440_DigitalWrite_speed.ino" utilise l'affichage dans
le moniteur série et pas la librairie <LiquidCrystal.h> qui est utilisée dans ce programme.

Connections :
LCD   Arduino (pin) ou tension
 1 - Vss = 0 V
 2 - Vcc = 5 V
 3 - V0 = Tension variable réglée par une résistance variable, pour la lumineusité de l'affichage
 4 - RS = pin 8 (Register Select) 
 5 - R/W = 0 V donc uniquement en Write
 6 - E = pin 9  Enable, mise à haut pour accepter les données.
 7 .. 10 = connecté à la commande de rétro-éclairage "back light"
11 - D4 = pin 4, données sur 4 bits 
12 - D5 = pin 5, données sur 4 bits 
13 - D6 = pin 6, données sur 4 bits 
14 - D7 = pin 7, données sur 4 bits 
15 - LED + = 220 Ohm - +5V
16 - LED - = 0V

Autres connexions :
pin  2 et 3 pour le signal
pin 13 pour indiquer si le HP est on ou off
pin  0, pin 1, utilisées pour la transmission par USB,
ADC  0 pour les lecture les boutons.

c.f. : http://www.arduino.cc/en/Tutorial/LiquidCrystal
c.f. : https://arduino-info.wikispaces.com/LCD-Pushbuttons

Quelques constantes :
=====================
Elles sont définies dans le fichier : Arduino.h
Sous Linux KUbuntu, ce fichier se trouve dans le dossier : /usr/share/arduino/hardware/arduino/core/arduino

#define HIGH 0x1
#define LOW  0x0

#define INPUT 0x0
#define OUTPUT 0x1
#define INPUT_PULLUP 0x2

#define true 0x1
#define false 0x0

#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105

#define SERIAL  0x0
#define DISPLAY 0x1

#define LSBFIRST 0
#define MSBFIRST 1
======================================================== */

// include the library code:
#include <LiquidCrystal.h>

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Dernier numéro de menu. Non utilisé
#define MENUMAX  8

#define pinTrigger 2 // pour le signal du trigger
#define pinSignal  3 // pour le signal de mesure de temps de l'instruction

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
String strS = ""; 
// c.f. : https://www.arduino.cc/en/Reference/StringObject

char acStr[20]; // Pour conversion de nombre en un string.

int  nButtonPush = btnNONE; // Bouton pressé
byte bMenu1 = 5;  // indique le menu sélectionné

byte bEtat1 = 0; // Etat du signal 1,  LOW=0 ou HIGH=1

unsigned long lwTemps1 = 0; // temps en micro secondes
unsigned long lwTemps2 = 0; // temps en micro secondes
unsigned long lwTempsActionLast = 0; // temps en micro secondes de la dernière commande 

byte bNbCycle = 0; // Nombre de boucle dans loop() avant la lecture de boutons

// Pour des mesures de temps
// Utilisation du compteur TCNT1, qui est incrémenté de 1 16'000'000 de fois par seconde, donc toutes les 0.0625 [us].
uint16_t overhead = 0; // Nombre de cycles pour lire la valeur du compteur TCNT1.  Un cycle = 0.0626 [us].
uint16_t start    = 0; // valeur du compteur TCNT1 avant l'instruction
uint16_t end1     = 0; // valeur du compteur TCNT1 après l'instruction
uint16_t wTmp     = 0; // Un word pour un traitement temporaire.
byte bOverflow    = 0; // Mémorise s'il y a eu un overfloe

void setup() {
//============  
// set up the LCD's number of columns and rows: 
lcd.begin(16, 2);

// Affichage du type de programme en cours.
lcd.print("Oscillo, mes. T ");
lcd.setCursor(0, 1);
lcd.print("ex0448 20200626 ");

delay(1200); // Attente pour afficher le programme en cours

 // efface le texte
lcd.setCursor(0, 0);
lcd.print("                ");
lcd.setCursor(0, 1);
lcd.print("                ");
//lcd.autoscroll();

lwTemps1 = micros();
lwTemps2 = lwTemps1;
lwTempsActionLast   = lwTemps1;

pinMode(13, OUTPUT);
pinMode( pinSignal, OUTPUT); 
pinMode( pinTrigger, OUTPUT); 

digitalWrite( pinSignal, LOW); 
digitalWrite( pinTrigger, LOW);
bNbCycle = 0;

// Définit la vitesse de comptage du timer 1.
TCCR1A = 0; // Mode normal d'opération
TCCR1B = 1; // Mode normal et incrémente le compteur TCNT1 toutes les 0.0625 [us], soit 16'000'000 de fois par seconde.
TIMSK1 = 0; // Désactive toutes les interruptions du timer 1.
            // Le timer 0 continue d'être utilisé, pour les fonctions : "delay", "delayMicroseconds", "millis" et "micros".

overhead = 8; // Nombre de cycles utilisés pour les deux lectures du compteur TCNT1

AfficheEtat();
} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Button pressed according to the value of adc_key_in
static byte bButtonLast = btnNONE; // Last button
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// La lecture des boutons sont centrée autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

if (nAdc_key_in > 790) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
  }
else if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.

  if (bButtonCount == 3) {
    bButtonCount = 0;
    return bButton;  // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 1;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons

void AfficheEtat() {
//==================
// Affichage du menu et d'information
// Temps de mesure maximale = 65531 Cycles = 4.0956875 [ms]

byte bMenucheck = 0;

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles pour ne rien faire = 0 cycles
  cli();
  start = TCNT1;
  // 4 cycles, même si on ne fait rien.
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%1d) rien", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T=%5d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles pour lire la valeur du registre 16 bits TCNT1 = 4 cycles <=> 0.25 [us]
  cli();
  start = TCNT1;
  wTmp = TCNT1; // 4 cycles
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%1d) lect de TCNT1", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T=%5d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles pour lire deux fois la valeur du registre 16 bits TCNT1 = 8 cycles <=> 0.5 [us]
  cli();
  start = TCNT1;
  wTmp = TCNT1; // 4 cycles
  wTmp = TCNT1; // + 4 cylces
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%1d) lect 2x TCNT1", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T=%5d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles pour lire trois fois la valeur du registre 16 bits TCNT1 = 12 cycles <=> 0.75[us]
  cli();
  start = TCNT1;
  wTmp = TCNT1; // 4 cycles
  wTmp = TCNT1; // + 4 cylces
  wTmp = TCNT1; // + 4 cylces
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%1d) lect 3x TCNT1", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T=%5d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "lcd.print(acStr)" = 65536 + 7096 = 72'632 cycles <=> 4.5395 [ms]
  // Ici on dépasse le temps de mesure maximale des 65535 cycles.
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  lcd.print("2) affich. LCD  ");
  end1 = TCNT1;
  sei();

  bOverflow = TIFR1; // Mémorise le overflow flag
  
  sprintf(acStr, "%1d) affich. LCD  ", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  if (bOverflow & 1) { // Il y a eu un overflow, le compteur TCNT1 a dépassé 65535.
    lcd.setCursor(0, 1);
    lcd.print("Overflow : TCNT1");
    delay(1000);
    }
  
  sprintf(acStr, "T=%5d [cy] + >", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "lcd.setCursor(0, 0);" = 4515 cycles <=> 282.1875 [us]
  lcd.setCursor(0, 0);
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  lcd.setCursor(0, 0);
  end1 = TCNT1;
  sei();

  bOverflow = TIFR1; // Mémorise le overflow flag

  sprintf(acStr, "%1d) lcd.setcursor", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);
  
  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);

  if (bOverflow & 1) { // Il y a eu un overflow, le compteur TCNT1 a dépassé 65535.
    lcd.setCursor(0, 1);
    lcd.print("Overflow : TCNT1");
    }
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "sprintf" = 2495 cycles <=> 155.9375 [us]
  lcd.setCursor(0, 0);
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  end1 = TCNT1;
  sei();

  bOverflow = TIFR1; // Mémorise le overflow flag

  sprintf(acStr, "%1d) sprintf(...);", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);

  if (bOverflow & 1) { // Il y a eu un overflow, le compteur TCNT1 a dépassé 65535.
    lcd.setCursor(0, 1);
    lcd.print("Overflow : TCNT1");
    }
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Mesure le temps du "sprintf". T ≃ 156 [us], varie légèrement dans cette mesure. Les interruptions ne sont pas stoppée !

  lwTemps1 = micros();  
  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
  lwTemps2 = micros();

  sprintf(acStr, "%2d) T sprintf   ", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %8d [us]", lwTemps2 - lwTemps1);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Mesure le temps du "sprintf" avec conversion en float. T ≃ 270 [us]

  lwTemps1 = micros();  
  float f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT = %3d.%03d :", (int)f, (int)(f*1000)%1000);
  lwTemps2 = micros();

  sprintf(acStr, "%2d) T sprintf +f", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);
  
  f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT= %3d.%03d [ms]", (int)f, (int)(f*1000)%1000);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "digitalWrite(2, HIGH);" = 58 cycles <=> 3.625 [us]
  // Valeur différente de celle obtenue avec ex0440_DigitalWrite_speed !?!
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  digitalWrite(2, HIGH);  // 58 cycles
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%2d) digitalWrite", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "digitalWrite(2, HIGH);" = 58 cycles <=> 3.625 [us]
  // Valeur différente de celle obtenue avec ex0440_DigitalWrite_speed !?!
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  digitalWrite(2, HIGH);  // 58 cycles
  digitalWrite(2, HIGH);  // 58 cycles
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%2d) digitalW 2 x", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "PORTD = ...;" = 2 cycles <=> 0.125 [us], Accès direct au port D.
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  //PORTD &= ~_BV(PD2);  // _BV(PD2) = 0b00000100    ~_BV(PD2) = 0b11111011   2 cycles.
  //PORTD &= 0b11111011;   // mise à 0   équivalent à : PORTD = PORTD & 0b11111011;    2 cycles
  PORTD |= 0b00000100; // mise à 1   équivalent à : PORTD = PORTD | 0b00000100;    2 cycles
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%2d) PORTD |= 0b.", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "delay(2);".
  // Variable entre 32126 cycles <=> 2'007.875 [us] et 32143 cycles <=> 2'008.9375 [us].
  
  //cli(); // Il faut accepter les interruptions, car la fonction "delay" utilise l'interruption du timer 0.
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  delay(2); // attente de 2 [ms]
  end1 = TCNT1;
  //sei();

  sprintf(acStr, "%2d) delay(2)    ", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T=%5d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Nombre de cycles d'exéction d'un "dtostrf" = 1770 cycles <=> 110.625 [us]
  cli();
  TCNT1 = 0;
  TIFR1 = 1; // Efface les flags (bits) du registre "Timer/Counter1", qui détecte les "overflow"
  start = TCNT1;
  dtostrf( (lwTemps2 - lwTemps1) / 1000.0, 8, 3, acStr);
  end1 = TCNT1;
  sei();

  sprintf(acStr, "%1d) dtostrf(...) ", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, "T= %4d [cycles]", end1 - start - overhead);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 == bMenucheck) {
  // Mesure le temps du "dtostrf".  T ≃ 160 [us]

  lwTemps1 = micros();
  dtostrf( (lwTemps2 - lwTemps1) / 1000.0, 8, 3, acStr);
  // dtostrf fonctionne bien, alors qu'il a un bug dans tinkercad
  lwTemps2 = micros();

  sprintf(acStr, "%1d) dtostrf(...) ", bMenucheck);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  float f = (lwTemps2 - lwTemps1) / 1000.0;
  sprintf(acStr, "dT= %3d.%03d [ms]", (int)f, (int)(f*1000)%1000);
  lcd.setCursor(0, 1);
  lcd.print(acStr);
  }

bMenucheck++;  if (bMenu1 >= bMenucheck) {
  // Pour afficher des informations sur le bouton pressé  
  // Indique le bouton pressé
  String astrS[6];  // Indices vont de astrS[0] à astrS[5],  l'indice  6  n'est pas valide !

  // Texte qui peut etre affiché
  astrS[btnNONE]   = "None  ";
  astrS[btnSELECT] = "Select";
  astrS[btnLEFT]   = "Left  ";
  astrS[btnDOWN]   = "Down  ";
  astrS[btnUP]     = "Up    ";
  astrS[btnRIGHT]  = "Right ";

  int nAdc_key_in = analogRead(0);

  sprintf(acStr, "Menu=%2d         ", bMenu1);
  lcd.setCursor(0, 0);
  lcd.print(acStr);

  sprintf(acStr, " adc=%4d ", nAdc_key_in);
  String strS = astrS[nButtonPush] + acStr;
  lcd.setCursor(0, 1);
  lcd.print(strS);
  }
} // AfficheEtat

void MenuTreat() {
//================
// Pour afficher des informations sur le bouton pressé

if (micros() - lwTempsActionLast < 200000ul)  return; // Pas deux changements de menu en moins de 0,2 [s]
lwTempsActionLast = micros(); // Mémorise quand le dernier changement de menu a eu lieu.

AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1

if (micros() - lwTempsActionLast < 200000ul)  return; // Pas deux changements de menu en moins de 0,2 [s]
lwTempsActionLast = micros(); // Mémorise quand le dernier changement de menu a eu lieu.

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = 1;
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
  }

AfficheEtat();
} // Menu1_Change

void loop() {
//===========
// Boucle principale, pour tester le temps d'exécution d'instructions.

// Ne lit l'état des boutons que tous les ... cycles
bNbCycle++;
if (bNbCycle == 4) {
   // Lecture de l'état des boutons
   bNbCycle = 0;
   digitalWrite( pinTrigger, 1); // 4.6 à 5.0 us de décalage entre les deux "digitalWrite"

   nButtonPush = read_LCD_buttons();

   if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Changement de menu
   if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
   digitalWrite( pinTrigger, 0);
   }

bEtat1 = 1 - bEtat1;
digitalWrite( pinSignal, bEtat1); 
} // loop

//FIN
ex0440 blink
ex0448_Menu_LCD_vitesses_tests.ino
Mesures précises de temps d'exécution de diverses instructions.


    - Suivant, ex0450   ;   Précédent, ex0448
Commentaires et informations sur le Timer 1   TOP
Ce sont des commentaires généraux, valident pour les exemples ex0450 à ex0464 qui suivent.

/*
Plusieurs registres définissent la manière de générer des imulsions sur les pin 3, 5, 6, 9, 10 et 11.
Il y a trois groupes de 2 : 
Timer pin1 pin2 registres
  0    6    5    TCCR0B  TCCR0A OCR1A OCR0B TCNT0
  1    9   10    TCCR1B  TCCR1A OCR1A OCR1B TCNT1
  2   11    3    TCCR2B  TCCR2A OCR2A OCR2B TCNT2
Ce qui suit n'utilisera pas cette manière de faire qui correspond au PMW.

Ici, on se limitera au Timer 1.

Référence = Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf

"TCCR" signifie Timer Counter Control Register

Structure des registres 8-bits de contrôle :
TCCR1A - [COM1A1, COM1A0, COM1B1,   COM1B0,   reserved, reserved, WGM11,    WGM10]
TCCR1B - [ICNC1,  ICES1,  reserved, WGM13,    WGM12,    CS12,     CS11,     CS10]
TCCR1C - [F0C1A,  F0C1B,  reserved, reserved, reserved, reserved, reserved, reserved]
TIMSK1 - [  -  ,    -  ,  ICE1,        -    ,    -    , OCIE1B,   OCIE1A,   TOIE1]  c.f. page 135
bit           7       6        5         4        3        2         1        0

ICE1 = 1 --- Interruption lorsque le Flag "ICF1" est mis à 1
OCIE1B = 1 --- Interruption lorsque le Flag "OCF1B" est mis à 1. Ce Flag est mis à 1 lorsque le compteur TCNT1 == OCR1B
OCIE1A = 1 --- Interruption lorsque le Flag "OCF1A" est mis à 1. Ce Flag est mis à 1 lorsque le compteur TCNT1 == OCR1A
TOIE1 = 1 --- Interruption lorsque le Flag "TOV1" est mis à 1. Ce Flag est mis à 1 lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)

OCR1A et OCR1B sont deux registres 16 bits qui permettent le contrôle précis de générations d'interruptions,
lorsque le compteur 16 bits  TCNT1  atteint un de ces registres.

Status Register ( Timer/Counter1 Interrupt Flag Register) :  c.f. page 136
TIFR1 - [   - ,   - ,   ICF1,    - ,    - , OCF1B, OCF1A,  TOV1]
bit         7      6      5      4      3      2      1      0

OCF1B est mis à 1 lorsque : TCNT1 == OCR1B. Indépendemment du fait qu'une interruption est exécutée ou non.
OCF1A est mis à 1 lorsque : TCNT1 == OCR1A. Indépendemment du fait qu'une interruption est exécutée ou non.
TOV1 est mis à 1 lorsque : TCNT1 passe de 65535 à 0 (overflow). Indépendemment du fait qu'une interruption est exécutée ou non.
Dans chaque cas, le Flag est mis à zéro lorsque l'on écrit un  1  à la place de ce bit dans le registre TIFR1.
Il est aussi mis à zéro, si le bit de gestion d'interruptions est activé (mis à 1) et qu'une interruption a lieu.

ICF1 est mis à 1 lorsqu'une "capture event" à lieu sur le pin ICP1. pin ICP1 = digital I/O 8 = "Input Capture Pin".
Ainsi, il est possible de générer une interruption lorsqu'il se passe quelque chose sur la pin 8.  C.f. p. 117  et 133
Le bit  ICES1  du registre  TCCR1B  indique si l'interruption à lieu lors d'une transistion descendante (bit = 0) ou montante (bit = 1).
Lors d'un "event" sur la pin ICP1, le registre 16 bits  ICR1  mémorise la valeur du compteur  TCNT1  lors de l'interruption, 
pour mesurer très précisément quand l'interruption a eu lieu. C.f. p. 135.
ICF1  n'est pas utilisé dans ce programme !


COM1Ax bits == Compare Output Mode bits    == 0 dans ce prog.
OCR1A == Output Compare Register A         utilisé à partir de ex0451.  Registre de 16 bits, pouvant aller de 0 à 65535.
WGM1(0:3) 4 bits == Waveform Generation Mode bits   WGM1(0:3) = 0b0100 == 4  à partir de ex0451.

Les registres  TCNT1,  OCR1A  et  OCR1B  sont en mode R/W (écriture et lecture).
Ce sont des registres de 16 bits, allant de 0 à 65535.
Le compteur TCNT1 compte de 0 à OCR1A, puis revient à 0. 
Une interruption est générée si le Flag  OCIE1A  ou  OCIE1B  du registre TIMSK1 est mis à 1.

Utile pour la génération de PMW (Pulse Width Modulated).
(COM1A1, COM1A0)  C.f. p. 131   Permet d'activer ou non le bit OC1A   c.f. "Port A pins"   OC1A == pin 9
(0, 0) => OC1A est déconnecté, 
(0, 1) => Toggle OC1A on compare Match
(1, 0) => Clear OC1A  on Compare Match (Set output to low level)
(1, 1) => Clear OC1A  on Compare Match (Set output to high level)

(COM1B1, COM1B0)  C.f. p. 131   Permet d'activer ou non le bit OC1B   c.f. "Port B pins"   OC1B == pin 10
(0, 0) => OC1B est déconnecté,
(0, 1) => Toggle OC1B on compare Match
(1, 0) => Clear OC1B  on Compare Match (Set output to low level)
(1, 1) => Clear OC1B  on Compare Match (Set output to high level)

(WGM13, WGM12, WGM11, WGM10)   C.f. page 132
(0, 0, 0, 0) => le compteur  TCNT1  est incrémenté chaque  T * xxx [us]. Pour  xxx  c.f. bits CS1(0:2)
             Lorsque le compteur passe à 0, une interruption est générée si la valeur du masque : TIMSK1 = 0bxxxxxxx1
             Lors de cette interruption, une nouvelle valeur de départ peut être assignée à  TCNT1.
             À n'importe quelle moment, une nouvelle valeur peut être assignée à TCNT1.
(0, 0, 0, 1) => Pour PWM, phase mode.  C.f. p. 125
             Le compteur compte alternativement en incrémentant, puis décrémentant.
(0, 1, 0, 0) => Mode CTC "Clear Timer on Compare".  C.f. page 122.   C'est le mode que je conseille.
             Le registre  OCR1A  est utilisé ici.
             Lorsque le compteur  TCNT1 == OCR1A,  le compteur  TCNT1  est mis à 0 et
             une interruption est générée si la valeur du masque : TIMSK1 = 0bxxxxxx1x
...

Gère le temps entre deux incréments du compteur  TCNT1
(CS12, CS11, CS10)  C.f. page 134
(0, 0, 0) => No clock source, Timer/Counter stopped
(0, 0, 1) => T * 1    =  0.0625 [us]
(0, 1, 0) => T * 8    =  0.500  [us], division par    8 de la fréquence
(0, 1, 1) => T * 64   =  4.000  [us], division par   64 de la fréquence
(1, 0, 0) => T * 256  = 16.000  [us], division par  256 de la fréquence
(1, 1, 1) => T * 1024 = 64.000  [us], division par 1024 de la fréquence


Pour d'autres modes de fonctionnement. 
--------------------------------------
C.f. page 131
TCCR1A = [COM1A1 COM1A0 COM1B1 COM1B0 0 0 WGM11 WGM10
TCCR1A = 0b01000000; // Pour activer le bit OC1A ( == pin 9),
                     // Bits : WGM11 = 0 et WGM10 = 0
TCCR1B = [ICNC1 ICES1 0 WGM13 WGM12 CS12 CS11 CS10]
TCCR1B = 0b00001001; // wGM13 = 0  WGM12 = 1  
TCCR1C = [F0C1A F0C1B 0 0 0 0 0 0]

TCCR     = Timer Counter Control Register
CTC mode = Clear Timer on Compare
WGM mode = Wave Generation mode
COM mode = Compare Output mode  N'affecte pas la séquence du compteur TCNT1
TIMSK = Timer Interrupt Mask Register

Mode Normal, TCNT1 compte de 0 à 65535,  WGM = 0b0000  = 0
Mode CTC, TCNT1 compte de 0 à OCR1A,     WGM = 0b0100  = 4      Mode utilisé dans ce programme. C.f. p. 122
( Mode CTC, TCNT1 compte de 0 à ICR1,    WGM = 0b1100  = 12 )
( Fast  PWM Mode,  WGM = 5, 6, 7, 14 ou 15 )
( Phase PWM Mode,  WGM = 1, 2, 3, 10 ou 11 )
( Phase and Frequency Correct PWM Mode,   WGM = 8 ou 9 )
TIMSK = [ 0 0 ICIE1 - - OCIE1B OCIE1A TOIE1 ]
TOIE1  = 1 --- génération d'interruption lors d'un overflow        
OCIE1A = 1 --- génération d'interruption lorsque  TCNT1 == OCR1A   Ce qui est utilisé dans ce prog.
OCIE1B = 1 --- génération d'interruption lorsque  TCNT1 == OCR1B
ICIE1  = 1 --- génération d'interruption lorsque  TCNT1 == ICR1
====================================================================*/
ex045x_comments
Il n'y a pas d'images associée, car ce ne sont que des commentaires et informations.


    - Suivant, ex0451   ;   Précédent, ex045x, commentaires
ex0450_timer_1_interrupt.ino   TOP
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilise un Arduino Mega.
L'exemple suivant est meilleur. La méthode utilisée dans cet exemple n'est pas conseillée.

/*
ex0450_timer_1_interrupt.ino
Exemple d'utilisation du timer 1 pour générer des interruptions.

POUR ADRUINO  MEGA  car beaucoup de mémoire utilisée !

L'utilisation du timer 1 va simplifier la gestion, 
car ce timer a un compteur sur 16 bits 
contrairement aux timers 0 et 2 qui ont des compteurs sur 8 bits.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.

Suite de ex040x_timer_2_interrupt.ino.
Autre approche, à chaque passage dans la fonction de gestion d'interruption, on change l'état de la pin 13

C.f. ex0451_timer_1_interrupt_OCR1A.ino  pour une meilleure approche.
====================================================================*/

// Nombre de fois qu'il faut répéter la même fréquence
word volatile awRepete[] = {
   9,    7,    7,    7,    8,    7,    7,    7,    8,    7,    7,    7,    8,    7,    7, 
   7,    8,    7,    7,    7,    8,    7,    7,    8,    7,    7,    8,    7,    7,    8, 
   7,    7,    7,    8,    7,    8,    7,    7,    8,    7,    7,    8,    7,    7,    8, 
   7,    8,    7,    7,    8,    7,    7,    8,    7,    8,    7,    8,    7,    7,    8, 
   7,    8,    7,    8,    7,    8,    7,    7,    8,    7,    8,    7,    8,    7,    8, 
   7,    8,    7,    8,    7,    8,    7,    8,    7,    8,    7,    8,    7,    8,    8, 
   7,    8,    7,    8,    7,    8,    7,    8,    8,    7,    8,    7,    8,    8,    7, 
   8,    7,    8,    8,    7,    8,    8,    7,    8,    7,    8,    8,    7,    8,    8, 
   7,    8,    8,    7,    8,    8,    8,    7,    8,    8,    7,    8,    8,    7,    8, 
   8,    8,    7,    8,    8,    8,    7,    8,    8,    8,    7,    8,    8,    8,    8, 
   7,    8,    8,    8,    7,    8,    8,    8,    8,    8,    7,    8,    8,    8,    8, 
   8,    7,    8,    8,    8,    8,    8,    8,    8,    7,    8,    8,    8,    8,    8, 
   8,    8,    8,    8,    8,    7,    8,    8,    8,    8,    8,    8,    8,    8,    8, 
   8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8, 
   8,    8,    8,    8,    8,    8,    8,    8,    9,    8,    8,    8,    8,    8,    8, 
   8,    8,    8,    8,    9,    8,    8,    8,    8,    8,    8,    9,    8,    8,    8, 
   8,    8,    8,    9,    8,    8,    8,    8,    9,    8,    8,    8,    8,    9,    8, 
   8,    8,    9,    8,    8,    8,    9,    8,    8,    8,    9,    8,    8,    9,    8, 
   8,    8,    9,    8,    8,    9,    8,    8,    9,    8,    8,    9,    8,    8,    9, 
   8,    8,    9,    8,    9,    8,    8,    9,    8,    9,    8,    8,    9,    8,    9, 
   8,    9,    8,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9, 
   8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    9, 
   8,    9,    8,    9,    9,    8,    9,    8,    9,    9,    8,    9,    8,    9,    9, 
   8,    9,    9,    8,    9,    9,    8,    9,    9,    8,    9,    9,    8,    9,    9, 
   8,    9,    9,    9,    8,    9,    9,    9,    8,    9,    9,    9,    8,    9,    9, 
   9,    9,    8,    9,    9,    9,    9,    8,    9,    9,    9,    9,    9,    8,    9, 
   9,    9,    9,    9,    9,    8,    9,    9,    9,    9,    9,    9,    9,    9,    9, 
   9,    8,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9, 
   9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,   10,    9,    9,    9, 
   9,    9,    9,    9,    9,    9,    9,   10,    9,    9,    9,    9,    9,    9,    9, 
  10,    9,    9,    9,    9,   10,    9,    9,    9,    9,   10,    9,    9,    9,    9, 
  10,    9,    9,    9,   10,    9,    9,    9,   10,    9,    9,   10,    9,    9,   10, 
   9,    9,    9,   10,    9,   10,    9,    9,   10,    9,    9,   10,    9,    9,   10, 
   9,   10,    9,    9,   10,    9,   10,    9,   10,    9,    9,   10,    9,   10,    9, 
  10,    9,   10,    9,   10,    9,   10,    9,   10,    9,   10,    9,   10,   10,    9, 
  10,    9,   10,    9,   10,   10,    9,   10,    9,   10,   10,    9,   10,    9,   10, 
  10,    9,   10,   10,    9,   10,   10,    9,   10,   10,    9,   10,   10,   10,    9, 
  10,   10,    9,   10,   10,   10,    9,   10,   10,   10,   10,    9,   10,   10,   10, 
  10,    9,   10,   10,   10,   10,   10,    9,   10,   10,   10,   10,   10,   10,   10, 
   9,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10, 
  10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   10, 
  10,   10,   10,   10,   10,   11,   10,   10,   10,   10,   10,   10,   10,   11,   10, 
  10,   10,   10,   10,   11,   10,   10,   10,   10,   11,   10,   10,   10,   11,   10, 
  10,   10,   11,   10,   10,   10,   11,   10,   10,   11,   10,   10,   11,   10,   10, 
  11,   10,   10,   11,   10,   10,   11,   10,   11,   10,   10,   11,   10,   11,   10, 
  11,   10,   11,   10,   10,   11,   10,   11,   10,   11,   10,   11,   10,   11,   10, 
  11,   11,   10,   11,   10,   11,   10,   11,   10,   11,   11,   10,   11,   11,   10, 
  11,   10,   11,   11,   10,   11,   11,   10,   11,   11,   10,   11,   11,   11,   10, 
  11,   11,   11,   10,   11,   11,   11,   10,   11,   11,   11,   10,   11,   11,   11, 
  11,   11,   10,   11,   11,   11,   11,   11,   11,   11,   10,   11,   11,   11,   11, 
  11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11, 
  11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   12,   11,   11,   11, 
  11,   11,   11,   11,   12,   11,   11,   11,   11,   12,   11,   11,   11,   11,   12, 
  11,   11,   11,   12,   11,   11,   11,   12,   11,   11,   12,   11,   11,   12,   11, 
  11,   12,   11,   11,   12,   11,   11,   12,   11,   12,   11,   11,   12,   11,   12, 
  11,   12,   11,   12,   11,   12,   11,   12,   11,   12,   11,   12,   11,   12,   11, 
  12,   11,   12,   12,   11,   12,   11,   12,   12,   11,   12,   12,   11,   12,   11, 
  12,   12,   12,   11,   12,   12,   11,   12,   12,   12,   11,   12,   12,   12,   11, 
  12,   12,   12,   12,   11,   12,   12,   12,   12,   11,   12,   12,   12,   12,   12, 
  12,   12,   12,   11,   12,   12,   12,   12,   12,   12,   12,   12,   12,   12,   12, 
  12,   12,   12,   12,   12,   12,   12,   12,   12,   13,   12,   12,   12,   12,   12, 
  12,   12,   13,   12,   12,   12,   12,   12,   13,   12,   12,   12,   12,   13,   12, 
  12,   12,   13,   12,   12,   12,   13,   12,   12,   13,   12,   12,   13,   12,   12, 
  13,   12,   13,   12,   12,   13,   12,   13,   12,   12,   13,   12,   13,   12,   13, 
  12,   13,   12,   13,   12,   13,   12,   13,   13,   12,   13,   12,   13,   12,   13, 
  13,   12,   13,   13,   12,   13,   13,   12,   13,   13,   12,   13,   13,   12,   13, 
  13,   13,   12,   13,   13,   13,   12,   13,   13,   13,   13,   13,   12,   13,   13, 
  13,   13,   13,   13,   13,   12,   13,   13,   13,   13,   13,   13,   13,   13,   13, 
  13,   13,   13,   13,   13,   13,   13,   13,   13,   13,   14,   13,   13,   13,   13, 
  13,   13,   13,   14,   13,   13,   13,   13,   13,   14,   13,   13,   13,   14,   13, 
  13,   13,   14,   13,   13,   14,   13,   13,   14,   13,   13,   14,   13,   13,   14, 
  13,   14,   13,   13,   14,   13,   14,   13,   14,   13,   14,   13,   14,   13,   14, 
  13,   14,   13,   14,   14,   13,   14,   13,   14,   14,   13,   14,   13,   14,   14, 
  13,   14,   14,   14,   13,   14,   14,   13,   14,   14,   14,   14,   13,   14,   14, 
  14,   14,   13,   14,   14,   14,   14,   14,   14,   14,   14,   13,   14,   14,   14, 
  14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   15,   14,   14,   14, 
  14,   14,   14,   14,   14,   15,   14,   14,   14,   14,   15,   14,   14,   14,   15, 
  14,   14,   14,   15,   14,   14,   15,   14,   14,   15,   14,   14,   15,   14,   15, 
  14,   14,   15,   14,   15,   14,   15,   14,   15,   14,   15,   14,   15,   14,   15, 
  14,   15,   15,   14,   15,   14,   15,   15,   14,   15,   15,   14,   15,   15,   14, 
  15,   15,   15,   14,   15,   15,   15,   14,   15,   15,   15,   15,   15,   14,   15, 
  15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   15, 
  15,   15,   15,   15,   15,   15,   15,   15,   16,   15,   15,   15,   15,   15,   16, 
  15,   15,   15,   15,   16,   15,   15,   16,   15,   15,   15,   16,   15,   15,   16, 
  15,   16,   15,   15,   16,   15,   16,   15,   16,   15,   16,   15,   16,   15,   16, 
  15,   16,   15,   16,   16,   15,   16,   15,   16,   16,   15,   16,   16,   15,   16, 
  16,   16,   15,   16,   16,   16,   15,   16,   16,   16,   16,   15,   16,   16,   16, 
  16,   16,   16,   16,   16,   16,   16,   16,   16,   16,   16,   16,   16,   16,   16, 
  16,   16,   16,   16,   16,   16,   16,   17,   16,   16,   16,   16,   17,   16,   16, 
  16,   17,   16,   16,   16,   17,   16,   16,   17,   16,   17,   16,   16,   17,   16, 
  17,   16,   16,   17,   16,   17,   16,   17,   17,   16,   17,   16,   17,   16,   17, 
  17,   16,   17,   17,   16,   17,   17,   16,   17,   17,   17,   16,   17,   17,   17, 
  17,   16,   17,   17,   17,   17,   17,   17,   17,   16,   17,   17,   17,   17,   17, 
  17,   17,   17,   17,   18,   17,   17,   17,   17,   17,   17,   17,   18,   17,   17, 
  17,   17,   18,   17,   17,   17,   18,   17,   17,   18,   17,   17,   18,   17,   17, 
  18,   17,   18,   17,   18,   17,   18,   17,   18,   17,   18,   17,   18,   17,   18, 
  18,   17,   18,   18,   17,   18,   18,   17,   18,   18,   18,   17,   18,   18,   18, 
  17,   18,   18,   18,   18,   18,   18,   18,   18,   18,   17,   18,   18,   18,   18, 
  19,   18,   18,   18,   18,   18,   18,   18,   18,   18,   19,   18,   18,   18,   19, 
  18,   18,   18,   19,   18,   18,   19,   18,   18,   19,   18,   18,   19,   18,   19, 
  18,   19,   18,   19,   18,   19,   18,   19,   19,   18,   19,   18,   19,   19,   18, 
  19,   19,   19,   18,   19,   19,   19,   18,   19,   19,   19,   19,   19,   18,   19, 
  19,   19,   19,   19,   19,   19,   19,   19,   19,   19,   19,   19,   20,   19,   19, 
  19,   19,   19,   20,   19,   19,   19,   19,   20,   19,   19,   20,   19,   19,   20, 
  19,   20,   19,   19,   20,   19,   20,   19,   20,   19,   20,   20,   19,   20,   19, 
  20,   20,   19,   20,   20,   19,   20,   20,   20,   19,   20,   20,   20,   20,   19, 
  20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20, 
  20,   21,   20,   20,   20,   20,   21,   20,   20,   20,   21,   20,   20,   21,   20, 
  20,   21,   20,   21,   20,   21,   20,   21,   20,   21,   20,   21,   20,   21,   21, 
  20,   21,   20,   21,   21,   21,   20,   21,   21,   21,   20,   21,   21,   21,   21, 
  21,   21,   21,   21,   21,   21,   21,   21,   21,   21,   21,   21,   21,   21,   21, 
  22,   21,   21,   21,   21,   22,   21,   21,   22,   21,   21,   22,   21,   21,   22, 
  21,   22,   21,   22,   21,   22,   21,   22,   22,   21,   22,   21,   22,   22,   22, 
  21,   22,   22,   22,   21,   22,   22,   22,   22,   22,   22,   22,   21,   22,   22, 
  22,   22,   23,   22,   22,   22,   22,   22,   22,   22,   23,   22,   22,   22,   23, 
  22,   22,   23,   22,   22,   23,   22,   23,   22,   23,   22,   23,   22,   23,   22, 
  23,   23,   22,   23,   23,   22,   23,   23,   22,   23,   23,   23,   23,   23,   22, 
  23,   23,   23,   23,   23,   23,   23,   23,   23,   23,   24,   23,   23,   23,   23, 
  23,   24,   23,   23,   23,   24,   23,   23,   24,   23,   24,   23,   24,   23,   24, 
  23,   24,   23,   24,   23,   24,   24,   23,   24,   24,   24,   23,   24,   24,   24, 
  24,   24,   23,   24,   24,   24,   24,   24,   24,   24,   24,   24,   25,   24,   24, 
  24,   24,   24,   25,   24,   24,   25,   24,   24,   25,   24,   24,   25,   24,   25, 
  24,   25,   24,   25,   25,   24,   25,   25,   24,   25,   25,   25,   24,   25,   25, 
  25,   25,   25,   25,   25,   24,   25,   26,   25,   25,   25,   25,   25,   25,   25, 
  26,   25,   25,   25,   26,   25,   25,   26,   25,   25,   26,   25,   26,   25,   26, 
  25,   26,   26,   25,   26,   26,   25,   26,   26,   26,   25,   26,   26,   26,   26, 
  26,   26,   26,   26,   26,   26,   26,   26,   26,   26,   27,   26,   26,   26,   26, 
  27,   26,   26,   27,   26,   27,   26,   27,   26,   27,   26,   27,   26,   27,   27, 
  26,   27,   27,   27,   26,   27,   27,   27,   27,   27,   27,   27,   27,   27,   27, 
  27,   27,   27,   27,   27,   27,   28,   27,   27,   28,   27,   27,   28,   27,   27, 
  28,   27,   28,   28,   27,   28,   27,   28,   28,   27,   28,   28,   28,   28,   27, 
  28,   28,   28,   28,   28,   28,   28,   28,   28,   28,   29,   28,   28,   28,   28, 
  29,   28,   28,   29,   28,   29,   28,   29,   28,   29,   28,   29,   28,   29,   29, 
  28,   29,   29,   29,   29,   29,   28,   29,   29,   29,   29,   29,   29,   29,   30, 
  29,   29,   29,   29,   30,   29,   29,   30,   29,   29,   30,   29,   30,   29,   30, 
  30,   29,   30,   29,   30,   30,   30,   30,   29,   30,   30,   30,   30,   30,   30, 
  30,   30,   30,   30,   31,   30,   30,   30,   31,   30,   30,   31,   30,   31,   30, 
  31,   30,   31,   30,   31,   31,   30,   31,   31,   31,   30,   31,   31,   31,   31, 
  31,   31,   31,   31,   31,   32,   31,   31,   31,   31,   32,   31,   32,   31,   31, 
  32,   31,   32,   32,   31,   32,   31,   32,   32,   32,   32,   31,   32,   32,   32, 
  32,   32,   32,   32,   32,   32,   33,   32,   32,   32,   33,   32,   32,   33,   32, 
  33,   32,   33,   33,   32,   33,   32,   33,   33,   33,   33,   33,   32,   33,   33, 
  33,   33,   33,   34,   33,   33,   33,   33,   34,   33,   33,   34,   33,   34,   33, 
  34,   33,   34,   34,   33,   34,   34,   34,   34,   33,   34,   34,   34,   34,   34, 
  34,   35,   34,   34,   34,   35,   34,   34,   35,   34,   35,   34,   35,   34,   35, 
  34,   35,   35,   35,   34,   35,   35,   35,   35,   35,   35,   35,   35,   36,   35, 
  35,   35,   36,   35,   35,   36,   35,   36,   35,   36,   36,   35,   36,   36,   35, 
  36,   36,   36,   36,   36,   36,   36,   36,   36,   37,   36,   36,   36,   37,   36, 
  37,   36,   37,   36,   37,   36,   37,   37,   37,   36,   37,   37,   37,   37,   37, 
  37,   37,   37,   37,   38,   37,   37,   38,   37,   37,   38,   38,   37,   38,   37, 
  38,   38,   38,   37,   38,   38,   38,   38,   38,   38,   38,   39,   38,   38,   38, 
  39,   38,   39,   38,   39,   38,   39,   38,   39,   39,   39,   39,   38,   39,   39, 
  39,   39,   40,   39,   39,   39,   39,   40,   39,   40,   39,   40,   39,   40,   39, 
  40,   40,   40,   40,   40,   39,   40,   41,   40,   40,   40,   40,   40,   41,   40, 
  41,   40,   41,   40,   41,   40,   41,   41,   41,   41,   40,   41,   41,   41,   42, 
  41,   41,   41,   41,   42,   41,   42,   41,   42,   41,   42,   42,   41,   42,   42, 
  42,   42,   42,   42,   42,   42,   42,   43,   42,   42,   43,   42,   43,   42,   43, 
  42,   43,   43,   43,   43,   43,   42,   44,   43,   43,   43,   43,   43,   44,   43, 
  44,   43,   44,   43,   44,   44,   44,   43,   44,   44,   44,   44,   44,   44,   45, 
  44,   44,   45,   44,   45,   44,   45,   44,   45,   45,   45,   44,   45,   45,   45, 
  46,   45,   45,   45,   45,   46,   45,   46,   45,   46,   46,   45,   46,   46,   46, 
  46,   46,   46,   46,   46,   46,   47,   46,   47,   46,   47,   46,   47,   47,   46, 
  47,   47,   47,   47,   47,   47,   47,   48,   47,   47,   48,   47,   48,   48,   47, 
  48,   48,   48,   48,   48,   48,   48,   48,   48,   48,   49,   48,   49,   48,   49, 
  49,   48,   49,   49,   49,   49,   49,   49,   49,   50,   49,   49,   50,   49,   50, 
  50,   49,   50,   50,   50,   50,   50,   50,   50,   50,   51,   50,   50,   51,   50, 
  51,   51,   51,   50,   51,   51,   51,   51,   52,   51,   51,   52,   51,   51,   52, 
  52,   51,   52,   52,   52,   52,   52,   52,   52,   53,   52,   53,   52,   53,   52, 
  53,   53,   53,   53,   53,   53,   53,   53,   53,   54,   53,   53,   54,   54,   53, 
  54,   54,   54,   54,   54,   54,   55,   54,   54,   55,   54,   55,   55,   54,   55, 
  55,   55,   55,   55,   55,   56,   55,   56,   55,   56,   55,   56,   56,   56,   56, 
  56,   56,   56,   57,   56,   56,   57,   57,   56,   57,   57,   57,   57,   57,   57, 
  57,   58,   57,   58,   57,   58,   58,   58,   57,   58,   58,   59,   58,   58,   59, 
  58,   59,   58,   59,   59,   59,   59,   59,   59,   59,   60,   59,   59,   60,   60, 
  60,   59,   60,   60,   60,   61,   60,   60,   61,   60,   61,   61,   60,   61,   61, 
  61,   61,   62,   61,   61,   62,   62,   61,   62,   62,   62,   62,   62,   62,   63, 
  62,   63,   62,   63,   63,   63,   63,   63,   63,   63,   63,   64,   63,   64,   64, 
  64,   64,   64,   64,   64,   64,   65,   64,   65,   64,   65,   65,   65,   65,   65, 
  65,   66,   65,   66,   66,   65,   66,   66,   66,   66,   67,   66,   66,   67,   67, 
  66,   67,   67,   67,   68,   67,   67,   68,   67,   68,   68,   68,   68,   68,   68, 
  68,   69,   68,   69,   69,   68,   69,   70,   69,   69,   69,   70,   69,   70,   70, 
  70,   70,   70,   70,   71,   70,   71,   70,   71,   71,   71,   71,   72,   71,   71, 
  72,   72,   71,   72,   72,   73,   72,   72,   73,   72,   73,   73,   73,   73,   73, 
  73,   74,   73,   74,   74,   74,   74,   74,   74,   74,   75,   75,   74,   75,   75, 
  75,   75,   76,   75,   76,   76,   75,   76,   76,   77,   76,   76,   77,   77,   76, 
  77,   77,   78,   77,   77,   78,   78,   77,   78,   79,   78,   78,   79,   78,   79, 
  79,   79,   79,   79,   80,   79,   80,   79,   80,   80,   81,   80,   80,   81,   81, 
  81,   81,   81,   81,   81,   82,   82,   81,   82,   82,   83,   82,   82,   83,   83, 
  83,   83,   83,   83,   84,   84,   83,   84,   84,   85,   84,   84,   85,   85,   85, 
  85,   85,   85,   86,   86,   85,   86,   86,   87,   86,   87,   86,   87,   87,   87, 
  88,   87,   88,   88,   87,   88,   89,   88,   89,   88,   89,   89,   89,   90,   89, 
  90,   90,   90,   90,   90,   90,   91,   91,   91,   91,   91,   91,   92,   92,   91, 
  92,   93,   92,   93,   92,   93,   93,   93,   94,   93,   94,   94,   94,   94,   95, 
  94,   95,   95,   95,   95,   96,   95,   96,   96,   96,   96,   97,   97,   96,   97, 
  98,   97,   97,   98,   98,   98,   98,   99,   98,   99,   99,   99,  100,   99,  100, 
 100,  100,  100,  101,  101,  100,  101,  102,  101,  102,  102,  102,  102,  102,  103, 
 102,  103,  104,  103,  103,  104,  104,  104,  105,  104,  105,  105,  105,  105,  106, 
 106,  106,  106,  106,  107,  106,  107,  107,  108,  107,  108,  108,  108,  109,  109, 
 108,  109,  110,  109,  110,  110,  110,  110,  111,  111,  111,  111,  111,  112,  112, 
 112,  112,  113,  112,  113,  114,  113,  114,  113,  115,  114,  114,  115,  115,  115, 
 116,  116,  116,  116,  116,  117,  117,  117,  117,  118,  118,  118,  118,  118,  119, 
 119,  119,  120,  120,  120,  120,  120,  121,  121,  121,  122,  122,  121,  123,  122, 
 123,  123,  123,  124,  123,  124,  125,  124,  125,  125,  125,  126,  126,  126,  126, 
 127,  127,  127,  127,  128,  128,  128,  128,  129,  129,  130,  129,  130,  130,  131, 
 130,  131,  131,  132,  132,  132,  132,  133,  133,  133,  134,  133,  135,  134,  135, 
 135,  135,  135,  136,  136,  137,  136,  138,  137,  137,  138,  139,  138,  139,  139, 
 140,  139,  140,  141,  140,  141,  142,  141,  142,  143,  142,  143,  143,  144,  144, 
 144,  144,  145,  145,  146,  145,  147,  146,  147,  147,  147,  148,  148,  149,  148, 
 150,  149,  150,  150,  151,  150,  152,  151,  152,  152,  153,  153,  153,  154,  154, 
 154,  155,  155,  155,  156,  156,  156,  157,  158,  157,  158,  158,  159,  159,  160, 
 160,  160,  160,  161,  162,  161,  163,  162,  163,  163,  164,  164,  164,  165,  166, 
 165,  166,  167,  166,  168,  167,  168,  169,  169,  169,  170,  170,  170,  171,  171, 
 172,  172,  173,  173,  173,  174,  175,  174,  175,  176,  176,  177,  176,  178,  178, 
 178,  178,  179,  180,  180,  180,  181,  182,  182,  182,  183,  183,  183,  184,  185, 
 185,  186,  186,  186,  187,  187,  188,  189,  189,  189,  190,  190,  191,  191,  192, 
 192,  193,  193,  194,  194,  195,  195,  196,  197,  196,  198,  198,  198,  199,  199, 
 200,  201,  201,  201,  202,  203,  203,  204,  204,  205,  205,  206,  206,  207,  208, 
 208,  209,  209,  210,  210,  211,  211,  212,  213,  213,  214,  214,  215,  216,  216, 
 217,  217,  218,  218,  220,  219,  221,  221,  221,  222,  223,  224,  224,  224,  226, 
 225,  227,  227,  228,  228,  230,  229,  231,  231,  232,  232,  233,  234,  234,  235, 
 236,  236,  237,  238,  238,  240,  239,  241,  241,  242,  243,  243,  244,  245,  245, 
 246,  247,  248,  248,  249,  250,  250,  252,  252,  252,  254,  254,  255,  256,  256, 
 258,  258,  259,  259,  261,  261,  262,  262,  264,  264,  266,  266,  266,  268,  268, 
 269,  271,  270,  272,  273,  273,  274,  275,  276,  277,  278,  278,  280,  280,  281, 
 282,  283,  284,  284,  286,  286,  288,  288,  289,  290,  291,  292,  293,  294,  295, 
 295,  297,  298,  298,  300,  300,  302,  302,  303,  305,  305,  307,  307,  308,  310, 
 310,  312,  312,  314,  314,  316,  316,  318,  319,  320,  321,  321,  323,  325,  325, 
 326,  327,  329,  330,  330,  332,  333,  334,  335,  337,  337,  339,  340,  341,  342, 
 343,  345,  346,  347,  348,  349,  351,  352,  353,  354,  356,  356,  359,  359,  360, 
 362,  363,  365,  366,  367,  368,  370,  371,  372,  374,  375,  376,  378,  379,  381, 
 381,  384,  384,  387,  387,  389,  390,  392,  393,  395,  396,  398,  399,  401,  402, 
 403,  405,  407,  408,  410,  411,  413,  414,  416,  418,  419,  421,  422,  424,  426, 
 427,  429,  430,  433,  434,  435,  437,  439,  441,  443,  444,  446,  447,  450,  451, 
 453,  455,  456,  459,  460,  462,  464,  466,  468,  469,  472,  473,  476,  477,  479, 
 481,  484,  485,  487,  489,  491,  493,  496,  497,  499,  502,  503,  506,  508,  510, 
 512,  514,  516,  519,  521,  523,  525,  528,  529,  533,  534,  537,  538,  542,  543, 
 547,  548,  551,  553,  556,  558,  560,  563,  566,  568,  571,  573,  575,  579,  581, 
 583,  586,  589,  591,  594,  597,  600,  602,  605,  608,  611,  613,  616,  620,  622, 
 625,  627,  631,  634,  637,  639,  643,  646,  649,  652,  655,  658,  661,  665,  668, 
 671,  674,  677,  681,  684,  688,  690,  695,  697,  701,  705,  708,  711,  715,  719, 
 722,  726,  730,  733,  737,  740,  745,  748,  752,  756,  760,  764,  767,  772,  775, 
 780,  784,  788,  792,  796,  800,  805,  808,  814,  817,  822,  826,  831,  835,  840, 
 844,  849,  853,  858,  863,  867,  873,  877,  882,  887,  891,  897,  902,  907,  912, 
 917,  922,  928,  932,  939,  943,  949,  954,  960,  966,  971,  977,  982,  988,  994, 
1000, 1006, 1012, 1017, 1024, 1030, 1036, 1043, 1048, 1056, 1061, 1068, 1074, 1081, 1088, 
1094, 1102, 1107, 1115, 1122, 0};
// Le zéro à la fin indique que c'est la fin du tableau

word volatile wCompte = 0; // Compte le nombre de répétitions d'une fréquence
word volatile wInd = 0; // Indice de compteur de répétitions d'une fréquence

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruptions doivent être déclarées "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
word volatile wT0   =    255;  // Fixe le temps entre deux transitions
byte volatile bEtat = 0;
byte volatile bUp   = 1; // Indique si la fréquence va en augmentant ou diminuant

// (256 - wT0) * bFac * 0.0625 [us] = temps entre deux générations d'interruption du timer avant une transition

// bFac = 1024 => detla_T_max = 256 * 1024 + 0.0625 [us] = 16.384 [ms] 
// bFac = 1024 => detla_T_min =       1024 + 0.0625 [us] =  0.064 [ms] 

// bT0L * bFac * 0.0625 [us] = temps entre deux transitions

// (bT0L, bFac) = 
// (  0,    8) => temps = (256 -   0) *    8 * 0.0625 = 0.000128 [s] --- ~ 3'906.25 [Hz]
// (127,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] --- ~ 7'751.94 [Hz]
// (206,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] --- ~20'000.00 [Hz]
// (127,    8) => temps = (256 - 216) *    8 * 0.0625 = 0.000020 [s] --- ~25'000.00 [Hz]

// Formules de conversions
//========================
// Selon la valeur de TCCR2B, bFac varie selon le tableau suivant :
// TCCR2B  2  3  4   5   6    7
// bFac    1  4  8  16  32  128
// dT [s]    = bT0L * 2 * 8 * bFac * 62.5 * 10^(-9)  // 62.5e-9 = 1 / 16'000'000
// Fréquence = 16'000'000 / (bT0L * 2 * 8 * bFac)
// Fréquence =  1'000'000 / (bT0L * bFac)
//
// Pour Fréquence = Freq  donnée :
// bFac       128    32   16   8    4     1
// bT0L = Tronc(1000000 / (Freq * bFac)

void setup() {
//============
// Initialise le Timer 1 pour déclencher les interruptions après un certain temps

cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109
//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)

TCCR1A = 0b00000000; // default. Bits : WGM11 = 0 et WGM10 = 0
TCCR1B = 0b00000001; // Clock soit 0.125  micro-s et WGM12 = 0

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157
//TIMSK2 = 0b00000001; // Active les les interruptions dues au Timer/Counter 2. 
                     // Une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

// Fréquence = 8'000 [kHz] / (65536 - wT0 + 9)
// wT0       = 65536 + 9 - 8'000 / Fréquence [kHz]
// Fréquence min. = 8'000'000 / 65536 = 122.07 [Hz]

wT0      =      0; // Frequ =  122.05 Hz.
//wT0    =  -7991; // Frequ =  1 kHz.
//wT0    =   -791; // Frequ = 10 kHz.
//wT0    =   -400; // Frequ = 20 kHz. Limite. La période est légèrement trop grande
//wT0    =   -391; // Frequ = 40 kHz. Avec la précision de l'oscilloscope.
//wT0    =   -200; // Frequ = 40 kHz. Limite. La période est légèrement trop grande
//wT0    =   -191; // Frequ = 40 kHz. Avec la précision de l'oscilloscope.
//wT0    =   -160; // Frequ = 50 kHz. Limite. La période est légèrement trop grande
//wT0    =   -151; // Frequ = 50 kHz. Avec la précision de l'oscilloscope.
//wT0    =   -121; // Frequ = 61.538 kHz  // Proche de la fréquence maximale.
//wT0    =    -71; // Frequ = 100 kHz  // Trop élevé, ne fonctionne pas
//wT0    =    -27; // Frequ = 1 MHz.  // Trop élevé, ne fonctionne pas

wT0 = 61547;
wInd   = 0;
wCompte = awRepete[wInd];

// La fréquence va varier de ~2 kHz à ~25 kHz
// Elle double en 10 secondes.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);

//Serial.begin(9600);  // Initialise la communication série à une haute vitesse. Cette vitesse est celle standard
//Serial.begin(115200);  // Initialise la communication série à une haute vitesse. Vitesse de communication plus élevée.
} // setup

void loop() {
//===========
// Boucle principale
// Augmentation progressive de la fréquence

// L'augmentation progressive de la fréquence se fait par à-coups.
// Cela s'entend aux hautes fréquences !!!   Est-ce vrai ???

/*
      if ( ( (vTemps_delta > 0) && (vTemps >= vTemps_next_print) ) || 
           ( (vTemps_delta < 0) && (vTemps <= vTemps_next_print) ) ) {
        vTemps_next_print += vTemps_delta * 2.0; // Affichage toutes les ... secondes.
        Serial.print(vTemps); Serial.print(" : ");
        Serial.print(vFreq); Serial.print(" ");
        Serial.print(bT0H_max_new); Serial.print(" "); Serial.print(bT0L_max_new); Serial.print(" "); 
        Serial.print(bChanged_last); 
        Serial.println(" ");
        } /*  */
} // loop

// routine d'interruption du timer 1
ISR (TIMER1_OVF_vect) {
//=====================
// TCNT1 = valeur du compteur, qui est compté de sa valeur initiale à 65535, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.

// Transition de l'état
TCNT1 += wT0; // Fréquence = 8'000'000 / ( (65536+9 - wT0))

bEtat = 1 - bEtat;
digitalWrite(13, bEtat);

if (bUp) {
  // Fréquence montante
  wCompte -= 1;
  if (wCompte == 0) {
    // Augmentation de la fréquence.
    wT0++; // Passage à la fréquence suivante.
    wInd++;
    wCompte = awRepete[wInd];
  
    if (wCompte == 0) {
      // Passe en fréquence descendante
      bUp = 0;
      wT0--;
      wInd--;
      wCompte = awRepete[wInd];
      }
    }
  }
else {
  // Fréquence descendante
  wCompte -= 1;
  if (wCompte == 0) {
    // Diminution de la fréquence.
    wT0--; // Passage à la fréquence précédente.
    wInd--;
    wCompte = awRepete[wInd];
  
    if (wInd == 0) {
      // Passe en fréquence montante
      bUp = 1;
      wT0  = 61547;
      wInd = 0;
      wCompte = awRepete[wInd];
      }
    }
  }

} // ISR
ex0450_timer_1_interrupt.ino
Voir l'exemple suivant qui est meilleur.
Le schéma associé est le même que celui de l'exemple ex0451, mais utilise un Arduino mega au lieu d'un Arduino UNO.


    - Suivant, ex0452   ;   Précédent, ex0450
ex0451_timer_1_interrupt_OCR1A.ino   TOP
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilisation du registre OCR1A pour bien contrôler les intervalles de temps entre deux interruptions.
La fréquence d'oscillation de la pin 13 va augmenter avec le temps, puis diminuer et recommencer.
La mémoire des variables est presque entièrement utilisée.
C.f. l'ex0452 suivant, qui utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.

/*
ex0451_timer_1_interrupt_OCR1A.ino
Exemple d'utilisation du timer 1 pour générer des interruptions.

Utiliser le registre OCR1A pour générer les interruptions.

L'utilisation du timer 1 va simplifier la gestion, 
car ce timer a un compteur sur 16 bits 
contrairement au timer 0 et 2 qui ont des compteurs sur 8 bits.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps, puis diminuer et recommencer.
La fréquence va varier de ~2 kHz à ~25 kHz.  Elle double en 10 secondes.

Suite de ex0450_timer_1_interrupt.ino.
Autre approche, à chaque passage dans la fonction de gestion d'interruption, on change l'état de la pin 13
C'est une meilleure approche, qui utilise le registre OCR1A.
==========================================================*/

// Nombre de fois qu'il faut répéter la même fréquence
// c.f. https://www.carnetdumaker.net/articles/reduire-lempreinte-memoire-dun-programme-arduino-avec-progmem/#la-solution-progmem
//const word PROGMEM awRepete[] = {
word volatile awRepete[] = {
  14,   14,   14,   15,   14,   15,   15,   14,   15,   15,   14,   15,   15,   15,   14, 
  15,   15,   15,   15,   15,   15,   15,   15,   15,   15,   16,   15,   15,   15,   16, 
  15,   15,   16,   15,   16,   15,   16,   15,   16,   15,   16,   16,   15,   16,   16, 
  16,   15,   16,   16,   16,   16,   16,   16,   16,   16,   17,   16,   16,   16,   16, 
  17,   16,   17,   16,   16,   17,   16,   17,   17,   16,   17,   17,   16,   17,   17, 
  17,   17,   17,   17,   17,   17,   17,   17,   17,   17,   17,   18,   17,   17,   18, 
  17,   17,   18,   18,   17,   18,   17,   18,   18,   17,   18,   18,   18,   18,   18, 
  18,   18,   18,   18,   18,   18,   19,   18,   18,   19,   18,   19,   18,   19,   18, 
  19,   18,   19,   19,   19,   19,   18,   19,   19,   19,   19,   19,   20,   19,   19, 
  19,   20,   19,   19,   20,   19,   20,   19,   20,   20,   19,   20,   20,   20,   20, 
  20,   20,   20,   20,   20,   20,   20,   21,   20,   20,   21,   20,   21,   21,   20, 
  21,   21,   20,   21,   21,   21,   21,   21,   21,   21,   21,   22,   21,   21,   22, 
  21,   22,   21,   22,   21,   22,   22,   22,   21,   22,   22,   22,   23,   22,   22, 
  22,   22,   23,   22,   23,   22,   23,   22,   23,   23,   23,   23,   22,   23,   23, 
  24,   23,   23,   23,   24,   23,   23,   24,   24,   23,   24,   24,   23,   24,   24, 
  24,   24,   24,   25,   24,   24,   25,   24,   25,   24,   25,   24,   25,   25,   25, 
  25,   25,   25,   25,   25,   26,   25,   25,   26,   26,   25,   26,   26,   25,   26, 
  26,   26,   26,   27,   26,   26,   27,   26,   27,   26,   27,   27,   27,   26,   27, 
  28,   27,   27,   27,   27,   28,   27,   28,   28,   27,   28,   28,   28,   28,   28, 
  28,   29,   28,   28,   29,   29,   28,   29,   29,   29,   29,   29,   29,   29,   30, 
  29,   30,   29,   30,   30,   29,   30,   30,   30,   31,   30,   30,   31,   30,   31, 
  31,   30,   31,   31,   31,   32,   31,   31,   32,   31,   32,   32,   31,   32,   32, 
  33,   32,   32,   32,   33,   33,   32,   33,   33,   33,   33,   33,   34,   33,   33, 
  34,   34,   34,   33,   34,   35,   34,   34,   35,   34,   35,   35,   34,   35,   35, 
  36,   35,   35,   36,   36,   35,   36,   36,   36,   36,   37,   36,   37,   36,   37, 
  37,   37,   37,   37,   38,   37,   38,   38,   38,   38,   38,   38,   38,   39,   38, 
  39,   39,   39,   39,   39,   40,   39,   40,   40,   40,   40,   40,   40,   41,   40, 
  41,   41,   41,   41,   41,   42,   41,   42,   42,   42,   42,   42,   42,   43,   43, 
  42,   43,   44,   43,   43,   44,   44,   44,   44,   44,   44,   45,   44,   45,   45, 
  45,   46,   45,   46,   46,   46,   46,   46,   46,   47,   47,   47,   47,   47,   48, 
  47,   48,   48,   48,   49,   48,   49,   49,   49,   49,   50,   49,   50,   50,   50, 
  50,   51,   51,   51,   51,   51,   52,   51,   52,   52,   53,   52,   53,   53,   53, 
  53,   54,   53,   54,   54,   55,   54,   55,   55,   55,   56,   55,   56,   56,   56, 
  57,   57,   57,   57,   57,   58,   58,   58,   58,   59,   59,   59,   59,   60,   59, 
  60,   61,   60,   61,   61,   61,   62,   62,   62,   62,   62,   63,   63,   64,   63, 
  64,   64,   65,   64,   65,   66,   65,   66,   66,   66,   67,   67,   67,   68,   67, 
  69,   68,   69,   69,   69,   70,   69,   71,   70,   71,   71,   72,   72,   72,   72, 
  73,   73,   73,   74,   74,   75,   74,   76,   75,   76,   76,   76,   77,   78,   77, 
  78,   78,   79,   79,   79,   80,   80,   81,   81,   81,   82,   82,   82,   83,   84, 
  83,   84,   85,   85,   85,   86,   86,   86,   87,   88,   88,   88,   89,   89,   89, 
  90,   91,   91,   91,   92,   93,   92,   94,   93,   95,   94,   96,   95,   96,   97, 
  97,   98,   98,   99,   99,  100,  100,  101,  102,  102,  102,  103,  104,  104,  104, 
 106,  105,  107,  107,  107,  108,  109,  109,  110,  111,  111,  112,  112,  113,  114, 
 114,  115,  116,  116,  117,  118,  118,  119,  120,  120,  121,  122,  122,  123,  124, 
 125,  125,  126,  127,  128,  128,  129,  130,  131,  131,  133,  133,  134,  134,  136, 
 136,  137,  138,  139,  140,  141,  141,  143,  143,  144,  146,  146,  147,  148,  149, 
 149,  151,  152,  153,  154,  155,  155,  157,  158,  159,  160,  161,  163,  163,  164, 
 166,  166,  168,  169,  170,  171,  173,  173,  175,  176,  178,  178,  180,  181,  182, 
 184,  185,  187,  187,  189,  191,  191,  194,  194,  196,  198,  199,  200,  202,  204, 
 205,  207,  208,  210,  211,  213,  215,  216,  218,  220,  221,  223,  225,  227,  228, 
 231,  232,  234,  236,  238,  240,  242,  244,  245,  248,  250,  252,  254, 
                                                                               257,  258, 
 261,  263,  265,  267,  270,  272,  275,  277,  279,  282,  284,  287,  290,  292,  294, 
 298,  300,  303,  305,  309,  311,  315,  317,  320,  323,  327,  329,  333,  336,  339, 
 342,  346,  349,  353,  356,  360,  364,  367,  370,  375,  378,  383,  386,  390,  395, 
 398,  403,  407,  412,  416,  420,  425,  430,  434,  439,  444,  449,  454,  460,  464, 
 470,  475,  480,  487,  492,  497,  504,  510,  515,  522,  529,  534,  542,  548,  555, 
 561,  569,  576,  584,  591,  598,  607,  614,  622,  631,  639,  648,  656,  666,  674, 
 684,  693,  703,  713,  723,  734,  744,  755,  766,  777,  788,  801,  812,  825,  838, 
 850,  864,  877,  891,  905,  920,  934,  950,  965,  981,  998, 1015, 1031, 1050, 1067, 
1087, 1105, 1125, 1145, 1165, 1187, 1209, 1231, 1255, 1278, 1302, 1327, 1354, 1380, 1407, 
1435, 1465, 1495, 1525, 1557, 1591, 1624, 1659, 1695, 1733, 1771, 1811, 1853, 1895, 1939, 
1985, 2033, 2082, 2132, 2186, 2240, 2297, 2356, 2418, 2481, 2548, 2617, 2689, 2764, 2842, 
2923, 3009, 3097, 3190, 3287, 3389, 3495, 3606, 3723, 3846, 3974, 4110, 4252, 4402, 4560, 
4726, 4903, 5088, 5285, 5493, 5713, 5949, 6197, 6462, 6745, 7047, 7368, 7713, 8083, 8480,
   0 };
// Le zéro à la fin indique que c'est la fin du tableau

word volatile wCompte = 0; // Compte le nombre de répétitions d'une fréquence
word volatile wInd = 0; // Indice de compteur de répétitions d'une fréquence

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
word volatile wT0   =    255;  // Fixe le temps entre deux transitions
byte volatile bEtat = 0;
byte volatile bUp   = 1; // Indique si la fréquence va en augmentant ou diminuant

// (256 - wT0) * bFac * 0.0625 [us] = temps entre deux générations d'interruption du timer avant une transition

// bFac = 1024 => detla_T_max = 256 * 1024 + 0.0625 [us] = 16.384 [ms] 
// bFac = 1024 => detla_T_min =       1024 + 0.0625 [us] =  0.064 [ms] 

// bT0L * bFac * 0.0625 [us] = temps entre deux transitions

// (bT0L, bFac) = 
// (  0,    8) => temps = (256 -   0) *    8 * 0.0625 = 0.000128 [s] --- ~ 3'906.25 [Hz]
// (127,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] --- ~ 7'751.94 [Hz]
// (206,    8) => temps = (256 - 206) *    8 * 0.0625 = 0.000025 [s] --- ~20'000.00 [Hz]
// (127,    8) => temps = (256 - 216) *    8 * 0.0625 = 0.000020 [s] --- ~25'000.00 [Hz]

// Formules de conversions
//========================
// Selon la valeur de TCCR2B, bFac varie selon le tableau suivant :
// TCCR2B  2  3  4   5   6    7
// bFac    1  4  8  16  32  128
// dT [s]    = bT0L * 2 * 8 * bFac * 62.5 * 10^(-9)  // 62.5e-9 = 1 / 16'000'000
// Fréquence = 16'000'000 / (bT0L * 2 * 8 * bFac)
// Fréquence =  1'000'000 / (bT0L * bFac)
//
// Pour Fréquence = Freq  donnée :
// bFac       128    32   16   8    4     1
// bT0L = Tronc(1000000 / (Freq * bFac)

void setup() {
//============
// Initialise le Timer 2 pour déclencher les interruptions après un certain temps

cli(); // Désactive l'interruption globale

TCCR1A = 0b11000000; // default. Bits : (WGM11 = 0 ; WGM10 = 0)

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109
//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Une interruption est générée lorsque le compteur TCNT1 égale OCR1A.

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157
//TIMSK2 = 0b00000001; // Active les les interruptions dues au Timer/Counter 2. 
                     // Une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

//TCCR1B = 0b00001001; // Clock soit fact * 0.125  micro-s et (WGM13 = 0 ; WGM12 = 1)  fact =    1
TCCR1B = 0b00001010; // Clock soit fact * 0.125  micro-s et (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // Clock soit fact * 0.125  micro-s et (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // Clock soit fact * 0.125  micro-s et (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // Clock soit fact * 0.125  micro-s et (WGM13 = 0 ; WGM12 = 1)  fact = 1024

// Fréquence = 8'000 [kHz] / (wT0 * fact)
// Période   = wT0 * fact * 0.125 [us]
// wT0       = 8'000 / (fact * Fréquence [kHz])
// wT0       = Période / (fact * 0.125 [us])
// Une fréquence supérieure à ~60 [kHz] est non atteignable.

wT0     = 4000;
wInd    = 0;
wCompte = 1;
OCR1A   = wT0-1; // Limite TOP du compteur TCNT1 avant son retour à 0.

// La fréquence va varier de ~2 kHz à ~25 kHz
// Elle double en 10 secondes.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);

//Serial.begin(9600);  // Initialise la communication série à une haute vitesse. Cette vitesse est celle standard
//Serial.begin(115200);  // Initialise la communication série à une haute vitesse. Vitesse de communication plus élevée.
} // setup

void loop() {
//===========
// Boucle principale

/*
      if ( ( (vTemps_delta > 0) && (vTemps >= vTemps_next_print) ) || 
           ( (vTemps_delta < 0) && (vTemps <= vTemps_next_print) ) ) {
        vTemps_next_print += vTemps_delta * 2.0; // Affichage toutes les ... secondes.
        Serial.print(vTemps); Serial.print(" : ");
        Serial.print(vFreq); Serial.print(" ");
        Serial.print(bT0H_max_new); Serial.print(" "); Serial.print(bT0L_max_new); Serial.print(" "); 
        Serial.print(bChanged_last); 
        Serial.println(" ");
        } /*  */
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé.
ISR (TIMER1_COMPA_vect) {
//=======================
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0.
// Il est délicat d'utiliser la valeur du registre  TCNT1,  car elle augmente rapidement sans arrêt.
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.

// Transition de l'état

bEtat = 1 - bEtat;
digitalWrite(13, bEtat);

wCompte -= 1;

if (bUp) {
  // Fréquence montante
  if (wCompte == 0) {
    // Augmentation de la fréquence.
    OCR1A--; // Passage à la fréquence suivante.
    
    if      (OCR1A > 3000) wCompte =  1;
    else if (OCR1A > 2400) wCompte =  2;
    else if (OCR1A > 2000) wCompte =  3;
    else if (OCR1A > 1800) wCompte =  4;
    else if (OCR1A > 1600) wCompte =  5;
    else if (OCR1A > 1500) wCompte =  6;
    else if (OCR1A > 1400) wCompte =  7;
    else if (OCR1A > 1000) wCompte = 10; // Féqu < 1000 [Hz]
    else {
      wInd++;
      wCompte = awRepete[wInd];
      }
    //Serial.println(OCR1A);
  
    if (wCompte == 0) {
      // Passe en fréquence descendante
      bUp = 0;
      OCR1A++;      
      wInd--;
      wCompte = awRepete[wInd];
      }
    }
  }
else {
  // Fréquence descendante
  if (wCompte == 0) {
    // Diminution de la fréquence.
    OCR1A++; // Passage à la fréquence précédente.
    if (wInd > 0) {
      wInd--;
      wCompte = awRepete[wInd];
      }
    else if (OCR1A <= 1400) wCompte = 10;
    else if (OCR1A <= 1500) wCompte =  7;
    else if (OCR1A <= 1600) wCompte =  6;
    else if (OCR1A <= 1800) wCompte =  5;
    else if (OCR1A <= 2000) wCompte =  4;
    else if (OCR1A <= 2400) wCompte =  3;
    else if (OCR1A <= 3000) wCompte =  2;
    else wCompte = 1;
  
    if (OCR1A == wT0) {
      // Passe en fréquence montante
      bUp = 1;
      OCR1A  = wT0-1;
      wInd = 0;
      wCompte = 1;
      }
    }
  }

} // ISR
ex0405 blink
ex0451_timer_1_interrupt_OCR1A.ino

    - Suivant, ex0453   ;   Précédent, ex0451
ex0452_timer_1_interrupt_OCR1A_PROGMEM.ino   TOP
Similaire à l'ex0451, qui a le défaut d'utiliser presque toute la mémoire RAM pour stocker un tableau.
Cet exemple utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.
Cela libère la RAM de stockage des variables.
Exemple d'utilisation du timer 1 pour générer des interruptions à des fréquences variables.
Utilisation du registre OCR1A pour bien contrôler les intervalles de temps entre deux interruptions.
La fréquence d'oscillation de la pin 13 va augmenter avec le temps, puis diminuer et recommencer.

/*
ex0452_timer_1_interrupt_OCR1A_PROGMEM.ino
Exemple d'utilisation du timer 1 pour générer des interruption.

Utilisation de "PROGMEM" pour stocker des données dans la mémoire du programme

Utiliser le registre OCR1A pour générer les interruptions.

L'utilisation du timer 1 va simplifier la gestion, 
car ce timer a un compteur sur 16 bits 
contrairement au timer 0 et 2 qui ont des compteurs sur 8 bits.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.

Avec affichage dans le terminal série de la fréquence, qui augmente de 250 [Hz] à 25'000 [Hz], puis diminue.
Brancher la pin 13 sur un haut-parleur, pour entendre la fréquence générée et la limite de l'inaudible.
Cette limite est plus basse pour des personnes âgées.

Cette affichage génère des "tac" sur le haut-parleur, ce qui perturbe le son généré.

Suite de ex040x  et  ex045x
À chaque passage dans la fonction de gestion d'interruption, on change l'état de la pin 13
==========================================================*/

#include <avr/pgmspace.h>  // Pour stocker des tableaux non modifiables dans l'espace mémoire du programme
// c.f. https://www.carnetdumaker.net/articles/reduire-lempreinte-memoire-dun-programme-arduino-avec-progmem/#la-solution-progmem
// C.f. page 589 du livre : "Arduino Cookbook"
// PROGMEM  indique de stocker le tableau en mémoire du programme
// wCompte = pgm_read_word(&awRepete[wInd]);  permet de lire le contenu du tableau.

// Nombre de fois qu'il faut répéter la même fréquence
const word awRepete[]  PROGMEM = {
   4,    5,    4,    5,    4,    5,    4,    4,    5,    4,    5,    4,    5,    4,    5, 
   4,    5,    5,    4,    5,    4,    5,    4,    5,    4,    5,    4,    5,    5,    4, 
   5,    4,    5,    5,    4,    5,    5,    4,    5,    4,    5,    5,    4,    5,    5, 
   4,    5,    5,    4,    5,    5,    5,    4,    5,    5,    4,    5,    5,    5,    4, 
   5,    5,    5,    4,    5,    5,    5,    4,    5,    5,    5,    5,    5,    4,    5, 
   5,    5,    5,    5,    4,    5,    5,    5,    5,    5,    5,    5,    4,    5,    5, 
   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 
   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 
   6,    5,    5,    5,    5,    5,    5,    5,    6,    5,    5,    5,    5,    5,    6, 
   5,    5,    5,    5,    6,    5,    5,    5,    6,    5,    5,    5,    6,    5,    5, 
   5,    6,    5,    5,    6,    5,    5,    6,    5,    5,    6,    5,    5,    6,    5, 
   5,    6,    5,    6,    5,    5,    6,    5,    6,    5,    6,    5,    6,    5,    5, 
   6,    5,    6,    5,    6,    5,    6,    5,    6,    6,    5,    6,    5,    6,    5, 
   6,    6,    5,    6,    5,    6,    6,    5,    6,    6,    5,    6,    6,    5,    6, 
   6,    5,    6,    6,    5,    6,    6,    6,    5,    6,    6,    6,    5,    6,    6, 
   6,    6,    5,    6,    6,    6,    6,    6,    5,    6,    6,    6,    6,    6,    6, 
   6,    6,    6,    5,    6,    6,    6,    6,    6,    6,    6,    6,    6,    6,    6, 
   6,    6,    6,    6,    6,    7,    6,    6,    6,    6,    6,    6,    6,    6,    6, 
   7,    6,    6,    6,    6,    6,    7,    6,    6,    6,    6,    7,    6,    6,    6, 
   7,    6,    6,    7,    6,    6,    7,    6,    6,    7,    6,    6,    7,    6,    6, 
   7,    6,    7,    6,    6,    7,    6,    7,    6,    7,    6,    7,    6,    7,    6, 
   7,    6,    7,    6,    7,    6,    7,    7,    6,    7,    6,    7,    7,    6,    7, 
   7,    6,    7,    7,    6,    7,    7,    6,    7,    7,    7,    6,    7,    7,    7, 
   6,    7,    7,    7,    7,    7,    6,    7,    7,    7,    7,    7,    7,    7,    7, 
   7,    6,    7,    7,    7,    7,    7,    7,    7,    7,    7,    7,    8,    7,    7, 
   7,    7,    7,    7,    7,    7,    7,    8,    7,    7,    7,    7,    7,    8,    7, 
   7,    7,    8,    7,    7,    7,    8,    7,    7,    8,    7,    7,    8,    7,    8, 
   7,    7,    8,    7,    8,    7,    7,    8,    7,    8,    7,    8,    7,    8,    7, 
   8,    8,    7,    8,    7,    8,    8,    7,    8,    7,    8,    8,    7,    8,    8, 
   8,    7,    8,    8,    8,    7,    8,    8,    8,    8,    7,    8,    8,    8,    8, 
   8,    8,    8,    7,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8, 
   8,    9,    8,    8,    8,    8,    8,    8,    8,    9,    8,    8,    8,    8,    9, 
   8,    8,    9,    8,    8,    8,    9,    8,    8,    9,    8,    9,    8,    8,    9, 
   8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    9,    8,    9, 
   8,    9,    9,    8,    9,    9,    9,    8,    9,    9,    9,    8,    9,    9,    9, 
   9,    9,    8,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9, 
   9,    9,    9,    9,    9,    9,   10,    9,    9,    9,    9,   10,    9,    9,    9, 
  10,    9,    9,    9,   10,    9,    9,   10,    9,   10,    9,   10,    9,    9,   10, 
   9,   10,    9,   10,   10,    9,   10,    9,   10,   10,    9,   10,   10,    9,   10, 
  10,   10,    9,   10,   10,   10,   10,    9,   10,   10,   10,   10,   10,   10,   10, 
  10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   11,   10,   10,   10,   10, 
  11,   10,   10,   10,   11,   10,   10,   11,   10,   10,   11,   10,   11,   10,   11, 
  10,   11,   10,   11,   10,   11,   11,   10,   11,   10,   11,   11,   11,   10,   11, 
  11,   11,   10,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11, 
  11,   11,   11,   11,   11,   11,   11,   12,   11,   11,   11,   12,   11,   11,   12, 
  11,   11,   12,   11,   11,   12,   11,   12,   11,   12,   12,   11,   12,   11,   12, 
  12,   11,   12,   12,   11,   12,   12,   12,   12,   12,   11,   12,   12,   12,   12, 
  12,   12,   12,   12,   12,   12,   12,   13,   12,   12,   12,   12,   13,   12,   12, 
  12,   13,   12,   13,   12,   12,   13,   12,   13,   12,   13,   13,   12,   13,   12, 
  13,   13,   12,   13,   13,   13,   13,   12,   13,   13,   13,   13,   13,   13,   13, 
  13,   13,   13,   13,   13,   13,   14,   13,   13,   13,   14,   13,   13,   14,   13, 
  13,   14,   13,   14,   13,   14,   13,   14,   14,   13,   14,   14,   13,   14,   14, 
  14,   13,   14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   15,   14, 
  14,   14,   15,   14,   14,   15,   14,   14,   15,   14,   15,   14,   15,   14,   15, 
  15,   14,   15,   15,   15,   14,   15,   15,   15,   15,   15,   15,   15,   15,   15, 
  15,   15,   15,   16,   15,   15,   15,   16,   15,   15,   16,   15,   16,   15,   16, 
  15,   16,   16,   15,   16,   16,   16,   15,   16,   16,   16,   16,   16,   16,   16, 
  16,   16,   16,   17,   16,   16,   16,   17,   16,   16,   17,   16,   17,   16,   17, 
  16,   17,   17,   16,   17,   17,   17,   17,   17,   16,   17,   17,   17,   18,   17, 
  17,   17,   17,   17,   18,   17,   17,   18,   17,   18,   17,   18,   18,   17,   18, 
  18,   17,   18,   18,   18,   18,   18,   18,   18,   18,   18,   18,   18,   19,   18, 
  18,   19,   18,   18,   19,   18,   19,   19,   18,   19,   19,   19,   18,   19,   19, 
  19,   19,   19,   19,   19,   20,   19,   19,   19,   20,   19,   20,   19,   20,   19, 
  20,   20,   19,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   21, 
  20,   20,   21,   20,   21,   20,   21,   21,   20,   21,   21,   21,   21,   21,   21, 
  21,   21,   21,   21,   22,   21,   21,   22,   21,   22,   22,   21,   22,   22,   21, 
  22,   22,   22,   22,   22,   22,   23,   22,   22,   23,   22,   22,   23,   23,   22, 
  23,   23,   22,   23,   23,   23,   23,   23,   24,   23,   23,   23,   24,   23,   24, 
  23,   24,   24,   23,   24,   24,   24,   24,   24,   24,   24,   25,   24,   24,   25, 
  24,   25,   25,   24,   25,   25,   25,   25,   25,   25,   25,   25,   26,   25,   25, 
  26,   25,   26,   26,   25,   26,   26,   26,   26,   26,   27,   26,   26,   27,   26, 
  27,   26,   27,   27,   26,   27,   27,   27,   27,   28,   27,   27,   28,   27,   28, 
  27,   28,   28,   28,   28,   28,   28,   28,   29,   28,   28,   29,   28,   29,   29, 
  29,   29,   29,   29,   29,   29,   29,   30,   29,   30,   30,   29,   30,   30,   30, 
  30,   31,   30,   30,   31,   30,   31,   31,   30,   31,   31,   31,   32,   31,   31, 
  32,   31,   32,   32,   31,   32,   32,   32,   33,   32,   32,   33,   32,   33,   33, 
  33,   33,   33,   33,   33,   34,   33,   34,   34,   33,   34,   34,   34,   35,   34, 
  34,   35,   35,   34,   35,   35,   35,   35,   36,   35,   36,   35,   36,   36,   36, 
  36,   36,   37,   36,   37,   36,   37,   37,   37,   37,   37,   38,   37,   38,   38, 
  37,   38,   39,   38,   38,   39,   38,   39,   39,   39,   39,   39,   40,   39,   40, 
  40,   39,   40,   41,   40,   40,   41,   41,   41,   41,   41,   41,   41,   42,   42, 
  41,   42,   43,   42,   42,   43,   42,   43,   43,   43,   44,   43,   44,   44,   43, 
  44,   45,   44,   45,   44,   45,   45,   45,   46,   45,   46,   45,   46,   47,   46, 
  46,   47,   47,   47,   47,   47,   48,   47,   48,   48,   48,   49,   48,   49,   49, 
  49,   49,   49,   50,   50,   50,   50,   50,   51,   51,   51,   51,   51,   51,   52, 
  52,   52,   52,   53,   53,   53,   53,   53,   53,   54,   54,   54,   55,   54,   55, 
  55,   55,   55,   56,   56,   56,   56,   57,   57,   57,   57,   57,   58,   58,   58, 
  58,   59,   59,   59,   59,   59,   60,   60,   60,   61,   61,   61,   61,   62,   61, 
  62,   63,   62,   63,   63,   63,   64,   64,   64,   64,   65,   65,   65,   66,   66, 
  66,   66,   67,   67,   67,   67,   68,   68,   69,   68,   70,   69,   69,   70,   71, 
  70,   71,   71,   72,   71,   73,   72,   73,   73,   73,   74,   74,   75,   74,   75, 
  76,   76,   76,   76,   77,   77,   78,   78,   78,   79,   79,   79,   80,   80,   81, 
  81,   81,   82,   82,   82,   83,   83,   84,   84,   85,   84,   86,   85,   87,   86, 
  87,   88,   87,   89,   88,   90,   89,   90,   91,   91,   91,   92,   92,   93,   93, 
  94,   95,   94,   95,   96,   96,   97,   97,   98,   98,   99,   99,  100,  100,  101, 
 101,  102,  103,  103,  103,  105,  104,  105,  106,  107,  107,  107,  108,  109,  109, 
 110,  111,  111,  112,  112,  113,  114,  114,  115,  116,  116,  117,  118,  118,  119, 
 119,  121,  121,  122,  122,  123,  124,  125,  125,  126,  127,  128,  128,  129,  130, 
 131,  131,  132,  134,  133,  135,  136,  136,  137,  138,  139,  140,  141,  141,  143, 
 143,  144,  145,  147,  147,  148,  148,  150,  151,  152,  153,  154,  154,  156,  157, 
 158,  159,  160,  161,  162,  164,  164,  166,  166,  168,  169,  170,  171,  173,  173, 
 175,  176,  177,  179,  180,  181,  182,  184,  185,  186,  188,  189,  191,  191,  194, 
 194,  196,  198,  199,  200,  202,  204,  205,  206,  209,  209,  212,  213,  214,  217, 
 218,  219,  222,  223,  225,  227,  228,  231,  232,  234,  236,  238,  240,  242,  243, 
 246,  248,  250,  252,  254,  257,  258,  261,  263,  265,  267,  270,  272,  275,  277, 
 279,  282,  284,  287,  289,  293,  294,  298,  300,  303,  305,  309,  311,  314,  318, 
 320,  323,  327,  329,  333,  336,  339,  342,  346,  349,  353,  356,  360,  363,  367, 
 371,  375,  378,  383,  386,  390,  395,  398,  403,  407,  412,  416,  420,  425,  430, 
 434,  439,  444,  449,  454,  459,  465,  470,  475,  480,  487,  491,  498,  504,  509, 
 516,  522,  529,  534,  542,  548,  555,  561,  569,  576,  584,  591,  598,  607,  614, 
 622,  631,  639,  648,  656,  665,  675,  684,  693,  703,  713,  723,  734,  744,  754, 
 766,  777,  789,  801,  812,  825,  838,  850,  864,  877,  891,  905,  919,  935,  950, 
 965,  981,  998, 1015, 1031, 1050, 1067, 1086, 1106, 1125, 1145, 1165, 1187, 1209, 1231, 
1254, 1278, 1303, 1327, 1353, 1380, 1408, 1435, 1465, 1494, 1526, 1557, 1590, 1624, 1660, 
1695, 1733, 1771, 1811, 1853, 1895, 1939, 1985, 2033, 2081, 2133, 2186, 2240, 2297, 2356, 
2418, 2481, 2548, 2617, 2689, 2764, 2842, 2923, 3008, 3098, 3190, 3287, 3389, 3495, 3606, 
3723, 3846, 3974, 4110, 4252, 4402, 4560, 4726, 4902, 5089, 5284, 5493, 5714, 5948, 6198, 
6462, 6745, 7047, 7368, 7713, 8083, 8479,
   0 };
// Le zéro à la fin indique que c'est la fin du tableau

word volatile wCompte = 0; // Compte le nombre de répétitions d'une fréquence
word volatile wInd = 0; // Indice de compteur de répétitions d'une fréquence

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
word volatile wT0   = 4000; // Fixe le temps entre deux transitions, au départ.
byte volatile bEtat = 0;    // État de la pin 13
byte volatile bUp   = 1;    // Indique si la fréquence va en augmentant (1) ou diminuant (0)

byte bEtat0  = 0;  // Pour détecter les transitions d'état dans la boucle principale.
word wTempsL = 0;  // Temps modulo 65536  en fact * 0.0625 [us] 
word wTempsH = 0;  // Temps      / 65536  en fact * 0.0625 [us]
                   // = temps en  multiple de 32.767 [ms], pour fact == 8.
word wTmp = 0; // Temporaire


// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// bFac    1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * bFac * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * bFac)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(16'000'000 / (Freq * bFac)

// Une fréquence supérieure à ~60 [kHz] est non atteignable.

void setup() {
//============
// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A

cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109
//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157
//TIMSK2 = 0b00000001; // Active les les interruptions dues au Timer/Counter 2. 
                     // Désavticé : une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

//TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

wT0     = 4000;
wInd    = 0;
wCompte = 1;
OCR1A   = wT0-1; // Limite TOP du compteur TCNT1 avant son retour à 0.

// La fréquence va varier de 250 [Hz] à ~25 kHz
// Elle double en 10 secondes.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);

//Serial.begin(9600);  // Initialise la communication série vitesse par défaut.
Serial.begin(115200);  // Initialise la communication série à une haute vitesse.
} // setup

void loop() {
//===========
// Boucle principale. Tout se passe dans la routine de gestion d'interruption.

if (bEtat != bEtat0) {
  // Il y a eu une transistion d'état, incrémente le temps en conséquence.
  bEtat0 = bEtat;
  
    wTmp    = wTempsL;
    wTempsL = wTempsL + OCR1A+1;  // Temps en fact * 0.0625 [us] == 0.5 [us]
  
  if (wTempsL < wTmp) {
    // Une boucle de temps a été faite
    wTempsH++;
  
    if (wTempsH % 16 == 0) {
      // Affichage d'information
      // F("texte") fait en sorte que le texte soit stocké dans la page programme et non en mémoire des variables.
      Serial.print(F("Temps = ")); Serial.print(wTempsH * 0.032768); 
      Serial.print(F(" : Frequ = "));  Serial.print(1000.0 / (OCR1A+1));
      Serial.println(" ");
      }
    }
  }
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé.
ISR (TIMER1_COMPA_vect) {
//=======================
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.

// Transition de l'état

bEtat = 1 - bEtat;
digitalWrite(13, bEtat);

wCompte -= 1;

if (bUp) {
  // Fréquence montante
  if (wCompte == 0) {
    // Augmentation de la fréquence.
    OCR1A--; // Passage à la fréquence suivante.
    
    if      (OCR1A > 3000) wCompte =  1;
    else if (OCR1A > 2400) wCompte =  2;
    else if (OCR1A > 2000) wCompte =  3;
    else if (OCR1A > 1800) wCompte =  4;
    else { // OCR1A <= 1800   fréque ~= 555 [Hz]
      wCompte = 2*pgm_read_word(&awRepete[wInd]);
      wInd++;
      }
    //Serial.println(OCR1A);
  
    if (wCompte == 0) {
      // Passe en fréquence descendante
      bUp = 0;
      OCR1A++;      
      wInd--;
      wCompte = pgm_read_word(&awRepete[wInd]);
      }
    }
  }
else {
  // Fréquence descendante
  if (wCompte == 0) {
    // Diminution de la fréquence.
    OCR1A++; // Passage à la fréquence précédente.
    if (wInd > 0) {
      wInd--;
      wCompte = 2*pgm_read_word(&awRepete[wInd]);
      }
    else if (OCR1A <= 1800) wCompte =  5;
    else if (OCR1A <= 2000) wCompte =  4;
    else if (OCR1A <= 2400) wCompte =  3;
    else if (OCR1A <= 3000) wCompte =  2;
    else wCompte = 1;
  
    if (OCR1A == wT0) {
      // Passe en fréquence montante
      bUp = 1;
      OCR1A  = wT0-1;
      wInd = 0;
      wCompte = 1;
      }
    }
  }

} // ISR
ex0452_timer_1_interrupt_OCR1A_PROGMEM.ino
Le schéma associé est le même que celui de l'exemple ex0451.

    - Suivant, ex0460   ;   Précédent, ex0452
ex0453_timer_1_interrupt_OCR1A_PROGMEM_LED.ino   TOP
Similaire à l'ex0452.
Utilise l'instruction PROGMEM pour stocker un tableau de valeurs constantes dans la mémoire du programme.
Utilisation de l'afficheur LCD1602 pour indiquer la fréquence générée.

/*
ex0453_timer_1_interrupt_OCR1A_PROGMEM_LED.ino
Exemple d'utilisation du timer 1 pour générer des interruption.

Utilisation de "PROGMEM" pour stocker des données dans la mémoire du programme

Utiliser le registre OCR1A pour générer les interruptions.

L'utilisation du timer 1 va simplifier la gestion, 
car ce timer a un compteur sur 16 bits 
contrairement au timer 0 et 2 qui ont des compteurs sur 8 bits.

La fréquence de clignotement de la LED pin 13 va augmenter avec le temps.

Avec affichage dans l'afficheur LED de la fréquence, qui augmente de 250 [Hz] à 25'000 [Hz], puis diminue.
Brancher la pin 13 sur un haut-parleur, pour entendre la fréquence générée et la limite de l'inaudible.
Cette limite est plus basse pour des personnes âgées.

Cette affichage génère des légers "tac" sur le haut-parleur, ce qui perturbe un peu le son généré.

Suite de ex0452
À chaque passage dans la fonction de gestion d'interruption, on change l'état de la pin 13
==========================================================*/

#include <avr/pgmspace.h>  // Pour stocker des tableaux non modifiables dans l'espace mémoire du programme
// c.f. https://www.carnetdumaker.net/articles/reduire-lempreinte-memoire-dun-programme-arduino-avec-progmem/#la-solution-progmem
// C.f. page 589 du livre : "Arduino Cookbook"
// PROGMEM  indique de stocker le tableau en mémoire du programme
// wCompte = pgm_read_word(&awRepete[wInd]);  permet de lire le contenu du tableau.

// Nombre de fois qu'il faut répéter la même fréquence
const word awRepete[]  PROGMEM = {
   4,    5,    4,    5,    4,    5,    4,    4,    5,    4,    5,    4,    5,    4,    5, 
   4,    5,    5,    4,    5,    4,    5,    4,    5,    4,    5,    4,    5,    5,    4, 
   5,    4,    5,    5,    4,    5,    5,    4,    5,    4,    5,    5,    4,    5,    5, 
   4,    5,    5,    4,    5,    5,    5,    4,    5,    5,    4,    5,    5,    5,    4, 
   5,    5,    5,    4,    5,    5,    5,    4,    5,    5,    5,    5,    5,    4,    5, 
   5,    5,    5,    5,    4,    5,    5,    5,    5,    5,    5,    5,    4,    5,    5, 
   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 
   5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5,    5, 
   6,    5,    5,    5,    5,    5,    5,    5,    6,    5,    5,    5,    5,    5,    6, 
   5,    5,    5,    5,    6,    5,    5,    5,    6,    5,    5,    5,    6,    5,    5, 
   5,    6,    5,    5,    6,    5,    5,    6,    5,    5,    6,    5,    5,    6,    5, 
   5,    6,    5,    6,    5,    5,    6,    5,    6,    5,    6,    5,    6,    5,    5, 
   6,    5,    6,    5,    6,    5,    6,    5,    6,    6,    5,    6,    5,    6,    5, 
   6,    6,    5,    6,    5,    6,    6,    5,    6,    6,    5,    6,    6,    5,    6, 
   6,    5,    6,    6,    5,    6,    6,    6,    5,    6,    6,    6,    5,    6,    6, 
   6,    6,    5,    6,    6,    6,    6,    6,    5,    6,    6,    6,    6,    6,    6, 
   6,    6,    6,    5,    6,    6,    6,    6,    6,    6,    6,    6,    6,    6,    6, 
   6,    6,    6,    6,    6,    7,    6,    6,    6,    6,    6,    6,    6,    6,    6, 
   7,    6,    6,    6,    6,    6,    7,    6,    6,    6,    6,    7,    6,    6,    6, 
   7,    6,    6,    7,    6,    6,    7,    6,    6,    7,    6,    6,    7,    6,    6, 
   7,    6,    7,    6,    6,    7,    6,    7,    6,    7,    6,    7,    6,    7,    6, 
   7,    6,    7,    6,    7,    6,    7,    7,    6,    7,    6,    7,    7,    6,    7, 
   7,    6,    7,    7,    6,    7,    7,    6,    7,    7,    7,    6,    7,    7,    7, 
   6,    7,    7,    7,    7,    7,    6,    7,    7,    7,    7,    7,    7,    7,    7, 
   7,    6,    7,    7,    7,    7,    7,    7,    7,    7,    7,    7,    8,    7,    7, 
   7,    7,    7,    7,    7,    7,    7,    8,    7,    7,    7,    7,    7,    8,    7, 
   7,    7,    8,    7,    7,    7,    8,    7,    7,    8,    7,    7,    8,    7,    8, 
   7,    7,    8,    7,    8,    7,    7,    8,    7,    8,    7,    8,    7,    8,    7, 
   8,    8,    7,    8,    7,    8,    8,    7,    8,    7,    8,    8,    7,    8,    8, 
   8,    7,    8,    8,    8,    7,    8,    8,    8,    8,    7,    8,    8,    8,    8, 
   8,    8,    8,    7,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8,    8, 
   8,    9,    8,    8,    8,    8,    8,    8,    8,    9,    8,    8,    8,    8,    9, 
   8,    8,    9,    8,    8,    8,    9,    8,    8,    9,    8,    9,    8,    8,    9, 
   8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    8,    9,    9,    8,    9, 
   8,    9,    9,    8,    9,    9,    9,    8,    9,    9,    9,    8,    9,    9,    9, 
   9,    9,    8,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9,    9, 
   9,    9,    9,    9,    9,    9,   10,    9,    9,    9,    9,   10,    9,    9,    9, 
  10,    9,    9,    9,   10,    9,    9,   10,    9,   10,    9,   10,    9,    9,   10, 
   9,   10,    9,   10,   10,    9,   10,    9,   10,   10,    9,   10,   10,    9,   10, 
  10,   10,    9,   10,   10,   10,   10,    9,   10,   10,   10,   10,   10,   10,   10, 
  10,   10,   10,   10,   10,   10,   10,   10,   10,   10,   11,   10,   10,   10,   10, 
  11,   10,   10,   10,   11,   10,   10,   11,   10,   10,   11,   10,   11,   10,   11, 
  10,   11,   10,   11,   10,   11,   11,   10,   11,   10,   11,   11,   11,   10,   11, 
  11,   11,   10,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11,   11, 
  11,   11,   11,   11,   11,   11,   11,   12,   11,   11,   11,   12,   11,   11,   12, 
  11,   11,   12,   11,   11,   12,   11,   12,   11,   12,   12,   11,   12,   11,   12, 
  12,   11,   12,   12,   11,   12,   12,   12,   12,   12,   11,   12,   12,   12,   12, 
  12,   12,   12,   12,   12,   12,   12,   13,   12,   12,   12,   12,   13,   12,   12, 
  12,   13,   12,   13,   12,   12,   13,   12,   13,   12,   13,   13,   12,   13,   12, 
  13,   13,   12,   13,   13,   13,   13,   12,   13,   13,   13,   13,   13,   13,   13, 
  13,   13,   13,   13,   13,   13,   14,   13,   13,   13,   14,   13,   13,   14,   13, 
  13,   14,   13,   14,   13,   14,   13,   14,   14,   13,   14,   14,   13,   14,   14, 
  14,   13,   14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   14,   15,   14, 
  14,   14,   15,   14,   14,   15,   14,   14,   15,   14,   15,   14,   15,   14,   15, 
  15,   14,   15,   15,   15,   14,   15,   15,   15,   15,   15,   15,   15,   15,   15, 
  15,   15,   15,   16,   15,   15,   15,   16,   15,   15,   16,   15,   16,   15,   16, 
  15,   16,   16,   15,   16,   16,   16,   15,   16,   16,   16,   16,   16,   16,   16, 
  16,   16,   16,   17,   16,   16,   16,   17,   16,   16,   17,   16,   17,   16,   17, 
  16,   17,   17,   16,   17,   17,   17,   17,   17,   16,   17,   17,   17,   18,   17, 
  17,   17,   17,   17,   18,   17,   17,   18,   17,   18,   17,   18,   18,   17,   18, 
  18,   17,   18,   18,   18,   18,   18,   18,   18,   18,   18,   18,   18,   19,   18, 
  18,   19,   18,   18,   19,   18,   19,   19,   18,   19,   19,   19,   18,   19,   19, 
  19,   19,   19,   19,   19,   20,   19,   19,   19,   20,   19,   20,   19,   20,   19, 
  20,   20,   19,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   20,   21, 
  20,   20,   21,   20,   21,   20,   21,   21,   20,   21,   21,   21,   21,   21,   21, 
  21,   21,   21,   21,   22,   21,   21,   22,   21,   22,   22,   21,   22,   22,   21, 
  22,   22,   22,   22,   22,   22,   23,   22,   22,   23,   22,   22,   23,   23,   22, 
  23,   23,   22,   23,   23,   23,   23,   23,   24,   23,   23,   23,   24,   23,   24, 
  23,   24,   24,   23,   24,   24,   24,   24,   24,   24,   24,   25,   24,   24,   25, 
  24,   25,   25,   24,   25,   25,   25,   25,   25,   25,   25,   25,   26,   25,   25, 
  26,   25,   26,   26,   25,   26,   26,   26,   26,   26,   27,   26,   26,   27,   26, 
  27,   26,   27,   27,   26,   27,   27,   27,   27,   28,   27,   27,   28,   27,   28, 
  27,   28,   28,   28,   28,   28,   28,   28,   29,   28,   28,   29,   28,   29,   29, 
  29,   29,   29,   29,   29,   29,   29,   30,   29,   30,   30,   29,   30,   30,   30, 
  30,   31,   30,   30,   31,   30,   31,   31,   30,   31,   31,   31,   32,   31,   31, 
  32,   31,   32,   32,   31,   32,   32,   32,   33,   32,   32,   33,   32,   33,   33, 
  33,   33,   33,   33,   33,   34,   33,   34,   34,   33,   34,   34,   34,   35,   34, 
  34,   35,   35,   34,   35,   35,   35,   35,   36,   35,   36,   35,   36,   36,   36, 
  36,   36,   37,   36,   37,   36,   37,   37,   37,   37,   37,   38,   37,   38,   38, 
  37,   38,   39,   38,   38,   39,   38,   39,   39,   39,   39,   39,   40,   39,   40, 
  40,   39,   40,   41,   40,   40,   41,   41,   41,   41,   41,   41,   41,   42,   42, 
  41,   42,   43,   42,   42,   43,   42,   43,   43,   43,   44,   43,   44,   44,   43, 
  44,   45,   44,   45,   44,   45,   45,   45,   46,   45,   46,   45,   46,   47,   46, 
  46,   47,   47,   47,   47,   47,   48,   47,   48,   48,   48,   49,   48,   49,   49, 
  49,   49,   49,   50,   50,   50,   50,   50,   51,   51,   51,   51,   51,   51,   52, 
  52,   52,   52,   53,   53,   53,   53,   53,   53,   54,   54,   54,   55,   54,   55, 
  55,   55,   55,   56,   56,   56,   56,   57,   57,   57,   57,   57,   58,   58,   58, 
  58,   59,   59,   59,   59,   59,   60,   60,   60,   61,   61,   61,   61,   62,   61, 
  62,   63,   62,   63,   63,   63,   64,   64,   64,   64,   65,   65,   65,   66,   66, 
  66,   66,   67,   67,   67,   67,   68,   68,   69,   68,   70,   69,   69,   70,   71, 
  70,   71,   71,   72,   71,   73,   72,   73,   73,   73,   74,   74,   75,   74,   75, 
  76,   76,   76,   76,   77,   77,   78,   78,   78,   79,   79,   79,   80,   80,   81, 
  81,   81,   82,   82,   82,   83,   83,   84,   84,   85,   84,   86,   85,   87,   86, 
  87,   88,   87,   89,   88,   90,   89,   90,   91,   91,   91,   92,   92,   93,   93, 
  94,   95,   94,   95,   96,   96,   97,   97,   98,   98,   99,   99,  100,  100,  101, 
 101,  102,  103,  103,  103,  105,  104,  105,  106,  107,  107,  107,  108,  109,  109, 
 110,  111,  111,  112,  112,  113,  114,  114,  115,  116,  116,  117,  118,  118,  119, 
 119,  121,  121,  122,  122,  123,  124,  125,  125,  126,  127,  128,  128,  129,  130, 
 131,  131,  132,  134,  133,  135,  136,  136,  137,  138,  139,  140,  141,  141,  143, 
 143,  144,  145,  147,  147,  148,  148,  150,  151,  152,  153,  154,  154,  156,  157, 
 158,  159,  160,  161,  162,  164,  164,  166,  166,  168,  169,  170,  171,  173,  173, 
 175,  176,  177,  179,  180,  181,  182,  184,  185,  186,  188,  189,  191,  191,  194, 
 194,  196,  198,  199,  200,  202,  204,  205,  206,  209,  209,  212,  213,  214,  217, 
 218,  219,  222,  223,  225,  227,  228,  231,  232,  234,  236,  238,  240,  242,  243, 
 246,  248,  250,  252,  254,  257,  258,  261,  263,  265,  267,  270,  272,  275,  277, 
 279,  282,  284,  287,  289,  293,  294,  298,  300,  303,  305,  309,  311,  314,  318, 
 320,  323,  327,  329,  333,  336,  339,  342,  346,  349,  353,  356,  360,  363,  367, 
 371,  375,  378,  383,  386,  390,  395,  398,  403,  407,  412,  416,  420,  425,  430, 
 434,  439,  444,  449,  454,  459,  465,  470,  475,  480,  487,  491,  498,  504,  509, 
 516,  522,  529,  534,  542,  548,  555,  561,  569,  576,  584,  591,  598,  607,  614, 
 622,  631,  639,  648,  656,  665,  675,  684,  693,  703,  713,  723,  734,  744,  754, 
 766,  777,  789,  801,  812,  825,  838,  850,  864,  877,  891,  905,  919,  935,  950, 
 965,  981,  998, 1015, 1031, 1050, 1067, 1086, 1106, 1125, 1145, 1165, 1187, 1209, 1231, 
1254, 1278, 1303, 1327, 1353, 1380, 1408, 1435, 1465, 1494, 1526, 1557, 1590, 1624, 1660, 
1695, 1733, 1771, 1811, 1853, 1895, 1939, 1985, 2033, 2081, 2133, 2186, 2240, 2297, 2356, 
2418, 2481, 2548, 2617, 2689, 2764, 2842, 2923, 3008, 3098, 3190, 3287, 3389, 3495, 3606, 
3723, 3846, 3974, 4110, 4252, 4402, 4560, 4726, 4902, 5089, 5284, 5493, 5714, 5948, 6198, 
6462, 6745, 7047, 7368, 7713, 8083, 8479,
   0 };
// Le zéro à la fin indique que c'est la fin du tableau

word volatile wCompte = 0; // Compte le nombre de répétitions d'une fréquence
word volatile wInd = 0; // Indice de compteur de répétitions d'une fréquence

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
word volatile wT0   = 4000; // Fixe le temps entre deux transitions, au départ.
byte volatile bEtat = 0;    // État de la pin 13
byte volatile bUp   = 1;    // Indique si la fréquence va en augmentant (1) ou diminuant (0)

byte bEtat0  = 0;  // Pour détecter les transitions d'état dans la boucle principale.
word wTempsL = 0;  // Temps modulo 65536  en fact * 0.0625 [us] 
word wTempsH = 0;  // Temps      / 65536  en fact * 0.0625 [us]
                   // = temps en  multiple de 32,767 [ms], pour fact == 8.
word wTmp = 0; // Temporaire

// Inclus la librairie qui gère l'affichage
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

char acStr[20];   // Un "array" de caractères, pour conversion de nombre en un String.
String strS = ""; // Une chaine de caractères.
float vTmp = 0.0; // Temporaire

// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// bFac    1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * bFac * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * bFac)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(16'000'000 / (Freq * bFac)

// Une fréquence supérieure à ~60 [kHz] est non atteignable.

void setup() {
//============
// set up the LCD's number of columns and rows: 
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.setCursor(0, 0);
lcd.print("Timer freq. gen.");
lcd.setCursor(0, 1);
lcd.print("ex0453 20200611 ");
delay(1200);
lcd.setCursor(0, 0);
lcd.print("                "); // efface le texte
lcd.setCursor(0, 1);
lcd.print("                ");

// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A

cli(); // Désactive l'interruption globale

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  millis()  et  micros()
// Lorsque le Timer 0 est désactivé, les 3 fonctions ci-dessus ne fonctionnent plus !
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109
//TCCR0B = 0b00000000; // Désactive le Timer/Counter0

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157
//TIMSK2 = 0b00000001; // Active les les interruptions dues au Timer/Counter 2. 
                     // Désavticé : une interruption est générée lorsque le compteur TCNT2 passe de 255 à 0 (overflow)

//TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

wT0     = 4000;
wInd    = 0;
wCompte = 1;
OCR1A   = wT0-1; // Limite TOP du compteur TCNT1 avant son retour à 0.

// La fréquence va varier de 250 [Hz] à ~25 kHz
// Elle double en 10 secondes.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);
} // setup

void loop() {
//===========
// Boucle principale. Tout se passe dans la routine de gestion d'interruption.

if (bEtat != bEtat0) {
  // Il y a eu une transistion d'état, incrémente le temps en conséquence.
  bEtat0 = bEtat;
  
    wTmp    = wTempsL;
    wTempsL = wTempsL + OCR1A+1;  // Temps en fact * 0.0625 [us] == 0.5 [us]
  
  if (wTempsL < wTmp) {
    // Une boucle de temps a été faite
    wTempsH++;
  
    if (wTempsH % 16 == 0) {
      // Affichage d'information
      // F("texte") fait en sorte que le texte soit stocké dans la page programme et non en mémoire des variables.
      vTmp = wTempsH * 0.032768;
      sprintf(acStr, "T=%4d.%01d [ms]", (int)vTmp, (int)(vTmp*10)%10);
      lcd.setCursor(0, 0);
      lcd.print(acStr);
      
      vTmp = 1000.0 / (OCR1A+1);
      sprintf(acStr, "F=%4d.%01d [kHz]", (int)vTmp, (int)(vTmp*10)%10);
      lcd.setCursor(0, 1);
      lcd.print(acStr);
      }
    }
  }
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé.
ISR (TIMER1_COMPA_vect) {
//=======================
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.

// Transition de l'état

bEtat = 1 - bEtat;
digitalWrite(13, bEtat);

wCompte -= 1;

if (bUp) {
  // Fréquence montante
  if (wCompte == 0) {
    // Augmentation de la fréquence.
    OCR1A--; // Passage à la fréquence suivante.
    
    if      (OCR1A > 3000) wCompte =  1;
    else if (OCR1A > 2400) wCompte =  2;
    else if (OCR1A > 2000) wCompte =  3;
    else if (OCR1A > 1800) wCompte =  4;
    else { // OCR1A <= 1800   fréque ~= 555 [Hz]
      wCompte = 2*pgm_read_word(&awRepete[wInd]);
      wInd++;
      }
    //Serial.println(OCR1A);
  
    if (wCompte == 0) {
      // Passe en fréquence descendante
      bUp = 0;
      OCR1A++;      
      wInd--;
      wCompte = pgm_read_word(&awRepete[wInd]);
      }
    }
  }
else {
  // Fréquence descendante
  if (wCompte == 0) {
    // Diminution de la fréquence.
    OCR1A++; // Passage à la fréquence précédente.
    if (wInd > 0) {
      wInd--;
      wCompte = 2*pgm_read_word(&awRepete[wInd]);
      }
    else if (OCR1A <= 1800) wCompte =  5;
    else if (OCR1A <= 2000) wCompte =  4;
    else if (OCR1A <= 2400) wCompte =  3;
    else if (OCR1A <= 3000) wCompte =  2;
    else wCompte = 1;
  
    if (OCR1A == wT0) {
      // Passe en fréquence montante
      bUp = 1;
      OCR1A  = wT0-1;
      wInd = 0;
      wCompte = 1;
      }
    }
  }

} // ISR
ex0453 blink
ex0453_timer_1_interrupt_OCR1A_PROGMEM_LED.ino
Dans cet exemple, les boutons de l'afficheur ne sont pas utilisés.
Ils seront utilisés à partir de l'exemple suivant.

    - Suivant, ex0462   ;   Précédent, ex0453
ex0460_timer_1_interrupt_OCR1A_LCD.ino   TOP
Utilisation du timer 1 pour générer des fréquences sur les pins 2 et 3 de l'arduino.
Utilisation du timer 0 pour des mesures de temps.
Utilisation de l'afficheur LCD 1602 avec les 5 boutons, pour avoir un menu qui sélectionne la fréquence désirée.
Un menu permet d'augmenter la fréquence par 24e d'octave.
Utilise des instructions de bas niveau, pour accéder plus rapidement aux ports de l'Arduino et pour lire la valeur de l'ADC0, sans attendre les 100 micro-secondes de temps de conversion.

/*
ex0460_timer_1_interrupt_OCR1A_LCD.ino

Exemple d'utilisation du timer 1 pour générer des interruptions.
Utilise le registre OCR1A pour générer les interruptions.
Le timer 0 est utilisé pour une mesure du temps, sans génération d'interruptions.
Les fonctions "delay", "delayMicroseconds", "millis" et "micros" ne sont pas utilisables.
Elles sont désactivées, pour augmenter la précision de la période de génération d'interruptions.

L'affichage LCD, avec les 5 boutons de commande est utilisé.

Les pins 2 et 3 sont utilisées pour le signal, en opposition.
Avec la touche "Select", on peut activer ou désactiver le signal.
La pin 13 indique si le signal est activé (1) ou désactivé (0).

Accès beaucoup plus rapide aux changement d'état des pins qu'avec la fonction "digitalWrite".
C.f. "Arduino Cookbook" de Michael Margolis, édition O'reilly, page 628 et
"AVR_Assembler_Lecture6.pdf", page 53

Permet de générer une fréquence de 0.12 [Hz] à 120'000 [Hz].

La lecture de l'ADC 0, pour connaître le bouton pressé est fait de manière
beaucoup plus rapide qu'en utilisant la fonction analogRead.
Le temps de conversion est toujours d'environ 100 [us], 
mais aucune attente n'est faite en l'initialisation du début de conversion et le résultat de la mesure, 100 [us] plus tard.
[us] = micro-seconde.
==========================================================*/

// Inclus la bibliothèque qui gère l'affichage.
#include <LiquidCrystal.h>

// Initialise la bibliothèque avec les pins utilisés par l'afficheur LCD 2x16 muni de 5 boutons.
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Nombre de menus
#define MENU_MAX 8

#define pinFreqP  2 // pour générer une fréquence
#define pinFreqN  3 // = état opposé à celui de pintFreqP lors du fonctionnement.

char acStr[20];   // Un "array" de caractères, pour conversion de nombre en un String.
String strS = ""; // Une chaine de caractères.

int  nButtonPush = btnNONE; // Indique le bouton pressé
byte bMenu1 = 4;  // indique le menu sélectionné
byte bMenu1_Last = 0;  // indique le dernier menu sélectionné
word wTime0 = 61;  // Compteur décrémenté de 1 toutes les 0.016384 [s] ~= 1/61 [s]
                   // Utilisé pour ne pas lire l'état des boutons trop souvent.

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat = 0;        // État des pins : pinFreqP et pinFreqN
byte volatile bEtat_enable = 1; // Active ou désactive le signal.  1 = Actif
word volatile OCR1A_new = 4000-1; // Fixe le temps entre deux transitions
word volatile OCR1A_old = 4000-1; // Pour déterminer la variation de la variable OCR1A_new, qu'elle soit d'au moins +-1
float        vOCR1A_calc_new = 0.0; // Nouvelle valeur de OCR1A pour générer la fréquence désirée.

word  wFact = 8; // Facteur de division de la fréquence d'incrémentation de TCNT1. Est modifiable suivant la fréquence générée.
float vFreq = 250.0; // [Hz] Fréquence générée.
float vFreq_base = 1000000.0; // [Hz] fréquence à laquelle le compteur TCNT1 est incrémenté. = 8000000.0 / vFreq
float vFreq_delta = 0.0; // Variation de fréquence demandée

// Pour la conversion ADC
uint8_t bLow, bHigh;
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// bFac    1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * bFac * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * bFac)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(8'000'000 / (Freq * bFac)

// Une fréquence supérieure à ~120 [kHz] est non atteignable.

void setup() {
//============

// Indique le nombre de lignes et de colonnes de l'afficheur LCD 16x2.
lcd.begin(16, 2);
// Affiche un message au départ, indiquant le progr. et la date
lcd.setCursor(0, 0);
lcd.print("Timer freq. LCD.");
lcd.setCursor(0, 1);
lcd.print("ex0460 20200617 ");
delay(1200);
lcd.setCursor(0, 0);
lcd.print("                "); // efface le texte
lcd.setCursor(0, 1);
lcd.print("                ");

cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  delayMicroseconds,  millis()  et  micros(), qui sont désactivées.
// Lorsque le Timer 0 est désactivé, les 4 fonctions ci-dessus ne fonctionnent plus !
TCCR0A = 0b00000000; // default. Bits : COM0A1=0 ; COM0A0=0 ; COM0B1=0 ; COM0B0=0 ; / ; / ; WGM01=0 ; WGM00=0, donc pas de PWM lié au timer 0
TCCR0B = 0b00000101; // Clock * 1024 soit 64  micro-s et WGM02 = 0
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109

// Ne pas utiliser TCNT0, mais le bit 0 de TIFR0 pour compter le nombre de boucles du compteur liées au timer 0.

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157

// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A
// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

//TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

OCR1A_new = 4000-1;      // Fréquence de départ : Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * fact) = 1'000'000 / 4000 = 250 [Hz].   T = 4 [ms].
OCR1A     = OCR1A_new; // Limite TOP du compteur TCNT1 avant son retour à 0.

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);         // La pin 13 indique si un signal est généré ou si les sorties des pins 2 et 3 sont à 0 [V].
pinMode( pinFreqN, OUTPUT);  // controle une borne de l'haut-parleur
pinMode( pinFreqP, OUTPUT);  // controle l'autre borne de l'haut-parleur = le niveau opposé à la pinFreqN

digitalWrite( 13, bEtat_enable);
digitalWrite( pinFreqN, LOW);  // borne 1 à 0 V
digitalWrite( pinFreqP, LOW);  // borne 2 à 0 V

AfficheEtat();

// Pour la lecture de l'ADC
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0
// start the conversion
sbi(ADCSRA, ADSC);

} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
// Une lecture toutes les 1/61 seconde.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Bouton pressé selon la lecture de l'ADC.
static byte bButtonLast = btnNONE; // Dernier bouton pressé.
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

//nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
//ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0

// ADSC is cleared when the conversion finishes
// while (bit_is_set(ADCSRA, ADSC)); // Plus besoin d'attendre, car cela est fait grace à : lwTempsLastReadBtn

// Lecture de ADCL en premier. Il est mis dans un buffer et non modifié tant que ADCH n'a pas été lu.
// La lecture de ADCL en deuxième bloquerait la lecture de l'ADC.
// Il faut donc lire ADCH en deuxième.
bLow  = ADCL;
bHigh = ADCH;

// Démarre une conversion, qui sera terminée dans environ 100 [us].
// La fin de conversion peut se détecter en lisant ADSC, qui sera mis à 0 une fois la conversion terminée.
// ADSC n'est pas utilisé ici, car on laisse amplement le temps de terminer la conversion en n'en faisant que 61 par seconde.
sbi(ADCSRA, ADSC);

if (bHigh >= 3) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE;
  }
// Ici, on sait qu'un bouton a été pressé.

nAdc_key_in = word(bHigh, bLow); // Conversion en un word.

// Les lectures des boutons sont centrées autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

     if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.
  if (bButtonCount == 3) {
    bButtonCount = 0; 
    wTime0 = 20; // Pour indiquer qu'un bouton a été traité, il faudra attendre avant de traiter à nouveau un bouton.
    return bButton;   // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 0;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons


void AfficheEtat() {
//==================
// Affichage de la fréquence ou de la variation de fréquence, suivant le menu

if (bMenu1 <= MENU_MAX) {
  // Affiche Fréquence du signal 1
  if (bMenu1 != bMenu1_Last) {
    lcd.setCursor(0, 0);
    if      (bMenu1 == 1) lcd.print("Frequ +-0.001 Hz");
    else if (bMenu1 == 2) lcd.print("Frequ +- 0.01 Hz");
    else if (bMenu1 == 3) lcd.print("Frequ +- 0.10 Hz");
    else if (bMenu1 == 4) lcd.print("Frequ +- 1 Hz   ");
    else if (bMenu1 == 5) lcd.print("Frequ +- 10 Hz  ");
    else if (bMenu1 == 6) lcd.print("Frequ +- 100 Hz ");
    else if (bMenu1 == 7) lcd.print("Frequ +- 1000 Hz");
    else if (bMenu1 == 8) lcd.print("+- octave / 24  "); // Ainsi, 24 changements doublent ou divisent la fréquence (change d'une octave).
    }
  
  dtostrf(vFreq, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
  strS = acStr;
  strS = "Frequ.= " + strS;
  lcd.setCursor(0, 1); // Affichage en début de 2e ligne de l'afficheur LCD.
  lcd.print(strS);
  }

} // AfficheEtat

void MenuTreat() {
//================
// Un bouton UP, DOWN ou SELECT a été pressé, traite l'action adéquat.

vFreq_delta = 0.0;

if (bMenu1 == 1) {
  // Changement de fréquence du signal 1 de +-1 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.001;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.001;
  }

if (bMenu1 == 2) {
  // Changement de fréquence du signal 1 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.01;
  }

if (bMenu1 == 3) {
  // Changement de fréquence du signal 1 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.1;
  }

if (bMenu1 == 4) {
  // Changement de fréquence du signal 1 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -1.0;
  }

if (bMenu1 == 5) {
  // Changement de fréquence du signal 1 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -10.0;
  }

if (bMenu1 == 6) {
  // Changement de fréquence du signal 1 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -100.0;
  }

if (bMenu1 == 7) {
  // Changement de fréquence du signal 1 de +-1000 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  1000.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -1000.0;
  }

if (bMenu1 == 8) {
  // Changement de fréquence du signal pour que 24 changement passe à l'octave suivante.
//  if (nButtonPush == btnUP)   vFreq_delta =  0.059463094 * vFreq; // pour que 12 changement passe à l'octave suivante. racine_12e(2) = 1.059463094
//  if (nButtonPush == btnDOWN) vFreq_delta = -0.056125687 * vFreq; // pour que 12 changement passe à l'octave précédente. 1 - 1/racine_12e(2)
  if (nButtonPush == btnUP)   vFreq_delta =  0.029302237 * vFreq; // pour que 24 changement passe à l'octave suivante. racine_24e(2) = 1.029302237
  if (nButtonPush == btnDOWN) vFreq_delta = -0.028468059 * vFreq; // pour que 24 changement passe à l'octave précédente. 1 - 1/racine_24e(2)
  }

if (vFreq_delta != 0) {
  // Il y a une demande de changement de fréquence
  vFreq = vFreq + vFreq_delta;
  
  OCR1A_old = OCR1A_new;
  vOCR1A_calc_new = -1 + vFreq_base / vFreq;

  // Teste que les capacités du compteur TCNT1 lié au timer 1 ne soient pas dépassées
  if (vOCR1A_calc_new > 65535.0) {
    // Passe à un diviseur de fréquence (wFact) plus grand, pour générer une plus basse fréquence, une plus haute période.
    OCR1A_old = 0; // Assure que : OCR1A_old != OCR1A_new
    
    if (wFact == 1024) {
      // Assure de ne pas descendre en-dessous d'une fréquence trop basse, non atteignable par le Timer 1 de l'Arduino.
      vFreq = 0.1193;
      }
    else if (wFact == 256) {
      wFact = 1024;
      TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024
      }
    else if (wFact == 64) {
      wFact = 256;
      TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
      }
    else if (wFact == 8) {
      wFact = 64;
      TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
      }
    else if (wFact == 1) {
      wFact = 8;
      TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR1A_calc_new = -1 + vFreq_base / vFreq;
    } // if (vOCR1A_calc_new > 65535.0)

  // Teste si l'on peut être plus précis dans la fréquence générée.
  else if (vOCR1A_calc_new < 7500.0) {
    // Passe à un diviseur de fréquence plus petit, pour être plus précis dans la fréquence générée.
   
    if (wFact == 1) {
      // Teste que la fréquence ne soit pas trop grande, sinon dépasse les capacités de génération de fréquences de l'Arduino.
      if (vFreq > 120000.0) {  // Fréquence max. = 120 [kHz].
        vFreq = 120000.0;
        OCR1A_old = 0; // Assure que : OCR1A_old != OCR1A_new
        }
      }
    else {
      OCR1A_old = 0; // Assure que : OCR1A_old != OCR1A_new
      
      if (wFact == 8) {
        wFact = 1;
        TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1) fact =    1
        }
      else if (wFact == 64) {
        wFact = 8;
        TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
        }
      else if (wFact == 256) {
        wFact = 64;
        TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
        }
      else if (wFact == 1024) {
        wFact = 256;
        TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
        }
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR1A_calc_new = -1 + vFreq_base / vFreq;
    } // else if (vOCR1A_calc_new < 7500.0)
  
  OCR1A_new = word(vOCR1A_calc_new);
  
  if (OCR1A_new == OCR1A_old) {
    // Force le changement de fréquence minimal.
    if (vFreq_delta > 0) OCR1A_new--;
    else                 OCR1A_new++;    
    }
  
  vFreq = vFreq_base / (OCR1A_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.
  } // if (vFreq_delta != 0) 

// Le bouton "Select" active ou désactive le signal
if (nButtonPush == btnSELECT) {
  bEtat_enable = 1 - bEtat_enable;
  digitalWrite(13, bEtat_enable); // Indique sur l'état de la pin 13 si le signal est arrêté ou actif.
  digitalWrite( pinFreqP, bEtat & bEtat_enable);       // off si signal désactivé
  digitalWrite( pinFreqN, (1 - bEtat) & bEtat_enable); // off si signal désactivé
  }

AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1
// Un bouton LEFT ou RIGHT a été pressé

bMenu1_Last = bMenu1; // Mémorise le dernier menu

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = MENU_MAX;  // boucle
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
   if (bMenu1 > MENU_MAX) bMenu1 = 1;  // boucle
  }

AfficheEtat();
} // Menu1_Change


void loop() {
//===========
// Boucle principale. Voir également la routine de gestion d'interruption du timer 1

if (TIFR0 & 1) {
  // Le compteur du timer 0 a fait une boucle complète
  // Une boucle se fait en environ 1/61 [s]. Il y a donc 61 boucles par seconde. 
  // Ceci laisse le temps au convertisseur ADC de terminer sa conversion
  TIFR0 = 1; // Efface les flags du registre TIFR0
  
  // Ne lit l'état des boutons que si un bouton a été traité il y a plus de 0,33 seconde.
  if (wTime0 == 0) {
     // Lecture de l'état des boutons, pour changer la fréquence ou la phase du signal
     nButtonPush = read_LCD_buttons();
  
     if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Un bouton pressé, changement de menu
     if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
     }

  if (wTime0 > 0) wTime0--; // Pour assurer qu'un bouton pressé ne soit pas traité trop rapidement plusieurs fois à la suite.

  } // if (TIFR0 & 1) {
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé, ce qui est le cas dans ce programme.
ISR (TIMER1_COMPA_vect) {
//=======================
// Transition de l'état
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0. 
// Il est préférable de ne pas utiliser directement le registre TCNT1, car sa valeur peut varier très rapidement.
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.

OCR1A = OCR1A_new; // Définit la durée de la prochaine période de transition.

if (bEtat_enable) {
  // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
  // C.f. AVR_Assembler_Lecture6.pdf
  bEtat = 1 - bEtat;
  if (bEtat) PORTD = (PORTD | 0b00001000) & 0b11111011; // Pour accéder beaucoup plus rapidement aux pins de l'Arduino.
  else       PORTD = (PORTD | 0b00000100) & 0b11110111;
  // PORTD contrôle les pins de  0 à  7. bit des unités = port 0
  // PORTB contrôle les pins de  8 à 13  <=> bits 0 à 5
  // PORTC contrôle les pins de 14 à 19  <=> bits 0 à 5. Correspond aux pins analogiques, pouvant aussi être utilisés en digital.
  }

/*
// Autre manière possible de faire.
if (bEtat_enable) {
  // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
  // C.f. AVR_Assembler_Lecture6.pdf
  bEtat = 1 - bEtat;
  if (bEtat) { bitSet(PORTD, 3); bitClear(PORTD, 2); } // Pour accéder beaucoup plus rapidement aux pins de l'Arduino.
  else       { bitSet(PORTD, 2); bitClear(PORTD, 3); }
  // PORTD contrôle les pins de  0 à  7. bit des unités = port 0
  // PORTB contrôle les pins de  8 à 13  <=> bits 0 à 5
  // PORTC contrôle les pins de 14 à 19  <=> bits 0 à 5. Correspond aux pins analogiques, pouvant aussi être utilisés en digital.
  }
*/  
/*
// Manière plus claire de faire, mais plus de 10 fois plus lente.
if (bEtat_enable) {
  digitalWrite(pinFreqP, bEtat); // prend environ 4 [us] = 4 micro-secondes
  bEtat = 1 - bEtat;
  digitalWrite(pinFreqN, bEtat); // prend environ 4 [us] = 4 micro-secondes
  }
*/  

} // ISR

// FIN.
ex0140 blink
ex0460_timer_1_interrupt_OCR1A_LCD.ino
La fréquence générée est réglable avec les boutons de l'afficheur LCD 1602.


    - Suivant, ex0463   ;   Précédent, ex0460
ex0462_timer_1_interrupt_LCD_sinus.ino   TOP
Utilisation des mêmes techniques que dans l'ex0460, avec comme objectif de générer un signal sinusoïdal, juste en chargeant - déchargeant un condensateur ou en maintenant constant sa tension en mettant sous haute impédence la pin 3 qui lui est reliée.
Les commentaires du code explique comment cela est obtenu.

/*
ex0462_timer_1_interrupt_LCD_sinus.ino

Suite de ex0461...

Le but est de générer un signal qui se rapproche d'une sinusoïdale de fréquence entre 300 et 500 [Hz].

Utilisation de la pin 3 de l'Arduino pour charger, maintenir constant ou décharger un condensateur.
Les pins 0 à 19 de l'Arduino ont 3 états possibles :
1) 0 [V], du courant entre dans la pin
2) 5 [V], du courant sort de la pin
3) haute impédance, c'est comme si elle était débranchée. Un condensateur branché ainsi reste dans son état de charge.

Les pin 14 à 19, correspondants à la lecture de tensions analogiques (ADC),
peuvent aussi être utilisée comme des entrées et sorties digitales.

Lors de la montée du sinus, on alterne entre la charge du condensateur et le maintien constant.
En chargent par petites durées, aux bons instants, une tension sinusoïdal peut être générée.

Lors de la descente du sinus, on alterne entre la décharge du condensateur et le maintient constant.
Les instant de charges et décharges sont stockés dans un tableau, soit de 100 valeurs soit de 200 valeurs.
Ces valeurs ont été calculées dans un programme Python.  
Le programme est copié à la fin de ce commentaire.
Elles se basent sur un modèle de charge - décharge d'un condensateur à travers une résistance.
La fréquence utilisée dans le modèle est de 400 [Hz].
R = 2200 Ohms
C = 220 [nF].
La tension oscille entre 1 [V] et 4 [V].

On peut faire varier légèrement la fréquence, mais cela éloigne les données de ce qu'elles devraient être,
la courbe s'éloigne d'une sinusoïdale.

Exemple d'utilisation du timer 1 pour générer des interruptions.
Utilise le registre OCR1A pour générer les interruptions.
Le timer 0 est utilisé pour une mesure du temps, sans génération d'interruption.
Les fonctions "delay", "delayMicroseconds", "millis" et "micros" ne sont pas utilisables.
Elles sont désactivées, pour augmenter la précision de la période de génération d'interruptions.

L'affichage LCD, avec les 5 boutons de commande est utilisé.

Les pins 2 et 3 sont utilisés pour le signal, en opposition.
Avec la touche "Select", on peut activer ou désactiver le signal.
La pin 13 indique si le signal est activé (1) ou désactivé (0).

Accès beaucoup plus rapide aux changement d'état des pins qu'avec la fonction "digitalWrite".
C.f. "Arduino Cookbook" de Michael Margolis, édition O'reilly, page 628 et
"AVR_Assembler_Lecture6.pdf", page 53

La lecture de l'ADC 0, pour connaître le bouton pressé est fait de manière
beaucoup plus rapide qu'en utilisant la fonction analogRead.
Le temps de conversion est toujours d'environ 100 [us], 
mais aucune attente n'est faite en l'initialisation du début de conversion et le résultat de la mesure, 100 [us] plus tard.
[us] = micro-seconde.

************** Programme en langage Python servant à calculer le tableau des instants de charges du condensateur. ************

# Cacluls_durees_sinus.py
'''
2020 06 18

Pour Arduino
Le but est de générer un signal quazi sinusoïdal avec un Arduino,
en utilisant une résistance 2200 [Ohms] et un condensateur de 220 nF.

Théorie :
Le signal désiré est Us(t) = 2.5 - 1.5 * cos(2 * pi * 400 * t)
La charge du condensateur est : Uc( n ) =  5 [V] * (1 - exp( -(t_0 + n * dt) / RC) )
t_0 est choisi pour que Uc(0) = 1 [V]
n = 0 au départ.
Chaque fois que la tension du signal dépasse celle de ( Uc(n) + Uc(n+1) ) / 2, 
on charge le condensateur durant un temps  dt  et on incrémente n de 1.
Les différents instants (nombre * dt) durant lesquels il faut charger le condensateur sont mémorisés dans un tableau.
(On ne mémorise que "nombre").

La décharge est symétrique.

RC = R * C est choisi pour que la tension aux bornes du condensateur lorsque U = 2.5 [V]
croit légèrement plus rapidement que la tension du signal désiré.
R est telle que le courant de l'Arduino est entre 1 et 5 mA. 
R = 2200 Ohms est un choix raisonnable.

Une version plus évoluée, ferait ces calculs dans l'Arduino,
pour pouvoir changer la fréquence et garder un signal sinusoïdal variant entre 1 et 4 [V].
'''

from math import *

# Données :
R = 2200.0 # Ohms
C = 220.0e-9 # F
Freq = 400.0 # Hz
nbSlots = 200 # nombre d'interruptions durant une demi période.

# Constantes déduites des données
Periode = 1.0 / Freq
dt = Periode / (2.0 * nbSlots)
print("dt [us] = ", dt * 1e6)
dt_RC = dt / (R * C)
print("dt_RC = ", dt_RC)

TC0 = log(1 - 1/5)
print("TC0_RC = ", 5 * (1 - exp(TC0)) ) # donne 1 [V] au départ.

nC = 0 # Compte le nombre de fois que le condensateur est chargé
nTS = 0 # nTS * dt = temps écoulé
UC = 1.0 # Tension en Volts aux bornes du condensateur
US = 1.0 # Tension du signal au temps nTS * dt

compte = 0 # Pour une impression utilisable en copier-coller directement dans le programme

print('--------------- Départ ------------------')
print('=========================================')
#print('{:6d}'.format(floor(temps*1000)), '{:5d}'.format(OCR1A), '{:5d}'.format(compte), '{:8.3f}'.format(1/T))

# while ( nTS <= nbSlots/10):
while ( nTS < nbSlots + 10 ):
    nC += 1
    UC_last = UC
    UC = 5 * (1 - exp(TC0 - dt_RC * nC))

    # Détermine quand il faut charger le condensateur durant un "slot de temps", pour atteindre la tension désirée du signal
    while (US < (UC_last + UC) / 2) and (nTS < nbSlots + 10):
        nTS += 1
        US = 2.5 - 1.5 * cos(2 * pi * Freq * dt * nTS)

    #print('{:4d}'.format(nC), '{:4d}'.format(nTS), '{:8.3f}'.format(UC), '{:8.3f}'.format(US))

    print('{:3d}'.format(nTS), end=',')
    compte += 1
    if (compte == 20): compte = 0; print()
==========================================================*/

// Inclus la bibliothèque qui gère l'affichage.
#include <LiquidCrystal.h>

// Initialise la bibliothèque avec les pins utilisés par l'afficheur LCD 2x16 muni de 5 boutons.
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Valeurs calculées pour une fréquence de 400 [Hz].
// R = 2200 [Ohms]
// C = 220 [nF]
/*
// 100 gestions d'interruption par demi-période.
#define CENT 100
const byte volatile abT[] = {
  9, 15, 19, 23, 25, 28, 30, 33, 35, 37, 39, 40, 42, 44, 45, 47, 48, 50, 51, 52,
 54, 55, 56, 57, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75,
 77, 78, 79, 80, 81, 82, 84, 85, 86, 88, 90, 92, 94, 98
 };
*/
// 200 gestions d'interruption par demi-période.
#define CENT 200
const byte volatile abT[] = {
 12, 21, 27, 32, 36, 40, 43, 46, 49, 52, 54, 57, 59, 62, 64, 66, 68, 70, 72, 74,
 76, 77, 79, 81, 83, 84, 86, 87, 89, 91, 92, 94, 95, 96, 98, 99,101,102,103,105,
106,107,109,110,111,112,114,115,116,117,118,120,121,122,123,124,125,127,128,129,
130,131,132,133,134,135,137,138,139,140,141,142,143,144,145,146,148,149,150,151,
152,153,154,155,157,158,159,160,161,163,164,165,166,168,169,170,172,173,175,176,
178,180,182,184,186,189,193
 };

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Nombre de menus
#define MENU_MAX 6

#define pinFreqN  3 // = état opposé à celui de pintFreqP lors du fonctionnement.
#define pinFreqP  2 // pour générer une fréquence

char acStr[20];   // Un "array" de caractères, pour conversion de nombre en un String.
String strS = ""; // Une chaine de caractères.

int  nButtonPush = btnNONE; // Indique le bouton pressé
byte bMenu1 = 5;  // indique le menu sélectionné
byte bMenu1_Last = 0;  // indique le dernier menu sélectionné
word wTime0 = 61;  // Compteur décrémenté de 1 toutes les 0.016384 [s] ~= 1/61 [s]
                   // Utilisé pour ne pas lire l'état des boutons trop souvent.

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat = 0;        // État des pins : pinFreqP et pinFreqN
byte volatile bEtat_enable = 1; // Active ou désactive le signal.  1 = Actif
word volatile OCR1A_new = 4000-1; // Fixe le temps entre deux transitions
word volatile OCR1A_old = 4000-1; // Pour déterminer la variation de la variable OCR1A_new, qu'elle soit d'au moins +-1
float        vOCR1A_calc_new = 0.0; // Nouvelle valeur de OCR1A pour générer la fréquence désirée.

word  wFact = 1; // Facteur de division de la fréquence d'incrémentation de TCNT1. Est modifiable suivant la fréquence générée.
float vFreq = 400.0; // [Hz] Fréquence générée.
float vFreq_base = 8000000.0; // [Hz] fréquence à laquelle le compteur TCNT1 est incrémenté. = 8000000.0 / vFreq
float vFreq_delta = 0.0; // Variation de fréquence demandée

// Pour la conversion ADC
uint8_t bLow, bHigh;
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// bFac    1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * bFac * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * bFac)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(8'000'000 / (Freq * bFac)

// Une fréquence supérieure à ~120 [kHz] est non atteignable.

void setup() {
//============

// Indique le nombre de lignes et de colonnes de l'afficheur LCD 16x2.
lcd.begin(16, 2);
// Affiche un message au départ, indiquant le progr. et la date
lcd.setCursor(0, 0);
lcd.print("Timer freq. LCD.");
lcd.setCursor(0, 1);
lcd.print("ex0460 20200617 ");
delay(1200);
lcd.setCursor(0, 0);
lcd.print("                "); // efface le texte
lcd.setCursor(0, 1);
lcd.print("                ");

// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A

cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  delayMicroseconds,  millis()  et  micros(), qui sont désactivées.
// Lorsque le Timer 0 est désactivé, les 4 fonctions ci-dessus ne fonctionnent plus !
TCCR0A = 0b00000000; // default. Bits : COM0A1=0 ; COM0A0=0 ; COM0B1=0 ; COM0B0=0 ; / ; / ; WGM01=0 ; WGM00=0, donc pas de PWM lié au timer 0
TCCR0B = 0b00000101; // Clock * 1024 soit 64  micro-s et WGM02 = 0
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109

// Ne pas utiliser TCNT0, mais le bit 0 de TIFR0 pour compter le nombre de boucles du compteur liées au timer 0.

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
//TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);         // La pin 13 indique si un signal est généré ou si les sorties des pins 2 et 3 sont à 0 [V].
pinMode( pinFreqN, OUTPUT);  // controle une borne de l'haut-parleur
pinMode( pinFreqP, OUTPUT);  // controle l'autre borne de l'haut-parleur = le niveau opposé à la pinFreqN

digitalWrite( 13, bEtat_enable);
digitalWrite( pinFreqN, LOW);  // borne 1 à 0 V
digitalWrite( pinFreqP, LOW);  // borne 2 à 0 V

vOCR1A_calc_new = -1 + vFreq_base / (CENT * vFreq);
OCR1A_new = word(vOCR1A_calc_new);
OCR1A     = OCR1A_new; // Limite TOP du compteur TCNT1 avant son retour à 0.
vFreq = (vFreq_base / CENT) / (OCR1A_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.

AfficheEtat();

// Pour la lecture de l'ADC
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0
// start the conversion
sbi(ADCSRA, ADSC);

} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
// Une lecture toutes les 1/61 seconde.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Bouton pressé selon la lecture de l'ADC.
static byte bButtonLast = btnNONE; // Dernier bouton pressé.
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

//nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
//ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0

// ADSC is cleared when the conversion finishes
// while (bit_is_set(ADCSRA, ADSC)); // Plus besoin d'attendre, car cela est fait grace à : lwTempsLastReadBtn

// Lecture de ADCL en premier. Il est mis dans un buffer et non modifié tant que ADCH n'a pas été lu.
// La lecture de ADCL en deuxième bloquerait la lecture de l'ADC.
// Il faut donc lire ADCH en deuxième.
bLow  = ADCL;
bHigh = ADCH;

// Démarre une conversion, qui sera terminée dans environ 100 [us].
// La fin de conversion peut se détecter en lisant ADSC, qui sera mis à 0 une fois la conversion terminée.
// ADSC n'est pas utilisé ici, car on laisse amplement le temps de terminer la conversion en n'en faisant que 61 par seconde.
sbi(ADCSRA, ADSC);

if (bHigh >= 3) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE;
  }
// Ici, on sait qu'un bouton a été pressé.

nAdc_key_in = word(bHigh, bLow); // Conversion en un word.

// Les lectures des boutons sont centrées autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

     if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.
  if (bButtonCount == 3) {
    bButtonCount = 0; 
    wTime0 = 20; // Pour indiquer qu'un bouton a été traité, il faudra attendre avant de traiter à nouveau un bouton.
    return bButton;   // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 0;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons


void AfficheEtat() {
//==================
// Affichage de la fréquence ou de la variation de fréquence, suivant le menu

if (bMenu1 <= MENU_MAX) {
  // Affiche Fréquence du signal 1
  if (bMenu1 != bMenu1_Last) {
    lcd.setCursor(0, 0);
    if      (bMenu1 == 1) lcd.print("Frequ +-0.001 Hz");
    else if (bMenu1 == 2) lcd.print("Frequ +- 0.01 Hz");
    else if (bMenu1 == 3) lcd.print("Frequ +- 0.10 Hz");
    else if (bMenu1 == 4) lcd.print("Frequ +- 1 Hz   ");
    else if (bMenu1 == 5) lcd.print("Frequ +- 10 Hz  ");
    else if (bMenu1 == 6) lcd.print("Frequ +- 100 Hz ");
    }
  
  dtostrf(vFreq, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
  strS = acStr;
  strS = "Frequ.= " + strS;
  lcd.setCursor(0, 1); // Affichage en début de 2e ligne de l'afficheur LCD.
  lcd.print(strS);
  }

} // AfficheEtat

void MenuTreat() {
//================
// Un bouton UP, DOWN ou SELECT a été pressé, traite l'action adéquat.

vFreq_delta = 0.0;

if (bMenu1 == 1) {
  // Changement de fréquence du signal 1 de +-1 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.001;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.001;
  }

if (bMenu1 == 2) {
  // Changement de fréquence du signal 1 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.01;
  }

if (bMenu1 == 3) {
  // Changement de fréquence du signal 1 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta = -0.1;
  }

if (bMenu1 == 4) {
  // Changement de fréquence du signal 1 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -1.0;
  }

if (bMenu1 == 5) {
  // Changement de fréquence du signal 1 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -10.0;
  }

if (bMenu1 == 6) {
  // Changement de fréquence du signal 1 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta = -100.0;
  }

if (vFreq_delta != 0) {
  // Il y a une demande de changement de fréquence
  vFreq = vFreq + vFreq_delta;
  
  OCR1A_old = OCR1A_new;
  vOCR1A_calc_new = -1 + vFreq_base / (CENT * vFreq);
  OCR1A_new = word(vOCR1A_calc_new);
  
  if (OCR1A_new == OCR1A_old) {
    // Force le changement de fréquence minimal.
    if (vFreq_delta > 0) OCR1A_new--;
    else                 OCR1A_new++;    
    }
  
  vFreq = (vFreq_base / CENT) / (OCR1A_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.
  } // if (vFreq_delta != 0) 

// Le bouton "Select" active ou désactive le signal
if (nButtonPush == btnSELECT) {
  bEtat_enable = 1 - bEtat_enable;
  digitalWrite(13, bEtat_enable); // Indique sur l'état de la pin 13 si le signal est arrêté ou actif.
  digitalWrite( pinFreqP, bEtat & bEtat_enable);       // off si signal désactivé
  digitalWrite( pinFreqN, (1 - bEtat) & bEtat_enable); // off si signal désactivé
  }

AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1
// Un bouton LEFT ou RIGHT a été pressé

bMenu1_Last = bMenu1; // Mémorise le dernier menu

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = MENU_MAX;  // boucle
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
   if (bMenu1 > MENU_MAX) bMenu1 = 1;  // boucle
  }

AfficheEtat();
} // Menu1_Change


void loop() {
//===========
// Boucle principale. Voir également la routine de gestion d'interruption du timer 1

if (TIFR0 & 1) {
  // Le compteur du timer 0 a fait une boucle complète
  // Une boucle se fait en environ 1/61 [s]. Il y a donc 61 boucles par seconde. 
  // Ceci laisse le temps au convertisseur ADC de terminer sa conversion
  TIFR0 = 1; // Efface les flags du registre TIFR0
  
  // Ne lit l'état des boutons que si un bouton a été traité il y a plus de 0,33 seconde.
  if (wTime0 == 0) {
     // Lecture de l'état des boutons, pour changer la fréquence ou la phase du signal
     nButtonPush = read_LCD_buttons();
  
     if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Un bouton pressé, changement de menu
     if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
     }

  if (wTime0 > 0) wTime0--; // Pour assurer qu'un bouton pressé ne soit pas traité trop rapidement plusieurs fois à la suite.

  } // if (TIFR0 & 1) {
} // loop

byte volatile bTS = 0; // Compte combien de fois la routine d'interruption a été appelée. Correspond au temps écoulé.
byte volatile bTC = 0; // Compte le nombre de boucles durant lesquelles le condensateur a été chargé.

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé, ce qui est le cas dans ce programme.
ISR (TIMER1_COMPA_vect) {
//=======================
// Transition de l'état
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0.
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0. 
// Il est préférable de ne pas utiliser directement le registre TCNT1, car sa valeur peut varier très rapidement.
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B.
// C.f. : https://www.arduino.cc/en/Reference/PortManipulation  pour la manipulation de PORT,
// pour accéder beaucoup plus rapidement aux ports de l'Arduino.

bTS++;

if ( bTS == abT[bTC] ) { // Charge le condensateur
  //pinMode( pinFreqN, OUTPUT);  // controle une borne de l'haut-parleur
  DDRD = DDRD | 0b00001000;   // pinFreqN = 3, donc le bit 3 est mis à : OUTPUT == 1
  if (bEtat) PORTD = PORTD | 0b00001000; // Pour accéder beaucoup plus rapidement aux pins de l'Arduino.
  else       PORTD = PORTD & 0b11110111;
  bTC++; // On a chargé le condensateur durant une boucle supplémentaire.
  }
else { // Met en mode input, avec haute impédence, le condensateur reste à tension constante.
  //pinMode( pinFreqN, INPUT); // Met la pin à haute impédence
  DDRD = DDRD & 0b11110111;   // pinFreqN = 3, donc le bit 3 est mis à : INPUT == 0
  PORTD = PORTD & 0b11110111; // Pour ne pas avoir de "PULL-UP", pin 3 mise dans un état de haute impédence.
  }

if (bTS < CENT) return;

// On passe de la phase montante à la phase descendante ou de la descendante à la montante.
bTS = 0;
bTC = 0;

OCR1A = OCR1A_new; // Définit la durée de la prochaine période de transition. Elle reste généralement la même

if (bEtat_enable) {
  // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
  // C.f. AVR_Assembler_Lecture6.pdf
  bEtat = 1 - bEtat;
  if (bEtat) PORTD = PORTD & 0b11111011; // Pour accéder beaucoup plus rapidement aux pins de l'Arduino.
  else       PORTD = PORTD | 0b00000100;
  // PORTD contrôle les pins de  0 à  7. bit des unités = port 0
  // PORTB contrôle les pins de  8 à 13  --- bits 0 à 5
  // PORTC contrôle les pins de 14 à 19  --- bits 0 à 5. Correspond aux pins analogiques, pouvant aussi être utilisés en digital.
  }
} // ISR

// FIN.
ex0462 blink
ex0462_timer_1_interrupt_LCD_sinus.ino
Génération d'un signal sinusoïdal, observable avec un oscilloscope.


    - Suivant, ex0464   ;   Précédent, ex0462
ex0463_timer_1_interrupt_digital_oscillo_LCD.ino   TOP
Utilisation du timer 1 pour générer des fréquences sur les pins 11 et 12 de l'arduino.
Utilisation du timer 0 pour des mesures de temps.
Utilisation de l'afficheur LCD 1602 avec les 5 boutons, pour avoir un menu qui sélectionne les fréquences désirées.
Utilise des instructions de bas niveau, pour accéder plus rapidement aux ports de l'Arduino et pour lire la valeur de l'ADC0, sans attendre les 100 micro-secondes de temps de conversion.
Voir les commentaire de l'ex0464, qui est une version plus complète et mieux aboutie de génération d'une paire de fréquence ajustable.

/*
ex0463_timer_1_interrupt_digital_oscillo_LCD.ino

Génère deux signaux de fréquences ajustables.
Un sur la pin 11 et l'autre sur la pin 12.

Permet d'étudier des déphasages, des battements et des figures de lissajous sur un OSCILLOSCOPE.
Il est également possible de gérer précisément le déphasage entre les deux signaux.
C.f. ex1150_digital_oscillo_LCD,ino

En parcourant le menu, on peut choisir 4 paires de fréquences mystères,
à déterminer à l'aide d'un oscilloscope, comme exercice.

Le code "uududd" permet d'afficher les fréquences générées,
par défaut elles ne sont pas générées.
En pressant sur la touche "Select" au démarrage, le code est mis à "uupudd".

C'est une version plus sophistiquée que celle de ex1150_digital_oscillo_LCD.ino,
car elle utilise le timer 1 pour générer les interruptions et
le timer 0 pour le temps.

Utilisation du timer 1 pour générer des interruptions.
Utilise le registre OCR1A pour générer les interruptions.
Le timer 0 est utilisé pour une mesure du temps, sans génération d'interruption.
Les fonctions "delay", "delayMicroseconds", "millis" et "micros" ne sont pas utilisables.
Elles sont désactivées, pour augmenter la précision de la période de génération d'interruptions.

L'affichage LCD, avec les 5 boutons de commande est utilisé.

La pin 13 indique si le signal est activé (1) ou désactivé (0).

Accès beaucoup plus rapide aux changement d'état des pins qu'avec la fonction "digitalWrite".
C.f. "Arduino Cookbook" de Michael Margolis, édition O'reilly, page 628 et
"AVR_Assembler_Lecture6.pdf", page 53

Permet de générer une fréquence de 0.12 [Hz] à 120'000 [Hz].

La lecture de l'ADC 0, pour connaître le bouton pressé est fait de manière
beaucoup plus rapide qu'en utilisant la fonction analogRead.
Le temps de conversion est toujours d'environ 100 [us], 
mais aucune attente n'est faite en l'initialisation du début de conversion et le résultat de la mesure, 100 [us] plus tard.
[us] = micro-seconde.
==========================================================*/

// Inclus la bibliothèque qui gère l'affichage.
#include <LiquidCrystal.h>

// Initialise la bibliothèque avec les pins utilisés par l'afficheur LCD 2x16 muni de 5 boutons.
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Nombre de menus
#define MENU_MAX 12

// Les deux pins sur lesquelles les signaux sont générés.
#define PIN11  11
#define PIN12  12

char acStr[20];   // Un "array" de caractères, pour conversion de nombre en un String.
String strS = ""; // Une chaine de caractères.

int  nButtonPush = btnNONE; // Indique le bouton pressé
byte bMenu1 = 4;  // indique le menu sélectionné
byte bMenu1_Last = 0;  // indique le dernier menu sélectionné
word wTime0 = 61;  // Compteur décrémenté de 1 toutes les 0.016384 [s] ~= 1/61 [s]
                   // Utilisé pour ne pas lire l'état des boutons trop souvent.
byte bMenu_mytere = 0; // Définit la fréquence mystère à déterminer

byte bAfficheF = 1; // indique si la fréquence est affichée ou non. Est initialisée dans la fonction setup().
String strCode = "uududd"; // Code pour demander d'afficher les fréquences.

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat11 = 0;      // État de la pin 11
byte volatile bEtat12 = 0;      // État de la pin 12
byte volatile bEtat_enable = 1; // Active ou désactive le signal.  1 = Actif
word volatile wOCR11_new = 4000-1; // Fixe le temps entre deux transitions du signal sur la pin 11
word volatile wOCR12_new = 4000-1; // Fixe le temps entre deux transitions du signal sur la pin 12
word volatile wOCR11_old = 4000-1;
word volatile wOCR12_old = 4000-1;
word volatile wOCR11     = 4000-1; 
word volatile wOCR12     = 4000-1;
byte volatile bPinNext   = 12; // Indique quelle pin changera d'état à la prochaine interruption

word  wFact = 1; // Facteur de division de la fréquence d'incrémentation de TCNT1. Est modifiable suivant la fréquence générée.
float vFreq11 = 400.0; // [Hz] Fréquence générée.
float vFreq12 = 400.0; // [Hz] Fréquence générée.
float vFreq_base = 8000000.0; // [Hz] fréquence à laquelle le compteur TCNT1 est incrémenté. = 8000000.0 / vFreq
float vFreq_delta11 = 0.0; // Variation de fréquence 1 demandée
float vFreq_delta12 = 0.0; // Variation de fréquence 2 demandée
float vOCR11_calc_new = 0.0; // Pour le calcule de wOCR11_new en fonction de la fréquence désirée.
float vOCR12_calc_new = 0.0; // Pour le calcule de wOCR12_new en fonction de la fréquence désirée.

// Pour la conversion ADC
uint8_t bLow, bHigh;
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// wFact   1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * wFact * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * wFact)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(8'000'000 / (Freq * wFact)

// Une fréquence supérieure à ~120 [kHz] est non atteignable.

void setup() {
//============

// Indique le nombre de lignes et de colonnes de l'afficheur LCD 16x2.
lcd.begin(16, 2);
// Affiche un message au départ, indiquant le progr. et la date
lcd.setCursor(0, 0);
lcd.print("Timer freq. LCD.");
lcd.setCursor(0, 1);
lcd.print("ex0463 20200620 ");
delay(1200);
lcd.setCursor(0, 0);
lcd.print("                "); // efface le texte
lcd.setCursor(0, 1);
lcd.print("                ");

// Pour afficher les fréquences, si au départ on presse sur le bouton "Select".
if ( (analogRead(0) > 555) &&  (analogRead(0) < 1000) ) { bAfficheF = 1; strCode = "uududd"; }
else { bAfficheF = 0; strCode = ""; }

// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A
cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  delayMicroseconds,  millis()  et  micros(), qui sont désactivées.
// Lorsque le Timer 0 est désactivé, les 4 fonctions ci-dessus ne fonctionnent plus !
TCCR0A = 0b00000000; // default. Bits : COM0A1=0 ; COM0A0=0 ; COM0B1=0 ; COM0B0=0 ; / ; / ; WGM01=0 ; WGM00=0, donc pas de PWM lié au timer 0
TCCR0B = 0b00000101; // Clock * 1024 soit 64  micro-s et WGM02 = 0
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109

// Ne pas utiliser TCNT0, mais le bit 0 de TIFR0 pour compter le nombre de boucles du compteur liées au timer 0.

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
//TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

vOCR11_calc_new = -1 + vFreq_base / vFreq11;
wOCR11_new = round(vOCR11_calc_new);
wOCR11 = wOCR11_new + wOCR11_new / 3; // "+ wOCR11_new / 3"  crée un déphasage au départ
vFreq11 = vFreq_base / (wOCR11_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.

vOCR12_calc_new = -1 + vFreq_base / vFreq12;
wOCR12_new = round(vOCR12_calc_new);
wOCR12 = wOCR12_new;
vFreq12 = vFreq_base / (wOCR12_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.

OCR1A = wOCR12_new;

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);         // La pin 13 indique si un signal est généré ou si les sorties des pins 2 et 3 sont à 0 [V].
pinMode( PIN11, OUTPUT);
pinMode( PIN12, OUTPUT);

digitalWrite( 13, bEtat_enable);
digitalWrite( PIN11, bEtat11); 
digitalWrite( PIN12, bEtat12); 

AfficheEtat();

// Pour la lecture de l'ADC
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0
// start the conversion
sbi(ADCSRA, ADSC);

} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
// Une lecture toutes les 1/61 seconde.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Bouton pressé selon la lecture de l'ADC.
static byte bButtonLast = btnNONE; // Dernier bouton pressé.
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

//nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
//ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0

// ADSC is cleared when the conversion finishes
// while (bit_is_set(ADCSRA, ADSC)); // Plus besoin d'attendre, car cela est fait grace à : lwTempsLastReadBtn

// Lecture de ADCL en premier. Il est mis dans un buffer et non modifié tant que ADCH n'a pas été lu.
// La lecture de ADCL en deuxième bloquerait la lecture de l'ADC.
// Il faut donc lire ADCH en deuxième.
bLow  = ADCL;
bHigh = ADCH;

// Démarre une conversion, qui sera terminée dans environ 100 [us].
// La fin de conversion peut se détecter en lisant ADSC, qui sera mis à 0 une fois la conversion terminée.
// ADSC n'est pas utilisé ici, car on laisse amplement le temps de terminer la conversion en n'en faisant que 61 par seconde.
sbi(ADCSRA, ADSC);

if (bHigh >= 3) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE;
  }
// Ici, on sait qu'un bouton a été pressé.

nAdc_key_in = word(bHigh, bLow); // Conversion en un word.

// Les lectures des boutons sont centrées autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

     if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.
  if (bButtonCount == 3) {
    bButtonCount = 0; 
    wTime0 = 20; // Pour indiquer qu'un bouton a été traité, il faudra attendre avant de traiter à nouveau un bouton.
    return bButton;   // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 0;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons


void AfficheEtat() {
//==================
// Affichage de la fréquence ou de la variation de fréquence, suivant le menu

if (bMenu1 <= MENU_MAX) {
  // Affiche Fréquence du signal 1
  if (bMenu1 != bMenu1_Last) {
    lcd.setCursor(0, 0);
    if      (bMenu1 == 1) lcd.print("Freq 1 +-0.01 Hz");
    else if (bMenu1 == 2) lcd.print("Freq 1 +-0.1  Hz");
    else if (bMenu1 == 3) lcd.print("Freq 1 +-1    Hz");
    else if (bMenu1 == 4) lcd.print("Freq 1 +-10   Hz");
    else if (bMenu1 == 5) lcd.print("Freq 1 +-100  Hz");
    else if (bMenu1 == 6) lcd.print("Freq 2 +-0.01 Hz");
    else if (bMenu1 == 7) lcd.print("Freq 2 +-0.1  Hz");
    else if (bMenu1 == 8) lcd.print("Freq 2 +-1    Hz");
    else if (bMenu1 == 9) lcd.print("Freq 2 +-10   Hz");
    else if (bMenu1 ==10) lcd.print("Freq 2 +-100  Hz");
    else if (bMenu1 ==11) lcd.print("Freq mystere    ");
    else if (bMenu1 ==12) lcd.print("pass            ");
    }

  if (bMenu1 == 12) {
    // Menu de la demande d'un mot de passe (Code) pour afficher les fréquences
    // Le mot de passe est : up up down up down down
    // "Select" efface le mot tapé.
    lcd.setCursor(0, 0);
    lcd.print("C :             ");
  
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    // lcd.print(strCode);  // strCode = "uududd" est le bon Code.
    strS = "";
    for(int nn = 1; nn<=strCode.length(); nn++) strS += "*"; // Les lettres sont remplacées par des *
    lcd.print(strS);
    }
  else {
    if (bMenu1 <= 5) {
      dtostrf(vFreq11, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
      strS = acStr;
      strS = "Freq 1 =" + strS;
      //sprintf(acStr, "%8d", wOCR11_new);  strS = strS + acStr; // Pour des tests
      }
    else if (bMenu1 <= 10) {
      dtostrf(vFreq12, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
      strS = acStr;
      strS = "Freq 2 =" + strS;
      //sprintf(acStr, "%8d", wOCR12_new);  strS = strS + acStr; // Pour des tests
      }
    else { // bMenu1 == 11
      sprintf(acStr, "Freq Nr : %3d   ", bMenu_mytere);  
      strS = acStr;
      }

    if ( (!bAfficheF) && (bMenu1 <= 10) ) strS = "                "; // Efface la deuxième ligne
    lcd.setCursor(0, 1); // Affichage en début de 2e ligne de l'afficheur LCD.
    lcd.print(strS);
    }
  }
} // AfficheEtat

void MenuTreat() {
//================
// Un bouton UP, DOWN ou SELECT a été pressé, traite l'action adéquat.

vFreq_delta11 = 0.0;
vFreq_delta12 = 0.0;

if (bMenu1 == 1) {
  // Changement de fréquence du signal 1 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -0.01;
  }

if (bMenu1 == 2) {
  // Changement de fréquence du signal 1 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -0.1;
  }

if (bMenu1 == 3) {
  // Changement de fréquence du signal 1 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -1.0;
  }

if (bMenu1 == 4) {
  // Changement de fréquence du signal 1 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -10.0;
  }

if (bMenu1 == 5) {
  // Changement de fréquence du signal 1 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -100.0;
  }

if (bMenu1 == 6) {
  // Changement de fréquence du signal 2 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -0.01;
  }

if (bMenu1 == 7) {
  // Changement de fréquence du signal 2 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -0.1;
  }

if (bMenu1 == 8) {
  // Changement de fréquence du signal 2 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -1.0;
  }

if (bMenu1 == 9) {
  // Changement de fréquence du signal 2 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -10.0;
  }

if (bMenu1 == 10) {
  // Changement de fréquence du signal 2 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -100.0;
  }

if (bMenu1 == 11) {
  // Fréquence mystère, à déterminer avec l'oscilloscope
  // Elle n'est déterminée que si un bouton UP ou DOWN est pressé depuis ce menu
  if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) )  {
    if (nButtonPush == btnUP)  {
      bMenu_mytere++;
      if (bMenu_mytere >= 4) bMenu_mytere = 0;
      }
    if (nButtonPush == btnDOWN) {
      if (bMenu_mytere == 0) bMenu_mytere = 4;
      bMenu_mytere--;
      }

    if (bMenu_mytere == 0) {  
      vFreq11 =  500.0;
      vFreq12 = 1000.0;
      }
    else if (bMenu_mytere == 1) {
      vFreq11 = 800.0;
      vFreq12 = 800.08;
      }
    else if (bMenu_mytere == 2) {
      vFreq11 = 200.0;
      vFreq12 = 800.08;
      }
    else if (bMenu_mytere == 3) {
      vFreq11 = 2000.0;
      vFreq12 = 8000.0;
      }
      
    vOCR11_calc_new = -1 + vFreq_base / vFreq11;
    wOCR11_new = word(vOCR11_calc_new);
    vFreq11 = vFreq_base / (wOCR11_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.
    
    vOCR12_calc_new = -1 + vFreq_base / vFreq12;
    wOCR12_new = word(vOCR12_calc_new);
    vFreq12 = vFreq_base / (wOCR12_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.
    
    TCNT1 = 0;           // Pour redémarrer le compteur depuis 0.
    wOCR11 = wOCR11_new + wOCR11_new / 4; // Pour définir la phase
    wOCR12 = wOCR12_new; // Si on veu définir la phase
    OCR1A = wOCR12; // = min(wOCR11, wOCR12)
    bPinNext = 12;
    }
  }

if (bMenu1 == 12) {
  // Code pour affichage des fréquences
  if (nButtonPush == btnSELECT) strCode = ""; // Efface le code tapé
  if (nButtonPush == btnUP) strCode += "u";
  if (nButtonPush == btnDOWN) strCode += "d";
  if (strCode == "uududd") bAfficheF = 1; // Bon mot de passe
  else bAfficheF = 0; // Mauvais mot de passe, on affiche pas les fréquences générées.
  
  // Efface la deuxième ligne
  if (!bAfficheF) {
    strS = "                "; // N'affiche pas la fréquence
    lcd.setCursor(0, 1);
    lcd.print(strS);
    }
  }
  
if (vFreq_delta11 != 0) {
  // Il y a une demande de changement de fréquence sur la pin 11
  vFreq11 = vFreq11 + vFreq_delta11;
  
  wOCR11_old = wOCR11_new;
  vOCR11_calc_new = -1 + vFreq_base / vFreq11;

  // Teste que les capacités du compteur TCNT1 liée au timer 1 ne soient pas dépassées
  if (vOCR11_calc_new > 65535.0) {
    // Passe à un diviseur de fréquence (wFact) plus grand, pour générer une plus basse fréquence, une plus haute période.
    wOCR11_old = 0; // Assure que : wOCR11_old != wOCR11_new
    
    if (wFact == 1024) {
      // Assure de ne pas descendre en-dessous d'une fréquence trop basse, non atteignable par le Timer 1 de l'Arduino.
      vFreq11 = 0.1193;
      }
    else if (wFact == 256) {
      wFact = 1024;
      TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024
      }
    else if (wFact == 64) {
      wFact = 256;
      TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
      }
    else if (wFact == 8) {
      wFact = 64;
      TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
      }
    else if (wFact == 1) {
      wFact = 8;
      TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR11_calc_new = -1 + vFreq_base / vFreq11;
    } // if (vOCR11_calc_new > 65535.0)

  // Teste si l'on peut être plus précis dans la fréquence générée.
  else if (vOCR11_calc_new < 7500.0) {
    // Passe à un diviseur de fréquence plus petit, pour être plus précis dans la fréquence générée.
    // Si c'est compatible avec la deuxième fréquence et la fréquence max.

    if (wFact == 1) {
      // Teste que la fréquence ne soit pas trop grande, sinon dépasse les capacités de génération de fréquences de l'Arduino.
      if (vFreq11 > 120000.0) {  // Fréquence max. = 120 [kHz].
        vFreq11 = 120000.0;
        //wOCR11_old = 0; // Assure que : wOCR11_old != wOCR11_new
        }
      }
    else if (wOCR12_new < 7500) { // Il faut que la deuxième fréquence permette également le changement du facteur
      wOCR11_old = 0; // Assure que : wOCR11_old != wOCR11_new
      
      if (wFact == 8) {
        wFact = 1;
        TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1) fact =    1
        }
      else if (wFact == 64) {
        wFact = 8;
        TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
        }
      else if (wFact == 256) {
        wFact = 64;
        TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
        }
      else if (wFact == 1024) {
        wFact = 256;
        TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
        }
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR11_calc_new = -1 + vFreq_base / vFreq11;
    } // else if (vOCR11_calc_new < 7500.0)

  wOCR11_new = round(vOCR11_calc_new);
  
  if (wOCR11_new == wOCR11_old) {
    // Force le changement de fréquence minimal.
    if (vFreq_delta11 > 0) wOCR11_new--;
    else                   wOCR11_new++;    
    }
  
  vFreq11 = vFreq_base / (wOCR11_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.

  vOCR12_calc_new = -1 + vFreq_base / vFreq12; // Utile si : vFreq_base a changé
  wOCR12_new = round(vOCR12_calc_new);
  } // if (vFreq_delta11 != 0) 


if (vFreq_delta12 != 0) {
  // Il y a une demande de changement de fréquence sur la pin 12
  vFreq12 = vFreq12 + vFreq_delta12;
  
  wOCR12_old = wOCR12_new;
  vOCR12_calc_new = -1 + vFreq_base / vFreq12;

  // Teste que les capacités du compteur TCNT1 lié au timer 1 ne soient pas dépassées
  if (vOCR12_calc_new > 65535.0) {
    // Passe à un diviseur de fréquence (wFact) plus grand, pour générer une plus basse fréquence, une plus haute période.
    wOCR12_old = 0; // Assure que : wOCR12_old != wOCR12_new
    
    if (wFact == 1024) {
      // Assure de ne pas descendre en-dessous d'une fréquence trop basse, non atteignable par le Timer 1 de l'Arduino.
      vFreq12 = 0.1193;
      }
    else if (wFact == 256) {
      wFact = 1024;
      TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024
      }
    else if (wFact == 64) {
      wFact = 256;
      TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
      }
    else if (wFact == 8) {
      wFact = 64;
      TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
      }
    else if (wFact == 1) {
      wFact = 8;
      TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR12_calc_new = -1 + vFreq_base / vFreq12;
    } // if (vOCR12_calc_new > 65535.0)

  // Teste si l'on peut être plus précis dans la fréquence générée.
  else if (vOCR12_calc_new < 7500.0) {
    // Passe à un diviseur de fréquence plus petit, pour être plus précis dans la fréquence générée.
   
    if (wFact == 1) {
      // Teste que la fréquence ne soit pas trop grande, sinon dépasse les capacités de génération de fréquences de l'Arduino.
      if (vFreq12 > 120000.0) {  // Fréquence max. = 120 [kHz].
        vFreq12 = 120000.0;
        //wOCR12_old = 0; // Assure que : wOCR12_old != wOCR12_new
        }
      }
    else if (wOCR11_new < 7500) { // Il faut que la deuxième fréquence permette également le changement du facteur
      wOCR12_old = 0; // Assure que : wOCR12_old != wOCR12_new
      
      if (wFact == 8) {
        wFact = 1;
        TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1) fact =    1
        }
      else if (wFact == 64) {
        wFact = 8;
        TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
        }
      else if (wFact == 256) {
        wFact = 64;
        TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
        }
      else if (wFact == 1024) {
        wFact = 256;
        TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
        }
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR12_calc_new = -1 + vFreq_base / vFreq12;
    } // else if (vOCR12_calc_new < 7500.0)

  wOCR12_new = round(vOCR12_calc_new);
  
  if (wOCR12_new == wOCR12_old) {
    // Force le changement de fréquence minimal.
    if (vFreq_delta12 > 0) wOCR12_new--;
    else                   wOCR12_new++;    
    }
  
  vFreq12 = vFreq_base / (wOCR12_new + 1.0); // Fréquence réelle, légèrement différente de la fréquence désirée.

  vOCR11_calc_new = -1 + vFreq_base / vFreq11; // Utile si : vFreq_base a changé
  wOCR11_new = round(vOCR11_calc_new);
  } // if (vFreq_delta12 != 0) 

// Le bouton "Select" active ou désactive le signal
if ( (nButtonPush == btnSELECT) && (bMenu1 != 12) ) {
  bEtat_enable = 1 - bEtat_enable;
  digitalWrite(13, bEtat_enable); // Indique sur l'état de la pin 13 si le signal est arrêté ou actif.
  digitalWrite( PIN11, bEtat11 & bEtat_enable); // off si signal désactivé
  digitalWrite( PIN12, bEtat12 & bEtat_enable); // off si signal désactivé
  }

AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1
// Un bouton LEFT ou RIGHT a été pressé

bMenu1_Last = bMenu1; // Mémorise le dernier menu

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = MENU_MAX;  // boucle
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
   if (bMenu1 > MENU_MAX) bMenu1 = 1;  // boucle
  }

AfficheEtat();
} // Menu1_Change


void loop() {
//===========
// Boucle principale. Voir également la routine de gestion d'interruption du timer 1

if (TIFR0 & 1) {
  // Le compteur du timer 0 a fait une boucle complète
  // Une boucle se fait en environ 1/61 [s]. Il y a donc 61 boucles par seconde. 
  // Ceci laisse le temps au convertisseur ADC de terminer sa conversion
  TIFR0 = 1; // Efface les flags du registre TIFR0
  
  // Ne lit l'état des boutons que si un bouton a été traité il y a plus de 0,33 seconde.
  if (wTime0 == 0) {
     // Lecture de l'état des boutons, pour changer la fréquence ou la phase du signal
     nButtonPush = read_LCD_buttons();
  
     if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Un bouton pressé, changement de menu
     if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
     }

  if (wTime0 > 0) wTime0--; // Pour assurer qu'un bouton pressé ne soit pas traité trop rapidement plusieurs fois à la suite.

  } // if (TIFR0 & 1) {
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé, ce qui est le cas dans ce programme.
ISR (TIMER1_COMPA_vect) {
//=======================
// Transition de l'état
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0. 
// Une boucle complète fait  OCR1A+1  (cycles d'horloge / wFact)
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0. 
// Il est préférable de ne pas utiliser directement le registre TCNT1, car sa valeur peut varier très rapidement.
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B. (facteur = wFact)

if (!bEtat_enable) return; // changements d'états désactivés.

if (bPinNext == 11) {
  // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
  // C.f. AVR_Assembler_Lecture6.pdf
  // Transition d'état de la pin 11
  bEtat11 = 1 - bEtat11;
  if (bEtat11) PORTB = PORTB | 0b00001000;
  else         PORTB = PORTB & 0b11110111;
  // PORTD contrôle les pins de  0 à  7. bit des unités = port 0
  // PORTB contrôle les pins de  8 à 13  --- bits 0 à 5
  // PORTC contrôle les pins de 14 à 19  --- bits 0 à 5. Correspond aux pins analogiques, pouvant aussi être utilisés en digital.

  wOCR11 = wOCR11_new;
  wOCR12 -= OCR1A+1; // ici, on sait que : wOCR12 >= OCR1A+1

  if (wOCR12 < 128) { // 128 --- 8 [us] si fact = 1  ( 112 est trop petit ! )
    // Transition d'état de la pin 12, faite un peu trop tôt, pour éviter de la rater.
    bEtat12 = 1 - bEtat12;
    if (bEtat12) PORTB = PORTB | 0b00010000;
    else         PORTB = PORTB & 0b11101111;
    wOCR12 += wOCR12_new+1;
    }
  }
else {  // (bPinNext == 12)
  // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
  // C.f. AVR_Assembler_Lecture6.pdf
  // Transition d'état de la pin 12
  bEtat12 = 1 - bEtat12;
  if (bEtat12) PORTB = PORTB | 0b00010000;
  else         PORTB = PORTB & 0b11101111;

  wOCR12 = wOCR12_new;
  wOCR11 -= OCR1A; // ici, on sait que : wOCR11 >= OCR1A 

  if (wOCR11 < 128) { // 128 --- 8 [us] si fact = 1
    // Transition d'état de la pin 11, faite un peu trop tôt, pour éviter de la rater.
    bEtat11 = 1 - bEtat11;
    if (bEtat11) PORTB = PORTB | 0b00001000;
    else         PORTB = PORTB & 0b11110111;
    wOCR11 += wOCR11_new+1;
    }
    
  wOCR11--;
  }

// Définition de quand aura lieu la prochaine transistion d'état d'une pin et de laquelle.
if (wOCR11 < wOCR12) {
  OCR1A = wOCR11;
  bPinNext = 11;
  }
else { // wOCR11 >= wOCR12
  OCR1A = wOCR12;
  bPinNext = 12;
  }

// Pour des tests, voir si on rate une interruption
//if (TCNT1 >= OCR1A) { bEtat_enable = 0; OCR1A = wOCR11_new; wOCR11 = wOCR11_new;  wOCR12 = wOCR12_new + 200;}
} // ISR

// FIN.
ex0463 blink
ex0463_timer_1_interrupt_digital_oscillo_LCD.ino
Il est également possible de prendre le schéma de l'ex0460 avec le haut-parleur. Il faut brancher la "gate" du transistor sur la pin 11 ou la 12. Cela permet d'écouter la fréquence générée.


    - ex0464 supplément   ;   Précédent, ex0463
ex0464_timer_1_interrupt_digital_oscillo_LCD.ino   TOP
Extension de l'ex0463, avec des améliorations, version plus aboutie.
Si on utilise le schéma de l'ex0460 avec le haut-parleur et les bons branchements, deux cas limites sont intéressants à étudier.
1) Vers les basses fréquences, entre 10 et 80 [Hz], expérimenter à partir de quelle fréquence on endent un son continu et non des TACs distincts.
2) En augmentant la fréquence de 1'000 à 20'000 [Hz], à partir de quelle fréquence on entent plus rien.
Cela dépend beaucoup de l'âge de la personne qui écoute.
Les jeunes entendent jusqu'à entre 17'000 et 20'000 [Hz], des personnes âgées n'entendent plus rien au delà de 10'000 [Hz].
Par défaut, les fréquences ne sont pas affichées, car l'utilisation est destinées à des élèves qui apprennent à manier un oscilloscope.
En introduisant le bon code dans le bon menu, les fréquences apparaîssent.

/*
ex0464_timer_1_interrupt_digital_oscillo_LCD.ino

Suite de ex0463_timer_1_interrupt_digital_oscillo_LCD.ino
Fait de légères variations sur OCR1A de périodes d'interruption,
pour obtenir plus précisément la fréquence désirée.
La fréquence est définie au millième de Hz près.

Génère deux signaux d'une fréquence ajustables.
Un sur la pin 11 et l'autre sur la pin 12.

Permet d'étudier des déphasages, des battements et des figures de lissajous sur un OSCILLOSCOPE.
Il est également possible de gérer précisément le déphasage entre les deux signaux.
C.f. ex1150_digital_oscillo_LCD,ino

En parcourant le menu, on peut choisir 4 paires de fréquences mystères,
à déterminer à l'aide d'un oscilloscope, comme exercice.

Le code "uududd" permet d'afficher les fréquences générées,
par défaut elles ne sont pas générées.
En pressant sur la touche "Select" au démarrage, le code est mis à "uupudd".

Adjonction du cas où : code = "uududdd", qui impose que
le signal 2 soit de même fréquence que le signal 1, en opposition.
C'est pratique pour faire vibrer un haut-parleur au maximum.

C'est une version plus sophistiquée que celle de ex1150_digital_oscillo_LCD.ino,
car elle utilise le timer 1 pour générer les interruptions et
le timer 0 pour le temps.

Utilisation du timer 1 pour générer des interruptions.
Utilise le registre OCR1A pour générer les interruptions.
Le timer 0 est utilisé pour une mesure du temps, sans génération d'interruption.
Les fonctions "delay", "delayMicroseconds", "millis" et "micros" ne sont pas utilisables.
Elles sont désactivées, pour augmenter la précision de la période de génération d'interruptions.

L'affichage LCD, avec les 5 boutons de commande est utilisé.

La pin 13 indique si le signal est activé (1) ou désactivé (0).

Accès beaucoup plus rapide aux changement d'état des pins qu'avec la fonction "digitalWrite".
C.f. "Arduino Cookbook" de Michael Margolis, édition O'reilly, page 628 et
"AVR_Assembler_Lecture6.pdf", page 53

Permet de générer une fréquence de 0.12 [Hz] à 45'000 [Hz].

La lecture de l'ADC 0, pour connaître le bouton pressé est fait de manière
beaucoup plus rapide qu'en utilisant la fonction analogRead.
Le temps de conversion est toujours d'environ 100 [us], 
mais aucune attente n'est faite en l'initialisation du début de conversion et le résultat de la mesure, 100 [us] plus tard.
[us] = micro-seconde.
==========================================================*/

// Inclus la bibliothèque qui gère l'affichage.
#include <LiquidCrystal.h>

// Initialise la bibliothèque avec les pins utilisés par l'afficheur LCD 2x16 muni de 5 boutons.
//LiquidCrystal lcd(RS, E, D4, D5, D6, D7);
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Déclaration de Constantes
#define btnNONE   0
#define btnSELECT 1
#define btnLEFT   2
#define btnDOWN   3
#define btnUP     4
#define btnRIGHT  5

// Nombre de menus
#define MENU_MAX 14

// Les deux pins sur lesquelles les signaux sont générés.
#define PIN11  11
#define PIN12  12

char acStr[20];   // Un "array" de caractères, pour conversion de nombre en un String.
String strS = ""; // Une chaine de caractères.

int  nButtonPush = btnNONE; // Indique le bouton pressé
byte bMenu1 = 4;  // indique le menu sélectionné
byte bMenu1_Last = 0;  // indique le dernier menu sélectionné
word wTime0 = 61;  // Compteur décrémenté de 1 toutes les 0.016384 [s] ~= 1/61 [s]
                   // Utilisé pour ne pas lire l'état des boutons trop souvent.
byte bMenu_mytere = 0; // Définit la fréquence mystère à déterminer

byte bAfficheF = 1; // indique si la fréquence est affichée ou non. Est initialisée dans la fonction setup().
String strCode = "uududd"; // Code pour demander d'afficher les fréquences.

// Pour "volatile", c.f. https://www.arduino.cc/en/pmwiki.php?n=Reference/Volatile
// Les variables utilisées dans la fonction de gestion d'interruption doivent être déclarée "volatile".
// Cela impose que la variable est sauvegardée en RAM et non dans un registre.
// Une variable sauvegardée dans un registre pourrait être modifiée accidentellement entre deux accès à la fonction.
byte volatile bEtat11 = 0;      // État de la pin 11
byte volatile bEtat12 = 0;      // État de la pin 12
byte volatile bEtat_enable = 1; // Active ou désactive le signal.  1 = Actif
word volatile wOCR11_new = 4000-1; // Fixe le temps entre deux transitions du signal sur la pin 11
word volatile wOCR12_new = 4000-1; // Fixe le temps entre deux transitions du signal sur la pin 12
word volatile wOCR11     = 4000-1; 
word volatile wOCR12     = 4000-1;
byte volatile bPinNext   = 12; // Indique quelle pin changera d'état à la prochaine interruption

word volatile wOCR11_delta = 0; // Pour déterminer la différence entre la période désirée et celle obtenue 
word volatile wOCR11_gap   = 0; // Somme des différences entre les périodes désirées et celle appliquées
                                // Lorsque wOCR11_gap > 30000, on ajoute un cycle à la période, pour se rapprocher de la période désirée.
word volatile wOCR12_delta = 0; // Pour déterminer la différence entre la période désirée et celle obtenue 
word volatile wOCR12_gap   = 0; // Somme des différences entre les périodes désirées et celle appliquées
                                // Lorsque wOCR12_gap > 30000, on ajoute un cycle à la période, pour se rapprocher de la période désirée.

byte volatile bTmp = 0; // Pour des calculs temporaires

word  wFact = 1; // Facteur de division de la fréquence d'incrémentation de TCNT1. Est modifiable suivant la fréquence générée.
float vFreq11 = 400.0; // [Hz] Fréquence générée.
float vFreq12 = 400.0; // [Hz] Fréquence générée.
float vFreq_base = 8000000.0; // [Hz] fréquence à laquelle le compteur TCNT1 est incrémenté. = 8000000.0 / vFreq
float vFreq_delta11 = 0.0; // Variation de fréquence 1 demandée
float vFreq_delta12 = 0.0; // Variation de fréquence 2 demandée
float vOCR11_calc_new = 0.0; // Pour le calcule de wOCR11_new en fonction de la fréquence désirée.
float vOCR12_calc_new = 0.0; // Pour le calcule de wOCR12_new en fonction de la fréquence désirée.

// Pour la conversion ADC
uint8_t bLow, bHigh;
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))


// Formules de conversions
//========================
// Selon la valeur de TCCR1B, bFac varie selon le tableau suivant :
// TCCR1B  1  2  3   4       5  
// wFact   1  8  64  256  1024
// Temps entre deux transistions = (OCR1A+1) * wFact * 62.5 * 10^(-9) [s]  // 62.5e-9 = 1 / 16'000'000
// La période est le double du temps entre deux transistions
// Fréquence = 8'000'000 [Hz] / ((OCR1A+1) * wFact)
//
// Pour Fréquence = Freq  donnée :
// OCR1A = -1 + Tronc(8'000'000 / (Freq * wFact)

// Une fréquence supérieure à ~120 [kHz] est non atteignable.

void setup() {
//============

// Indique le nombre de lignes et de colonnes de l'afficheur LCD 16x2.
lcd.begin(16, 2);
// Affiche un message au départ, indiquant le progr. et la date
lcd.setCursor(0, 0);
lcd.print("Timer freq. LCD.");
lcd.setCursor(0, 1);
lcd.print("ex0464 20200621 ");
delay(1200);
lcd.setCursor(0, 0);
lcd.print("                "); // efface le texte
lcd.setCursor(0, 1);
lcd.print("                ");

// Pour afficher les fréquences, si au départ on presse sur le bouton "Select".
if ( (analogRead(0) > 555) &&  (analogRead(0) < 1000) ) { bAfficheF = 1; strCode = "uududd"; }
else { bAfficheF = 0; strCode = ""; }

// Initialise le Timer 1 pour déclencher les interruptions après temps défini dans OCR1A
cli(); // Désactive l'interruption globale

// de Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
// Timer/Counter0  C.f. page 93 
// Il gère les fonctions  delay(),  delayMicroseconds,  millis()  et  micros(), qui sont désactivées.
// Lorsque le Timer 0 est désactivé, les 4 fonctions ci-dessus ne fonctionnent plus !
TCCR0A = 0b00000000; // default. Bits : COM0A1=0 ; COM0A0=0 ; COM0B1=0 ; COM0B0=0 ; / ; / ; WGM01=0 ; WGM00=0, donc pas de PWM lié au timer 0
TCCR0B = 0b00000101; // Clock * 1024 soit 64  micro-s et WGM02 = 0
TIMSK0 = 0b00000000; // Désactive les interruptions dues au Timer/Counter0   C.f. p. 109

// Ne pas utiliser TCNT0, mais le bit 0 de TIFR0 pour compter le nombre de boucles du compteur liées au timer 0.

// Timer/Counter2  C.f. page 141
// Il est utilisé pour générer des  tone()
TIMSK2 = 0b00000000; // Désactive les les interruptions dues au Timer/Counter2  C.f. p. 157

// Timer/Counter1  C.f. page 111
// Il est utilisé dans la bibliothèque de commande de servo-moteurs.
//TIMSK1 = 0b00000001; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                       // Une interruption est générée lorsque le compteur TCNT1 passe de 65535 à 0 (overflow)
TIMSK1 = 0b00000010; // Active les les interruptions dues au Timer/Counter1  C.f. p. 135
                     // Lorsque le compteur TCNT1 égale OCR1A,  ( car WGM1(3:0) = 0b0100 == 4 )
                     // TCNT1 passe à 0.
                     // et une interruption est générée.   

TCCR1A = 0b00000000; // default. Bits : COM1A1=0 ; COM1A0=0 ; COM1B1=0 ; COM1B0=0 ; / ; / ; WGM11=0 ; WGM10=0, donc pas de PWM.

TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1)  fact =    1
//TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
//TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
//TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256
//TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024

vOCR11_calc_new = -1 + vFreq_base / vFreq11;
wOCR11_new = trunc(vOCR11_calc_new);
wOCR11 = wOCR11_new + wOCR11_new / 3; // "+ wOCR11_new / 3"  crée un déphasage au départ
wOCR11_delta = trunc(30000 * (vOCR11_calc_new - wOCR11_new)); // Ecart entre la fréquence désirée et celle obtenue
wOCR11_gap = 0;

vOCR12_calc_new = -1 + vFreq_base / vFreq12;
wOCR12_new = trunc(vOCR12_calc_new);
wOCR12 = wOCR12_new;
wOCR12_delta = trunc(30000 * (vOCR12_calc_new - wOCR12_new)); // Ecart entre la fréquence désirée et celle obtenue
wOCR12_gap = 0;

OCR1A = wOCR12;

sei(); // Active l'interruption globale

pinMode(13, OUTPUT);         // La pin 13 indique si un signal est généré ou si les sorties des pins 2 et 3 sont à 0 [V].
pinMode( PIN11, OUTPUT);
pinMode( PIN12, OUTPUT);

digitalWrite( 13, bEtat_enable);
digitalWrite( PIN11, bEtat11); 
digitalWrite( PIN12, bEtat12); 

AfficheEtat();

// Pour la lecture de l'ADC
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0
// start the conversion
sbi(ADCSRA, ADSC);

} // setup

int read_LCD_buttons() {
// ======================
// Lecture du bouton appuié
// Il faut détecter 3 fois de suite le même bouton pour qu'on estime détecté le bon bouton pressé.
// L'avantage de cette manière de faire est que la lecture des boutons ne prend que
// le temps d'une lecture analogique, soit environ 100 micro secondes.
// Une lecture toutes les 1/61 seconde.
int nAdc_key_in = 0;
byte bButton = btnNONE; // Bouton pressé selon la lecture de l'ADC.
static byte bButtonLast = btnNONE; // Dernier bouton pressé.
static byte bButtonCount = 0; // compte le nombre de fois que le même boutton est détecté à la suite.

//nAdc_key_in = analogRead(0);  // Lecture de la valeur du bouton pressé. 
// c.f. https://garretlab.web.fc2.com/en/arduino/inside/arduino/wiring_analog.c/analogRead.html
//ADMUX = 0b01000000;  // 01  => Default reference voltage  bit_5 = ADLAR = 0 => right adjusted  bits_3210 = 0 => port 0

// ADSC is cleared when the conversion finishes
// while (bit_is_set(ADCSRA, ADSC)); // Plus besoin d'attendre, car cela est fait grace à : lwTempsLastReadBtn

// Lecture de ADCL en premier. Il est mis dans un buffer et non modifié tant que ADCH n'a pas été lu.
// La lecture de ADCL en deuxième bloquerait la lecture de l'ADC.
// Il faut donc lire ADCH en deuxième.
bLow  = ADCL;
bHigh = ADCH;

// Démarre une conversion, qui sera terminée dans environ 100 [us].
// La fin de conversion peut se détecter en lisant ADSC, qui sera mis à 0 une fois la conversion terminée.
// ADSC n'est pas utilisé ici, car on laisse amplement le temps de terminer la conversion en n'en faisant que 61 par seconde.
sbi(ADCSRA, ADSC);

if (bHigh >= 3) { 
  // Aucun bouton pressé.
  bButtonLast = btnNONE;
  bButtonCount = 0;
  return btnNONE;
  }
// Ici, on sait qu'un bouton a été pressé.

nAdc_key_in = word(bHigh, bLow); // Conversion en un word.

// Les lectures des boutons sont centrées autour des valeurs  0, 144, 329, 504, 741
// J'ai ajouté environ 50 à ces valeurs pour la détection des boutons

     if (nAdc_key_in > 555) bButton = btnSELECT;
else if (nAdc_key_in > 380) bButton = btnLEFT; 
else if (nAdc_key_in > 195) bButton = btnDOWN; 
else if (nAdc_key_in >  55) bButton = btnUP; 
else                        bButton = btnRIGHT;
  
if (bButton == bButtonLast) {
  // Le même bouton a été détecté
  bButtonCount++;  // compte combien de fois de suite le même bouton est détecté.
  if (bButtonCount == 3) {
    bButtonCount = 0; 
    wTime0 = 20; // Pour indiquer qu'un bouton a été traité, il faudra attendre avant de traiter à nouveau un bouton.
    return bButton;   // Le même bouton a été détecté .. fois de suite, donc c'est le bon
    }
  }
else {
  // Un nouveau bouton est détecté.
  bButtonLast = bButton;
  bButtonCount = 0;
  }

return btnNONE; // On est pas encore sûr que le bon bouton est bButton.
} // read_LCD_buttons


void AfficheEtat() {
//==================
// Affichage de la fréquence ou de la variation de fréquence, suivant le menu

if (bMenu1 <= MENU_MAX) {
  // Affiche Fréquence du signal 1
  if (bMenu1 != bMenu1_Last) {
    lcd.setCursor(0, 0);
    if      (bMenu1 == 1) lcd.print("Freq 1 +-0.001Hz");
    else if (bMenu1 == 2) lcd.print("Freq 1 +-0.01 Hz");
    else if (bMenu1 == 3) lcd.print("Freq 1 +-0.1  Hz");
    else if (bMenu1 == 4) lcd.print("Freq 1 +-1    Hz");
    else if (bMenu1 == 5) lcd.print("Freq 1 +-10   Hz");
    else if (bMenu1 == 6) lcd.print("Freq 1 +-100  Hz");
    else if (bMenu1 == 7) lcd.print("Freq 2 +-0.001Hz");
    else if (bMenu1 == 8) lcd.print("Freq 2 +-0.01 Hz");
    else if (bMenu1 == 9) lcd.print("Freq 2 +-0.1  Hz");
    else if (bMenu1 ==10 ) lcd.print("Freq 2 +-1    Hz");
    else if (bMenu1 ==11) lcd.print("Freq 2 +-10   Hz");
    else if (bMenu1 ==12) lcd.print("Freq 2 +-100  Hz");
    else if (bMenu1 ==MENU_MAX) lcd.print("pass            ");
    else if (bMenu1 ==13) {
      if (strCode != "uududdd") { // Cas normal, pour l'étude du signal sur un oscilloscope
        lcd.print("Freq mystere    ");
        }
      else { // Cas particulier pour faire osciller un haut-parleur
        lcd.print("+- octave / 24  "); // Ainsi, 24 changements doublent ou divise la fréquence (change d'une octave).
        dtostrf(vFreq11, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
        strS = acStr;
        strS = "Freq 1 =" + strS;
        //lcd.setCursor(0, 1); // Affichage en début de 2e ligne de l'afficheur LCD.
        //lcd.print(strS);
        }
      }
    }

  if (bMenu1 == MENU_MAX) {
    // Menu de la demande d'un mot de passe (Code) pour afficher les fréquences
    // Le mot de passe est : up up down up down down
    // "Select" efface le mot tapé.
    lcd.setCursor(0, 0);
    lcd.print("C :             ");
  
    lcd.setCursor(0, 1);
    lcd.print("                ");
    lcd.setCursor(0, 1);
    // lcd.print(strCode);  // strCode = "uududd" est le bon Code.
    strS = "";
    for(int nn = 1; nn<=strCode.length(); nn++) strS += "*"; // Les lettres sont remplacées par des *
    lcd.print(strS);
    }
  else {
    if (bMenu1 <= 6) {
      dtostrf(vFreq11, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
      strS = acStr;
      strS = "Freq 1 =" + strS;
      //sprintf(acStr, "%8d", wOCR11_new);  strS = strS + acStr; // Pour des tests
      }
    else if (bMenu1 <= 12) {
      dtostrf(vFreq12, 8, 3, acStr); // Conversion d'un nombre à virgule en un string prenant 8 caractères, avec 3 chiffres après la virgule.
      strS = acStr;
      strS = "Freq 2 =" + strS;
      //sprintf(acStr, "%8d", wOCR12_new);  strS = strS + acStr; // Pour des tests
      }
    else if (strCode != "uududdd") { // bMenu1 == 13 = MENU_MAX -1
      sprintf(acStr, "Freq Nr : %3d   ", bMenu_mytere);  
      strS = acStr;
      }

    if ( (!bAfficheF) && (bMenu1 <= 12) ) strS = "                "; // Efface la deuxième ligne
    lcd.setCursor(0, 1); // Affichage en début de 2e ligne de l'afficheur LCD.
    lcd.print(strS);
    }
  }
} // AfficheEtat

void MenuTreat() {
//================
// Un bouton UP, DOWN ou SELECT a été pressé, traite l'action adéquat.

vFreq_delta11 = 0.0;
vFreq_delta12 = 0.0;

if (bMenu1 == 1) {
  // Changement de fréquence du signal 1 de +-1 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  0.001;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -0.001;
  }

if (bMenu1 == 2) {
  // Changement de fréquence du signal 1 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -0.01;
  }

if (bMenu1 == 3) {
  // Changement de fréquence du signal 1 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -0.1;
  }

if (bMenu1 == 4) {
  // Changement de fréquence du signal 1 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -1.0;
  }

if (bMenu1 == 5) {
  // Changement de fréquence du signal 1 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -10.0;
  }

if (bMenu1 == 6) {
  // Changement de fréquence du signal 1 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta11 =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta11 = -100.0;
  }

if (bMenu1 == 7) {
  // Changement de fréquence du signal 2 de +-1 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  0.001;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -0.001;
  }

if (bMenu1 == 8) {
  // Changement de fréquence du signal 2 de +-10 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  0.01;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -0.01;
  }

if (bMenu1 == 9) {
  // Changement de fréquence du signal 2 de +-100 milli-Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  0.1;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -0.1;
  }

if (bMenu1 == 10) {
  // Changement de fréquence du signal 2 de +-1 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  1.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -1.0;
  }

if (bMenu1 == 11) {
  // Changement de fréquence du signal 2 de +-10 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  10.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -10.0;
  }

if (bMenu1 == 12) {
  // Changement de fréquence du signal 2 de +-100 Hz
  if (nButtonPush == btnUP)   vFreq_delta12 =  100.0;
  if (nButtonPush == btnDOWN) vFreq_delta12 = -100.0;
  }

if (bMenu1 == 13) {
  if (strCode != "uududdd") {
    // Fréquence mystère, à déterminer avec l'oscilloscope
    // Elle n'est déterminée que si un bouton UP ou DOWN est pressé depuis ce menu
    if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) )  {
      if (nButtonPush == btnUP)  {
        bMenu_mytere++;
        if (bMenu_mytere >= 4) bMenu_mytere = 0;
        }
      if (nButtonPush == btnDOWN) {
        if (bMenu_mytere == 0) bMenu_mytere = 4;
        bMenu_mytere--;
        }
  
      if (bMenu_mytere == 0) {  
        vFreq11 =  500.0;
        vFreq12 = 1000.0;
        }
      else if (bMenu_mytere == 1) {
        vFreq11 = 800.0;
        vFreq12 = 800.08;
        }
      else if (bMenu_mytere == 2) {
        vFreq11 = 200.0;
        vFreq12 = 800.08;
        }
      else if (bMenu_mytere == 3) {
        vFreq11 = 2000.0;
        vFreq12 = 8000.0;
        }
  
      vOCR11_calc_new = -1 + vFreq_base / vFreq11;
      wOCR11_new   = trunc(vOCR11_calc_new);
      wOCR11_delta = trunc(30000 * (vOCR11_calc_new - wOCR11_new)); // Ecart entre la fréquence désirée et celle obtenue
      
      vOCR12_calc_new = -1 + vFreq_base / vFreq12;
      wOCR12_new   = trunc(vOCR12_calc_new);
      wOCR12_delta = trunc(30000 * (vOCR12_calc_new - wOCR12_new)); // Ecart entre la fréquence désirée et celle obtenue
      
      TCNT1 = 0;           // Pour redémarrer le compteur depuis 0.
      wOCR11 = wOCR11_new + wOCR11_new / 4; // Pour définir la phase
      wOCR12 = wOCR12_new; // Si on veu définir la phase
      OCR1A = wOCR12; // = min(wOCR11, wOCR12)
      bPinNext = 12;
      }
    } // if (strCode != "uududdd")
  else { // Cas particulier, on augmente ou diminue d'un 24e d'octave les deux fréquences.
    if (nButtonPush == btnUP) {
      vFreq_delta11 =  0.029302237 * vFreq11; // pour que 24 changement passe à l'octave suivante. racine_24e(2) = 1.029302237
      }
    if (nButtonPush == btnDOWN) {
      vFreq_delta11 = -0.028468059 * vFreq11; // pour que 24 changement passe à l'octave précédente. 1 - 1/racine_24e(2)
      }
    }
  } // if (bMenu1 == 13) 

if (bMenu1 == MENU_MAX) {
  // Code pour affichage des fréquences
  if (nButtonPush == btnSELECT) strCode = ""; // Efface le code tapé
  if (nButtonPush == btnUP) strCode += "u";
  if (nButtonPush == btnDOWN) strCode += "d";
  if ( (strCode == "uududd") || (strCode == "uududdd") ) bAfficheF = 1; // Bon mot de passe
  else bAfficheF = 0; // Mauvais mot de passe, on affiche pas les fréquences générées.
  
  // Efface la deuxième ligne
  if (!bAfficheF) {
    strS = "                "; // N'affiche pas la fréquence
    lcd.setCursor(0, 1);
    lcd.print(strS);
    }
  }
  
if (vFreq_delta11 != 0) {
  // Il y a une demande de changement de fréquence sur la pin 11
  if (vFreq11 + vFreq_delta11 > 0.1193) vFreq11 = vFreq11 + vFreq_delta11;
  vOCR11_calc_new = -1 + vFreq_base / vFreq11;

  // Teste que les capacités du compteur TCNT1 liée au timer 1 ne soient pas dépassées
  if (vOCR11_calc_new > 65535.0) {
    // Passe à un diviseur de fréquence (wFact) plus grand, pour générer une plus basse fréquence, une plus haute période.
    if (wFact == 1024) {
      // Assure de ne pas descendre en-dessous d'une fréquence trop basse, non atteignable par le Timer 1 de l'Arduino.
      vFreq11 = 0.1193;
      }
    else if (wFact == 256) {
      wFact = 1024;
      TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024
      }
    else if (wFact == 64) {
      wFact = 256;
      TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
      }
    else if (wFact == 8) {
      wFact = 64;
      TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
      }
    else if (wFact == 1) {
      wFact = 8;
      TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR11_calc_new = -1 + vFreq_base / vFreq11;
    } // if (vOCR11_calc_new > 65535.0)

  // Teste si l'on peut être plus précis dans la fréquence générée.
  else if (vOCR11_calc_new < 7500.0) {
    // Passe à un diviseur de fréquence plus petit, pour être plus précis dans la fréquence générée.
    // Si c'est compatible avec la deuxième fréquence et la fréquence max.

    if (wFact == 1) {
      // Teste que la fréquence ne soit pas trop grande, sinon dépasse les capacités de génération de fréquences de l'Arduino.
      if (vFreq11 > 45000.0) {  // Fréquence max. = 45 [kHz].
        vFreq11 = 45000.0;
        }
      }
    else if (wOCR12_new < 7500) { // Il faut que la deuxième fréquence permette également le changement du facteur
      if (wFact == 8) {
        wFact = 1;
        TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1) fact =    1
        }
      else if (wFact == 64) {
        wFact = 8;
        TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
        }
      else if (wFact == 256) {
        wFact = 64;
        TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
        }
      else if (wFact == 1024) {
        wFact = 256;
        TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
        }
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR11_calc_new = -1 + vFreq_base / vFreq11;
    } // else if (vOCR11_calc_new < 7500.0)

  wOCR11_new   = trunc(vOCR11_calc_new);
  wOCR11_delta = trunc(30000 * (vOCR11_calc_new - wOCR11_new)); // Ecart entre la fréquence désirée et celle obtenue

  if (strCode == "uududdd") {
    // Impose que la fréquence 12 soit la même que la fréquence 11
    // et que les signaux soient en opposition.
    vFreq12 = vFreq11;

    wOCR12_new   = wOCR11_new;
    wOCR12_delta = wOCR11_delta;

    cli(); // Désactive l'interruption globale

    if (bPinNext == 11) {
      wOCR12 = OCR1A + 1;
      wOCR12_gap = wOCR11_gap;      
      bEtat12 = 1 - bEtat11; // Signaux en opposition à la prochaine transition
      }
    else {
      wOCR11 = OCR1A + 1;
      wOCR11_gap = wOCR12_gap;
      bEtat11 = 1 - bEtat12; // Signaux en opposition à la prochaine transition
      }

    sei(); // Active l'interruption globale    
    } // Cas où les deux signaux ont la même fréquence et sont en opposition.
  
  vOCR12_calc_new = -1 + vFreq_base / vFreq12; // Utile si : vFreq_base a changé
  wOCR12_new   = trunc(vOCR12_calc_new);
  wOCR12_delta = trunc(30000 * (vOCR12_calc_new - wOCR12_new)); // Ecart entre la fréquence désirée et celle obtenue
  } // if (vFreq_delta11 != 0)


if (vFreq_delta12 != 0) {
  // Il y a une demande de changement de fréquence sur la pin 12
  if (vFreq12 + vFreq_delta12 > 0.1193) vFreq12 = vFreq12 + vFreq_delta12;
  vOCR12_calc_new = -1 + vFreq_base / vFreq12;

  // Teste que les capacités du compteur TCNT1 lié au timer 1 ne soient pas dépassées
  if (vOCR12_calc_new > 65535.0) {
    // Passe à un diviseur de fréquence (wFact) plus grand, pour générer une plus basse fréquence, une plus haute période.
    
    if (wFact == 1024) {
      // Assure de ne pas descendre en-dessous d'une fréquence trop basse, non atteignable par le Timer 1 de l'Arduino.
      vFreq12 = 0.1193;
      }
    else if (wFact == 256) {
      wFact = 1024;
      TCCR1B = 0b00001101; // (WGM13 = 0 ; WGM12 = 1)  fact = 1024
      }
    else if (wFact == 64) {
      wFact = 256;
      TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
      }
    else if (wFact == 8) {
      wFact = 64;
      TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
      }
    else if (wFact == 1) {
      wFact = 8;
      TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR12_calc_new = -1 + vFreq_base / vFreq12;
    } // if (vOCR12_calc_new > 65535.0)

  // Teste si l'on peut être plus précis dans la fréquence générée.
  else if (vOCR12_calc_new < 7500.0) {
    // Passe à un diviseur de fréquence plus petit, pour être plus précis dans la fréquence générée.
   
    if (wFact == 1) {
      // Teste que la fréquence ne soit pas trop grande, sinon dépasse les capacités de génération de fréquences de l'Arduino.
      if (vFreq12 > 45000.0) {  // Fréquence max. = 45 [kHz].
        vFreq12 = 45000.0;
        }
      }
    else if (wOCR11_new < 7500) { // Il faut que la deuxième fréquence permette également le changement du facteur
      if (wFact == 8) {
        wFact = 1;
        TCCR1B = 0b00001001; // (WGM13 = 0 ; WGM12 = 1) fact =    1
        }
      else if (wFact == 64) {
        wFact = 8;
        TCCR1B = 0b00001010; // (WGM13 = 0 ; WGM12 = 1)  fact =    8
        }
      else if (wFact == 256) {
        wFact = 64;
        TCCR1B = 0b00001011; // (WGM13 = 0 ; WGM12 = 1)  fact =   64
        }
      else if (wFact == 1024) {
        wFact = 256;
        TCCR1B = 0b00001100; // (WGM13 = 0 ; WGM12 = 1)  fact =  256      
        }
      }

    vFreq_base = 8000000.0 / wFact; // Changement de la fréquence de base
    vOCR12_calc_new = -1 + vFreq_base / vFreq12;
    } // else if (vOCR12_calc_new < 7500.0)

  wOCR12_new   = trunc(vOCR12_calc_new);
  wOCR12_delta = trunc(30000 * (vOCR12_calc_new - wOCR12_new)); // Ecart entre la fréquence désirée et celle obtenue

  vOCR11_calc_new = -1 + vFreq_base / vFreq11; // Utile si : vFreq_base a changé
  wOCR11_new   = trunc(vOCR11_calc_new);
  wOCR11_delta = trunc(30000 * (vOCR11_calc_new - wOCR11_new)); // Ecart entre la fréquence désirée et celle obtenue
  } // if (vFreq_delta12 != 0) 

// Le bouton "Select" active ou désactive le signal
if ( (nButtonPush == btnSELECT) && (bMenu1 != MENU_MAX) ) {
  bEtat_enable = 1 - bEtat_enable;
  digitalWrite(13, bEtat_enable); // Indique sur l'état de la pin 13 si le signal est arrêté ou actif.
  digitalWrite( PIN11, bEtat11 & bEtat_enable); // off si signal désactivé
  digitalWrite( PIN12, bEtat12 & bEtat_enable); // off si signal désactivé
  }

AfficheEtat();
} // MenuTreat

void Menu1_Change() {
//===================
// Changement du Menu1
// Un bouton LEFT ou RIGHT a été pressé

bMenu1_Last = bMenu1; // Mémorise le dernier menu

if (nButtonPush == btnLEFT) {
   bMenu1 -= 1;
   if (bMenu1 == 0) bMenu1 = MENU_MAX;  // boucle
  }

if (nButtonPush == btnRIGHT) {
   bMenu1 += 1;
   if (bMenu1 > MENU_MAX) bMenu1 = 1;  // boucle
  }

AfficheEtat();
} // Menu1_Change


void loop() {
//===========
// Boucle principale. Voir également la routine de gestion d'interruption du timer 1

if (TIFR0 & 1) {
  // Le compteur du timer 0 a fait une boucle complète
  // Une boucle se fait en environ 1/61 [s]. Il y a donc 61 boucles par seconde. 
  // Ceci laisse le temps au convertisseur ADC de terminer sa conversion
  TIFR0 = 1; // Efface les flags du registre TIFR0
  
  // Ne lit l'état des boutons que si un bouton a été traité il y a plus de 0,33 seconde.
  if (wTime0 == 0) {
     // Lecture de l'état des boutons, pour changer la fréquence ou la phase du signal
     nButtonPush = read_LCD_buttons();
  
     if ( (nButtonPush == btnLEFT) || (nButtonPush == btnRIGHT) ) Menu1_Change(); // Un bouton pressé, changement de menu
     if ( (nButtonPush == btnUP) || (nButtonPush == btnDOWN) || (nButtonPush == btnSELECT) ) MenuTreat();  // Un bouton pressé, traitement
     }

  if (wTime0 > 0) wTime0--; // Pour assurer qu'un bouton pressé ne soit pas traité trop rapidement plusieurs fois à la suite.

  } // if (TIFR0 & 1) {
} // loop

// routine d'interruption du timer 1, lorsque le registre OCR1A est utilisé, ce qui est le cas dans ce programme.
ISR (TIMER1_COMPA_vect) {
//=======================
// Transition de l'état
// TCNT1 = valeur du compteur, qui est compté de 0 à OCR1A, puis repasse à 0. 
// Une boucle complète fait  OCR1A+1  (cycles d'horloge / wFact)
// Lorsqu'on arrive dans cette fonction de gestion d'interruption, TCNT1 = 0. 
// Il est préférable de ne pas utiliser directement le registre TCNT1, car sa valeur peut varier très rapidement.
// Le compteur  TCNT1  est incrémenté de 1 chaque 0.0625 [us] * facteur défini dans  TCCR1B. (facteur = wFact)

if (!bEtat_enable) return; // changements d'états désactivés.

if (bPinNext == 11) {
  // Transition d'état de la pin 11
  bEtat11 = 1 - bEtat11;

  wOCR11 = wOCR11_new;
  wOCR11_gap += wOCR11_delta;
  if (wOCR11_gap > 30000) { // Il faut ajouter un cycle à wOCR11, pour se rapprocher de la fréquence désirée
    wOCR11++;
    wOCR11_gap -= 30000;
    }
  
  wOCR12 -= OCR1A; // ici, on sait que : wOCR12 > OCR1A

  if (wOCR12 < 160) { // 160 --- 10 [us] si fact = 1  ( 112 est trop petit ! )
    // Transition d'état de la pin 12, faite un peu trop tôt, pour éviter de la rater.

    if (wOCR12 < 20) { // 16 --- 1 [us] si fact = 1
      // Les deux transtions se font en même temps.
      bTmp = PORTB;
      if (bEtat11) bTmp |= 0b00001000;
      else         bTmp &= 0b11110111;
      if (bEtat12) bTmp &= 0b11101111;
      else         bTmp |= 0b00010000;

      PORTB = bTmp; // Les deux transitions en même temps
      }
    else { // La transition 11 se fait quelques micro-secondes avant la transition 12.
      if (bEtat11) PORTB = PORTB | 0b00001000; // Transition d'état de la pin 11
      else         PORTB = PORTB & 0b11110111;

      for (bTmp=0; bTmp<lowByte(wOCR12); bTmp+=24) {} // Pour attendre un peu
      if (bEtat12) PORTB = PORTB & 0b11101111; // Transition d'état de la pin 12
      else         PORTB = PORTB | 0b00010000;
      }

    // Changement de l'état 12
    bEtat12 = 1 - bEtat12;
    wOCR12 += wOCR12_new+1;

    wOCR12_gap += wOCR12_delta;
    if (wOCR12_gap > 30000) { // Il faut ajouter un cycle à wOCR12, pour se rapprocher de la fréquence désirée
      wOCR12++;
      wOCR12_gap -= 30000;
      }
    } // if (wOCR12 < 160)
  else { // Les deux transitions ne sont pas trop rapprochées, wOCR12 >= 160
    // Manière beaucoup plus rapide de changer l'état des pins, permet de générer de plus hautes fréquences.
    // C.f. AVR_Assembler_Lecture6.pdf
    if (bEtat11) PORTB = PORTB | 0b00001000;
    else         PORTB = PORTB & 0b11110111;
    // PORTD contrôle les pins de  0 à  7. bit des unités = port 0
    // PORTB contrôle les pins de  8 à 13  --- bits 0 à 5
    // PORTC contrôle les pins de 14 à 19  --- bits 0 à 5. Correspond aux pins analogiques, pouvant aussi être utilisés en digital.
    }

  wOCR12--;
  }
else {  // (bPinNext == 12)
  // Transition d'état de la pin 12
  bEtat12 = 1 - bEtat12;
  
  wOCR12 = wOCR12_new;
  wOCR12_gap += wOCR12_delta;
  if (wOCR12_gap > 30000) { // Il faut ajouter un cycle à wOCR12, pour se rapprocher de la fréquence désirée
    wOCR12++;
    wOCR12_gap -= 30000;
    }

  wOCR11 -= OCR1A; // ici, on sait que : wOCR11 >= OCR1A

  if (wOCR11 < 160) { // 160 --- 10 [us] si fact = 1
    // Transition d'état de la pin 11, faite un peu trop tôt, pour éviter de la rater.

    if (wOCR11 < 20) { // 16 --- 1 [us] si fact = 1
      // Les deux transtions se font en même temps
      bTmp = PORTB;
      if (bEtat12) bTmp |= 0b00010000;
      else         bTmp &= 0b11101111;
      if (bEtat11) bTmp &= 0b11110111;
      else         bTmp |= 0b00001000;

      PORTB = bTmp; // Les deux transitions en même temps
      }
    else { // La transition 12 se fait quelques micro-secondes avant la transition 11.
      if (bEtat12) PORTB |= 0b00010000; // Transition d'état de la pin 12
      else         PORTB &= 0b11101111;
      
      for (bTmp=0; bTmp<lowByte(wOCR11); bTmp+=24) {} // Pour attendre un peu
      if (bEtat11) PORTB = PORTB & 0b11110111; // Transition d'état de la pin 11
      else         PORTB = PORTB | 0b00001000;
      }

    // Changement de l'état 11
    bEtat11 = 1 - bEtat11;
    wOCR11 += wOCR11_new+1;

    wOCR11_gap += wOCR11_delta;
    if (wOCR11_gap > 30000) { // Il faut ajouter un cycle à wOCR11, pour se rapprocher de la fréquence désirée
      wOCR11++;
      wOCR11_gap -= 30000;
      }
    } // if (wOCR11 < 160)
  else { // Les deux transitions ne sont pas trop rapprochées, wOCR11 >= 160
    if (bEtat12) PORTB = PORTB | 0b00010000; // Transition d'état de la pin 12
    else         PORTB = PORTB & 0b11101111;
    }
    
  wOCR11--;
  }

// Définition de quand aura lieu la prochaine transistion d'état d'une pin et de laquelle.
if (wOCR11 < wOCR12) {
  OCR1A = wOCR11;
  bPinNext = 11;
  }
else { // wOCR11 >= wOCR12
  OCR1A = wOCR12;
  bPinNext = 12;
  }

// Pour des tests, voir si on rate une interruption
//if (TCNT1 >= OCR1A) { bEtat_enable = 0; OCR1A = wOCR11_new; wOCR11 = wOCR11_new;  wOCR12 = wOCR12_new + 200;}
} // ISR

// FIN.
ex0463 blink
ex0464_timer_2_interrupt.ino
Il est également possible de prendre le schéma de l'ex0460 avec le haut-parleur. Il faut brancher la "gate" du transistor sur la pin 11 ou la 12. Cela permet d'écouter la fréquence générée.

    - Fin, ex9999   ;   Précédent, ex0464
ex0464_timer_1_interrupt_digital_oscillo_LCD.ino   TOP
Supplément, sur la génération de sons de fréquences variables.
Voici 4 fichiers au format ".wav" de sons de fréquences variables.
Il est intéressant de voir la forme des ondes dans un logiciel telle que Audacity.

1) Suite de "TACs" de fréquences variants de 1 à 40 [Hz] en 40 secondes.    
Il est intéressant de voir quand on arrête de distinguer les "TACs" et on commence à entendre un son continu.
2) Suite de "TACs" de fréquences variants de 40 à 1 [Hz] en 40 secondes.    
Il est intéressant de voir quand on arrête d'entendre un son continu et on commence à distinguer les "TACs".
3) Son sinusoïdal de fréquences variants de 1 à 100 [Hz] en 10 secondes.    
Il est intéressant de voir quand on commence à entendre un son. Au début, on entend rien.
Nous n'entendons pas les basses fréquences. De plus, les haut-parleurs filtres et éliminent les basses fréquences.
4) Son sinusoïdal de fréquences variants de 5 à 20 [kHz] en 17 secondes.    
La qualité écoutée dans le navigateur n'est peut-être pas suffisante, vu les hautes fréquences en jeu.
La fréquence passe de 5'000 [Hz] à 22'000 [Hz] en augmentant de 1'000 [Hz] par seconde.
Il est intéressant de voir quand on arrête d'entendre un son.
Le jeunes endendent des sons de fréquences bien plus élevés que les personnes âgées.

°) Le programme écrit dans l'environnement Scilab pour générer les fichiers de sons.
La "function ex7()" génère le son sinusoïdal de fréquence variable de 5 à 22 [kHz] en 17 secondes.
°) Une série d'exercices liés aux programmes scilab de générations de sons.


                                Précédent, ex0464
TOP

Plan du Site : Home   arrow   Microcontroleur   arrow   code_exemples.html   arrow   code_exemples2.html ( = http://www.juggling.ch/gisin/microcontroleur/code_exemples2.html )


Page mise à jour le 28 juin 2020 par Bernard Gisin     ( Envoyer un e-mail )
Hébergement par : www.infomaniak.ch