Tonos y melodías

Vamos a explicar desde cero cómo reproducir melodías usando tonos generados por un timer en un AVR128DA28. Los tonos serán reproducidos por un buzzer o altavoz piezoeléctrico y en específico usé este de TDK PS1240P02BT. Cómo ejemlo de estudio usaremos una melodía muy famosa que la usa Arduino.
Note
Te aconsejo que uses un analizador lógico para no ir a ciegas.
Vamos a ir descomponiendo la melodía en notas y luego en tonos y así hasta llegar al código fuente que los genera. Es un ejemplo maravilloso donde se aplica ingeniería inversa, matemática y programación.
¿Qué es una melodía? Es una sucesión organizada de sonidos de diferentes alturas y duraciones, percibida como una sola entidad musical que desarrolla una idea. Los sonidos son representados por tonos.
Comencemos, para eso conectamos un analizador lógico (logic analyzer) a la salida del microcontrolador que reproduce dicha melodía para obtener una muestra de pulsos que se parecen a la siguiente imagen:

Estas son las notas y la duración de cada de la melodía. La primera línea son las notas, y la segunda los tiempos.
NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
4, 8, 8, 4, 4, 4, 4, 4
Si buscamos en la librería de tonos musicales que suele estar en un fichero llamado pitches.h obtenemos solo estas cuatro notas que conforman la melodía:
#define NOTE_G3 196
#define NOTE_A3 220
#define NOTE_B3 247
#define NOTE_C4 262
Tenemos a la mano toda la información para empezar a realizar los cálculos.
Note
Los valores decimales que se obtienen de los cálculos y los que se aprecian en las imágenes obtenidas por el analizador lógico no son exactos, hay un margen de error que se debe a múltiples factores.
Por ejemplo, la nota G3 tiene el valor 196, que corresponde a la frecuencia de 196 Hz.

Si observamos la imagen anterior, notamos que hay una serie de pulsos que los llamaremos toggle. Un toggle es un valor alto o bajo, la cantidad de estos son 25 como se muestra en la imagen, y este valor se puede calcular usando la nota y el tiempo.
Volvamos a analizar la nota G3 que está acompañada de un tiempo con el valor 8 que equivale a una corchea (jerga de la música), esto quiere decir, una duración de 1/8s es equivalente a 125ms, en la imagen anterior está como 124,60.
Como resultado tenemos 49 toggles que es equivalente a 24.5 períodos, si redondeamos son 25 períodos. Todo empieza a encajar según lo que nos dice la imagen anterior.
Entre nota y nota se define un tiempo llamado pausa a partir de la siguiente formula:
El tiempo de la nota G3 es 8 y aplicando la formula, obtenemos 37,5ms como se muestra en la siguiente imagen:

Se han realizado los cálculos para la nota G3, estos pasos se debe repetir por cada nota hasta terminar la melodía. Hasta ahora se han identificado tres variables que son necesarias para poder programar; tms, tpause y toggles.
Para crear los pulsos de la forma deseada para cada tono vamos a usar uno de los timer/counter de varios que tiene el microcontrolador junto a las interrupciones, en específico el timer/counter A (TCA) que tiene una resolución de 16-bit, quiere decir que el contador va desde el 0 hasta 65535 (216 − 1). Para entender su funcionamiento, vamos a estudiar el modo NORMAL que es el que viene por defecto y es el más simple de comprender. Es importante saber que su funcionamiento y configuración es muy abstracto, por lo que voy a hacer el mejor esfuerzo al explicarlo.
Important
El reloj interno debe estar bien configurado a 24MHz (24.000.000 Hz). Ver la función
clk_init().
Hay una serie de términos básicos que hay que dominar para entender cómo funciona el timer/counter y poder configurar el Microcontrolador. Vamos a conocerlos antes de profundizar aún más en el funcionamiento:
- Counter: Es un contador que incrementa o decrementa de forma automática en cada ciclo del reloj o evento. Es el registro interno del contador
TCA0.SINGLE.CNT. Al ser un contador de 16-bit, quiere decir que el contador va desde el0hasta65535 (216 − 1). Entonces el counter acumulatick. - Tick: Cada vez que el
Counterse incrementa1. - Prescaled: Es un divisor que se le pone a la velocidad del reloj y es usado para indicar cada cuanto hace
tick. Son valores que van desde 1 hasta 64 y suelen ser estos: 1, 2, 4, 8, 16, 32, 64. - PERiod: Es el valor máximo del contador (TOP) antes de volver a cero
0. No es el período en segundos. - Overflow: Es el desbordamiento, y ocurre cuando el contador llega a su valor máximo (TOP) y vuelve a empezar. Cuando esto ocurre, se dispara la interrupción
TCA0_OVF_vect. - Interrupción: Es un mecanismo que detiene temporalmente la ejecución normal de un programa para atender un evento, ejecutando una rutina especial llamada
ISR(Interrupt Service Routine) y al terminar regresa al punto donde estaba el programa.
Vamos a ubicar la mayoría de los términos en dos gráficas y así poder visualizarlo mejor:

El timer/counter A es un acumulador de 16 bits que llega a 65535, ajustando el prescale a 16 para la frecuencia a 24MHz (24.000.000 Hz) nos permite definir un valor PER que indica hasta donde debe llegar el contador, entonces el timer cuenta de 0 hasta 3826 ticks y luego se reinicia overflow. En ese momento que se reinicia se produce una interrupción llamada TCA0_OVF_vect que se usa para togglear el pin en este caso.
El tick ocurre cada cierto tiempo y está definido por:
Para terminar, cada incremento del contador (tick) ocurre cada 0.667µs y el overflow a los 2.55ms:
Ahora deberás relacionar todo lo que te conté con el código fuente, será mucho más fácil sabiendo de donde proviene cada fórmula y que hace cada variable que configura el timer/counter y su relación con la interrupción.
| |
El ejemplo 2.9 muestra el código de configuración del timer/counter A (TCA) en modo NORMAL de la guía de migración que use para la función tone(uint32_t freq, uint32_t dur).
Para terminar, este es el esquema del circuito:

Compilar y subirlo
Para compilarlo deberá ejecutar el siguiente comando:
| |
Para subir el programa al microcontrolador, deberá ejecutar el siguiente comando:
| |
Si todo fue bien, podrá disfrutar del sonido.