-
Notifications
You must be signed in to change notification settings - Fork 195
Capítulo 19: Secuenciando notas
Ejemplos de este capítulo en github
Hasta ahora hemos generado sonidos en canales diferentes, de manera independiente. Implementaremos un secuenciador que nos permita tocar 4 notas que sonarán consecutivamente en el altavoz. Reproduciremos la secuencia: DO, RE, MI, (silencio) y se volverá al comienzo. Esto nos abre la puerta a la creación de melodías sencillas :-)
Una manera de lograr la secuenciación de las notas es utilizar un multiplexor, con el que se seleccionará en cada momento el canal a escuchar
En este ejemplo, tocaremos las notas DO, RE, MI, que se introducirán por las entradas 0, 1 y 2 respectivamente del multiplexor. Por la cuarta pondremos un "0" para que haya silencio.
La entrada de selección la conectamos a un contador de 2 bits, de forma que seleccione alternativamente los canales 0, 1, 2 y 3. Este contador tendrá una entrada de reloj que determinará la duración de la nota, que establecemos en 250ms.
El esquema del circuito se muestra en esta figura:
La descripción en verilog asociada a este esquema es:
//-- Fichero: secnotas.v
//-- Incluir las constantes del modulo del divisor
`include "divider.vh"
//-- Parameteros:
//-- clk: Reloj de entrada de la placa iCEstick
//-- ch_out: Canal de salida
module secnotas(input wire clk, output reg ch_out);
//-- Parametros: notas a tocar
//-- Se define como parametro para poder modificarlas desde el testbench
//-- para hacer pruebas
parameter N0 = `DO_4;
parameter N1 = `RE_4;
parameter N2 = `MI_4;
parameter DUR = `T_250ms;
//-- Cables de salida de los canales
wire ch0, ch1, ch2;
//-- Selección del canal del multiplexor
reg [1:0] sel = 0;
//-- Reloj con la duracion de la nota
wire clk_dur;
//-- Canal 0
divider #(N0)
CH0 (
.clk_in(clk),
.clk_out(ch0)
);
//-- Canal 1
divider #(N1)
CH1 (
.clk_in(clk),
.clk_out(ch1)
);
//-- canal 2
divider #(N2)
CH2 (
.clk_in(clk),
.clk_out(ch2)
);
//-- Multiplexor de seleccion del canal de salida
always @*
case (sel)
0 : ch_out <= ch0;
1 : ch_out <= ch1;
2 : ch_out <= ch2;
3 : ch_out <= 0;
default : ch_out <= 0;
endcase
//-- Contador para seleccion de nota
always @(posedge clk_dur)
sel <= sel + 1;
//-- Divisor para marcar la duración de cada nota
divider #(DUR)
TIMER0 (
.clk_in(clk),
.clk_out(clk_dur)
);
endmodule
En el fichero divider.vh se han añadido constantes para indicar la duración de las notas
`define T_1s 12000000
`define T_500ms 6000000
`define T_250ms 3000000
En el ejemplo se usa una duración de 250ms
Para sintetizarlo conectamos a la entrada de reloj la señal de 12Mhz de la IceStick y la de salida al pin 44, donde colocaremos el zumbador al hacer las pruebas
Para sintetizar ejecutamos el comando:
$ make sint
Los recursos empleados son:
Recurso | ocupación |
---|---|
PIOs | 6 / 96 |
PLBs | 25 / 160 |
BRAMs | 0 / 16 |
Lo cargamos en la FPGA con el comando:
$ sudo iceprog secnotas.bin
Alimentamos el zumbador desde los pines de 3.3v y gnd de la IceStick. El esquema es el siguiente:
En esta foto se muestra el zumbador conectado a la iCEstick, listo para hacer las pruebas:
En este vídeo de youtube se muestra el ejemplo en acción, tocando las tres notas:
No se simula la generación de las notas reales porque llevaría muchísimo tiempo. En vez de eso se comprueba que por el canal de salida salen alternativamente las señales de entrada al multiplexor
//-- Fichero: secnotas_tb.v
module secnotas_tb();
//-- Registro para generar la señal de reloj
reg clk = 0;
//-- Salidas de los canales
wire ch_out;
//-- Instanciar el componente y establecer el valor del divisor
//-- Se pone un valor bajo para simular (de lo contrario tardaria mucho)
secnotas #(.N0(4), .N1(3), .N2(2), .DUR(10))
dut(
.clk(clk),
.ch_out(ch_out)
);
//-- Generador de reloj. Periodo 2 unidades
always
# 1 clk <= ~clk;
//-- Proceso al inicio
initial begin
//-- Fichero donde almacenar los resultados
$dumpfile("secnotas_tb.vcd");
$dumpvars(0, secnotas_tb);
# 200 $display("FIN de la simulacion");
$finish;
end
endmodule
Para realizar la simulación se ejecuta:
$ make sim
El resultado es:
Las señales ch0, ch1 y ch2 son las salidas de los divisores. Cada una de una frecuencia. La entrada de selección del multiplexor cambia cíclicamente: 00, 01, 10, 11 (en binario). Finalmente se observa cómo en la salida aparece el canal que está seleccionado. Cuando es el 3, la salida está a cero (silencio).
- Ejercicio 1: Modificar los valores de los divisores para que salga otra secuencia de notas diferente
- Ejercicio 2: Colocar más divisores y un multiplexor de 8 a 1 para tocar una secuencia de 8 notas (todas de la misma duración)
TODO
0 You are leaving the privative sector (EN)
1 ¡Hola mundo! (EN) (RU)
2 De un bit a datos (EN)
3 Puerta NOT (EN)
4 Contador de 26 bits (EN)
5 Prescaler de N bits (EN)
6 Múltiples prescalers (EN)
7 Contador de 4 bits con prescaler (EN)
8 Registro de 4 bits (EN)
9 Inicializador (EN)
10 Registro de desplazamiento (EN)
11 Multiplexor de 2 a 1 (EN)
12 Multiplexor de M a 1 (EN)
13 Inicializando registros (EN)
14 Registro de N bits con reset síncrono
15 Divisor de frecuencias
16 Contador de segundos
17 Generando tonos audibles
18 Tocando notas
19 Secuenciando notas
20 Comunicaciones serie asíncronas
21 Baudios y transmisión
22 Reglas de diseño síncrono
23 Controladores y autómatas finitos
24 Unidad de transmisión serie asíncrona
25 Unidad de recepción serie asíncrona
26 Memoria ROM
27 Memoria ROM genérica
28 Memoria RAM
29 Puertas triestado
30 Hacia el microprocesador y más allá