module audio_receive #(parameter WL = 6'd32) ( // WL(字长,音频数据位宽)
//系统时钟 50MHz
input rst_n , // 复位信号
//wm8978接口
input aud_bclk , // es8388位时钟
input aud_lrc , // 左右声道信号
input aud_adcdat, // 音频输入数据
//用户接口
(* mark_debug = "true" *) output reg rx_done , // FPGA接收数据完成
output reg [31:0] adc_data // FPGA接收到的数据
);
//寄存器定义
reg aud_lrc_d0; // aud_lrc延迟一个时钟周期的信号
(* mark_debug = "true" *) reg [ 5:0] rx_cnt; // 接收数据计数器
reg [31:0] adc_data_t; // 临时存储音频数据的数据缓存
//连线定义
(* mark_debug = "true" *) wire lrc_edge ; // 左右声道信号的边沿
//*****************************************************
//** 主代码
//*****************************************************
assign lrc_edge = aud_lrc ^ aud_lrc_d0; // LRC信号的变化边沿检测
//为了保证在aud_lrc变化的第二个AUD_BCLK周期开始采集aud_adcdat,延迟一拍进行采集
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n)
aud_lrc_d0 <= 1'b0;
else
aud_lrc_d0 <= aud_lrc;
end
//采集32位音频数据的计数
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) begin
rx_cnt <= 6'd0;
end
else if(lrc_edge == 1'b1)
rx_cnt <= 6'd0;
else if(rx_cnt < 6'd35)
rx_cnt <= rx_cnt + 1'b1;
end
//将采集到的音频数据按时间顺序存入一个临时寄存器
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) begin
adc_data_t <= 32'b0;
end
else if(rx_cnt < WL)
adc_data_t[WL - 1'd1 - rx_cnt] <= aud_adcdat;
end
//计数结束时将数据输出到adc_data,并使能rx_done,表示一次采集完成
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) begin
rx_done <= 1'b0;
adc_data <= 32'b0;
end
else if(rx_cnt == WL) begin
rx_done <= 1'b1;
adc_data<= adc_data_t;
end
else
rx_done <= 1'b0;
end
endmodule// 定义音频发送模块,参数WL为字长(音频数据位宽),默认32位
module audio_send #(parameter WL = 6'd32) (
// 系统复位
input rst_n , // 复位信号(低电平有效)
// wm8978接口信号(音频 codec 接口)
input aud_bclk , // es8388位时钟(用于同步数据位传输)
input aud_lrc , // 左右声道信号(区分左/右声道数据传输)
output reg aud_dacdat, // 音频输出数据(发送到 codec 的串行数据)
// 用户接口
input [31:0] dac_data , // 待发送的音频数据(用户输入的并行数据)
output reg tx_done // 发送完成标志(一次音频数据位发送完成时置位)
);
// 寄存器定义
reg aud_lrc_d0; // aud_lrc延迟一个时钟周期的信号(用于边沿检测)
(* mark_debug = "true" *) reg [ 5:0] tx_cnt; // 发送数据计数器(记录已发送的数据位数)
reg [31:0] dac_data_t; // 临时存储待发送音频数据的缓存(暂存并行数据,用于串行发送)
// 线网定义
(* mark_debug = "true" *) wire lrc_edge; // 左右声道信号的边沿(检测aud_lrc的跳变)
//*****************************************************
//** 主代码
//*****************************************************
// 通过异或运算检测aud_lrc的跳变边沿(0→1或1→0)
assign lrc_edge = aud_lrc ^ aud_lrc_d0;
// 对aud_lrc信号延迟一拍,确保在aud_lrc变化的第二个AUD_BCLK周期开始处理数据(避免亚稳态)
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) // 复位时,延迟信号置0
aud_lrc_d0 <= 1'b0;
else // 正常工作时,将当前aud_lrc值延迟一拍保存
aud_lrc_d0 <= aud_lrc;
end
// 发送32位音频数据的计数逻辑(控制数据发送节奏)
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) begin // 复位时,计数器清零,数据缓存置0
tx_cnt <= 6'd0;
dac_data_t <= 32'd0;
end
else if(lrc_edge == 1'b1) begin // 检测到LRC边沿时,计数器复位并加载新的待发送数据
tx_cnt <= 6'd0;
dac_data_t <= dac_data;
end
else if(tx_cnt < 6'd35) // 未到计数上限时,计数器自增(35为预留的计数范围,覆盖32位数据)
tx_cnt <= tx_cnt + 1'b1;
end
// 发送完成标志控制(指示一次32位数据发送完成)
always @(posedge aud_bclk or negedge rst_n) begin
if(!rst_n) // 复位时,发送完成标志置0
tx_done <= 1'b0;
else if(tx_cnt == WL) // 当计数器达到数据位宽(32)时,置位发送完成标志
tx_done <= 1'b1;
else // 其他时刻,发送完成标志清零
tx_done <= 1'b0;
end
// 将待发送的音频数据按位串行发送(在BCLK下降沿输出,保证数据稳定)
always @(negedge aud_bclk or negedge rst_n) begin
if(!rst_n) // 复位时,输出数据置0
aud_dacdat <= 1'b0;
else if(tx_cnt < WL) // 未发送完32位数据时,按顺序输出缓存中的每一位(高位先发送)
aud_dacdat <= dac_data_t[WL - 1'd1 - tx_cnt];
else // 发送完成后,输出数据置0
aud_dacdat <= 1'b0;
end
endmodule// 定义音频播放模块(负责音频信号的接收、处理及通过ES8388 codec芯片输出)
module audio_speak(
input sys_clk , // 系统时钟(50MHz,模块主时钟)
input sys_rst_n , // 系统复位(低电平有效)
input [1:0] volume, // 音量控制信号(2位,用于调节输出音量)
// es8388音频接口(主机模式)
input aud_bclk , // es8388位时钟(用于同步音频数据的位传输)
input aud_lrc , // 左右声道信号(区分当前传输的是左声道还是右声道数据)
input aud_adcdat, // 音频输入数据(从es8388传入的ADC采样数据)
output aud_mclk , // es8388主时钟(提供给es8388的核心工作时钟)
output aud_dacdat, // 音频输出数据(发送到es8388的DAC数据)
// es8388控制接口(I2C协议)
output aud_scl , // es8388的SCL信号(I2C时钟线)
inout aud_sda , // es8388的SDA信号(I2C数据线,双向)
output led, // LED指示灯(通常用于指示模块工作状态)
output ilaclk // 预留时钟输出(具体用途需结合系统设计,可能用于外部设备同步)
);
// 线网定义(内部连接信号)
wire [31:0] adc_data; // FPGA接收到的音频数据(从es8388的ADC获取)
wire rst_n,locked; // rst_n:内部复位信号;locked:PLL锁定信号(指示PLL输出时钟稳定)
//*****************************************************
//** 主代码
//*****************************************************
reg [7:0] rst_cnt=0; // 复位计数器(用于生成上电后的延迟复位信号,8位宽)
// 复位计数器逻辑:上电后开始计数,计数到最高位为1时停止,用于延迟释放PLL复位
always @(posedge sys_clk) // 时钟沿为系统时钟上升沿
begin
if (rst_cnt[7]) // 当计数器最高位(第7位)为1时,保持当前值(计数完成)
rst_cnt <= rst_cnt;
else // 否则计数器自增1(持续计数)
rst_cnt <= rst_cnt+1'b1;
end
// 例化PLL(锁相环)模块,生成es8388所需的主时钟
clk_wiz_0 u_pll_clk // PLL模块实例化,模块名为u_pll_clk
(
.refclk(sys_clk), // 输入参考时钟(连接系统50MHz时钟)
.reset(!rst_cnt[7]), // PLL复位信号(当复位计数器最高位为0时复位PLL,即上电初期复位)
.stdby(1'b0), // 待机模式控制(固定为0,不进入待机)
.extlock(locked), // 输出PLL锁定信号(高电平表示PLL输出时钟稳定)
.clk0_out(chipwatcherclk), // 输出时钟0(chipwatcherclk,用途需结合系统,可能用于监控)
.clk1_out(aud_mclk) // 输出时钟1(aud_mclk,提供给es8388的主时钟,通常为12.288MHz)
);
// 例化es8388控制模块(负责配置es8388及处理音频数据收发)
es8388_ctrl u_es8388_ctrl( // 控制模块实例化,模块名为u_es8388_ctrl
.clk (sys_clk ), // 时钟信号(连接系统50MHz时钟)
.rst_n (locked ), // 复位信号(PLL锁定后有效,确保时钟稳定后工作)
.aud_bclk (aud_bclk ), // 连接es8388的位时钟
.aud_lrc (aud_lrc ), // 连接es8388的左右声道信号
.aud_adcdat (aud_adcdat ), // 连接es8388的音频输入数据(ADC数据)
.aud_dacdat (aud_dacdat ), // 连接到es8388的音频输出数据(DAC数据)
.aud_scl (aud_scl ), // 连接到es8388的I2C时钟线(SCL)
.aud_sda (aud_sda ), // 连接到es8388的I2C数据线(SDA)
.volume (volume), // 音量控制信号(传入2位音量控制值)
.adc_data (adc_data ), // 输出接收到的音频数据(传给内部处理)
.dac_data (adc_data ), // 输入待发送的音频数据(此处直接将ADC数据环回给DAC,即输入音频直接输出)
.rx_done (), // 接收完成标志(未使用,悬空)
.tx_done () // 发送完成标志(未使用,悬空)
);
assign led = locked; // LED指示灯连接到PLL锁定信号(锁定时LED亮,指示系统时钟就绪)
endmodule // 模块定义结束// 设置仿真时间单位为1ns,时间精度为1ns(用于仿真时的时间尺度定义)
`timescale 1ns/1ns
// 定义ES8388音频 codec芯片的配置模块(负责通过I2C协议配置ES8388的寄存器参数)
module es8388_config(
input clk , // 时钟信号(模块工作时钟,通常为系统主时钟)
input rst_n , // 复位信号(低电平有效,复位时停止配置操作)
input [1:0] volume , // 音量控制信号(2位,用于动态调整输出音量的配置参数)
output aud_scl , // 连接到ES8388的I2C时钟线(SCL,用于同步数据传输)
inout aud_sda // 连接到ES8388的I2C数据线(SDA,双向传输配置数据)
);
// 参数定义(模块配置常量)
parameter SLAVE_ADDR = 7'h10 ; // ES8388的I2C从机地址(7位,用于I2C通信时寻址芯片)
parameter WL = 6'd32 ; // 字长(音频数据的位宽,此处为32位,用于匹配音频数据格式)
parameter BIT_CTRL = 1'b0 ; // 寄存器地址位宽控制(0表示8位地址+8位数据,1表示16位配置模式)
parameter CLK_FREQ = 26'd50_000_000; // I2C驱动模块的参考时钟频率(50MHz,与系统主时钟一致)
parameter I2C_FREQ = 18'd250_000 ; // I2C总线的SCL时钟频率(250KHz,符合ES8388的I2C通信要求)
// 线网定义(内部连接信号)
wire clk_i2c ; // I2C操作的工作时钟(由I2C驱动模块生成,用于同步配置逻辑)
wire i2c_exec ; // I2C操作执行信号(高电平触发一次I2C写操作)
wire i2c_done ; // I2C操作完成标志(一次I2C写操作结束后置高)
wire cfg_done ; // ES8388配置完成标志(所有寄存器配置完成后置高)
wire [15:0] reg_data ; // 待配置的寄存器信息(高8位为寄存器地址,低8位为配置数据)
//*****************************************************
//** 主代码
//*****************************************************
// 例化ES8388寄存器配置序列生成模块(负责按顺序生成需要配置的寄存器地址和数据)
i2c_reg_cfg #(
.WL (WL ) // 传递字长参数(音频数据位宽,用于匹配芯片数据格式配置)
) u_i2c_reg_cfg(
.clk (clk_i2c ), // 模块工作时钟(连接I2C驱动模块生成的clk_i2c)
.rst_n (rst_n ), // 复位信号(低电平有效,复位时停止配置序列生成)
.i2c_exec (i2c_exec ), // 输出I2C执行信号(触发I2C驱动模块执行写操作)
.i2c_data (reg_data ), // 输出待配置的寄存器数据(地址+数据,共16位)
.volume (volume), // 输入音量控制信号(用于动态调整音量相关寄存器配置)
.i2c_done (i2c_done ), // 输入I2C操作完成标志(表示一次写操作结束,准备下一次)
.cfg_done (cfg_done ) // 输出配置完成标志(所有寄存器配置完毕后置高)
);
// 例化I2C驱动模块(负责将寄存器配置数据通过I2C协议发送给ES8388)
i2c_dri #(
.SLAVE_ADDR (SLAVE_ADDR), // 传递ES8388的I2C从机地址(用于寻址芯片)
.CLK_FREQ (CLK_FREQ ), // 传递参考时钟频率(50MHz,用于生成I2C时钟)
.I2C_FREQ (I2C_FREQ ) // 传递I2C总线频率(250KHz,设置SCL时钟速度)
) u_i2c_dri(
.clk (clk ), // 模块参考时钟(系统50MHz时钟,用于生成I2C时钟)
.rst_n (rst_n ), // 复位信号(低电平有效,复位时I2C总线停止工作)
.i2c_exec (i2c_exec ), // 输入I2C执行信号(由配置序列模块触发,开始一次写操作)
.bit_ctrl (BIT_CTRL ), // 输入寄存器位宽控制(0表示8位地址+8位数据模式)
.i2c_rh_wl (1'b0 ), // I2C读写控制(0表示写操作,配置芯片时只需要写寄存器)
.i2c_addr (reg_data[15:8]), // 输入寄存器地址(取reg_data的高8位)
.i2c_data_w (reg_data[ 7:0]), // 输入要写入的数据(取reg_data的低8位)
.i2c_data_r (), // 输出读取的数据(配置阶段无需读操作,悬空)
.i2c_done (i2c_done ), // 输出I2C操作完成标志(反馈给配置序列模块)
.scl (aud_scl ), // 输出I2C时钟信号(连接到ES8388的SCL引脚)
.sda (aud_sda ), // 双向I2C数据信号(连接到ES8388的SDA引脚)
.dri_clk (clk_i2c ) // 输出I2C工作时钟(提供给配置序列模块同步使用)
);
endmodule // 模块定义结束// 定义ES8388音频 codec芯片的控制模块(整合配置、音频接收和发送功能)
module es8388_ctrl(
input clk , // 时钟信号(系统主时钟,用于模块同步)
input rst_n , // 复位信号(低电平有效,复位时所有功能停止)
// 音频接口(主机模式,FPGA作为主机控制数据传输)
input aud_bclk , // ES8388的位时钟(用于同步音频数据的位级传输)
input aud_lrc , // 左右声道信号(区分当前传输左声道还是右声道数据)
input aud_adcdat , // 音频输入数据(从ES8388的ADC输出的串行数据)
output aud_dacdat , // 音频输出数据(发送到ES8388的DAC的串行数据)
// 控制接口(I2C协议,用于配置ES8388寄存器)
output aud_scl , // ES8388的I2C时钟线(SCL,控制数据传输节奏)
inout aud_sda , // ES8388的I2C数据线(SDA,双向传输配置信息)
// 用户接口(与外部逻辑交互)
output [31:0] adc_data , // 输出的接收音频数据(并行格式,从ES8388接收后转换)
input [31:0] dac_data , // 输入的待发送音频数据(并行格式,将要发送到ES8388)
input [1:0] volume , // 音量控制信号(2位,用于动态调整输出音量)
output rx_done , // 接收完成标志(一次完整音频数据接收完成后置高)
output tx_done // 发送完成标志(一次完整音频数据发送完成后置高)
);
// 参数定义
parameter WL = 6'd24; // 字长(音频数据的位宽,此处为24位,匹配ES8388的ADC/DAC精度)
//*****************************************************
//** 主代码
//*****************************************************
// 例化ES8388配置模块(负责通过I2C协议配置ES8388的工作参数,如采样率、增益等)
es8388_config #(
.WL (WL) // 传递音频数据位宽参数(24位,用于匹配配置中的数据格式)
) u_es8388_config(
.clk (clk), // 连接系统时钟(为配置逻辑提供同步时钟)
.rst_n (rst_n), // 连接系统复位信号(复位时停止配置)
.volume (volume), // 连接音量控制信号(将外部音量调节传递给配置模块)
.aud_scl (aud_scl), // 输出I2C时钟信号(连接到ES8388的SCL引脚)
.aud_sda (aud_sda) // 双向I2C数据信号(连接到ES8388的SDA引脚)
);
// 例化音频接收模块(负责将ES8388输出的串行音频数据转换为并行数据)
audio_receive #(
.WL (WL) // 传递音频数据位宽参数(24位,指定接收数据的位数)
) u_audio_receive(
.rst_n (rst_n), // 连接系统复位信号(复位时停止接收)
.aud_bclk (aud_bclk), // 连接ES8388的位时钟(同步接收串行数据)
.aud_lrc (aud_lrc), // 连接左右声道信号(区分接收左/右声道数据)
.aud_adcdat (aud_adcdat), // 连接ES8388的音频输入数据(接收串行ADC数据)
.adc_data (adc_data), // 输出并行音频数据(传递给用户接口)
.rx_done (rx_done) // 输出接收完成标志(传递给用户接口)
);
// 例化音频发送模块(负责将并行音频数据转换为串行数据发送给ES8388)
audio_send #(
.WL (WL) // 传递音频数据位宽参数(24位,指定发送数据的位数)
) u_audio_send(
.rst_n (rst_n), // 连接系统复位信号(复位时停止发送)
.aud_bclk (aud_bclk), // 连接ES8388的位时钟(同步发送串行数据)
.aud_lrc (aud_lrc), // 连接左右声道信号(区分发送左/右声道数据)
.aud_dacdat (aud_dacdat), // 输出串行音频数据(发送到ES8388的DAC)
.dac_data (dac_data), // 输入并行音频数据(来自用户接口)
.tx_done (tx_done) // 输出发送完成标志(传递给用户接口)
);
endmodule // 模块定义结束// 定义I2C驱动模块(实现I2C总线协议的底层驱动,支持对从设备的读写操作)
module i2c_dri(
input clk , // I2C驱动模块的参考时钟(频率为CLK_FREQ,用于生成I2C时钟)
input rst_n , // 复位信号(低电平有效,复位时I2C总线停止工作)
input i2c_exec , // I2C操作执行信号(高电平触发一次I2C读写操作)
input bit_ctrl , // 寄存器地址位宽控制(0=8位地址,1=16位地址)
input i2c_rh_wl , // I2C读写控制(0=写操作,1=读操作)
input [15:0] i2c_addr , // I2C要访问的寄存器地址(16位,根据bit_ctrl截取使用)
input [ 7:0] i2c_data_w , // I2C要写入的数据(8位)
output reg [ 7:0] i2c_data_r , // I2C读取到的数据(8位,读操作时有效)
output reg i2c_done , // I2C一次操作完成标志(操作结束后置高)
output reg scl , // I2C总线的SCL时钟信号(控制数据传输节奏)
inout sda , // I2C总线的SDA数据信号(双向,用于传输数据)
output reg dri_clk // 内部I2C驱动时钟(用于同步状态机和数据传输)
);
// 参数定义(模块配置常量)
parameter SLAVE_ADDR = 7'b0010000; // 从设备的I2C地址(7位,用于寻址目标设备)
parameter CLK_FREQ = 26'd50_000_000; // I2C驱动模块的参考时钟频率(50MHz)
parameter I2C_FREQ = 18'd250_000; // I2C总线的SCL时钟频率(250KHz,需符合从设备要求)
// 本地参数定义(状态机状态编码)
localparam st_idle = 8'b0000_0001; // 空闲状态(等待I2C操作触发)
localparam st_sladdr = 8'b0000_0010; // 发送从设备地址状态(传输7位从地址+1位读写位)
localparam st_addr16 = 8'b0000_0100; // 发送16位寄存器地址的高8位状态
localparam st_addr8 = 8'b0000_1000; // 发送寄存器地址的低8位状态(16位地址时为高8位后,8位地址时直接发送)
localparam st_data_wr = 8'b0001_0000; // 写数据状态(向寄存器写入8位数据)
localparam st_addr_rd = 8'b0010_0000; // 读操作时重新发送从地址(带读标志)状态
localparam st_data_rd = 8'b0100_0000; // 读数据状态(从寄存器读取8位数据)
localparam st_stop = 8'b1000_0000; // 发送停止信号状态(结束I2C操作)
// 寄存器定义
reg sda_dir ; // SDA方向控制(1=输出,0=输入,控制SDA为输出还是高阻)
reg sda_out ; // SDA输出寄存器(当sda_dir=1时,SDA线上的值由该寄存器决定)
reg st_done ; // 状态完成标志(当前状态的操作执行完毕后置高)
reg wr_flag ; // 写操作标志(存储i2c_rh_wl的值,用于状态机判断读写)
reg [ 6:0] cnt ; // 状态内计数器(用于控制当前状态内的时序步骤)
reg [ 7:0] cur_state ; // 状态机当前状态
reg [ 7:0] next_state ; // 状态机下一个状态
reg [15:0] addr_t ; // 地址缓存(存储i2c_addr,避免输入信号变化影响内部操作)
reg [ 7:0] data_r ; // 读数据缓存(临时存储读取到的数据位)
reg [ 7:0] data_wr_t ; // 写数据缓存(存储i2c_data_w,避免输入信号变化影响内部操作)
reg [ 9:0] clk_cnt ; // 时钟分频计数器(用于生成dri_clk)
// 线网定义
wire sda_in ; // SDA输入信号(当sda_dir=0时,读取SDA线上的值)
wire [8:0] clk_divide ; // 分频系数(用于计算dri_clk的频率,由参考时钟和I2C频率决定)
//*****************************************************
//** 主代码
//*****************************************************
// SDA信号方向控制:sda_dir=1时输出sda_out,=0时为高阻(输入)
assign sda = sda_dir ? sda_out : 1'bz;
// 读取SDA线上的输入信号(无论方向,均能获取线上电平)
assign sda_in = sda ;
// 计算分频系数:dri_clk频率为I2C_FREQ的8倍(用于细化SCL时序控制)
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 3; // 等效于(CLK_FREQ/(I2C_FREQ*8))
// 生成内部驱动时钟dri_clk(频率为I2C_FREQ的8倍,用于精确控制SCL的高低电平时间)
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin // 复位时,dri_clk初始为高,计数器清零
dri_clk <= 1'b1;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide - 1'd1) begin // 计数到分频系数-1时,翻转dri_clk并清零计数器
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else // 未到计数终点时,计数器自增
clk_cnt <= clk_cnt + 1'b1;
end
// 状态机同步时序:在dri_clk上升沿更新当前状态
always @(posedge dri_clk or negedge rst_n) begin
if(rst_n == 1'b0)
cur_state <= st_idle; // 复位时回到空闲状态
else
cur_state <= next_state; // 否则更新为下一个状态
end
// 状态机组合逻辑:根据当前状态和条件判断下一个状态
always @( * ) begin
case(cur_state)
st_idle: begin // 空闲状态:等待i2c_exec触发
if(i2c_exec) // 若收到执行信号,进入发送从地址状态
next_state = st_sladdr;
else // 否则保持空闲
next_state = st_idle;
end
st_sladdr: begin // 发送从地址状态:等待当前状态完成
if(st_done) begin
if(bit_ctrl) // 若地址为16位,下一步发送高8位地址
next_state = st_addr16;
else // 若地址为8位,下一步发送8位地址
next_state = st_addr8 ;
end
else // 状态未完成,保持当前状态
next_state = st_sladdr;
end
st_addr16: begin // 发送16位地址高8位状态:等待完成
if(st_done) // 完成后,下一步发送低8位地址
next_state = st_addr8;
else // 未完成,保持当前状态
next_state = st_addr16;
end
st_addr8: begin // 发送地址低8位(或8位地址)状态:等待完成
if(st_done) begin
if(wr_flag==1'b0) // 若为写操作,下一步写数据
next_state = st_data_wr;
else // 若为读操作,下一步重新发送从地址(带读标志)
next_state = st_addr_rd;
end
else // 未完成,保持当前状态
next_state = st_addr8;
end
st_data_wr: begin // 写数据状态:等待完成
if(st_done) // 完成后,下一步发送停止信号
next_state = st_stop;
else // 未完成,保持当前状态
next_state = st_data_wr;
end
st_addr_rd: begin // 读操作重新发送从地址状态:等待完成
if(st_done) // 完成后,下一步读数据
next_state = st_data_rd;
else // 未完成,保持当前状态
next_state = st_addr_rd;
end
st_data_rd: begin // 读数据状态:等待完成
if(st_done) // 完成后,下一步发送停止信号
next_state = st_stop;
else // 未完成,保持当前状态
next_state = st_data_rd;
end
st_stop: begin // 发送停止信号状态:等待完成
if(st_done) // 完成后,回到空闲状态
next_state = st_idle;
else // 未完成,保持当前状态
next_state = st_stop ;
end
default: next_state= st_idle; // 默认回到空闲状态
endcase
end
// 状态机时序逻辑:根据当前状态和计数器值,控制SCL、SDA及标志信号
always @(posedge dri_clk or negedge rst_n) begin
// 复位时初始化所有信号
if(rst_n == 1'b0) begin
scl <= 1'b1; // SCL初始为高(I2C总线空闲时SCL和SDA均为高)
sda_out <= 1'b1; // SDA初始为高
sda_dir <= 1'b1; // SDA默认输出(初始为高)
i2c_done <= 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; // SCL保持高(空闲)
sda_out <= 1'b1; // SDA保持高(空闲)
sda_dir <= 1'b1; // SDA为输出
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; // 锁存要写的数据
end
end
st_sladdr: begin // 发送从设备地址:传输7位从地址+1位写标志(0)
case(cnt)
7'd1 : sda_out <= 1'b0; // 步骤1:SDA拉低(生成I2C起始信号:SCL高时SDA从高变低)
7'd3 : scl <= 1'b0; // 步骤3:SCL拉低(准备发送数据位)
7'd4 : sda_out <= SLAVE_ADDR[6]; // 步骤4:发送从地址第6位(最高位)
7'd5 : scl <= 1'b1; // 步骤5:SCL拉高(从设备采样数据位)
7'd7 : scl <= 1'b0; // 步骤7:SCL拉低(准备下一位)
7'd8 : sda_out <= SLAVE_ADDR[5]; // 步骤8:发送从地址第5位
7'd9 : scl <= 1'b1; // 步骤9:SCL拉高(采样)
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd12: sda_out <= SLAVE_ADDR[4]; // 步骤12:发送从地址第4位
7'd13: scl <= 1'b1; // 步骤13:SCL拉高
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd16: sda_out <= SLAVE_ADDR[3]; // 步骤16:发送从地址第3位
7'd17: scl <= 1'b1; // 步骤17:SCL拉高
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd20: sda_out <= SLAVE_ADDR[2]; // 步骤20:发送从地址第2位
7'd21: scl <= 1'b1; // 步骤21:SCL拉高
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd24: sda_out <= SLAVE_ADDR[1]; // 步骤24:发送从地址第1位
7'd25: scl <= 1'b1; // 步骤25:SCL拉高
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd28: sda_out <= SLAVE_ADDR[0]; // 步骤28:发送从地址第0位(最低位)
7'd29: scl <= 1'b1; // 步骤29:SCL拉高
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: sda_out <= 1'b0; // 步骤32:发送读写位(0=写,因为此时是写地址阶段)
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(从设备采样读写位)
7'd35: scl <= 1'b0; // 步骤35:SCL拉低
7'd36: begin // 步骤36:切换SDA为输入,等待从设备应答
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1; // 步骤37:SCL拉高(从设备发送应答位)
7'd38: st_done <= 1'b1; // 步骤38:标记当前状态完成
7'd39: begin // 步骤39:SCL拉低,计数器清零,准备下一状态
scl <= 1'b0;
cnt <= 1'b0;
end
default : ; // 其他计数步骤无操作
endcase
end
st_addr16: begin // 发送16位地址的高8位
case(cnt)
7'd0 : begin // 步骤0:SDA切换为输出,准备发送地址位
sda_dir <= 1'b1 ;
sda_out <= addr_t[15]; // 发送地址第15位(高8位最高位)
end
7'd1 : scl <= 1'b1; // 步骤1:SCL拉高(从设备采样)
7'd3 : scl <= 1'b0; // 步骤3:SCL拉低
7'd4 : sda_out <= addr_t[14]; // 步骤4:发送地址第14位
7'd5 : scl <= 1'b1; // 步骤5:SCL拉高
7'd7 : scl <= 1'b0; // 步骤7:SCL拉低
7'd8 : sda_out <= addr_t[13]; // 步骤8:发送地址第13位
7'd9 : scl <= 1'b1; // 步骤9:SCL拉高
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd12: sda_out <= addr_t[12]; // 步骤12:发送地址第12位
7'd13: scl <= 1'b1; // 步骤13:SCL拉高
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd16: sda_out <= addr_t[11]; // 步骤16:发送地址第11位
7'd17: scl <= 1'b1; // 步骤17:SCL拉高
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd20: sda_out <= addr_t[10]; // 步骤20:发送地址第10位
7'd21: scl <= 1'b1; // 步骤21:SCL拉高
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd24: sda_out <= addr_t[9]; // 步骤24:发送地址第9位
7'd25: scl <= 1'b1; // 步骤25:SCL拉高
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd28: sda_out <= addr_t[8]; // 步骤28:发送地址第8位(高8位最低位)
7'd29: scl <= 1'b1; // 步骤29:SCL拉高
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: begin // 步骤32:切换SDA为输入,等待应答
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(采样应答)
7'd34: st_done <= 1'b1; // 步骤34:标记状态完成
7'd35: begin // 步骤35:SCL拉低,计数器清零
scl <= 1'b0;
cnt <= 1'b0;
end
default : ; // 其他步骤无操作
endcase
end
st_addr8: begin // 发送地址低8位(或8位地址)
case(cnt)
7'd0: begin // 步骤0:SDA切换为输出,发送地址位
sda_dir <= 1'b1 ;
sda_out <= addr_t[7]; // 发送地址第7位(低8位最高位)
end
7'd1 : scl <= 1'b1; // 步骤1:SCL拉高
7'd3 : scl <= 1'b0; // 步骤3:SCL拉低
7'd4 : sda_out <= addr_t[6]; // 步骤4:发送地址第6位
7'd5 : scl <= 1'b1; // 步骤5:SCL拉高
7'd7 : scl <= 1'b0; // 步骤7:SCL拉低
7'd8 : sda_out <= addr_t[5]; // 步骤8:发送地址第5位
7'd9 : scl <= 1'b1; // 步骤9:SCL拉高
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd12: sda_out <= addr_t[4]; // 步骤12:发送地址第4位
7'd13: scl <= 1'b1; // 步骤13:SCL拉高
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd16: sda_out <= addr_t[3]; // 步骤16:发送地址第3位
7'd17: scl <= 1'b1; // 步骤17:SCL拉高
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd20: sda_out <= addr_t[2]; // 步骤20:发送地址第2位
7'd21: scl <= 1'b1; // 步骤21:SCL拉高
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd24: sda_out <= addr_t[1]; // 步骤24:发送地址第1位
7'd25: scl <= 1'b1; // 步骤25:SCL拉高
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd28: sda_out <= addr_t[0]; // 步骤28:发送地址第0位(最低位)
7'd29: scl <= 1'b1; // 步骤29:SCL拉高
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: begin // 步骤32:切换SDA为输入,等待应答
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(采样应答)
7'd34: st_done <= 1'b1; // 步骤34:标记状态完成
7'd35: begin // 步骤35:SCL拉低,计数器清零
scl <= 1'b0;
cnt <= 1'b0;
end
default : ; // 其他步骤无操作
endcase
end
st_data_wr: begin // 写数据:向寄存器发送8位数据
case(cnt)
7'd0: begin // 步骤0:SDA切换为输出,发送数据位
sda_out <= data_wr_t[7]; // 发送数据第7位(最高位)
sda_dir <= 1'b1;
end
7'd1 : scl <= 1'b1; // 步骤1:SCL拉高(采样)
7'd3 : scl <= 1'b0; // 步骤3:SCL拉低
7'd4 : sda_out <= data_wr_t[6]; // 步骤4:发送数据第6位
7'd5 : scl <= 1'b1; // 步骤5:SCL拉高
7'd7 : scl <= 1'b0; // 步骤7:SCL拉低
7'd8 : sda_out <= data_wr_t[5]; // 步骤8:发送数据第5位
7'd9 : scl <= 1'b1; // 步骤9:SCL拉高
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd12: sda_out <= data_wr_t[4]; // 步骤12:发送数据第4位
7'd13: scl <= 1'b1; // 步骤13:SCL拉高
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd16: sda_out <= data_wr_t[3]; // 步骤16:发送数据第3位
7'd17: scl <= 1'b1; // 步骤17:SCL拉高
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd20: sda_out <= data_wr_t[2]; // 步骤20:发送数据第2位
7'd21: scl <= 1'b1; // 步骤21:SCL拉高
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd24: sda_out <= data_wr_t[1]; // 步骤24:发送数据第1位
7'd25: scl <= 1'b1; // 步骤25:SCL拉高
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd28: sda_out <= data_wr_t[0]; // 步骤28:发送数据第0位(最低位)
7'd29: scl <= 1'b1; // 步骤29:SCL拉高
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: begin // 步骤32:切换SDA为输入,等待应答
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(采样应答)
7'd34: st_done <= 1'b1; // 步骤34:标记状态完成
7'd35: begin // 步骤35:SCL拉低,计数器清零
scl <= 1'b0;
cnt <= 1'b0;
end
default : ; // 其他步骤无操作
endcase
end
st_addr_rd: begin // 读操作:重新发送从地址(带读标志1)
case(cnt)
7'd0 : begin // 步骤0:SDA切换为输出,准备发送
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 : scl <= 1'b1; // 步骤1:SCL拉高
7'd2 : sda_out <= 1'b0; // 步骤2:SDA拉低(生成重复起始信号)
7'd3 : scl <= 1'b0; // 步骤3:SCL拉低
7'd4 : sda_out <= SLAVE_ADDR[6]; // 步骤4:发送从地址第6位
7'd5 : scl <= 1'b1; // 步骤5:SCL拉高
7'd7 : scl <= 1'b0; // 步骤7:SCL拉低
7'd8 : sda_out <= SLAVE_ADDR[5]; // 步骤8:发送从地址第5位
7'd9 : scl <= 1'b1; // 步骤9:SCL拉高
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd12: sda_out <= SLAVE_ADDR[4]; // 步骤12:发送从地址第4位
7'd13: scl <= 1'b1; // 步骤13:SCL拉高
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd16: sda_out <= SLAVE_ADDR[3]; // 步骤16:发送从地址第3位
7'd17: scl <= 1'b1; // 步骤17:SCL拉高
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd20: sda_out <= SLAVE_ADDR[2]; // 步骤20:发送从地址第2位
7'd21: scl <= 1'b1; // 步骤21:SCL拉高
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd24: sda_out <= SLAVE_ADDR[1]; // 步骤24:发送从地址第1位
7'd25: scl <= 1'b1; // 步骤25:SCL拉高
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd28: sda_out <= SLAVE_ADDR[0]; // 步骤28:发送从地址第0位
7'd29: scl <= 1'b1; // 步骤29:SCL拉高
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: sda_out <= 1'b1; // 步骤32:发送读写位(1=读)
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(采样)
7'd35: scl <= 1'b0; // 步骤35:SCL拉低
7'd36: begin // 步骤36:切换SDA为输入,等待应答
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37: scl <= 1'b1; // 步骤37:SCL拉高(采样应答)
7'd38: st_done <= 1'b1; // 步骤38:标记状态完成
7'd39: begin // 步骤39:SCL拉低,计数器清零
scl <= 1'b0;
cnt <= 1'b0;
end
default : ; // 其他步骤无操作
endcase
end
st_data_rd: begin // 读数据:从寄存器接收8位数据
case(cnt)
7'd0: sda_dir <= 1'b0; // 步骤0:SDA切换为输入(准备接收数据)
7'd1: begin // 步骤1:读取数据第7位,SCL拉高
data_r[7] <= sda_in;
scl <= 1'b1;
end
7'd3: scl <= 1'b0; // 步骤3:SCL拉低
7'd5: begin // 步骤5:读取数据第6位,SCL拉高
data_r[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7: scl <= 1'b0; // 步骤7:SCL拉低
7'd9: begin // 步骤9:读取数据第5位,SCL拉高
data_r[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11: scl <= 1'b0; // 步骤11:SCL拉低
7'd13: begin // 步骤13:读取数据第4位,SCL拉高
data_r[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15: scl <= 1'b0; // 步骤15:SCL拉低
7'd17: begin // 步骤17:读取数据第3位,SCL拉高
data_r[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19: scl <= 1'b0; // 步骤19:SCL拉低
7'd21: begin // 步骤21:读取数据第2位,SCL拉高
data_r[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23: scl <= 1'b0; // 步骤23:SCL拉低
7'd25: begin // 步骤25:读取数据第1位,SCL拉高
data_r[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27: scl <= 1'b0; // 步骤27:SCL拉低
7'd29: begin // 步骤29:读取数据第0位,SCL拉高
data_r[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31: scl <= 1'b0; // 步骤31:SCL拉低
7'd32: begin // 步骤32:SDA切换为输出,发送非应答(结束读取)
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd33: scl <= 1'b1; // 步骤33:SCL拉高(从设备采样非应答)
7'd34: st_done <= 1'b1; // 步骤34:标记状态完成
7'd35: begin // 步骤35:SCL拉低,计数器清零,输出读取数据
scl <= 1'b0;
cnt <= 1'b0;
i2c_data_r <= data_r;
end
default : ; // 其他步骤无操作
endcase
end
st_stop: begin // 发送停止信号:结束I2C操作
case(cnt)
7'd0: begin // 步骤0:SDA切换为输出,拉低(准备停止信号)
sda_dir <= 1'b1;
sda_out <= 1'b0;
end
7'd1 : scl <= 1'b1; // 步骤1:SCL拉高(停止信号条件:SCL高时SDA从低变高)
7'd3 : sda_out <= 1'b1; // 步骤3:SDA拉高(生成停止信号)
7'd15: st_done <= 1'b1; // 步骤15:标记状态完成
7'd16: begin // 步骤16:计数器清零,置位操作完成标志
cnt <= 1'b0;
i2c_done <= 1'b1;
end
default : ; // 其他步骤无操作
endcase
end
endcase
end
end
endmodule // 模块定义结束// 定义I2C寄存器配置模块(生成ES8388芯片所需的寄存器配置序列,通过I2C驱动模块发送)// 定义I2C寄存器配置模块(生成ES8388芯片所需的寄存器配置序列,通过I2C驱动模块发送)
module i2c_reg_cfg (
input clk , // i2c_reg_cfg模块的工作时钟(用于同步寄存器配置时序,与I2C驱动时钟关联)
input rst_n , // 复位信号(低电平有效,复位时停止配置)
input i2c_done , // I2C一次操作完成的反馈信号(高电平表示当前寄存器配置完成)
input [1:0] volume , // 音量控制选择信号(2位,用于动态调整输出音量等级)
output reg i2c_exec , // I2C操作执行信号(高电平触发一次寄存器配置)
output reg cfg_done , // ES8388配置完成标志(所有寄存器配置完毕后置高)
output reg [15:0] i2c_data // 配置数据(高8位为寄存器地址,低8位为配置值)
);
// 参数定义(模块配置常量)
parameter WL = 6'd32; // 字长(音频数据的位宽,用于匹配ES8388的ADC/DAC数据格式)
// 本地参数定义
localparam REG_NUM = 5'd24; // 需要配置的寄存器总数(共24个寄存器)
localparam SPEAK_VOLUME = 6'd50; // 扬声器音量范围(0~63,此处为参考值)
// 寄存器定义
reg [1:0] wl ; // 字长编码(根据WL参数生成的2位编码,用于配置数据格式)
reg [7:0] start_init_cnt; // 启动初始化延迟计数器(系统上电后延迟一段时间再开始配置,确保稳定)
reg [4:0] init_reg_cnt ; // 配置寄存器计数器(记录已配置的寄存器数量)
reg [5:0] phone_volume ; // 实际音量值(0~63,根据volume输入转换得到,默认40)
// 根据volume输入转换为实际音量值(0~63)
always @ (volume) begin // 组合逻辑:输入volume变化时立即更新
case(volume)
2'b00 : phone_volume = 6'd15; // volume=00时,音量设为15(较小)
2'b01 : phone_volume = 6'd30; // volume=01时,音量设为30(中等)
2'b10 : phone_volume = 6'd45; // volume=10时,音量设为45(较大)
2'b11 : phone_volume = 6'd60; // volume=11时,音量设为60(最大)
default : phone_volume = 6'd40; // 默认音量40
endcase
end
//****************************************************
//** 主代码
//*****************************************************
// 根据音频数据位宽WL生成对应的2位编码(用于配置ES8388的数据格式)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,编码清零
wl <= 2'b00;
else begin
case(WL) // 不同位宽对应不同编码
6'd16: wl <= 2'b00; // 16位数据对应编码00
6'd20: wl <= 2'b01; // 20位数据对应编码01
6'd24: wl <= 2'b10; // 24位数据对应编码10
6'd32: wl <= 2'b11; // 32位数据对应编码11
default:
wl <= 2'd00; // 默认编码00
endcase
end
end
// 系统上电后延迟一段时间再开始配置(确保ES8388芯片稳定启动)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,计数器清零
start_init_cnt <= 8'd0;
else if(start_init_cnt < 8'hff) // 未计数到最大值(255)时,持续自增
start_init_cnt <= start_init_cnt + 1'b1;
end
// 控制I2C执行信号(触发寄存器配置)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,执行信号置低
i2c_exec <= 1'b0;
else if(init_reg_cnt == 5'd0 & start_init_cnt == 8'hfe) // 初始时,延迟计数器即将满时,触发第一次配置
i2c_exec <= 1'b1;
else if(i2c_done && init_reg_cnt < REG_NUM) // 每次配置完成且未配置完所有寄存器时,触发下一次配置
i2c_exec <= 1'b1;
else // 其他情况,执行信号置低
i2c_exec <= 1'b0;
end
// 记录已配置的寄存器数量
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,计数器清零
init_reg_cnt <= 5'd0;
else if(i2c_exec) // 每次执行信号有效时,计数器自增(表示开始配置下一个寄存器)
init_reg_cnt <= init_reg_cnt + 1'b1;
end
// 配置完成标志(所有寄存器配置完毕后置高)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,标志置低
cfg_done <= 1'b0;
else if(i2c_done & (init_reg_cnt == REG_NUM) ) // 当最后一个寄存器配置完成时,标志置高
cfg_done <= 1'b1;
end
// 根据计数器值输出对应的寄存器地址和配置数据(按顺序配置ES8388的寄存器)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) // 复位时,配置数据清零
i2c_data <= 16'b0;
else begin
case(init_reg_cnt)
// R0:ADC和DAC使用相同的电源配置,启用VREF和VMID
5'd0 : i2c_data <= {8'h00 ,8'h16};
// R1:关闭所有模拟输入通路(初始状态)
5'd1 : i2c_data <= {8'h01 ,8'h00};
// R2:关闭所有模拟输出通路(初始状态)
5'd2 : i2c_data <= {8'h02 ,8'h00};
// R3:设置ADC输入源为默认(初始状态)
5'd3: i2c_data <= {8'h03 ,8'h00};
// R4:设置DAC输出源配置
5'd4 : i2c_data <= {8'h04 ,8'h3c};
// R8:主模式,MCLK时钟频率由BCLK自动适配
5'd5 : i2c_data <= {8'h08 ,8'h80};
// R9:麦克风增益设置为6dB
5'd6 : i2c_data <= {8'h09 ,8'h22};
// R12:ADC数据格式设为24位I2S模式
5'd7 : i2c_data <= {8'h0c,8'h00};
// R13:ADC采样率设置为48KSPS(12.288MHz / 256)
5'd8 : i2c_data <= {8'h0d,8'h02};
// R16:左声道ADC数字增益设为0dB(无增益)
5'd9 : i2c_data <= {8'h10,8'h00};
// R17:右声道ADC数字增益设为0dB(无增益)
5'd10: i2c_data <= {8'h11,8'h00};
// R18:ALC(自动电平控制)和PGA(可编程增益放大器)范围设置
5'd11: i2c_data <= {8'h12,8'h00};
// R23:DAC数据格式设为24位I2S模式
5'd12: i2c_data <= {8'h17,8'h00};
// R24:DAC采样率设置为48KSPS(12.288MHz / 256)
5'd13: i2c_data <= {8'h18,8'h02};
// R26:左声道DAC数字增益设为0dB(无增益)
5'd14: i2c_data <= {8'h1a,8'h00 };
// R27:右声道DAC数字增益设为0dB(无增益)
5'd15: i2c_data <= {8'h1b,8'h00};
// R39:启用DAC混音器配置
5'd16: i2c_data <= {8'h27,8'hb8};
// R42:启用另一组DAC混音器配置
5'd17: i2c_data <= {8'h2a,8'hb8};
// R43:ADC和DAC使用同一个LRC(左右声道时钟)同步
5'd18: i2c_data <= {8'h2b,8'h80};
// R2E:左声道输出1(LOUT1)使能,增益设为-6dB(衰减6dB)
5'd19: i2c_data <= {8'h2e,8'h1A};
// R2F:右声道输出1(ROUT1)使能,增益设为-6dB
5'd20: i2c_data <= {8'h2f,8'h1A};
// R30:左声道输出2(LOUT2)使能,增益设为-6dB
5'd21: i2c_data <= {8'h30,8'h1A};
// R31:右声道输出2(ROUT2)使能,增益设为-6dB
5'd22: i2c_data <= {8'h31,8'h1A};
// R10:ADC输入选择为1通道(麦克风输入),0x00对应麦克风,0x50对应Line-in
5'd23: i2c_data <= {8'h0a,8'h00};
default : ; // 其他计数情况,无操作
endcase
end
end
endmodule // 模块定义结束set_pin_assignment { aud_adcdat } { LOCATION = R14; }
set_pin_assignment { aud_bclk } { LOCATION = M6; }
set_pin_assignment { aud_dacdat } { LOCATION = P6; }
set_pin_assignment { aud_lrc } { LOCATION = M7; }
set_pin_assignment { aud_mclk } { LOCATION = N5; }
set_pin_assignment { aud_scl } { LOCATION = R12; }
set_pin_assignment { aud_sda } { LOCATION = R9; }
set_pin_assignment { ilaclk } { LOCATION = T5; }
set_pin_assignment { led } { LOCATION = B14; }
set_pin_assignment { sys_clk } { LOCATION = R7; }
set_pin_assignment { sys_rst_n } { LOCATION = A9; }
set_pin_assignment { volume[0] } { LOCATION = A10; }
set_pin_assignment { volume[1] } { LOCATION = B10; }
i2c只在初始化读取一次,我要求i2c能够重复写入,同时开关A10和B10能够控制音量大小,修改以上代码
最新发布