Pour produire le signal sonore, nous allons utiliser une fonction du microcontrôleur qui se nomme NCO, pour Numerically Controlled Oscillator; il s'agit d'un oscillateur qui produit un signal en créneaux dont la fréquence peut être contrôlée à volonté par notre programme.
Le circuit
Le signal produit par l'oscillateur sera accessible par la sortie RC0 du microcontrôleur. Il est fortement déconseillé de brancher un haut-parleur de 8 Ω directement sur la sortie d'un microcontrôleur, car les courants générés dépassent largement les limites permises. Je vous suggère plutôt de piloter indirectement le haut-parleur en utilisant un transistor: le faible courant qui circulera dans la base du transistor contrôlera un courant plus intense qui circulera dans le haut-parleur.
Il existe plusieurs variantes (on peut utiliser un transistor bipolaire ou un transistor à effet de champ), voici le circuit que j'ai utilisé:
Même circuit, en version Fritzing:
Si vous désirez vous simplifier la vie, vous pouvez aussi utiliser un buzzer piézo qui, compte tenu de sa grande impédance, peut sans problème être branché directement à la sortie RC0. Mais la qualité sonore sera moins bonne.
Configuration du projet avec MCC
Nous allons d'abord créer un nouveau projet dans MPLAB Xpress, et ensuite effectuer quelques réglages au moyen du MPLAB Xpress Code Configurator (MCC). Si vous n'êtes pas familier avec ces outils, il pourrait s'avérer utile de jeter un oeil sur cet article.
Dans MCC, vous localisez "NCO" dans la liste intitulée "Device Resources". Vous cliquez deux fois sur "NCO1" afin de l'ajouter à votre projet :
NCO1 apparaît maintenant dans la liste "Project resources":
Je rappelle que NCO signifie Numerically Controlled Oscillateur: il s'agit d'un oscillateur dont nous pourrons contrôler la fréquence à volonté.
Les paramètres par défaut de NCO1 sont adéquats: vous n'avez pas à modifier quoi que ce soit: "NCO mode" est réglé à "FDC_mode", "Output polarity" est à "active_io" et "Clock Source" est réglé à "FOSC".
Notez qu'avec ces réglages, la fréquence maximale de l'oscillateur sera de 500 000 Hz (cette information sera utile lors de la rédaction de notre programme).
Nous allons associer la broche RC0 à l'oscillateur NCO1, en cliquant le cadenas approprié dans le bas de l'écran (sur la ligne NCO1 - ouput), puisque c'est là que nous avons branché notre haut-parleur:
Ne pas oublier de cliquer sur le bouton "Generate" afin que nos réglages soient exportés vers le projet que nous avons créé dans MPLAB Xpress.
Calcul des fréquences
Chaque note musicale de la gamme est associée à une fréquence bien précise. Par exemple, une fréquence de 130,81 Hz donne un do.
Pour choisir la fréquence à laquelle vibrera notre oscillateur, nous devons régler la valeur du registre d'incrémentation ("increment register") qui est un nombre à 16 bits réparti sur deux octets: NCO1INCH (haute valeur) et NCO1INCL (basse valeur). Plus cette valeur est grande, plus la fréquence d'oscillation sera élevée.
Puisque la valeur maximale du timer est de 1 048 575 et que la fréquence maximale de l'oscillateur est de 500 000 Hz, nous calculons la valeur du registre d'incrémentation de la façon suivante:
Par exemple, pour produire une fréquence de 130,81 Hz correspond à un do, l'équation nous donne une valeur décimale de 549 qui se traduit par le nombre binaire 1000100101. Il reste à placer les 8 derniers bits (00100101) dans le registre NCO1INCL et les bits excédentaires (10) dans le registre NCO1INCH. Dans le script présenté ci-dessous, c'est ainsi que j'ai défini la note "C3":
const int NoteC3[2] = {0b00000010, 0b00100101};
Le script
J'ai cherché à faire un programme qui rendrait facile l'écriture d'une mélodie. J'ai donc défini toutes les notes de la gamme sur 3 octaves (ces constantes portent des noms de la forme "NoteA3", "NoteC2", etc. Il s'agit de la convention dans laquelle "A" correspond à un LA, "B" correspond à un SI, etc.
Pour chaque note qu'on désire jouer, il s'agit d'utiliser la commande "Playnote" en donnant comme argument le nom de la note (comme par exemple "NoteC2" pour le do le plus grave) et la durée (1 pour une noire, 2 pour une blanche, 4 pour une ronde...).
Vous pouvez modifier la valeur de la variable globale "tempo" pour changer la vitesse à laquelle la mélodie sera jouée.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************** | |
On joue une mélodie grâce au NCO | |
(numerically controlled oscillator) du | |
MPLAB Xpress Evaluation Board | |
La sortie est RC0. | |
Plus d'informations: | |
http://electroniqueamateur.blogspot.com/2018/02/jouer-une-melodie-avec-le-mplab-xpress.html | |
**************************************************/ | |
#include "mcc_generated_files/mcc.h" | |
const int tempo = 100; //durée, en millisecondes, de la note la plus courte | |
const int pause = 20; // durée, en millisecondes, de la courte pause après chaque note; doit être plus petite que tempo! | |
//Définition des notes | |
const int NoteC2[2] = {0b00000001, 0b00010010}; | |
const int NoteDb2[2]= {0b00000001, 0b00100011}; | |
const int NoteD2[2] = {0b00000001, 0b00110100}; | |
const int NoteEb2[2] = {0b00000001, 0b01000110}; | |
const int NoteE2[2] = {0b00000001, 0b01011010}; | |
const int NoteF2[2] = {0b00000001, 0b01101110}; | |
const int NoteGb2[2] = {0b00000001, 0b10000100}; | |
const int NoteG2[2] = {0b00000001, 0b10011011}; | |
const int NoteAb2[2] = {0b00000001, 0b10110011}; | |
const int NoteA2[2] = {0b00000001, 0b11001101}; | |
const int NoteBb2[2] = {0b00000001, 0b11101001}; | |
const int NoteB2[2] = {0b00000010, 0b00000110}; | |
const int NoteC3[2] = {0b00000010, 0b00100101}; | |
const int NoteDb3[2]= {0b00000010, 0b01000101}; | |
const int NoteD3[2] = {0b00000010, 0b01101000}; | |
const int NoteEb3[2] = {0b00000010, 0b10001100}; | |
const int NoteE3[2] = {0b00000010, 0b10110011}; | |
const int NoteF3[2] = {0b00000010, 0b11011100}; | |
const int NoteGb3[2] = {0b00000011, 0b00001000}; | |
const int NoteG3[2] = {0b00000011, 0b00110110}; | |
const int NoteAb3[2] = {0b00000011, 0b01100111}; | |
const int NoteA3[2] = {0b00000011, 0b10011011}; | |
const int NoteBb3[2] = {0b00000011, 0b11010010}; | |
const int NoteB3[2] = {0b00000100, 0b00001100}; | |
const int NoteC4[2] = {0b00000100, 0b01001001}; | |
const int NoteDb4[2]= {0b00000100, 0b10001011}; | |
const int NoteD4[2] = {0b00000100, 0b11010000}; | |
const int NoteEb4[2] = {0b00000101, 0b00011001}; | |
const int NoteE4[2] = {0b00000101, 0b01100111}; | |
const int NoteF4[2] = {0b00000101, 0b10111001}; | |
const int NoteGb4[2] = {0b00000110, 0b00010000}; | |
const int NoteG4[2] = {0b00000110, 0b01101100}; | |
const int NoteAb4[2] = {0b00000110, 0b11001110}; | |
const int NoteA4[2] = {0b00000111, 0b100110101}; | |
const int NoteBb4[2] = {0b00000111, 0b10100011}; | |
const int NoteB4[2] = {0b00001000, 0b00010111}; | |
const int NoteC5[2] = {0b00001000, 0b10010011}; | |
// Puisque __delay_ms() n'accepte pas de variable, on | |
// écrit une routine qui attend pendant un certain | |
// nombre de millisecondes | |
attend(int milliseconds) | |
{ | |
while(milliseconds > 0) | |
{ | |
__delay_ms(1); | |
milliseconds--; | |
} | |
} | |
// on joue la note avec la fréquence et la durée requises | |
void playnote(int laNote[2], int duree) | |
{ | |
NCO1CONbits.N1EN = 1; // activation du NCO | |
// réglage de la fréquence | |
NCO1INCH = laNote[0]; // increment register (high value) | |
NCO1INCL = laNote[1]; // increment register (low value) | |
attend(duree*tempo-pause); // on attend pendant que la note joue | |
NCO1CONbits.N1EN = 0; // désactivation du NCO | |
attend(pause); // court silence pour séparer les notes les unes des autres | |
} | |
void main(void) | |
{ | |
SYSTEM_Initialize(); | |
while (1) | |
{ | |
// playnote (note, durée) | |
playnote(NoteC2, 3); | |
playnote(NoteC2, 1); | |
playnote(NoteE2, 3); | |
playnote(NoteE2, 1); | |
playnote(NoteG2, 3); | |
playnote(NoteG2, 1); | |
playnote(NoteA2, 3); | |
playnote(NoteA2, 1); | |
playnote(NoteBb2, 3); | |
playnote(NoteBb2, 1); | |
playnote(NoteA2, 3); | |
playnote(NoteA2, 1); | |
playnote(NoteG2, 3); | |
playnote(NoteG2, 1); | |
playnote(NoteA2, 3); | |
playnote(NoteG2, 1); | |
playnote(NoteC4, 3); | |
playnote(NoteC4, 1); | |
playnote(NoteE4, 3); | |
playnote(NoteE4, 1); | |
playnote(NoteG4, 3); | |
playnote(NoteG4, 1); | |
playnote(NoteA4, 3); | |
playnote(NoteA4, 1); | |
playnote(NoteBb4, 3); | |
playnote(NoteBb4, 1); | |
playnote(NoteA4, 3); | |
playnote(NoteA4, 1); | |
playnote(NoteG4, 3); | |
playnote(NoteG4, 1); | |
playnote(NoteA4, 3); | |
playnote(NoteG4, 1); | |
playnote(NoteF3, 3); | |
playnote(NoteF3, 1); | |
playnote(NoteA3, 3); | |
playnote(NoteA3, 1); | |
playnote(NoteC4, 3); | |
playnote(NoteC4, 1); | |
playnote(NoteD4, 3); | |
playnote(NoteD4, 1); | |
playnote(NoteEb4, 3); | |
playnote(NoteEb4, 1); | |
playnote(NoteD4, 3); | |
playnote(NoteD4, 1); | |
playnote(NoteC4, 3); | |
playnote(NoteC4, 1); | |
playnote(NoteD4, 3); | |
playnote(NoteC4, 1); | |
playnote(NoteF2, 3); | |
playnote(NoteF2, 1); | |
playnote(NoteA2, 3); | |
playnote(NoteA2, 1); | |
playnote(NoteC3, 3); | |
playnote(NoteC3, 1); | |
playnote(NoteD3, 3); | |
playnote(NoteD3, 1); | |
playnote(NoteEb3, 3); | |
playnote(NoteEb3, 1); | |
playnote(NoteD3, 3); | |
playnote(NoteD3, 1); | |
playnote(NoteC3, 3); | |
playnote(NoteC3, 1); | |
playnote(NoteD3, 3); | |
playnote(NoteC3, 1); | |
} | |
} | |
Voici, pour terminer, une courte vidéo permettant d'entendre le résultat.
D'autres articles du même genre
J'ai déjà publié plusieurs articles impliquant le MPLAB Xpress Evaluation Board: programmation des entrées/sorties, communication série par USB, utilisation d'une entrée analogique, contrôle d'un moteur à courant continu.
Si vous désirez jouer une mélodie en utilisant autre chose que le MPLAB Xpress Evaluation Board, ces articles pourraient vous intéresser: brancher un haut-parleur à l'Arduino, jouer une mélodie avec le STM32 Nucleo, jouer une mélodie avec l'ATTiny85, Bip bip: 5 circuits qui produisent un son.
Yves Pelletier (Twitter, Facebook)
Aucun commentaire:
Enregistrer un commentaire