I. Ressources matérielles▲
Cette fois, j'utilise un buzzer acheté chez un revendeur de matériel électronique. La première chose que j'avais à faire était de trouver la documentation du constructeur (datasheet), car il n'y avait aucune indication sur la capsule du buzzer excepté le nom de la marque, KEPO. Après quelques recherches, j'ai découvert la référence KPR-2310. Il s'agit d'un buzzer piézoélectrique conçu pour fonctionner avec des signaux périodiques rectangulaires 12 V crête à crête. La bonne nouvelle est que le courant maximal consommé par ce composant n'excède pas 3 mA, ce qui est largement en deçà des 40 mA tolérés par broche d'entrée-sortie de l'ATmega328P. Cela signifie que je peux alimenter le buzzer sans résistance de protection. J'étais par contre inquiet du fait que ce buzzer était conçu pour du 12 V, et qu'il risquait de ne pas fonctionner avec le niveau de tension 5 V d'une entrée-sortie de la carte Arduino. Il s'avère malgré tout que le niveau sonore produit est tout à fait convenable.
II. Utilisation d'un Timer▲
Afin de produire un son, il fallait configurer une entrée-sortie pour générer un signal rectangulaire périodique à une certaine fréquence. Il y a au moins deux manières pour cela : soit en faisant basculer directement l'état du port, soit en utilisant un timer. J'ai décidé d'utiliser la sortie OC0A du Timer0, qui correspond à la broche 6 du port D de l'ATmega328P, soit le connecteur 6 de la carte Arduino UNO. Ces informations sont contenues dans la documentation de l'ATmega328P et les schémas de l'Arduino UNO.
Le Timer0 a plusieurs modes de fonctionnement, et celui qui correspond le mieux à mon besoin est le mode Clear Timer on Compare Match (CTC). Avec ce mode de fonctionnement, je peux faire basculer la sortie OC0A à une fréquence que je peux régler, et ainsi générer un signal périodique rectangulaire de rapport cyclique 50 % pour piloter le buzzer. La fréquence peut être réglée en utilisant la formule suivante provenant de la documentation :
kitxmlcodelatexdvpf_{OCnx}=\frac{f_{clkIO}}{2\cdot N\cdot (1+OCRnx)}finkitxmlcodelatexdvpOù kitxmlcodeinlinelatexdvpf_{OCnx}finkitxmlcodeinlinelatexdvp est la fréquence du signal, kitxmlcodeinlinelatexdvpf_{clkIO}finkitxmlcodeinlinelatexdvp vaut 16 MHz pour l'Arduino UNO, kitxmlcodeinlinelatexdvpNfinkitxmlcodeinlinelatexdvp est la valeur du prescaler, et kitxmlcodeinlinelatexdvpOCRnxfinkitxmlcodeinlinelatexdvp est la sortie Output Compare Register voulue, dans mon cas la sortie OCR0A correspondant à la broche OC0A. Par exemple, si on veut une note correspondant à la fréquence standard 440 Hz (NDLR : la fréquence du la du diapason standard), on fixe le prescaler à 256 et le registre OCR0A à 70 (NDLR : 16.106 / (2 x 256 x (1 + 70)) ≈ 440, CQFD). Avec cette information, j'ai écrit le code buzzer.c suivant qui joue deux notes, une à 440 Hz et l'autre à 880 Hz (NDLR : c'est-à -dire la fréquence du la de l'octave supérieure) :
#include <avr/io.h>
#include <util/delay.h>
enum
t0_prescaler
{
T0_PRESCALER_1 =
_BV
(
CS00),
T0_PRESCALER_8 =
_BV
(
CS01),
T0_PRESCALER_64 =
_BV
(
CS00) |
_BV
(
CS01),
T0_PRESCALER_256 =
_BV
(
CS02),
T0_PRESCALER_1024 =
_BV
(
CS02) |
_BV
(
CS00),
}
;
static
void
t0_set_prescaler
(
enum
t0_prescaler ps)
{
TCCR0B =
ps;
}
static
unsigned
short
t0_get_prescaler_rate
(
enum
t0_prescaler ps)
{
unsigned
short
rate;
switch
(
ps)
{
case
T0_PRESCALER_1:
rate =
1
;
break
;
case
T0_PRESCALER_8:
rate =
8
;
break
;
case
T0_PRESCALER_64:
rate =
64
;
break
;
case
T0_PRESCALER_256:
rate =
256
;
break
;
case
T0_PRESCALER_1024:
rate =
1024
;
break
;
default
:
rate =
0
;
break
;
}
return
rate;
}
static
unsigned
long
div_round
(
unsigned
long
d, unsigned
long
q)
{
return
(
d +
(
q/
2
)) /
q;
}
static
void
t0_set_ctc_a
(
unsigned
long
hz, unsigned
long
timer_freq)
{
OCR0A =
div_round
(
timer_freq, hz*
2
) -
1
;
TCCR0A =
_BV
(
COM0A0) // toggle
|
_BV
(
WGM01); // CTC
}
int
main
(
void
)
{
unsigned
long
timer_freq;
enum
t0_prescaler ps =
T0_PRESCALER_256;
DDRD |=
_BV
(
DDD6);
t0_set_prescaler
(
ps);
timer_freq =
div_round
(
F_CPU, t0_get_prescaler_rate
(
ps));
while
(
1
)
{
t0_set_ctc_a
(
440
, timer_freq);
_delay_ms
(
200
);
t0_set_ctc_a
(
880
, timer_freq);
_delay_ms
(
200
);
}
return
0
;
}
Soyez conscient que le code ne vérifie pas la validité des valeurs renseignées, et que les valeurs de fréquence possibles sont peut-être limitées.
III. Téléversement du code et tests▲
Pour téléverser le programme dans l'Arduino UNO, j'ai lancé les commandes suivantes :
$
avr-gcc -Os -DF_CPU
=
16000000UL -mmcu
=
atmega328p -o buzzer buzzer.c
$
avr-objcopy -O ihex -R .eeprom buzzer buzzer.hex
$
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200
-U flash:w:buzzer.hex
J'ai relié le buzzer aux connecteurs GND et 6 de l'Arduino UNO, et le buzzer s'est mis à jouer les deux notes l'une après l'autre dans une boucle sans fin.
À partir de là , il est possible de trouver d'autres solutions, d'autres broches et différentes manières pour gérer les notes, par exemple en utilisant des interruptions au lieu de pauses pour contrôler la durée des notes. On pourrait aussi gérer deux buzzers connectés à deux broches et jouer une mélodie polyphonique.
IV. Notes de la Rédaction Developpez.com▲
Cet article est la traduction du billet écrit par Francesco Balducci (alias Balau). Retrouvez la version originale de cet article ainsi que les autres chapitres consacrés à Arduino sur son blog.
Nous remercions les membres de la Rédaction de Developpez.com pour le travail de traduction et de relecture qu'ils ont effectué, en particulier : f-leb, lolo78 et Malick SECK.