直插式精巧I/O模块:WIZ812MJ数据手册V1.1

本文详细介绍了WIZ812MJ网络模块的功能特性,包括其硬件TCP/IP应用、支持的网络标准、引脚分配、时序图等关键信息。模块集成了W5100硬件TCP/IP芯片和MAG-JACK网络接口,支持多种网络配置和操作,适用于快速开发互联网应用系统。

812mj-1

1. 简介

WIZ812MJ是一款内嵌了W5100(硬件TCP/IP芯片,内置PHY)、带其他胶合逻辑的MAG-JACK(带变压器的RJ45)网络模块。它可以当作一个组件使用,而且不需要为W5100和变压器准备接口。对于那些想要快速的开发互联网应用系统的用户来说,WIZ812MJ是一个理想的选择。

想了解更多关于硬件TCP/IP应用的信息,请参阅W5100的数据手册。

WIZ812MJ 由 W5100 和 MAG-JACK 组成。

  • TCP/IP, MAC 协议层: W5100
  • 物理层: 内置于 W5100
  • 接口: MAG-JACK(带变压器的RJ45)

1.1. 特点

  • 支持10/100 Base TX
  • 支持半/全双工
  • 支持自动协商和自动交叉检测
  • 符合IEEE 802.3/802.3u标准
  • 工作电压3.3V,承受5V I/O信号
  • 支持网络状态指示器LED
  • 内置硬件互联网协议:TCP,IP Ver.4,UDP,ICMP,ARP,PPPoE,IGMP
  • 内置硬件以太网协议:DLC,MAC
  • 支持同时的4个独立连接
  • 支持单片机总线接口和SPI接口
  • 支持直接/间接模式总线访问
  • 支持接口API供应用程序开发
  • 2.54mm间距2 x10排针接口
  • 温度:0 ~ 70℃ (工作), -40 ~ 85℃ (存储)

1.2. 模块图

812mj-2

 

1.3.   WIZ811MJ和WIZ812MJ之间的区别

812mj-3

 

2.引脚分配 & 描述

 

2.1. 引脚分配

812mj-4

 

812mj-5

I : 输入                               O : 输出

I/O : 双向输入输出                            P : 电源

2.2. 电源&地

812mj-6

2.3. 单片机接口

812mj-7

 

2.4. 其他信号

812mj-8

 

3.时序图

WIZ812MJ提供了W5100的如下接口。

-.直接/间接模式总线访问

-.SPI访问

3.1.         复位时序

812mj-9

 

3.2.         寄存器/存储器的读时序

812mj-10

 

3.3.         寄存器/存储器的写时序

812mj-11

 

3.4.         SPI 时序

812mj-12

 

4.尺寸

812mj-13

 

812mj-14

 

812mj-15

 

5.原理图

812mj-16

 

812mj-17

 

6.原件清单

812mj-18

 

感谢您的关注!

更多信息:WIZ812MJ

 


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→11→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能够控制音量大小,修改以上代码
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值