Antes que nada debo aclarar que no estamos hablando de música con la calidad un CD, ni stereo, ni nada por el estilo, es mas, dudo que tenga alguna calidad, lo digo para no crear falsas expectativas.
En esta ocasión vamos a interpretar alguna melodía utilizando la técnica que vimos en "Generar sonido con PIC" y sólo lo haremos a modo didáctico, ya que es muy útil para comprender como el microcontrolador administra los tiempos y como se utilizan los puertos. Este ejercicio bien podría reemplazar al ya mítico parpadeo de un LED con PIC16F84A ya que es, en teoría, el mismo principio pero con el agregado del control de la frecuencia.
Ahora bien, aquí viene lo mas interesante, investigando un poco me enteré de como es esto de las notas al encontrar este artículo: Frecuencias de las notas musicales
, en el que se explica la fórmula para obtener la frecuencia de cada nota musical. Una de las fórmulas, la mas sencilla para llevar a cabo en un programa, es esta:

Con esta fórmula pude sacar las frecuencias para las octavas 0 a la 6, que son las que mejor se ejecutan en el PIC, mas arriba o mas abajo ya es molesto o inaudible. Dichas frecuencias estan en la siguiente tabla:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
| DO | 32,70 | 65,40 | 130,81 | 261,62 | 523,25 | 1046,50 | 2093,00 |
| DO# | 34,64 | 69,29 | 138,59 | 277,18 | 554,36 | 1108,73 | 2217,46 |
| RE | 36,70 | 73,41 | 146,83 | 293,66 | 587,32 | 1174,65 | 2349,31 |
| RE# | 38,89 | 77,78 | 155,56 | 311,12 | 622,25 | 1244,50 | 2489,01 |
| MI | 41,20 | 82,40 | 164,81 | 329,62 | 659,25 | 1318,51 | 2637,02 |
| FA | 43,65 | 87,30 | 174,61 | 349,22 | 698,45 | 1396,91 | 2793,82 |
| FA# | 46,24 | 92,49 | 184,99 | 369,99 | 739,98 | 1479,97 | 2959,95 |
| SOL | 48,99 | 97,99 | 195,99 | 391,99 | 783,99 | 1567,98 | 3135,96 |
| SOL# | 51,91 | 103,82 | 207,65 | 415,30 | 830,60 | 1661,21 | 3322,43 |
| LA | 55,00 | 110,00 | 220,00 | 440,00 | 880,00 | 1760,00 | 3520,00 |
| LA# | 58,27 | 116,54 | 233,08 | 466,16 | 932,32 | 1864,65 | 3729,31 |
| SI | 61,73 | 123,47 | 246,94 | 493,88 | 987,76 | 1975,53 | 3951,06 |
En esta otra tabla están representadas los microsegundos necesarios entre estado alto y bajo para generar dichas frecuencias:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
| DO | 15289,02 | 7644,51 | 3822,25 | 1911,12 | 955,56 | 477,78 | 238,89 |
| DO# | 14430,91 | 7215,45 | 3607,72 | 1803,86 | 901,93 | 450,96 | 225,48 |
| RE | 13620,97 | 6810,48 | 3405,24 | 1702,62 | 851,31 | 425,65 | 212,82 |
| RE# | 12856,48 | 6428,24 | 3214,12 | 1607,06 | 803,53 | 401,76 | 200,88 |
| MI | 12134,90 | 6067,45 | 3033,72 | 1516,86 | 758,43 | 379,21 | 189,60 |
| FA | 11453,82 | 5726,91 | 2863,45 | 1431,72 | 715,86 | 357,93 | 178,96 |
| FA# | 10810,97 | 5405,48 | 2702,74 | 1351,37 | 675,68 | 337,84 | 168,92 |
| SOL | 10204,20 | 5102,10 | 2551,05 | 1275,52 | 637,76 | 318,88 | 159,44 |
| SOL# | 9631,48 | 4815,74 | 2407,87 | 1203,93 | 601,96 | 300,98 | 150,49 |
| LA | 9090,90 | 4545,45 | 2272,72 | 1136,36 | 568,18 | 284,09 | 142,04 |
| LA# | 8580,67 | 4290,33 | 2145,16 | 1072,58 | 536,29 | 268,14 | 134,07 |
| SI | 8099,07 | 4049,53 | 2024,76 | 1012,38 | 506,19 | 253,09 | 126,54 |
Observando la última tabla con atención se ve que la nota de la octava siguiente es igual a la octava actual dividido por 2, o con la equivalencia en CCS C una rotación a la derecha.
De esto se deduce que teniendo un array con los valores de las diferentes notas se puede ir rotando a la derecha tantas veces como octavas queramos subir para obtener la nota y octava precisas.
En C, la función encargada de hacerlo se ve así:
| #define Speaker PIN_B0 #define nDO 0 // DO #define nDO_ 1 // DO# #define nRE 2 // RE #define nRE_ 3 // RE# #define nMI 4 // MI #define nFA 5 // FA #define nFA_ 6 // FA# #define nSOL 7 // SOL #define nSOL_ 8 // SOL# #define nLA 9 // LA #define nLA_ 10 // LA# #define nSI 11 // SI int16 FreqNota[12]={ // retardos entre estado alto // y bajo para generar las notas 15289, // DO 14430, // DO# 13620, // RE 12856, // RE# 12134, // MI 11453, // FA 10810, // FA# 10204, // SOL 9631, // SOL# 9090, // LA 8580, // LA# 8099 // SI }; void Play(int nota, int octava, int16 duracion){ int16 fn; int16 mS_Transcurridos=0; // Contadores necesarios // para controlar la duración int16 CiclosL=0; // Contandor de uS fn=FreqNota[nota]; // Define los retardos para generar // la frecuencia de cada nota fn>>=(octava); // Adapta la frecuencia a la octava actual // haciendo una rotación // a la derecha por octava do{ output_high(Speaker); // Genera la frecuancia delay_us(fn); // con los retardos mientras CiclosL+=(fn); // aumenta el contador de // ciclos transcurridos output_low(Speaker); // en dos partes para repartir el delay_us(fn); // trabajo entre estado alto y bajo. CiclosL+=(fn); // CiclosL+=25; // Compensador. while(CiclosL>999){ // Se queda en el bucle mientras CiclosL // sea menor a 1000 (1 mS) CiclosL-=1000; // Le resta 1000 a CiclosL mS_Transcurridos++; // y le suma 1 a mS_Transcurridos. CiclosL+=25; // Compensador. } }while (duracion>mS_Transcurridos); // Repite el bucle hasta que haya // pasado el tiempo indicado. } |
Bueno, sabiendo como ejecutar las notas musicales ahora es tiempo de interpretar una melodía.
Como aclaré antes de música no tengo conocimientos, pero buscando alguna melodía a interpretar en el PIC recordé que en BASIC (el antíguo) había una función llamada PLAY y que interpretaba las notas musicales con el PC Speaker. Buscando en Google encontré un artículo titulado DJ QBASIC
donde hay diez canciones conocidas. Ahora lo que resta es adaptar el código BASIC a CCS con la función PLAY para C que vimos en la entrada "Generar sonido con PIC", y es cuando vemos otro pequeño inconveniente, las notas musicales en BASIC están en el sistema de notación musical inglés y nosotros usamos el latino, en la WikiPedia encontré este artículo donde hablan de eso y se muestra la equivalencia: Escala musical
.
En base a eso hice este ejemplo que interpreta una melodía según se pulse una tecla, si se pulsa la tecla conectada a RB1 suena "Pop Corn", si se pulsa RB2 suena "Ecuador" y si se pulsa RB3 suena "The lion sleep tonight".
| /////////////////////////////////////////////////////////////////////// // // // PICMusic v.1.00 // // (c) 2010 Gerardo Ariel Ramírez. // // // /////////////////////////////////////////////////////////////////////// // // // uControlador: PIC16F876A Lenguaje: CCS C // // Xtal: 4MHz // // // /////////////////////////////////////////////////////////////////////// #include <16f876a.h> #use delay(clock=4000000) #use fast_io(all) #fuses HS #FUSES NOPUT #FUSES NOBROWNOUT #define Speaker PIN_B0 #define nDO 0 // DO #define nDO_ 1 // DO# #define nRE 2 // RE #define nRE_ 3 // RE# #define nMI 4 // MI #define nFA 5 // FA #define nFA_ 6 // FA# #define nSOL 7 // SOL #define nSOL_ 8 // SOL# #define nLA 9 // LA #define nLA_ 10 // LA# #define nSI 11 // SI int16 FreqNota[12]={ // retardos entre estado alto // y bajo para generar las notas 15289, // DO 14430, // DO# 13620, // RE 12856, // RE# 12134, // MI 11453, // FA 10810, // FA# 10204, // SOL 9631, // SOL# 9090, // LA 8580, // LA# 8099 // SI }; void Play(int nota,int octava,int16 duracion); void PlayCancion(int cancion); void main(){ set_tris_b(14); // B<3:1>: Pulsadores B0: Speaker while (true){ if(input(PIN_B1))PlayCancion(1); //Si pulso switch 1 toca // Pop Corn if(input(PIN_B2))PlayCancion(2); //Si pulso switch 2 toca // Ecuador if(input(PIN_B3))PlayCancion(3); //Si pulso switch 3 toca // The lion sleep tonight } } void Play(int nota, int octava, int16 duracion){ int16 fn; int16 mS_Transcurridos=0; int16 CiclosL=0; fn=FreqNota[nota]; // Define los retardos para generar // la frecuencia de cada nota fn>>=(octava); // Adapta la frecuencia // a la octava actual do{ output_high(Speaker); // Genera la frecuancia delay_us(fn); // con los retardos mientras CiclosL+=(fn); // aumenta el contador de // ciclos transcurridos output_low(Speaker); // en dos partes para repartir el delay_us(fn); // trabajo entre estado alto y bajo. CiclosL+=(fn); // CiclosL+=25; // Compensador. while(CiclosL>999){ // Se queda en el bucle mientras // CiclosL sea menor a 1000 (1 mS) CiclosL-=1000; // Le resta 1000 a CiclosL mS_Transcurridos++; // y le suma 1 a mS_Transcurridos. CiclosL+=25; // Compensador. } }while (duracion>mS_Transcurridos); // Repite el bucle hasta // que haya pasado el // tiempo indicado. } void PlayCancion(int cancion){ switch (cancion){ case 1: //POP CORN play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL ,4,166); play (nRE_ ,4,166); play (nSOL ,4,166); play (nDO ,4,166); delay_ms (166); play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL ,4,166); play (nRE_ ,4,166); play (nSOL ,4,166); play (nDO ,4,166); delay_ms (166); play (nDO ,5,166); play (nRE ,5,166); play (nRE_ ,5,166); play (nRE ,5,166); play (nRE_ ,5,166); play (nDO ,5,166); play (nRE ,5,166); play (nDO ,5,166); play (nRE ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL_ ,4,166); play (nDO ,5,166); break; case 2: //ECUADOR play (nLA ,3,100); delay_ms (200); play (nMI ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nLA ,3,100); delay_ms (200); play (nMI ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (200); play (nSOL ,3,100); delay_ms (200); play (nMI ,4,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nMI ,4,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (200); play (nLA ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); break; case 3: //The lion sleep tonight play (nDO ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (125); play (nMI ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (250); play (nMI ,3,125); play (nFA ,3,125); delay_ms (250); play (nMI ,3,125); delay_ms (125); play (nRE ,3,125); delay_ms (250); play (nDO ,3,125); delay_ms (250); play (nRE ,3,125); play (nMI ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (125); play (nDO ,3,125); delay_ms (250); delay_ms (125); play (nMI ,3,125); delay_ms (125); play (nRE ,3,500); break; } } |
Si no lo quieres copiar, lo quieres modificar o lo quieres ya compilado, te puedes bajar el proyecto completo haciendo click en este link
.
hola me parece interesante tu nota, estoy haciendo algo parecido, lo que no se es como hago para no tener una sola salida, quiera tener 8 salidas cada una correspondiemte a una nota, y como entrada un pequeño teclado, cosa que si presiono dos teclas se escuchan los dos sonidos, me puedes ayudar en esa parte?
ResponderSuprimirSaludos
Se podría probar con un cristal de 20MHz y controlando los retardos con el Timer0, aún así habría que ver si el PIC es capaz de controlar las frecuencias sin saltos.
ResponderSuprimirRecuerda que es un microcontrolador de gama baja y no es tan eficaz como un procesador o incluso algun microcontrolador de mas alta gama, pero sería cuestión de probar.
Saludos.
estas chavo compa, aprende .ASM
ResponderSuprimirSe podría hacer en Assembler, pero no le veo la necesidad de usarlo aquí, ya que esto es un ejemplo a modo de introducción, muy básico. Utilizando CCS C se simplifican muchas cosas, lo que ayuda a la comprensión.
ResponderSuprimirSaludos.
Sos un grande....cuando sea mayor quiero saber la mitad de lo que ya sabés...
ResponderSuprimirMe la paso leyendo sobre pics y su programacion y no termino de armar nada, solo leo, leo y leo. No me Animo !!
A mi me pasaba lo mismo hasta no hace mucho, sólo es cuestión de empezar por armar algo, por mas simple que sea, te darás cuenta que es mas sencillo de lo que parece a simple vista.
ResponderSuprimirSi no tienes los materiales, o no te atreves con el soldador, puedes intentarlo con Proteus (que tiene un simulador en tiempo real).
Ánimo y suerte!
Saludos.
me doy cuenta de que en el link que proporcionas el esquematico no esta en proteus...pues no veo ningun archivo .dns en que program puedo ver el esquematico???
ResponderSuprimirEs que no es una simulación, es el proyecto en CCS C, o sea debes conectar un altavoz al pin B0, tres pulsadores a los pines B1, B2 y B3 del pic y quemarlo con el HEX.
ResponderSuprimirSaludos.
no m epodrian facilitar el diagrama de conexiones...me ayudaria mucho ya que necesito armarlo urgentement
ResponderSuprimirmi mail es floresgomezanthony@yahoo.es
La conexión la debes hacer igual que en esta entrada http://picrobot.blogspot.com/2008/12/probador-de-pic16f876.html
ResponderSuprimirSolo debes conectar un pequeño altavoz al pin B0, y tres pulsadores a los pines B1, B2 y B3
Saludos.
Disculpa y si quisiera poner botones para cada nota en el pic, como se haria
ResponderSuprimirBuenas, puedes modificar la parte donde aparecen todas las notas musicales para cada canción por solo la nota deseada.
ResponderSuprimirY cambiar la parte de
if(input(PIN_B1))PlayCancion(1);
por
if(input(PIN_x))PlayCancion(z);
Donde x lo cambias por el pin de entrada y z por el numero de nota. De esta forma no podrías hacer sonar dos notas al mismo tiempo.
Saludos
Hola, esta muy interesante tu proyecto, un favor podrías colocar el link de descarga del proyecto en otro sitio diferente a megaup... gracias!!
ResponderSuprimir