WM8731 de reproductor de música con tarjeta SD basado en FPGA
Tabla de contenido
2. Módulo de configuración de registro WM8731
prefacio
Este tema es un diseño de curso de eda que hice a principios de julio. Después de más de un mes, todavía tengo algunos recuerdos. Quiero registrar mis ideas generales. Después de todo, puedo hacerlo con éxito con mi propia perseverancia. También es muy memorable para mí. Este artículo registra principalmente la configuración de uso del chip de voz WM8731, que utiliza la configuración I2C.
Consejo: El siguiente es el texto de este artículo, todos los cuales son originales del propio autor. No es fácil escribir un artículo. Espero que adjunte un enlace a este artículo cuando lo vuelva a publicar.
1. Módulo de controlador I2C
Este módulo es responsable de completar la transmisión de datos de configuración desde FPGA al chip WM8731. El bus I2C consta de una línea de datos SDA y una línea de reloj SCL para formar una línea de comunicación, que se puede utilizar para enviar o recibir datos. La transmisión de datos bidireccional se puede realizar entre el control maestro y el IC controlado. La velocidad de transmisión de datos puede alcanzar 100 kbit/s en modo estándar, 400 kbit/s en modo rápido y 3,4 Mbit/s en modo de alta velocidad. Todo tipo de los dispositivos controlados están conectados en paralelo en el bus e identificados por la dirección del dispositivo (SLAVE ADDR, consulte el manual del dispositivo para obtener más detalles). La topología física del bus I2C de la placa de desarrollo que uso se muestra en la Figura 1. En esta figura, podemos ver que la dirección del chip de audio que quiero operar es 0x34.
I2C_SCL en la figura es una línea de reloj en serie e I2C_SDA es una línea de datos en serie. Dado que los dispositivos I2C generalmente usan una estructura de drenaje abierto para conectarse al bus, tanto I2C_SCL como I2C_SDA deben conectarse a una resistencia pull-up. Por lo tanto, , cuando el bus está inactivo, ambas líneas están en un estado de nivel alto. Cuando cualquier dispositivo conectado al bus emite un nivel bajo, el bus se descompondrá, es decir, el SDA y el SCL de cada dispositivo están en un " relación cableada y".
El código de este módulo es realmente bastante largo y se utiliza la programación general de la máquina de estado. Puede aprender los principios en línea e intentar programar usted mismo. Si no sabe cómo hacerlo, puede buscarlo en Baidu.
No adjunté el código del controlador IIC antes, pero si hay un socio pequeño que lo necesita, lo adjuntaré. Si no lo entiende, simplemente consulte los comentarios del programa directamente.
module clg_i2c_dri
#(
parameter SLAVE_ADDR = 7'b0011010 , //EEPROM从机地址
parameter CLK_FREQ = 26'd50_000_000, //模块输入的时钟频率
parameter I2C_FREQ = 18'd250_000 //IIC_SCL的时钟频率
)
(
input clk ,
input rst_n ,
//i2c interface
input i2c_exec , //I2C触发执行信号
input bit_ctrl , //字地址位控制(16b/8b)
input i2c_rh_wl , //I2C读写控制信号
input [15:0] i2c_addr , //I2C器件内地址
input [ 7:0] i2c_data_w , //I2C要写的数据
output reg [ 7:0] i2c_data_r , //I2C读出的数据
output reg i2c_done , //I2C一次操作完成
output reg i2c_ack , //I2C应答标志 0:应答 1:未应答
output reg scl , //I2C的SCL时钟信号
inout sda , //I2C的SDA信号
//user interface
output reg dri_clk //驱动I2C操作的驱动时钟
);
//localparam define
localparam st_idle = 8'b0000_0001; //空闲状态
localparam st_sladdr = 8'b0000_0010; //发送器件地址(slave address)
localparam st_addr16 = 8'b0000_0100; //发送16位字地址
localparam st_addr8 = 8'b0000_1000; //发送8位字地址
localparam st_data_wr = 8'b0001_0000; //写数据(8 bit)
localparam st_addr_rd = 8'b0010_0000; //发送器件地址读
localparam st_data_rd = 8'b0100_0000; //读数据(8 bit)
localparam st_stop = 8'b1000_0000; //结束I2C操作
//reg define
reg sda_dir ; //I2C数据(SDA)方向控制
reg sda_out ; //SDA输出信号
reg st_done ; //状态结束
reg wr_flag ; //写标志
reg [ 6:0] cnt ; //计数
reg [ 7:0] cur_state ; //状态机当前状态
reg [ 7:0] next_state; //状态机下一状态
reg [15:0] addr_t ; //地址
reg [ 7:0] data_r ; //读取的数据
reg [ 7:0] data_wr_t ; //I2C需写的数据的临时寄存
reg [ 9:0] clk_cnt ; //分频时钟计数
//wire define
wire sda_in ; //SDA输入信号
wire [8:0] clk_divide ; //模块驱动时钟的分频系数
//*****************************************************
//** main code
//*****************************************************
//SDA控制
assign sda = sda_dir ? sda_out : 1'bz; //SDA数据输出或高阻
assign sda_in = sda ; //SDA数据输入
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b0;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle: begin //空闲状态
if(i2c_exec) begin
next_state = st_sladdr;
end
else
next_state = st_idle;
end
st_sladdr: begin
if(st_done) begin
if(bit_ctrl) //判断是16位还是8位字地址
next_state = st_addr16;
else
next_state = st_addr8 ;
end
else
next_state = st_sladdr;
end
st_addr16: begin //写16位字地址
if(st_done) begin
next_state = st_addr8;
end
else begin
next_state = st_addr16;
end
end
st_addr8: begin //8位字地址
if(st_done) begin
if(wr_flag==1'b0) //读写判断
next_state = st_data_wr;
else
next_state = st_addr_rd;
end
else begin
next_state = st_addr8;
end
end
st_data_wr: begin //写数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_wr;
end
st_addr_rd: begin //写地址以进行读数据
if(st_done) begin
next_state = st_data_rd;
end
else begin
next_state = st_addr_rd;
end
end
st_data_rd: begin //读取数据(8 bit)
if(st_done)
next_state = st_stop;
else
next_state = st_data_rd;
end
st_stop: begin //结束I2C操作
if(st_done)
next_state = st_idle;
else
next_state = st_stop ;
end
default: next_state= st_idle;
endcase
end
//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done <= 1'b0;
i2c_ack <= 1'b0;
cnt <= 1'b0;
st_done <= 1'b0;
data_r <= 1'b0;
i2c_data_r<= 1'b0;
wr_flag <= 1'b0;
addr_t <= 1'b0;
data_wr_t <= 1'b0;
end
else begin
st_done <= 1'b0 ;
cnt <= cnt +1'b1 ;
case(cur_state)
st_idle: begin //空闲状态
scl <= 1'b1;
sda_out <= 1'b1;
sda_dir <= 1'b1;
i2c_done<= 1'b0;
cnt <= 7'b0;
if(i2c_exec) begin
wr_flag <= i2c_rh_wl ;
addr_t <= i2c_addr ;
data_wr_t <= i2c_data_w;
i2c_ack <= 1'b0;
end
end
st_sladdr: begin //写地址(器件地址和字地址)
case(cnt)
7'd1 : sda_out <= 1'b0; //开始I2C
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b0; //0:写
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr16: begin
case(cnt)
7'd0 : begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[15]; //传送字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[14];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[13];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[12];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[11];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[10];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[9];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[8];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr8: begin
case(cnt)
7'd0: begin
sda_dir <= 1'b1 ;
sda_out <= addr_t[7]; //字地址
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= addr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= addr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= addr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= addr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= addr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= addr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= addr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_wr: begin //写数据(8 bit)
case(cnt)
7'd0: begin
sda_out <= data_wr_t[7]; //I2C写8位数据
sda_dir <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_wr_t[6];
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_wr_t[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= data_wr_t[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= data_wr_t[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= data_wr_t[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= data_wr_t[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= data_wr_t[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_addr_rd: begin //写地址以进行读数据
case(cnt)
7'd0 : begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1;
7'd2 : sda_out <= 1'b0; //重新开始
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= SLAVE_ADDR[5];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= SLAVE_ADDR[4];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= SLAVE_ADDR[3];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= SLAVE_ADDR[2];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= SLAVE_ADDR[1];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= SLAVE_ADDR[0];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= 1'b1; //1:读
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
end
default : ;
endcase
end
st_data_rd: begin //读取数据(8 bit)
case(cnt)
7'd0: sda_dir <= 1'b0;
7'd1: begin
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0;
7'd5: begin
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0;
7'd9: begin
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0;
7'd13: begin
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0;
7'd17: begin
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0;
7'd21: begin
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0;
7'd25: begin
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0;
7'd29: begin
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31: scl <= 1'b0;
7'd32: begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1;
7'd34: st_done <= 1'b1; //非应答
7'd35: begin
scl <= 1'b0;
cnt <= 1'b0;
i2c_data_r <= data_r;
end
default : ;
endcase
end
st_stop: begin //结束I2C操作
case(cnt)
7'd0: begin
sda_dir <= 1'b1; //结束I2C
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1;
7'd15: st_done <= 1'b1;
7'd16: begin
cnt <= 1'b0;
i2c_done <= 1'b1; //向上层模块传递I2C结束信号
end
default : ;
endcase
end
endcase
end
end
endmodule
2. Módulo de configuración de registro WM8731
Este módulo completa principalmente la configuración de los 11 registros del chip WM8731, establece el valor de los registros por adelantado y luego completa la transferencia de parámetros de registro a través del módulo I2C. La configuración específica del registro se muestra en la Figura 2. Configuré WM8731 como modo esclavo, y el reloj de bit y los relojes de canal izquierdo y derecho que necesita son generados por el módulo de generación de reloj WM8731. ¿Cómo se deben configurar los 11 registros? Puede consultar el manual del chip. También he subido los datos del manual del chip, que incluye una versión en inglés y una versión en chino traducida por Google. Para ser honesto, según mi experiencia, si Si desea tener éxito, este manual del chip debe leerse repetidamente. No recuerdo cuántas veces lo leí en ese momento. Como mi inglés no es muy bueno, lo leo en una combinación de chino e inglés.
La configuración del volumen en la figura se transmite a través de la señal de entrada, lo cual es conveniente para que el módulo de control de botones posterior complete el control de volumen del módulo del reproductor de música. El número de muestreo del séptimo registro R7 también se puede ajustar. Dado que el muestreo de la canción es de 16 bits, wl está configurado en 00 de forma predeterminada, es decir, el número de muestreo de WM8731 está configurado para ser de 16 bits. Por supuesto, se puede ajustado de acuerdo a las necesidades.
3. Módulo de generación de reloj WM8731
Este módulo es responsable de generar el reloj de bit y el reloj de distinción de canal izquierdo y derecho requerido por WM8731. Cuando este módulo genera relojes que distinguen los canales izquierdo y derecho, se debe tener en cuenta que el bit más alto de los datos de audio de 16 bits en el modo alineado a la izquierda se recibe primero, y el bit más alto se puede usar cuando el primer flanco ascendente del llega el reloj de bits, y luego debe prestar atención a la recepción de 16 bits Después de los datos de audio, el reloj de bits ha reservado tres ciclos antes de recibir los siguientes datos de audio de 16 bits. El modo de alineación a la izquierda se muestra en la Figura 3. También hay formato I2S y modo alineado a la derecha que se pueden usar aquí, pero debe prestar atención a la diferencia en el diagrama de tiempo al usarlo y escribir el reloj correcto, de lo contrario, el efecto de la música no será bueno y habrá ruido.
Pongo el código de este módulo a continuación. Lo reescribí de acuerdo con una rutina. Debe recogerlo usted mismo. Primero debe leer el manual del chip WM8731, de lo contrario es posible que no lo entienda. Si no entiende, use un analizador lógico para observar cada señal, que es más intuitivo y vívido. Hay que tener especial cuidado en distinguir los relojes de los canales izquierdo y derecho, según el modo de alineación izquierda, cada vez que se reciban datos de audio de 16 bits habrá 3 ciclos inútiles, por lo que estos tres ciclos se generan en el comentario del código 36-1 , de modo que se ajuste a la alineación izquierda El formato del patrón.
el código se muestra a continuación:
module Audio_Clk(
clock_ref, //参考时钟
Rst_n, //复位信号
xck, //主时钟
Audio_LRCLK, //左右声道区分时钟
bclk, //位时钟
mode, //播放模式,顺序、随机、单曲循环可选
speed //播放速度
);
input clock_ref ; //wm8731振荡器时钟,选择18.432Mhz;
input Rst_n ;
input [1:0] speed ;
input [2:0] mode ;
output reg Audio_LRCLK ;
output xck ;
output reg bclk ;
parameter CLOCK_REF=18432000 ;
parameter CLOCK_SAMPLE0=48000;
parameter CLOCK_SAMPLE1=96000;
assign xck = clock_ref;
//assign bclk = (speed==2'b01)? bclk0:bclk1;
//assign Audio_LRCLK = (speed==2'b01)? Audio_LRCLK0:Audio_LRCLK1;
always @(*)
if((speed==2'b01)&&(mode<3'b101))
begin Audio_LRCLK<=Audio_LRCLK0;bclk<=bclk0; end
else if((speed==2'b10)&&(mode<3'b101))
begin Audio_LRCLK<=Audio_LRCLK1;bclk<=bclk1; end
else if(mode==3'b101)
begin Audio_LRCLK<=1'b0;bclk<=1'b0; end
reg Audio_LRCLK1;
reg [8:0]Audio_LRCLK1_cnt;
reg bclk1;
reg [3:0]bclk1_cnt;
//产生DAC和ADC的左右声道区分时钟,该时钟等于实际的芯片采样率
always@(posedge clock_ref or negedge Rst_n)
if(!Rst_n) begin
Audio_LRCLK1<=0;
Audio_LRCLK1_cnt<=0;
end
else if(Audio_LRCLK1_cnt>=(CLOCK_REF/(CLOCK_SAMPLE1*2)+(CLOCK_REF/(CLOCK_SAMPLE1*2*16*2))*6-1))
begin //36-1
Audio_LRCLK1<=~Audio_LRCLK1;
Audio_LRCLK1_cnt<=0;
end
else
Audio_LRCLK1_cnt<=Audio_LRCLK1_cnt+1'b1;
//产生I2S位时钟,BCLK的频率= 2 * 采样频率 * 采样位数,其中的2代表了2个声道。
always@(posedge clock_ref or negedge Rst_n)
if(!Rst_n) begin
bclk1<=0;
bclk1_cnt<=0;
end
else if(bclk1_cnt>=(CLOCK_REF/(CLOCK_SAMPLE1*2*16*2)-1)) begin
bclk1<=~bclk1;
bclk1_cnt<=0;
end
else
bclk1_cnt<=bclk1_cnt+1'b1;
reg Audio_LRCLK0;
reg [8:0]Audio_LRCLK0_cnt;
reg bclk0;
reg [3:0]bclk0_cnt;
//产生DAC和ADC的左右声道区分时钟,该时钟等于实际的芯片采样率
always@(posedge clock_ref or negedge Rst_n)
if(!Rst_n) begin
Audio_LRCLK0<=0;
Audio_LRCLK0_cnt<=0;
end
else if(Audio_LRCLK0_cnt>=(CLOCK_REF/(CLOCK_SAMPLE0*2)+(CLOCK_REF/(CLOCK_SAMPLE0*2*16*2))*6-1))
begin //36-1
Audio_LRCLK0<=~Audio_LRCLK0;
Audio_LRCLK0_cnt<=0;
end
else
Audio_LRCLK0_cnt<=Audio_LRCLK0_cnt+1'b1;
//产生I2S位时钟,BCLK的频率= 2 * 采样频率 * 采样位数,其中的2代表了2个声道。
always@(posedge clock_ref or negedge Rst_n)
if(!Rst_n) begin
bclk0<=0;
bclk0_cnt<=0;
end
else if(bclk0_cnt>=(CLOCK_REF/(CLOCK_SAMPLE0*2*16*2)-1)) begin
bclk0<=~bclk0;
bclk0_cnt<=0;
end
else
bclk0_cnt<=bclk0_cnt+1'b1;
endmodule
4. Módulo de envío de audio
Este módulo se encarga de enviar la señal digital leída desde la tarjeta SD al chip WM8731 para convertirla en una salida de señal analógica. Para garantizar que el chip WM8731 pueda leer datos confiables en el flanco ascendente del reloj de bits, el módulo envía datos en el flanco descendente del reloj de bits.Después de enviar datos de audio de 16 bits cada vez, permanecerá durante 3 bits ciclos de reloj antes de enviar datos de audio de 16 bits para ajustarse al modo justificado a la izquierda.
Parte del código de este módulo se muestra en la Figura 4. Se puede ver en el código que los datos se envían en el flanco descendente del reloj de bits, y el bit alto se envía primero y luego permanece durante ciclos de reloj de 3 bits. antes de enviar los siguientes datos de audio de 16 bits. La representación de depuración del analizador lógico en línea del módulo de envío de audio se muestra en la Figura 5. También se puede ver en la Figura 5 que los datos se envían en el flanco descendente del reloj de bits, y el bit alto se envía primero, y luego los siguientes datos de audio de 16 bits se envían después de 3 ciclos de reloj de bits después del envío, que cumple con los requisitos de temporización del modo alineado a la izquierda.
Resumir
Lo anterior es todo el contenido de este proyecto. Ha pasado mucho tiempo desde que hice esto, y la escritura no es muy buena. Este artículo solo presenta brevemente el funcionamiento de WM8731 en este proyecto.