Mélodies avec un Arduino UNO
Voici un programme écrit par Pierre-Yves Rochat, professeur du MOOC sur les µcontrôleurs de l’EPFL qui montre comment utiliser les timers et les interruptions de l’Arduino UNO (ou du Diduino) pour jouer des mélodies.
Le fonctionnement du programme est décrit dans ce document :
Voir aussi la vidéo d’introduction au MOOC :
/*
* JOUER DES MÉLODIES SUR ARDUINO UNO
*
* Programme original de Pierre-Yves Rochat — pyr.ch
* Source :
* https://d396qusza40orc.cloudfront.net/microcontroleurs/lecture_doc/MelodieDoc.pdf
*
* Modifié par Nicolas Jeanmonod, décembre 2014
*
*/
#define HPPin PORTD1 // Le HP du LearnCBot est sur la pin PORTD1
#define HautParleurEnSortie DDRD |= ( 1 << HPPin )
#define TicHautParleur PORTD ^= ( 1 << HPPin )
// DURÉE DES NOTES
// La durée des notes est définie sur les 3 bits de poids forts. La Noire doit
// être définie à la valeur 0. Cela permet de ne pas devoir la spécifier
// explicitement dans les partitions. Les valeurs des autres durées sont sans
// importance.
#define TripleCroche 0b00100000
#define DoubleCroche 0b01000000
#define Croche 0b01100000
#define Noire 0b00000000
#define NoireP 0b10000000
#define Blanche 0b10100000
#define BlancheP 0b11000000
#define Ronde 0b11100000
#define tCr DoubleCroche
#define dCr DoubleCroche
#define Cr Croche
#define Nr Noire
#define Np NoireP
#define Bl Blanche
#define Blp BlancheP
#define Ro Ronde
#define Fin 0
#define Reprise 0xFE
#define DivTimer8 0b010
const unsigned int NotePeriode[] =
{
4545, 4290, 4050, 3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408,
2273, 2145, 2025, 1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204,
1136, 1073, 1012, 956, 902, 851, 804, 758
};
const byte NoteFrequenceDiv8[] =
{
28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104,
110, 117, 123, 131, 139, 147, 156, 165
};
enum notes
{
la1, la1d, si1, do2, do2d, re2, re2d, mi2, fa2, fa2d, sol2, sol2d,
la2, la2d, si2, do3, do3d, re3, re3d, mi3, fa3, fa3d, sol3, sol3d,
la3, la3d, si3, do4, do4d, re4, re4d, mi4
};
unsigned int Pique;
byte MasqueDuree = 0b11100000;
byte MasqueNote = 0b00011111;
byte* DebutMelodie;
byte* PtMelodie;
unsigned int PeriodesRestantes;
unsigned int PeriodeCourante;
unsigned int PeriodesOff;
byte NoteCourante;
byte FrereJacques[] =
{
do3+Cr, re3+Cr, mi3+Cr, do3+Cr, do3+Cr, re3+Cr, mi3+Cr, do3+Cr,
mi3+Cr, fa3+Cr, sol3, mi3+Cr, fa3+Cr, sol3,
sol3+dCr, la3+dCr, sol3+dCr, fa3+dCr, mi3+Cr, do3+Cr, sol3+dCr, la3+dCr, sol3+dCr, fa3+dCr, mi3+Cr, do3+Cr,
do3+Cr, sol2+Cr, do3, do3+Cr, sol2+Cr, do3,
// Fin ou Reprise
Reprise
};
// https://books.google.ch/books?id=RCYakK7vmoEC&pg=PA459&lpg=PA459&dq=jingle+bells+garageband&source=bl&ots=Nxg1Yr-DCE&sig=4DhKrlQlvWNL8qNni7kGt5UcWss&hl=fr&sa=X&ei=H46YVI_eDoHlaLqqgYAF&ved=0CGIQ6AEwCQ#v=onepage&q=jingle%20bells%20garageband&f=false
byte JingleBells[]=
{
// Intro
fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, mi3+Cr, mi3+Cr, mi3+dCr, mi3+dCr,
sol3+Cr, sol3+Cr, fa3+Cr, re3+Cr, do3, sol3,
// Verse
sol2+Cr, mi3+Cr, re3+Cr, do3+Cr, sol2+Np, mi2+dCr, fa2+dCr,
sol2+Cr, mi3+Cr, re3+Cr, do3+Cr, la2+Bl,
la2+Cr, fa3+Cr, mi3+Cr, re3+Cr, si2+Bl,
sol3+Cr, sol3+Cr, fa3+Cr, re3+Cr, mi3+Bl,
sol2+Cr, mi3+Cr, re3+Cr, do3+Cr, sol2+Bl,
sol2+Cr, mi3+Cr, re3+Cr, do3+Cr, la2+Np, la2+Cr,
la2+Cr, fa3+Cr, mi3+Cr, re3+Cr, sol3+Cr, sol3+Cr, sol3+Cr, sol3+Cr,
la3+Cr, sol3+Cr, fa3+Cr, re3+Cr, do3, sol3,
// Chorus / Refrain
mi3+Cr, mi3+Cr, mi3, mi3+Cr, mi3+Cr, mi3,
mi3+Cr, sol3+Cr, do3+Cr, re3+Cr, mi3+Bl,
fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, mi3+Cr, mi3+Cr, mi3+dCr, mi3+dCr,
mi3+Cr, re3+Cr, re3+Cr, mi3+Cr, re3, sol3,
mi3+Cr, mi3+Cr, mi3, mi3+Cr, mi3+Cr, mi3,
mi3+Cr, sol3+Cr, do3+Cr, re3+Cr, mi3+Bl,
fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, mi3+Cr, mi3+Cr, mi3+dCr, mi3+dCr,
sol3+Cr, sol3+Cr, fa3+Cr, re3+Cr, do3+Bl,
// Outro
fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, fa3+Cr, mi3+Cr, mi3+Cr, mi3+Cr,
sol3+Cr, sol3+Cr, fa3+Cr, re3+Cr, do3+Bl,
// Fin ou Reprise
Fin
};
/*
*
*/
ISR( TIMER1_COMPA_vect )
{
if( PeriodesRestantes == 0 )
{
NoteCourante = *PtMelodie;
switch( NoteCourante )
{
case Fin:
TIMSK1 &= ~( 1 << OCIE1A );
return;
case Reprise:
PtMelodie = DebutMelodie;
NoteCourante = *PtMelodie;
break;
default:
break;
}
PtMelodie++;
PeriodeCourante = NotePeriode[ NoteCourante & MasqueNote ];
PeriodesRestantes = NoteFrequenceDiv8[ NoteCourante & MasqueNote ];
NoteCourante &= MasqueDuree;
switch( NoteCourante )
{
case TripleCroche: PeriodesRestantes *= 1; break;
case DoubleCroche: PeriodesRestantes *= 2; break;
case Croche: PeriodesRestantes *= 4; break;
case 0: PeriodesRestantes *= 8; break;
case NoireP: PeriodesRestantes *= 12; break;
case Blanche: PeriodesRestantes *= 16; break;
case BlancheP: PeriodesRestantes *= 24; break;
case Ronde: PeriodesRestantes *= 32; break;
}
PeriodesOff = ( PeriodesRestantes * Pique ) / 100;
}
OCR1A = TCNT1 + PeriodeCourante;
if( PeriodesRestantes > PeriodesOff ){ TicHautParleur; }
PeriodesRestantes--;
}
/*
*
*/
void
InitMelodie()
{
HautParleurEnSortie;
TCCR1B = ( DivTimer8 << CS10 ); // choix de la fréquence : 16 MHz / 8 = 2 MHz
TIMSK1 |= ( 1 << OCIE1A ); // enclenche l’interrupt du timer
sei(); // activation générale des interruptions
}
/*
*
*/
void
JoueMelodie( byte* melodie )
{
DebutMelodie = melodie;
PtMelodie = DebutMelodie;
PeriodesRestantes = 0;
InitMelodie();
}
/*
*
*/
int
mainInit()
{
// Nécessaire pour utiliser la pin PORTD1 sur le Diduino.
Serial.end();
// Toutes les pins de tous les ports en INPUT-PULLUP
DDRB = 0b00000000;
PORTB = 0b11111111;
DDRC = 0b00000000;
PORTC = 0b11111111;
DDRD = 0b00000000;
PORTD = 0b11111111;
// Excepté la LED du board
DDRB |= ( 1<<PORTB5 );
PORTB &=~( 1<<PORTB5 );
}
/*
*
*/
int
main()
{
mainInit();
Pique = 20;
switch( 1 )
{
case 0:
JoueMelodie( FrereJacques );
break;
case 1:
JoueMelodie( JingleBells );
break;
default:
break;
}
volatile unsigned int i;
while( true )
{
// Clignote la LED du board
for( i=0; i<65535; i++ ){}
PORTB ^= ( 1<<PORTB5 );
}
}