La caja de música digital [Actualizado]
23 de de febrero de el año 2016
http://www.technoblogy.com/show?11TQ
Este proyecto es una simple caja de música digital, que puede reproducir canciones de una manera análoga a una caja de música mecánica tradicional [1] . Se basa en un ATtiny85, y utiliza ningún otro componente aparte de un altavoz y un condensador:
caja de música digital, basado en un ATtiny85.
Las notas tienen un sonido de campana, con un sobre en descomposición, y hay cuatro canales, por lo que hasta cuatro notas pueden jugar simultáneamente. Las formas de onda y los sobres se calculan dinámicamente, evitando la necesidad de tablas de búsqueda de valores precalculados. Mi programa de demostración juega "Feliz Cumpleaños"; esto es lo que parece: musicbox.mp3 .
Se puede programar fácilmente para reproducir cualquier canción que te gusta, y que podría ser utilizado como base para una tarjeta electrónica de felicitación, timbre electrónico, u otro proyecto basado en la música.
Programación de la melodía
La melodía se especifica mediante la aplicación de bits en una serie de números de 32 bits (largo), un poco como las clavijas en una caja de música mecánica. Esto le da a la caja de música de una gama de poco más de dos octavas. Los acordes se juegan con un ritmo constante de 4 latidos por segundo, por lo que al insertar un silencio que proporcione una fila de ceros. Por ejemplo, aquí están las codificaciones para el primer verso de "Feliz Cumpleaños":
La primera estrofa de "Feliz Cumpleaños" codificado como 12 números binarios de 32 bits.
La melodía se almacena en la memoria del programa, y el programa tarda menos de 1Kbytes; cada acorde ocupa 4 bytes, por lo que hay espacio para una suma de alrededor de 1800 acordes!
Los acordes se presentan con una interrupción de Watchdog. Cada llamada a la rutina de servicio de interrupción lee la siguiente definición acorde y, a continuación, explora a través de los 32 bits, la salida de una nota cuando se encuentra un '1':
ISR (WDT_vect) { sei (); // Permitir interrupciones WDTCR | = 1 << WDIE; Acorde a largo sin signo = pgm_read_dword (y Sonido [TunePtr]); si (Acorde == 0xFF) return; TunePtr ++; // Leer los bits de Acorde for (int Nota = 0; Nota <32; Nota ++) { if ((Acorde y 0x80000000)! = 0) { Frecuencia [Chan] = Escala [Nota]; Amp [Chan] = 1 << (Decay + 5); Chan = (Chan + 1)% Canales; } Acorde = Acorde << 1; } }
El final de la melodía está marcado por un valor de 0xFF.
La generación de las formas de onda
Al igual que mi proyecto anterior Synth Tiny esta caja de música utiliza DDS (Direct Digital Synthesis) para generar las formas de onda. Sin embargo, el Synth Tiny no incluyó los sobres; los sonidos eran ya sea dentro o fuera, como un órgano. Para hacer este sonido proyecto como una caja de música que quería dar las formas de onda en descomposición sobres, como campanas. Esto se consigue normalmente mediante el uso de una tabla de valores para definir la forma de la envolvente, y luego multiplicando el valor actual de la forma de onda por los valores sucesivos de la envolvente. Sin embargo, la ATtiny85 no tiene una instrucción de multiplicación hardware, y una multiplicación software sería demasiado lento para esto, ya que necesita ser realizada para cada muestra a la frecuencia de muestreo de 20 kHz. Además, quería evitar tener una tabla de valores de envolvente.
La solución es hacer que la forma de onda básica una onda cuadrada, por lo que sólo hay que multiplicar la amplitud de la envolvente por 1 o -1. Además, al hacer la forma de la envolvente de un decaimiento lineal, por lo que podemos calcular los valores de amplitud simplemente decrementar un contador.
Preparar
La caja de música utiliza tanto de los temporizadores ATtiny85, y el temporizador de vigilancia. Estos se configuran en la configuración () :
void setup () { // Habilitar el PLL 64 MHz y el uso como fuente de Timer1 PLLCSR = 1 << PCKE | 1 << PLLE; // Configurar temporizador / Contador1 para la salida PWM TIMSK = 0; // Temporizador interrumpe OFF TCCR1 = 1 << CS10; // 1: 1 preescalar GTCCR = 1 << PWM1B | 2 << COM1B0; // PWM B, clara sobre el partido OCR1B = 128; DDRB = 1 << DDB4; // Habilitar salida PWM en el pin 4 // Configurar temporizador / 20kHz para Counter0 interrupción de muestras de salida. TCCR0A = 3 << WGM00; // Rápida PWM TCCR0B = 1 << WGM02 | 2 << CS00; // 1/8 preescalar OCR0A = 99; // Dividir por 100 TIMSK = 1 << OCIE0A; // Habilitar comparar partido, desactivar desbordamiento // Configurar temporizador de vigilancia para la interrupción 4 Hz para la salida de la nota. WDTCR = 1 << WDIE | Tempo << WDP0; // 4 Hz interrupción }
La primera sección se convierte en el bucle de enganche de fase 64MHz (PLL), y especifica esto como la fuente de reloj del temporizador / Contador1.
Timer / Counter1 se establece entonces en el modo PWM, para hacer que actúe como un convertidor de analógico a digital, utilizando el valor en OCR1B para variar el ciclo de trabajo. La frecuencia de la onda cuadrada es especificado por OCR1C; lo dejamos en su valor predeterminado, 255, que divide el reloj por 256, dando una onda cuadrada de 250 kHz. Esto es lo suficientemente alta por encima de nuestra frecuencia de muestreo para evitar problemas de anti-aliasing.
Timer / Counter0 se utiliza para generar una interrupción a la salida de las muestras. La velocidad de esta interrupción es el reloj del sistema 16MHz dividido por un pre-escalador de 8, y un valor en OCR0A de 99 + 1, dando 20 kHz. La interrupción llama a una rutina de servicio de interrupción ISR (TIMER0_COMPA_vect) que calcula y da salida a las muestras.
Por último, el temporizador de vigilancia está configurado para dar una alarma de 4 Hz, que se utiliza para jugar cada acorde de la melodía.
Dar salida a las muestras
Para cada canal hay tres variables; Frecuencia [] , el valor de la nota actual, Acc [] , el acumulador de fase, y Amp [] , el valor de amplitud sobre. Para cada muestra, la [] Frec se añade valor al acumulador de fase, Acc [] . El bit de la parte superior de Acc [] se usa para generar la onda cuadrada para el canal. Cuanto mayor sea el valor de Freq [] , mayor es la frecuencia generada por el bit de la parte superior. Por último, la forma de onda se multiplica por el sobre, Amp [] . Los cuatro canales se suman entre sí, y el resultado es la salida a la salida analógica.
La parte fundamental del programa es la rutina de servicio de interrupción del temporizador / Counter0, que da salida a las muestras de forma de onda a la salida analógica, y esto se le llama a una velocidad de 20 kHz. Para cada canal se actualiza el acumulador de frecuencia y amplitud, y calcula el valor de la nota actual. Estos se suman entre sí, y la suma se envían al equipo del temporizador / Contador1 comparar registrarse OCR1B para dar un valor analógico en el pin 4:
ISR (TIMER0_COMPA_vect) { firmado carbón Temp, Máscara, Env, Nota, Suma = 0; for (int c = 0; c <Canales, c ++) { Acc [c] = Acc [c] + Frec [c]; Amp [c] = Amp [c] - (Amp [c] = 0); Temp = Acc [c] >> 8; Máscara = Temperatura >> 7; Env = Amp [c] >> decaimiento; Nota = (Env ^ Máscara) + (Máscara y 1); Suma = suma + Nota; } OCR1B = Suma + 128; }
Para minimizar los clics que es importante que el código en esta rutina toma un tiempo de ejecución consistente, por lo que las declaraciones condicionales deben ser evitados.
El sobre de cada nota es generada por un contador de amplitud para cada canal, Amp [c], que se decrementa hasta que llegue a cero. La manera obvia de hacer esto es:
si (Amp [c] = 0) Amp [c] -;
He reemplazado esto con la siguiente declaración equivalente, lo que evita el salto condicional:
Amp [c] = Amp [c] - (Amp [c] = 0);
La onda cuadrada es generada por probar el bit superior del acumulador para cada canal, Acc [c] , y la máscara se establece en 0x00 si el bit es cero o 0xFF si está establecido, lo que podría hacerse de esta manera:
if ((Acc [c] 0x8000) = 0) Máscara = 0xFF; otra máscara = 0x00;
Esta es la alternativa, evitando una rama:
Temp = Acc [c] >> 8; Máscara = Temperatura >> 7;
donde Temperatura y la máscara son declarados como números signed char (8 bits).
Por último, la nota se establece ya sea + o - la amplitud de la corriente de la envolvente, Env :
si (Máscara = 0) Nota = Env; Nota demás = - Env;
Esto se codifica sin un condicional como sigue:
Nota = (Env ^ Máscara) + (Máscara y 1);
El circuito
El circuito es el mismo que para mi artículo anterior, Synth Tiny , con la salida PWM alimentado directamente a un altavoz 8Ω a través de un condensador electrolítico para eliminar el DC. La inductancia del altavoz filtra los componentes de alta frecuencia de la forma de onda:
Circuito para la caja de música, basada en una ATtiny85.
Accioné el circuito de tres baterías alcalinas AAA de 1,5 V, lo que dio lo suficiente tensión. Si está utilizando una fuente de alimentación externa y se obtiene un sonido distorsionado o ningún sonido que pueda necesitar para desacoplar la alimentación con 100nF y 47μF condensadores a través de las líneas de suministro cerca de la ATtiny85.
Para una tarjeta de felicitación es posible que desee utilizar un altavoz delgada, disponible de Sparkfun [2] o HobbyTronics en el Reino Unido [3] .
Si desea alimentar la salida de un amplificador de audio debe incluir un filtro de paso bajo, de lo contrario corre el riesgo de sobrecargar el amplificador. Ver la forma de onda Generación utilizando un ATtiny85 para un circuito adecuado.
La compilación del programa
Para ser capaz de soportar cuatro canales de la ATtiny85 deba llevarse a cabo con un reloj de 16 MHz; Afortunadamente proporciona una opción de reloj 16MHz, sin la necesidad de un cristal, utilizando el PLL interno para impulsar el reloj de 8 MHz interna a 16MHz.
He compilado el programa con la extensión del núcleo de Arduino-Tiny al IDE Arduino [4] . Conectar los ATtiny85 a la pequeña Junta programador AVR; ver kit de ATtiny-Based Principiante . Seleccionar los ATtiny85 @ 16 MHz (PLL interno; 4,3 V DBO) opción en la Juntas de menú y seleccione Burn Bootloader para programar los fusibles ATtiny85 para un reloj interno de 16MHz. A continuación, cargar el programa al ATtiny85.
El programa no utiliza ninguna función Arduino-específicas, por lo que debería ser posible compilar con cualquier núcleo ATtiny85, o un núcleo vacío; consulte Uso del IDE Arduino Sin Cores .
Aquí está todo el programa de la caja de música, incluyendo los datos de "Feliz Cumpleaños" que se utiliza en el prototipo: Programa de la caja de música .
Actualizar
16 de de marzo de 2016: He arreglado un error en el programa que causó una salida distorsionada cuando el programa fue compilado con la versión 1.5.x o posterior del IDE de Arduino. Resulta que el error era debido a fallar para inicializar la variable Suma en el ISR (TIMER0_COMPA_vect) de rutina, y he corregido en el programa anterior. No estoy seguro de por qué el error no fue un problema con la versión 1.0.6 del IDE Arduino.