基于FPGA的I2C实验Verilog源代码
使用Verilog来实现EEPROM的读写,进行一个简单的I2C实战应用
这个 I2C 总线的时序是一致的。 EEPROM 应答 输出应答 sclk 的第九个周期给出,低电平应答。如果主机没有收到应答, 需要重新配置。 数据传输时序 sda 数据线在 scl 时钟的下降沿中间变化,可以避免产生误触开始结束标 志。 I2C Design i2c_start 为高电平有效,传输完成后会产生一个 i2c_done 结束标志,表示 操作完成。 I2C 状态转移图 I2C 写操作
SDA 双向 Pin A2,A1,A0 Device Addr default all 0,只操作一片可悬空引脚。 WP 接地正常读写,WP 接 Vcc 写操作被禁止 字节寻址地址ห้องสมุดไป่ตู้是由 12(AT24C32)或 13bit(AT24C64)的地址组成, 需要操作 16 位字地址高 3 或 4 位忽略即可。 Device Address 8’hA0 写器件地址,8’hA1 读器件地址 写字节操作 随机读字节操作 我这个芯片是双字节数据地址,所以在写数据地址时要写两次,先是高字 节后是低字节。 开始结束标志
(1)产生 start 位 (2)传送器件地址 ID_Address,器件地址的最后一位为数据的传输方向 位,R/W,低电平 0 表示主机往从机写数据(W),1 表示主机从从机读数据 (R)。这里按照手册给出的操作图,应该是 W 即低电平。ACK 应答,应答 是从机发送给主机的应答,这里不用管。 (3)传送写入器件寄存器地址,即数据要写入的位置。同样 ACK 应答 不用管。 (4)传送要写入的数据。ACK 应答不用管。 (5)产生 stop 信号。 I2C 读操作 (1)产生 start 信号
其他部分没啥好说的根据时序图写就行了,需要注意的一点是我们应该在 sclk 的高电平的中间采样数据,在 sclk 低电平的中间改变数据,当 sclk 为高 电平的时候,sda 为出现下降沿为 start 位, sda 出现上升沿为 stop 位,所以 在 sclk 为高电平的时候 sda 应该保持稳定不能随意乱动。这就又回到了数据 传输有效的条件,只有在 sclk 为低电平期间,才允许数据变化,在高电平期 间,不允许数据变化,否则就会出现起始位或结束位。 EEPROM 有个仿真模型,在夏雨闻老师的书里面就有,这个模型默认是 200khz 的 sclk 驱动,仿真的时候可以将时间参数改小,我这里也分享出来。 仿真模型代码点击阅读原文可以查看。 根据仿真模型仿真的话基本不会有什幺问题,需要注意的是操作的完成标 志。从仿真上看到输入读写都没问题,但是 stop 标志没产生好,仿真看到读 写操作没问题,但实际还是不行的,需要严格按照 EEPROM 的手册操作时序 进行,差一点就不行。 我最后使用拨码开关作为读写使能,数码管显示读出来的输出,最后实现 了对指定存储地址读写数据。
用VERILOG HDL实现I2C总线功能
摘要:简述了i2c总线的特点;介绍了开发fpga时i2c总线模块的设计思想;给出并解释了用verilog hdl实现部分i2c总线功能的程序,以及i2c总线主从模式下的仿真时序图。
关键词:i2c总线 fpga verilog hdl 时序开发fpga时,利用eda工具设计芯片实现系统功能已经成为支撑电子设计的通用平台,并逐步向支持系统级的设计方向发展。
在软件设计过程中,越来越强调模块化设计。
i2c总线是philips公司推出的双向两线串行通讯标准,具有接口线少、通讯效率高等特点。
把i2c总线设计成相应的模块,有利于相关fpca的开发。
目前有一些介绍相关开发的资料,但都是利用vhdl语言或ahdl语言实现的。
本文给出利用verilog hdl语言设计的i2c总线模块。
1 i2c总线概述i2c总线系统由两根总线即scl(串行时钟)线和sda(串行数据)线构成。
这种总线可以设计成很多种通讯配置,但本文只讨论主从系统的应用。
主器件控制总线通讯,开始/结束传送、发送信息并产生i2c系统时钟。
在写操作过程中,从器件一旦被主控器件寻址,就执行特定的相应功能。
在读操作过程中,主控器件从从器件那里获得数据。
在整个主从传送过程中,所有的事件都通过主控器件的scl 时钟线达到同步。
连到总线上的器件的接口形式必须是漏极开路或集电极开路输出状态。
通过上拉电阻,使得两根总线在空闲的状态下都为高电平状态。
因此i2c总线上具有线与功能,即总线上的所有器件都达到高电子状态时,i2c总线才能达到高电平状态,从而使总线上的高速器件和慢速器件工作同步。
在i2c协议中,从器件地址是一个唯一的7位地址。
接下来是一个读写方向标志位,读状态是高电平、写状态是低电子。
2 i2c模块的设计与实现根据i2c协议中传输过程的特点,i2c模块可以划分为字节发送模块、字节接收模块、开始条件模块、停止条件模块。
其中,字节发送模块、字节接收模块和停止条件模块为基本模块。
i2cslavefpga实现代码
LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_UNSIGNED.ALL;USE IEEE.STD_LOGIC_ARITH.ALL;ENTITY i2c_slave ISGENERIC (WR_OP_MODE: STD_LOGIC :='1');PORT (----------I2C Bus--------scl : IN STD_LOGIC ;----I2C bus clksda : INOUT STD_LOGIC ;---------sys clk----addsys_clk : IN std_logic;reset : IN STD_LOGIC ;---------Logic Out-------i2c_addr : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);i2c_data_out : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);wrd : OUT STD_LOGIC ; ---0 write 1 readwrd_en : OUT STD_LOGIC ;---1 activei2c_scl_out : OUT STD_LOGIC ;----falling edge---------Logic In-------i2c_data_in : IN STD_LOGIC_VECTOR(7 DOWNTO 0);--------dv_id in--------dv_id_in : IN STD_LOGIC_VECTOR(3 DOWNTO 0) );END i2c_slave;ARCHITECTURE beh OF i2c_slave ISCONSTANT RESET_ACTIVE : STD_LOGIC := '0';CONSTANT IDLE : STD_LOGIC_VECTOR :="0001";----Grad code CONSTANT HEADER : STD_LOGIC_VECTOR :="0011"; CONSTANT ACK_HEADER : STD_LOGIC_VECTOR :="0010"; CONSTANT RCV_REG_ADDR : S TD_LOGIC_VECTOR :="0110"; CONSTANT ACK_REG_ADDR : S TD_LOGIC_VECTOR :="0111"; CONSTANT RCV_DA TA : STD_LOGIC_VECTOR :="0101"; CONSTANT ACK_DA TA : STD_LOGIC_VECTOR :="0100"; CONSTANT XMIT_DA TA : STD_LOGIC_VECTOR :="1010"; CONSTANT W AIT_ACK : STD_LOGIC_VECTOR :="1011";----bit counter constantCONSTANT CNT_DONE : STD_LOGIC_VECTOR :="0111";---i2c signalSIGNAL sda_in : STD_LOGIC ;SIGNAL detect_start,detect_stop : STD_LOGIC;sIGNAL state : STD_LOGIC_VECTOR(3 DOWNTO 0);----bit counter 0 to 7SIGNAL bit_cnt : STD_LOGIC_VECTOR(3 DOWNTO 0);SIGNAL bit_cnt_ld,bit_cnt_en : STD_LOGIC ;SIGNAL addr_match : STD_LOGIC ;---header dataSIGNAL i2c_header : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL i2c_header_en : STD_LOGIC;---shift dataSIGNAL i2c_shift_data : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL i2c_shift_en : STD_LOGIC ;---addrSIGNAL addr_flag : STD_LOGIC ;SIGNAL addr_reg : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL get_data_flag : STD_LOGIC ;----slave_sdaSIGNAL slave_sda : STD_LOGIC ;SIGNAL sda_dir : STD_LOGIC ;---load dataSIGNAL load_data_reg_tem : STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL load_data_flag,load_data_flag_dl : STD_LOGIC ;SIGNAL load_data_en : STD_LOGIC ;SIGNAL shift_out : STD_LOGIC ;--wrd_enSIGNAL wrd_en_reg,wrd_en_reg_dl : STD_LOGIC;signal scl_reg : std_logic;signal scl_reg_shift : std_logic_vector(5 downto 0);BEGIN------------jitter -----------------gen_scl_reg : PROCESS (sys_clk,reset) ISBEGINIF (reset=RESET_ACTIVE) THENscl_reg<='1';scl_reg_shift<=(others=>'1');ELSIF (sys_clk'EVENT AND sys_clk='1') THENscl_reg_shift<=scl_reg_shift(4 downto 0)&scl;if scl_reg_shift(5 downto 0)="111111" thenscl_reg<='1';elsif scl_reg_shift(5 downto 0)="000000" thenscl_reg<='0';elsescl_reg<=scl_reg;end if;END IF;END PROCESS gen_scl_reg;--************************Get Out**************************i2c_addr<=addr_reg;i2c_data_out<=i2c_shift_data;wrd<=i2c_header(0);--wrd_en<=wrd_en_reg AND wrd_en_reg_dl;wrd_en<=wrd_en_reg;i2c_scl_out<=scl_reg;gen_sda : PROCESS (sda_dir,slave_sda,sda) ISBEGINIF (sda_dir='1') THENsda<=slave_sda;ELSEsda_in<=sda;sda<='Z';END IF;END PROCESS gen_sda;wrd_en_reg<=get_data_flag OR load_data_flag;-- ************************ START/STOP Detect Process ************************ -- This process detects the start and stop conditions.-- by using SDA as a clock-------------------------------------start_det : PROCESS (sda_in,reset) ISBEGINIF (reset=RESET_ACTIVE OR state=HEADER) THENdetect_start<='0';--ELSIF (sda'EVENT AND sda='0') THENELSIF (sda'EVENT AND sda='0') THEN---change by lq 3.5IF (scl_reg='1') THEN ---or scl='1'??detect_start<='1';ELSEdetect_start<='0';END IF;END IF;END PROCESS start_det;stop_det : PROCESS (sda_in,reset,detect_start) ISBEGINIF (reset=RESET_ACTIVE OR detect_start='1') THENdetect_stop<='0';ELSIF (sda'EVENT AND sda/='0') THEN----IF (scl_reg='1') THEN ---or scl='1'??detect_stop<='1';ELSEdetect_stop<='0';END IF;END IF;END PROCESS stop_det;--******************I2C Header Process****************--i2c_header_en<='1' WHEN (detect_start='1' OR state=HEADER) ELSE '0'; i2c_header_en<='1' WHEN (state=HEADER) ELSE '0';gen_i2cheader : PROCESS (reset,scl_reg,detect_stop) ISBEGINIF (reset=RESET_ACTIVE OR detect_stop='1') THENi2c_header<=( OTHERS =>'0');----------------ELSIF (scl_reg'EVENT AND scl_reg='1') THEN -----rising_edge IF (i2c_header_en='1') THENi2c_header<=i2c_header(6 DOWNTO 0)&sda_in;END IF;END IF;END PROCESS gen_i2cheader;--******************Bit Counter Process******************gen_bit_cnter : PROCESS (reset,scl_reg) ISBEGINIF (reset=RESET_ACTIVE) THENbit_cnt<=( OTHERS =>'0');ELSIF (scl_reg'EVENT AND scl_reg='0') THEN -----falling_edgeIF (bit_cnt_ld='1') THENbit_cnt<=( OTHERS =>'0');ELSIF (bit_cnt_en='1') THENbit_cnt<=bit_cnt+1;ELSEbit_cnt<=( OTHERS =>'0');END IF;END IF;END PROCESS gen_bit_cnter;bit_cnt_ld<='1' WHEN (state=IDLE) OR (state=ACK_HEADER) OR (state=ACK_REG_ADDR) OR (state=ACK_DA TA) OR (state=W AIT_ACK) OR (detect_start='1')ELSE '0';bit_cnt_en<='1' WHEN (state=HEADER) OR (state=RCV_REG_ADDR) OR(state=RCV_DA TA)OR (state=XMIT_DA TA) ELSE '0';--*******************Addr Match Process******************addr_match<='1' WHEN i2c_header(7 DOWNTO 1)=("110"&dv_id_in) ELSE '0';--******************Shift data Process*******************get_shift_data : PROCESS (reset,scl_reg) ISBEGINIF (reset=RESET_ACTIVE) THENi2c_shift_data<=(OTHERS =>'0');ELSIF (scl_reg'EVENT AND scl_reg='1') THEN---change by lqIF (i2c_shift_en='1') THENi2c_shift_data<=i2c_shift_data(6 DOWNTO 0)&sda_in;END IF;END IF;END PROCESS get_shift_data;i2c_shift_en<='1' WHEN (state=RCV_REG_ADDR) OR (state=RCV_DA TA) ELSE '0';--******************Get addr Process******************addr_flag<='1' WHEN state=ACK_REG_ADDR ELSE '0';get_addr_reg : PROCESS (reset,scl_reg) ISBEGINIF (reset=RESET_ACTIVE) THENaddr_reg<=(OTHERS =>'0');ELSIF (scl_reg'EVENT AND scl_reg='0') THENIF (addr_flag='1') THENaddr_reg<=i2c_shift_data;END IF;END IF;END PROCESS get_addr_reg;get_data_flag<='1' WHEN state=ACK_DA TA ELSE '0';--******************Load Data Process****************gen_load : PROCESS (scl_reg,reset) ISBEGINIF (reset=RESET_ACTIVE) THENload_data_reg_tem<=( OTHERS =>'0');ELSIF (scl_reg'EVENT AND scl_reg='0') THENIF (load_data_flag='1') THENload_data_reg_tem<=i2c_data_in;ELSIF (load_data_en='1') THENload_data_reg_tem<=load_data_reg_tem(6 DOWNTO 0)&load_data_reg_tem(7);END IF;END IF;END PROCESS gen_load;--shift_out<=load_data_reg_tem(7) WHEN ((load_data_en='1') AND (load_data_flag_dl/='1')) ELSE i2c_data_in(7);shift_out<=load_data_reg_tem(7) ;-- WHEN ((load_data_en='1') AND (load_data_flag_dl/='1')) ELSE '0';load_data_en<='1' WHEN (state=XMIT_DA TA) ELSE '0';load_data_flag<='1' WHEN ((state=ACK_HEADER) AND (i2c_header(0)='1') AND (addr_match='1')) ELSE '0';get_load_flag_dl : PROCESS (reset,scl_reg) ISBEGINIF (reset=RESET_ACTIVE) THENload_data_flag_dl<='0';ELSIF (scl_reg'EVENT AND scl_reg='0') THENload_data_flag_dl<=load_data_flag;END IF;END PROCESS get_load_flag_dl;--***************Shift out Process**********************slave_sda<='0' WHEN ((state=ACK_HEADER) AND (addr_match='1')) OR (state=ACK_REG_ADDR) OR (state=ACK_DA TA)ELSE shift_out WHEN (state=XMIT_DA TA)ELSE '1';sda_dir<='1' WHEN ((state=ACK_HEADER) AND (addr_match='1')) OR (state=ACK_REG_ADDR) OR (state=ACK_DA TA)OR (state=XMIT_DA TA) ELSE '0';-- ************** Main State Machine Process ************state_machine : PROCESS (scl_reg,reset,detect_stop) ISBEGINIF (reset=RESET_ACTIVE OR detect_stop='1') THENstate <= IDLE;ELSIF (scl_reg'EVENT AND scl_reg='0') THENCASE (state) IS---------------IDLE STA TE--------WHEN IDLE => IF (detect_start='1') THENstate<=HEADER;END IF;---------------HEADER STA TE-------WHEN HEADER =>IF (bit_cnt=CNT_DONE) THENstate<=ACK_HEADER;END IF;---------------ACK_HEADER STA TE---WHEN ACK_HEADER => IF (addr_match='1') THENIF (i2c_header(0)='0') THEN---writestate<=RCV_REG_ADDR;ELSE ----readstate<=XMIT_DA TA;END IF;ELSEstate<=IDLE;END IF;--------------- RCV_REG_ADDR STA TE---WHEN RCV_REG_ADDR => IF (detect_start='1') THENstate<=HEADER;ELSIF (bit_cnt=CNT_DONE) THENstate<=ACK_REG_ADDR;END IF;----------------ACK_REG_ADDR STA TE--------WHEN ACK_REG_ADDR => state<=RCV_DA TA;--------------- RCV_DA TA STA TE--------WHEN RCV_DA TA => IF (detect_start='1') THENstate<=HEADER;ELSIF (bit_cnt=CNT_DONE) THENstate<=ACK_DA TA;END IF;--------------- ACK_DA TA STA TE-------WHEN ACK_DA TA => IF (WR_OP_MODE='1') THENstate<=RCV_REG_ADDR;ELSEstate<=RCV_DA TA;END IF;--------------- XMIT_DA TA STA TE------WHEN XMIT_DA TA => IF (detect_start='1') THENstate<=HEADER;ELSIF (bit_cnt=CNT_DONE) THENstate<=W AIT_ACK;END IF;--------------- W AIT_ACK STA TE-------WHEN WAIT_ACK => IF (sda_in='0') THENstate<= XMIT_DA TA;ELSEstate<= IDLE;END IF;WHEN OTHERS => state<= IDLE;-- i2c_header_en<='0';END CASE;END IF;END PROCESS state_machine;END beh;。
FPGA读写i2c_verilog
//
////////////////////////////////////////////////////////////////////////////////
module iic_com(
clk,rst_n,
sw1,sw2,
scl,sda,
dis_data
end
ACK2:begin
if(/*!sda*/`SCL_NEG) begin//从机响应信号
if(!sw1_r) begin
cstate <= DATA;//写操作
db_r <= `WRITE_DATA;//写入的数据
end
else if(!sw2_r) begin
db_r <= `DEVICE_READ;//送器件地址(读操作),特定地址读需要执行该步骤以下操作
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
// Module Name: iic_top
// Project Name:
// Target Device:
// Tool versions:
// Description:
//
// Dependencies:
I2C verilog (非常详细的i2c学习心得)
图 5. AT24C02/4/8/16 读指定地址存储单元的数据帧格式
首先是一堆输入输出、寄存器的定义,我们读程序的时候大可先不看这些,等到后面有 需要的时候再回过头来看这些定义, 这里需要注意的是 SDA 与 DATA 的类型, 都是 inout 型, SDA 我们很容易理解,因为主机和从机都会给 SDA 线上发信号,比如字节写入格式时,主机 先给 SDA 发了 1 个 8 位数据,然后作为应答位,从机要把 SDA 拉低,表示我已经接受到你 的信号了,在应答位时主机是不能操作 SDA 线的。然而 DATA 设定为 inout 型,我们可以看 到图 2,其实在字节写入格式时,DATA 是 signal 模块传输给 EEPROM_WR 模块,作为要写入 的数据。 而在字节读取格式时, EEPROM_WR 通过 SDA 从 EEPROM 中读取数据, 其实跟 DATA 是没有关系的,这里可以只将 DATA 设定为 input 型,设定为 inout 型是因为后续的程序会将 EEPROM_WR 读取到的数据发给 signal,与 signal 当初发送的数据进行比较,检验通信是否 正确。如果将比较数据这程序操作放在 EEPROM_WR 模块中,就可以将 DATA 设为 input。 提到 inout 类型,还得再多补充两句,inout,顾名思义,双向口既能作为输入又能作为 输出,可以节省管脚,在具体实现上一般是用三态门来实现,图 6 就是用三态门实现的 sda 总线的示意图。
单,使用发送数据线 TXD 和接收数据线 RXD 来传送数据,接收和发送可以单独进行也可以 同时进行。它传送数据的格式有严格的规定,每个数据以相同的位串形式传送,每个串行数 据由起始位,数据位,奇偶校验位和停止位组成。从起始位到停止位为一个字符的完整通信 格式。SPI 情况就相对多一些,根据时钟极性(CPOL)和时钟相位(CPHA)两个参数的不同有四 种基本情况,传送数据的格式与 UART 差不多。而 I2C 总线的协议要比 UART 和 SPI 复杂,能 掌握 I2C,也就能掌握 UART 和 SPI。言归正传,回到我们的 I2C 设计实例。 第一步首先了解,这个 I2C 实例的功能。 这个实例实现了通过 I2C 总线对 EEPROM 写入数据, 再将写入 EEPROM 中的数据读取出 来的一个过程。实例的重点在于对 I2C 总线协议时序的掌握,即用 I2C 总线要求的格式将数 据写入到 EEPROM 中,再读取出来。 什么是 EEPROM?EEPROM (Electrically Erasable Programmable Read‐Only Memory),电可 擦可编程只读存储器, 一种掉电后数据不丢失的存储芯片。 EEPROM 可以在电脑上或专用设 备上擦除已有信息,重新编程。所以,EEPROM 是可以写入数据也可以读取数据的,并且 EEPROM 掉电数据并不会丢失,在后面将程序烧写到开发板的过程中可以验证这一点。 第二步,简单地了解一下这个实例的各个模块。
i2cverilog代码
4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default:endcase//sda_r <= db_r[4'd7-num]; //送EEPROM地址(高bit开始)cstate <= ADD2;endend//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bitelsecstate <= ADD2;endACK2:beginif(/*!sda*/`SCL_NEG) begin//从机响应信号if(!sw1_r) begincstate <= DATA;//写操作db_r <= `WRITE_DATA; //写入的数据endelse if(!sw2_r) begindb_r <= `DEVICE_READ; //送器件地址(读操作)地址读需要执行该步骤以下操作cstate <= START2;//读操作endendelse cstate <= ACK2;//等待从机响应START2: begin //读操作起始位if(`SCL_LOW) beginsda_link <= 1'b1; //sda作为outputsda_r <= 1'b1;//拉高数据线sdacstate <= START2;endelse if(`SCL_HIG) begin //scl为高电平中间sda_r <= 1'b0;//拉低数据线sda,产生起始位信号cstate <= ADD3;endelse cstate <= START2; endADD3:begin//送读操作地址if(`SCL_LOW) beginif(num==4'd8) beginnum <= 4'd0;//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态(input)cstate <= ACK3; endelse beginnum <= num+1'b1; case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default:endcase//sda_r <= db_r[4'd7-num]; //送EEPROM地址(高bit开始)cstate <= ADD3;endend//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bitelse cstate <= ADD3;endACK3:beginif(/*!sda*/`SCL_NEG) begincstate <= DATA;//从机响应信号sda_link <= 1'b0;endelse cstate <= ACK3;//等待从机响应endDATA:beginif(!sw2_r) begin//读操作if(num<=4'd7) begin cstate <= DATA;if(`SCL_HIG) beginnum <= num+1'b1; case (num)4'd0: read_data[7] <= sda;4'd1: read_data[6] <= sda; 4'd2: read_data[5] <= sda; 4'd3: read_data[4] <= sda; 4'd4: read_data[3] <= sda;4'd5: read_data[2] <= sda; 4'd6: read_data[1] <= sda; 4'd7: read_data[0] <= sda;default:endcase//read_data[4'd7-num] <= sda; // 读数据(高开始)end//elseif(`SCL_NEG)read_data<={read_data[6:0],read_data[7]}; //数据循环右移endelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;//num计数清零cstate <= ACK4;endelse cstate <= DATA;endelse if(!sw1_r) begin //写操作sda_link <= 1'b1;if(num<=4'd7) begin cstate <= DATA;if(`SCL_LOW) beginsda_link <= 1'b1;//数据线sda作为outputnum <= num+1'b1; case (num)4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0];default:endcase//sda_r <= db_r[4'd7-num]; //写入数据(高bit开始)end//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //写入数据左移1bitendelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态cstate <= ACK4;endelse cstate <= DATA;endendACK4: beginif(/*!sda*/`SCL_NEG) begin //sda_r <= 1'b1; cstate <= STOP1; endelse cstate <= ACK4; endSTOP1:beginif(`SCL_LOW) beginsda_link <= 1'b1;sda_r <= 1'b0;cstate <= STOP1;endelse if(`SCL_HIG) begin sda_r <= 1'b1; //scl为高时,sda产生上升沿(结束信号)cstate <= STOP2;endelse cstate <= STOP1;endSTOP2:beginif(`SCL_LOW) sda_r <= 1'b1;else if(cnt_20ms==20'hffff0) cstate <= IDLE;else cstate <= STOP2;enddefault: cstate <= IDLE;endcaseendassign sda = sda_link ? sda_r:1'bz;assign dis_data = read_data;//---------------------------------------------endmodule写寄存器的标准流程为:1. Master发起START2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK3. Slave发送ACK4. Master发送reg addr(8bit),等待ACK5. Slave发送ACK6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK7. Slave发送ACK8. 第6步和第7步可以重复多次,即顺序写多个寄存器9. Master发起STOP读寄存器的标准流程为:1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK2. Slave发送ACK3. Master发送reg addr(8bit),等待ACK4. Slave发送ACK5. Master发起START6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK7. Slave发送ACK8. Slave发送data(8bit),即寄存器里的值9. Master发送ACK10. 第8步和第9步可以重复多次,即顺序读多个寄存器原理整理清楚编程思路就有了,首先要有开始工作的信号,分读和写来驱动I2C,本设计用M2408这款芯片进行工作,主要是了解到datasheet中时序的要求来进行编写。
i2s的verilog代码
i2s的verilog代码i2s的Verilog代码是指用Verilog语言编写的用于实现i2s (Inter-IC Sound)接口功能的代码。
i2s接口是一种常用的音频数据传输接口,广泛应用于音频芯片、音频设备和数字信号处理器等领域。
i2s接口由三根线组成,分别是时钟线(SCLK)、数据线(SD)和帧同步线(WS)。
时钟线用于同步数据的传输,数据线用于传输音频数据,帧同步线用于标志数据的起始和结束。
在Verilog代码中,首先需要定义i2s接口的输入输出端口,并根据接口规范进行信号的定义和赋值。
接下来可以根据具体的应用需求,编写相应的模块和逻辑来实现i2s接口的功能。
一个简单的i2s接口的Verilog代码示例如下:```verilogmodule i2s_interface (input wire SCLK, // 时钟线input wire SD, // 数据线input wire WS, // 帧同步线output wire audio // 音频数据输出);reg [15:0] audio_data; // 音频数据寄存器always @(posedge SCLK) beginif (WS) beginaudio_data <= SD; // 读取音频数据endendassign audio = audio_data; // 输出音频数据endmodule```上述代码中,定义了一个名为i2s_interface的模块,该模块包含了一个i2s接口,输入端口为SCLK、SD和WS,输出端口为audio。
在always块中,通过检测时钟线的上升沿,判断帧同步线的状态,如果帧同步线为高电平,则将数据线的数据存入音频数据寄存器中。
最后,通过assign语句将音频数据输出到audio端口。
需要注意的是,上述代码仅为示例,实际的i2s接口实现可能需要根据具体的应用需求进行修改和优化。
此外,在实际应用中,还需要考虑时序约束、数据格式、时钟频率等因素,以确保i2s接口的正常工作。
毕业设计(论文)-利用VHDL语言在FPGA上实现I2C总线控制器的功能模板
摘要随着微电子技术的发展,现场可编程逻辑门阵列FPGA(Field Programmable Gate Array)可以实现数字电路系统设计的功能。
尤其现场可编程逻辑门阵列FPGA具有集成度高的优点,受到工程界高度的重视。
I2C 总线以接口简单,成本底,可扩展性好在数字系统中得到了广泛的应用。
硬件描述语言是数字系统高层设计的核心,是实现数字系统设计新方法的关键技术之一。
本课题正是利用VHDL语言在FPGA上实现I2C总线控制器的功能。
首先研究了I2C总线的规范,又简要介绍了QuartusⅡ设计环境以及FPGA 的设计流程。
在此基础上,重点介绍了I2C控制器的总体设计方案,以及在QuartusⅡ平台上的时序仿真。
关键词Quartus II;I2C总线控制器;现场可编程逻辑门阵列;时序仿真AbstractWith the development of micro electric and EDA(electronic design automation)technology, FPGA(field programmable gates array) can realize the function of digital circuit system design .FPGA have the merit of filed programmability and High integration rate ,therefore is highly recognized for engineering.I2C bus is widely applied in the digital system as simple interface ,expedient use ,low cost and good expansibility .VHDL is considered as a core of digital system design and a key technique of implement digital system.The design realizes the function of I2C bus interface on the FPGA .At first the thesis deeply research I2C bus specification ,then briefly introduce the Quartus II design environment and the design method ,as well as FPGA design flow .In this foundation,I2C bus controller design scheme and the timing simulation under Quartus II is particularly introduced.Key words Quartus II;I2C bus controller ;FPGA ;timing simulation目录摘要 (I)Abstract .................................................................................................................. I I 第1章绪论.. (5)1.1 课题背景 (5)1.2 I2C总线的产生及发展 (6)1.3 FPGA的现状与展望 (6)1.4 相关工作 (6)第2章I2C总线技术的研究 (8)2.1 I2C总线的概念 (8)2.2 I2C总线的传输 (9)2.2.1 数据的有效性 (9)2.2.2 I2C总线数据传送的开始和停止条件 (9)2.2.3 I2C总线传输过程中的应答信号 (10)2.2.4 I2C总线数据传送的重复开始条件 (11)2.2.5 I2C总线的传输过程中的字节格式 (11)2.2.6 I2C总线的器件子地址 (11)2.2.7 I2C总线传输信号的时序 (12)2.3 本章小结 (14)第3章VHDL语言的基础知识 (15)3.1 VHDL语言的概述 (15)3.2 VHDL语言的特点 (15)3.3 VHDL语言的程序结构 (16)3.3.1 VHDL程序的库 (16)3.3.2 包集合 (16)3.3.3 实体说明 (17)3.3.4 构造体 (18)3.3.5 配置 (18)3.4 本章小结 (18)第4章设计工具和设计方法 (19)4.1 设计工具 (19)4.2 基于FPGA的数字电路的设计流程 (20)4.3 本章小结 (21)第5章I2C总线的功能设计 (23)5.1 I2C总线完成的功能 (23)5.2 用VHDL语言实现写操作时的串行转并行 (24)5.3 用VHDL语言实现顺序读操作时的并行转串行 (24)5.4 I2C总线控制器的顶层设计 (25)5.5 本章小结 (26)第6章I2C总线的硬件时序仿真 (27)6.1 器件的选择 (27)6.2 硬件仿真 (28)6.2.1 用VHDL语言实现写字节周期 (29)6.2.1 用VHDL语言实现顺序读字节周期 (30)6.2.3 用VHDL语言实现选择性读字节周期 (30)6.3 本章小结 (31)结论 (32)致谢 (33)参考文献 (34)第1章绪论1.1 课题背景近年来,随着社会的发展,电子产品越来越多的进入人们的生活和工作中,成为了我们生活中必不可少的一部分,随着计算机的普及,以及电子设备之间相互沟通的更加频繁,为了更方便的实现器件与器件之间的通信,研发人员从消费者电子、电讯和工业电子中许多看上去不相关的设计中寻找到了他们的相似之处,例如几乎每个系统都包括:(1)一些智能控制,通常是一个单片的微控制器。
基于fpga的eeprom设计
二线制I2C CMOS 串行EEPROM 的FPGA设计姓名:钱大成学号:080230114院系:物理院电子系2011年1月1日一、课程设计摘要:(1)背景知识:A、基本介绍:二线制I2C CMOS 串行EEPROM AT24C02/4/8/16 是一种采用CMOS 工艺制成的串行可用电擦除可编程只读存储器。
B、I2C (Inter Integrated Circuit)总线特征介绍:I2C 双向二线制串行总线协议定义如下:只有在总线处于“非忙”状态时,数据传输才能被初始化。
在数据传输期间,只要时钟线为高电平,数据线都必须保持稳定,否则数据线上的任何变化都被当作“启动”或“停止”信号。
图1 是被定义的总线状态。
①总线非忙状态(A 段)数据线SDA 和时钟线 SCL 都保持高电平。
②启动数据传输(B 段)当时钟线(SCL)为高电平状态时,数据线(SDA)由高电平变为低电平的下降沿被认为是“启动”信号。
只有出现“启动”信号后,其它的命令才有效。
③停止数据传输(C 段)当时钟线(SCL)为高电平状态时,数据线(SDA)由低电平变为高电平的上升沿被认为是“停止”信号。
随着“停在”信号出现,所有的外部操作都结束。
④数据有效(D 段)在出现“启动”信号以后,在时钟线(SCL)为高电平状态时数据线是稳定的,这时数据线的状态就要传送的数据。
数据线(SDA)上的数据的改变必须在时钟线为低电平期间完成,每位数据占用一个时钟脉冲。
每个数传输都是由“启动”信号开始,结束于“停止”信号。
⑤应答信号每个正在接收数据的EEPROM 在接到一个字节的数据后,通常需要发出一个应答信号。
而每个正在发送数据的EEPROM 在发出一个字节的数据后,通常需要接收一个应答信号。
EEPROM 读写控制器必须产生一个与这个应答位相联系的额外的时钟脉冲。
在EEPROM 的读操作中,EEPROM 读写控制器对EEPROM 完成的最后一个字节不产生应答位,但是应该给EEPROM 一个结束信号。
FPGA的verilog一些程序代码
设计一台电动机提速控制器。
当按钮S1按下1次后,电动机以速度1启动;再按下一次提高到转速2;再按下一次,提高到转速3,随后经过5s,自动提高到,转速4;当按钮s2按下时,从转速4退到转速3,再按下一次按钮s2,从转速3退到转速2,随后经过8s,退到转速1,再经过12s,停止电动机运转。
状态机源程序:module state ( v1,v2,v3,v4,d,m, t, td, k1, k2,clk); //output t,m, d,v1,v2,v3,v4;input clk, k1,k2,td;reg[7:0] d;reg t,v1,v2,v3,v4,m;reg [3:0] state, next;parameter s0=0, s1=1, s2=2, s3=3, s4=4, s5=5,s6=6,s7=7,s8=8; //采用十进制数字描述状态编码always @(posedge clk) //状态寄存器描述beginstate<=next;endalways @(state or s1 or td or s2)begincase (state )s0: begin t <= 1'b0; d<=5;m<=0; v1<=0;v2<=0;v3<=0;v4<=0;if (!k1) next <= s1; else next <= s0;ends1: begin t <= 1'b0; d<=5;m<=1; v1<=1;v2<=0;v3<=0;v4<=0;if (!k1) next <= s2; else next <= s1;ends2: begin t <= 1'b0; d<=5;m<=1; v1<=0;v2<=1;v3<=0;v4<=0;if (!k1) next <= s3; else next <= s2;ends3: begin t <= 1'b1; d<=5;m<=1; v1<=0;v2<=0;v3<=1;v4<=0;if(td) next <= s4; else next <= s3;ends4: begin t <= 1'b0; d<=8;m<=1; v1<=0;v2<=0;v3<=0;v4<=1;if(!k2) next <= s5; else next <= s4;ends5: begin t <= 1'b0; d<=8;m<=1; v1<=0;v2<=0;v3<=1;v4<=0;if(!k2) next <= s6; else next <= s5;ends6: begin t <= 1'b1; d<=8;m<=1; v1<=0;v2<=1;v3<=0;v4<=0;if(td) next <= s7; else next <= s6;ends7: begin t <= 1'b0; d<=12;m<=1; v1<=1;v2<=0;v3<=0;v4<=0;next <= s8;s8: begin t <= 1'b1; d<=12;m<=1; v1<=1;v2<=0;v3<=0;v4<=0;if(td) next <= s0; else next <= s8;enddefault : next <= s0;endcaseendendmodule计数器源程序:module jishu(clk,t,td,qq1,qq2,data); //clk时钟,t控制置数与计数,td是输出信号,data是计数初值input t,clk,data; //qq1是低4位,qq2是高4位output td;output [3:0] qq1,qq2;wire [3:0] qq1,qq2;wire[7:0] data;reg td;reg [7:0] qq;assign{qq2,qq1}=qq; //将两个4位数连接成8位数always @ (posedge clk or negedge t) //t可以异步控制置数或是计数beginif (t==0) begin qq<=data; td<=0; end //如果t=0,则定时器预置初值qq=dataelse if ((t==1) && (qq >=1)) begin qq <=qq-1; td<=0; end//如果t=1,且计数器数值大于等于1,则减法计数else td <=1; //否则td=0endendmodule译码器源程序:module yima(s,bcd); //s是译码器输出,bcd是译码器输出output[6:0] s;input[3:0] bcd; //输入BCD码reg [6:0] s;always @(bcd)begincase(bcd)4'd0: s=7'b0000001; //显示数字04'd1: s=7'b1001111; //显示数字14'd2: s=7'b0010010; //显示数字24'd3: s=7'b0000110; //显示数字34'd4: s=7'b1001100; //显示数字44'd5: s=7'b0100100;4'd6: s=7'b0100000;4'd7: s=7'b0001111;4'd8: s=7'b0000000;4'd9: s=7'b0000100; //显示数字94'd10: s=7'b0001000; //显示数字A4'd11: s=7'b1100000;4'd12: s=7'b0110001;4'd13: s=7'b1000010;4'd14: s=7'b0110000;4'd15: s=7'b0111000; //显示数字Fendcaseendendmodule顶层模块电路图:。
iic协议--Verilog及仿真
iic协议--Verilog及仿真1、协议原理:IIC(Inter-Integrated Circuit),i2c总线由数据线sda和时钟线scl这两条构成的串⾏总线,主机和从机可以在i2c总线上发送和接收数据。
scl时钟线作为控制,sda则包含有ack、nack、设备地址、字节地址、8bits数据。
起始信号(scl为⾼电平时,sda变成低电平)与结束信号(scl为⾼电平时,sda变成⾼电平)的状态:IIC单字节写时序有两种:1字节地址段器件单字节写时序、2字节地址段器件单字节写时序。
IIC单字节读时序有两种:1字节地址段器件单节读时序、2字节地址段器件单节读时序字节地址⾼三位xxx:这⾥使⽤的EEPROM的存储容量只有8192bits(1024bits*8)=210*23=213,所以16位的字节地址就多余了三位。
2、协议代码:1、这⾥实现的是2字节单次读写。
2、开始和结束时,虽然scl为⾼电平,sda仍要变化;接下来传输字节,scl为低电平,sda才能变化。
这⾥采取在scl⾼电平和低电平中线产⽣标志。
3、通过状态机来实现读写。
综合代码:module IIC_AT24C64(input sys_clk,input sys_rst_n,input iic_en,input [2:0]cs_bit,//可编程地址input [12:0]byte_address,//字节地址input write,input read,input [7:0]write_data,output reg[7:0]read_data,output reg scl,inout sda,output reg done);parameterSYS_CLK=50_000_000,//系统时钟50MHzSCL_CLK=200_000;//scl时钟200KHzreg [7:0]scl_cnt;//时钟计数parameter div_cnt=SYS_CLK/SCL_CLK;always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)scl_cnt<=8'd0;else if(scl_cnt == div_cnt-1'b1)scl_cnt<=8'd0;elsescl_cnt<=scl_cnt+1'b1;end//⽣成scl时钟线always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)scl<=1'b1;else if(scl_cnt == (div_cnt>>1)-1'b1)scl<=1'b0;else if(scl_cnt == div_cnt-1'b1)scl<=1'b1;elsescl<=scl;end//scl电平中线reg scl_high_middle;//scl⾼电平中线reg scl_low_middle;//scl低电平中线always @(posedge sys_clk or negedge sys_rst_n)beginscl_high_middle<=1'b0;scl_low_middle<=1'b0;endelse if(scl_cnt == (div_cnt>>2))scl_high_middle<=1'b1;else if(scl_cnt == (div_cnt>>1)+(div_cnt>>2))scl_low_middle<=1'b1;else beginscl_high_middle<=1'b0;scl_low_middle<=1'b0;endendreg [15:0]state;parameteridle=16'd1,//空闲状态w_or_r_start=16'd2,//设备地址device_ADDR=16'd3,//发送ACK1=16'd4,byte_ADDR_high=16'd5,//字节地址⾼8位ACK2=16'd6,byte_ADDR_low=16'd7,//字节地址低8位ACK3=16'd8,w_data=16'd9,//写数据ACK4=16'd10,r_start=16'd11,//读开始device_ADDR_r=16'd12,//设备地址读ACK5=16'd13,r_data=16'd14,//读数据NACK=16'd15,//⾮应答位stop=16'd16;reg sda_en;//sda数据线使能reg sda_reg;//sda数据暂存位reg [7:0]sda_data_out;//sda数据发给从机暂存reg [7:0]sda_data_in;//sda数据取之从机暂存reg [3:0]bit_cnt;//每⼀bitassign sda=sda_en?sda_reg:1'bz;//读写标志位reg w_flag;reg r_flag;always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginstate<=idle;w_flag<=1'b0;r_flag<=1'b0;sda_reg<=1'b1;done<=1'b0;sda_en<=1'b0;endelse begincase(state)idle:beginsda_reg<=1'b1;w_flag<=1'b0;r_flag<=1'b0;sda_en<=1'b0;sda_reg<=1'b1;done<=1'b0;if(iic_en && write)beginw_flag<=1'b1;sda_en<=1'b1;sda_reg<=1'b1;state<=w_or_r_start;endelse if(iic_en && read)beginr_flag<=1'b1;sda_en<=1'b1;sda_reg<=1'b1;state<=w_or_r_start;endelsestate<=idle;endw_or_r_start:beginif(scl_high_middle)beginsda_reg<=1'b0;sda_data_out<={4'b1010,cs_bit,1'b0};//在这⾥装好设备地址bit_cnt<=4'd8;state<=device_ADDR;endelse beginsda_reg<=1'b1;state<=w_or_r_start;endenddevice_ADDR:beginif(scl_low_middle)beginsda_reg<=sda_data_out[7];sda_data_out<={sda_data_out[6:0],1'b0};//在这⾥发出设备地址。
学习笔记一:I2C协议学习和Verilog实现
学习笔记⼀:I2C协议学习和Verilog实现1//////////////////////////////////////////////////2//clk = 20 MHz ,⼀个周期50ns3//sck = 100 kHz (scl) ,⼀个周期 1000ns4//I2C在sck下降沿更新数据,上升沿读取(采样)数据5///////////////////////////////////////////////////6module demo_I2C #(parameter F100K = 9'd200)(clk,rstn,start_sig,word_addr,wr_data,rd_data,done_sig,scl,sda,sq_i);78input clk ;9input rstn ;1011input [1:0] start_sig ; //12input [7:0] word_addr ; //word address13input [7:0] wr_data ; //Data14output [7:0] rd_data ; //Data from EEPROM15output done_sig ;1617output scl ; //sda和scl其实是⽤来作为仿真信号添加在这⾥的,寄存器信号都⽤rscl和rsda表⽰了,最后⽤assign将rscl和rsda赋值给sda和scl,连到模块外部仿真⽤ 18inout sda ; //sda表⽰当前sda的in或out的值1920output [4:0] sq_i ;21/************************************22在这⾥,iic_func_module.v 的步骤i已经被引出来了。
读者要知道步骤i在⽆论是在设计上还是仿真上都有许多的好处。
23步骤i在仿真中可以充当“调试跟踪”的作⽤,因为只要模块的那个部分出问题,步骤i就会指向它。
i2c_verilog范例
// $Locker: $
// $State: Exp $
//
// Change History:
//
$Log: i2c_master_top.v,v $
//
Revision 1.10 2003/09/01 10:34:38 rherveille
//
Fix a blocking vs. non-blocking error in the wb_dat output mux.
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
////
//// POSSIBILITY OF SUCH DAMAGE.
////
////
////
/////////////////////////////////////////////////////////////////////
// status register signals
wire irxack;
reg rxack;
// received aknowledge from slave
reg tip;
// transfer in progress
reg irq_flag; // interrupt pending flag
File: Edit1 9/19/2004, 7:54:04PM
/////////////////////////////////////////////////////////////////////
////
////
//// WISHBONE revB.2 compliant I2C Master controller Top-level ////
I2C串行EEPROMVerilog模型24AA01H
I2C串行EEPROMVerilog模型24AA01H//******************************************************************* ************************************// ** **// ** 24AA01H.v - Microchip 24AA01H 1K-BIT I2C SERIAL EEPROM WITH HALF-ARRAY WRITE-PROTECT **// ** (VCC = +1.7V TO +5.5V) **// ** **//******************************************************************* ************************************// ** **// ** This information is distributed under license from Young Engineering. **// ** COPYRIGHT (c) 2008 YOUNG ENGINEERING **// ** ALL RIGHTS RESERVED **// ** **// ** **// ** Young Engineering provides design expertise for the digital world **// ** Started in 1990, Young Engineering offers products and services for your electronic design **// ** project. We have the expertise in PCB, FPGA, ASIC, firmware, and software design. **// ** From concept to prototype to production, we can help you. **// ** **// ** /doc/3c16244062.html,/ **// ** **//******************************************************************* ************************************// ** This information is provided to you for your convenience and use with Microchip products only. ** // ** Microchip disclaims all liability arising from this information and its use. **// ** **// ** THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF ** // ** ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO ** // ** THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, **// ** PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE. **// ** MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL ORCONSEQUENTIAL **// ** DAMAGES, FOR ANY REASON WHATSOEVER. **// ** **// ** It is your responsibility to ensure that your application meets with your specifications. **// ** **//******************************************************************* ************************************// ** Revision : 1.0 **// ** Modified Date : 10/20/2008 **// ** Revision History: **// ** **// ** 10/20/2008: Initial design **// ** **//******************************************************************* ************************************// ** TABLE OF CONTENTS **//******************************************************************* ************************************// **---------------------------------------------------------------------------------------------------**// ** DECLARATIONS **// **---------------------------------------------------------------------------------------------------**// **---------------------------------------------------------------------------------------------------**// ** INITIALIZATION **// **---------------------------------------------------------------------------------------------------**// **---------------------------------------------------------------------------------------------------**// ** CORE LOGIC **// **---------------------------------------------------------------------------------------------------**// ** 1.01: START Bit Detection **// ** 1.02: STOP Bit Detection **// ** 1.03: Input Shift Register **// ** 1.04: Input Bit Counter **// ** 1.05: Control Byte Register**// ** 1.06: Byte Address Register **// ** 1.07: Write Data Buffer **// ** 1.08: Acknowledge Generator **// ** 1.09: Acknowledge Detect **// ** 1.10: Write Cycle Timer **// ** 1.11: Write Cycle Processor **// ** 1.12: Read Data Multiplexor **// ** 1.13: Read Data Processor **// ** 1.14: SDA Data I/O Buffer **// ** **// **---------------------------------------------------------------------------------------------------**// ** DEBUG LOGIC **// **---------------------------------------------------------------------------------------------------**// ** 2.01: Memory Data Bytes **// ** 2.02: Write Data Buffer **// ** **// **---------------------------------------------------------------------------------------------------**// ** TIMING CHECKS **// **---------------------------------------------------------------------------------------------------**// ** **//******************************************************************* ************************************`timescale 1ns/10psmodule M24AA01H (A0, A1, A2, WP, SDA, SCL, RESET);input A0; // unconnected pininput A1; // unconnected pininput A2; // unconnected pininput WP; // write protect pininout SDA; // serial data I/Oinput SCL; // serial data clockinput RESET; // system reset//******************************************************************* ************************************// ** DECLARATIONS**//******************************************************************* ************************************reg SDA_DO; // serial data - outputreg SDA_OE; // serial data - output enablewire SDA_DriveEnable; // serial data output enablereg SDA_DriveEnableDlyd;// serial data output enable - delayedreg [03:00] BitCounter; // serial bit counterreg START_Rcvd; // START bit received flagreg STOP_Rcvd; // STOP bit received flagreg CTRL_Rcvd; // control byte received flagreg ADDR_Rcvd; // byte address received flagreg MACK_Rcvd; // master acknowledge received flagreg WrCycle; // memory write cyclereg RdCycle; // memory read cyclereg [07:00] ShiftRegister; // input data shift registerreg [07:00] ControlByte; // control byte registerwire RdWrBit; // read/write control bitreg [06:00] StartAddress; // memory access starting address reg [02:00] PageAddress; // memory page addressreg [07:00] WrDataByte [0:7]; // memory write data bufferwire [07:00] RdDataByte; // memory read datareg [15:00] WrCounter; // write buffer counterreg [02:00] WrPointer; // write buffer pointerreg [06:00] RdPointer; // read address pointerreg WriteActive; // memory write cycle activereg [07:00] MemoryBlock [0:127];// EEPROM data memory arrayinteger LoopIndex; // iterative loop indexinteger tAA; // timing parameterinteger tWC; // timing parameter//******************************************************************* ************************************// ** INITIALIZATION **//******************************************************************* ************************************initial begin`ifdef VCC_1_7V_TO_2_5VtAA = 3500; // SCL to SDA output delaytWC = 5000000; // memory write cycle time`else`ifdef VCC_2_5V_TO_5_5VtAA = 900; // SCL to SDA output delaytWC = 5000000; // memory write cycle time`elsetAA = 900; // SCL to SDA output delaytWC = 5000000; // memory write cycle time`endif`endifendinitial begin SDA_DO = 0; SDA_OE = 0; endinitial begin START_Rcvd = 0; STOP_Rcvd = 0; CTRL_Rcvd = 0; ADDR_Rcvd = 0; MACK_Rcvd = 0; endinitial begin BitCounter = 0; ControlByte = 0; endinitial begin WrCycle = 0; RdCycle = 0;WriteActive = 0; end//******************************************************************* ************************************// ** CORE LOGIC **//******************************************************************* ************************************// -------------------------------------------------------------------------------------------------------// 1.01: START Bit Detection// -------------------------------------------------------------------------------------------------------always @(negedge SDA) beginif (SCL == 1) beginSTART_Rcvd <= 1;STOP_Rcvd <= 0;CTRL_Rcvd <= 0;ADDR_Rcvd <= 0;MACK_Rcvd <= 0;WrCycle <= #1 0;RdCycle <= #1 0;BitCounter <= 0;endend// -------------------------------------------------------------------------------------------------------// 1.02: STOP Bit Detection// -------------------------------------------------------------------------------------------------------always @(posedge SDA) beginif (SCL == 1) beginSTART_Rcvd <= 0;STOP_Rcvd <= 1;CTRL_Rcvd <= 0;ADDR_Rcvd <= 0;MACK_Rcvd <= 0;WrCycle <= #1 0;RdCycle <= #1 0;BitCounter <= 10;endend// -------------------------------------------------------------------------------------------------------// 1.03: Input Shift Register// -------------------------------------------------------------------------------------------------------always @(posedge SCL) beginShiftRegister[00] <= SDA;ShiftRegister[01] <= ShiftRegister[00];ShiftRegister[02] <= ShiftRegister[01];ShiftRegister[03] <= ShiftRegister[02];ShiftRegister[04] <= ShiftRegister[03];ShiftRegister[05] <= ShiftRegister[04];ShiftRegister[06] <= ShiftRegister[05];ShiftRegister[07] <= ShiftRegister[06];end// -------------------------------------------------------------------------------------------------------// 1.04: Input Bit Counter// -------------------------------------------------------------------------------------------------------always @(posedge SCL) beginif (BitCounter < 10) BitCounter <= BitCounter + 1;end// -------------------------------------------------------------------------------------------------------// 1.05: Control Byte Register// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (START_Rcvd & (BitCounter == 8)) beginif (!WriteActive & (ShiftRegister[07:04] == 4'b1010)) begin if (ShiftRegister[00] == 0) WrCycle <= 1;if (ShiftRegister[00] == 1) RdCycle <= 1;ControlByte <= ShiftRegister[07:00];CTRL_Rcvd <= 1;endSTART_Rcvd <= 0;endendassign RdWrBit = ControlByte[00];// -------------------------------------------------------------------------------------------------------// 1.06: Byte Address Register// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (CTRL_Rcvd & (BitCounter == 8)) beginif (RdWrBit == 0) beginStartAddress <= ShiftRegister[06:00];RdPointer <= ShiftRegister[06:00];ADDR_Rcvd <= 1;endWrCounter <= 0;WrPointer <= 0;CTRL_Rcvd <= 0;endend// -------------------------------------------------------------------------------------------------------// 1.07: Write Data Buffer// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (ADDR_Rcvd & (BitCounter == 8)) beginif ((WP == 0 || StartAddress[06] == 1'b0) & (RdWrBit == 0)) beginWrDataByte[WrPointer] <= ShiftRegister[07:00];WrCounter <= WrCounter + 1;WrPointer <= WrPointer + 1;endendend// -------------------------------------------------------------------------------------------------------// 1.08: Acknowledge Generator// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (!WriteActive) beginif (BitCounter == 8) beginif (WrCycle | (START_Rcvd & (ShiftRegister[07:04] ==4'b1010))) beginSDA_DO <= 0;SDA_OE <= 1;endendif (BitCounter == 9) beginBitCounter <= 0;if (!RdCycle) beginSDA_DO <= 0;SDA_OE <= 0;endendendend// -------------------------------------------------------------------------------------------------------// 1.09: Acknowledge Detect// -------------------------------------------------------------------------------------------------------always @(posedge SCL) beginif (RdCycle & (BitCounter == 8)) beginif ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;endendalways @(negedge SCL) MACK_Rcvd <= 0;// -------------------------------------------------------------------------------------------------------// 1.10: Write Cycle Timer// -------------------------------------------------------------------------------------------------------always @(posedge STOP_Rcvd) beginif (WrCycle & (WP == 0 || StartAddress[06]== 1'b0) & (WrCounter > 0)) beginWriteActive = 1;#(tWC);WriteActive = 0;endendalways @(posedge STOP_Rcvd) begin#(1.0);STOP_Rcvd = 0;end// -------------------------------------------------------------------------------------------------------// 1.11: Write Cycle Processor// -------------------------------------------------------------------------------------------------------always @(negedge WriteActive) beginfor (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) beginPageAddress = StartAddress[02:00] + LoopIndex;MemoryBlock[{StartAddress[06:03],PageAddress[02:00]}] = WrDataByte[LoopIndex[02:00]];endend// -------------------------------------------------------------------------------------------------------// 1.12: Read Data Multiplexor// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (BitCounter == 8) beginif (WrCycle & ADDR_Rcvd) beginRdPointer <= StartAddress + WrPointer + 1;endif (RdCycle) beginRdPointer <= RdPointer + 1;endendendassign RdDataByte = MemoryBlock[RdPointer[06:00]];// -------------------------------------------------------------------------------------------------------// 1.13: Read Data Processor// -------------------------------------------------------------------------------------------------------always @(negedge SCL) beginif (RdCycle) beginif (BitCounter == 8) beginSDA_DO <= 0;SDA_OE <= 0;endelse if (BitCounter == 9) beginSDA_DO <= RdDataByte[07];if (MACK_Rcvd) SDA_OE <= 1;endelse beginSDA_DO <= RdDataByte[7-BitCounter];endendend// -------------------------------------------------------------------------------------------------------// 1.14: SDA Data I/O Buffer// -------------------------------------------------------------------------------------------------------bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);assign SDA_DriveEnable = !SDA_DO & SDA_OE;always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;//******************************************************************* ************************************// ** DEBUG LOGIC **//******************************************************************* ************************************// -------------------------------------------------------------------------------------------------------// 2.01: Memory Data Bytes// -------------------------------------------------------------------------------------------------------wire [07:00] MemoryByte00 = MemoryBlock[00];wire [07:00] MemoryByte01 = MemoryBlock[01];wire [07:00] MemoryByte02 = MemoryBlock[02];wire [07:00] MemoryByte03 = MemoryBlock[03];wire [07:00] MemoryByte04 = MemoryBlock[04];wire [07:00] MemoryByte05 = MemoryBlock[05];wire [07:00] MemoryByte06 = MemoryBlock[06];wire [07:00] MemoryByte07 = MemoryBlock[07];wire [07:00] MemoryByte08 = MemoryBlock[08];wire [07:00] MemoryByte09 = MemoryBlock[09];wire [07:00] MemoryByte0A = MemoryBlock[10];wire [07:00] MemoryByte0B = MemoryBlock[11];wire [07:00] MemoryByte0C = MemoryBlock[12];wire [07:00] MemoryByte0D = MemoryBlock[13];wire [07:00] MemoryByte0E = MemoryBlock[14];wire [07:00] MemoryByte0F = MemoryBlock[15];// -------------------------------------------------------------------------------------------------------// 2.02: Write Data Buffer// -------------------------------------------------------------------------------------------------------wire [07:00] WriteData_0 = WrDataByte[00];wire [07:00] WriteData_1 = WrDataByte[01];wire [07:00] WriteData_2 = WrDataByte[02];wire [07:00] WriteData_3 = WrDataByte[03];wire [07:00] WriteData_4 = WrDataByte[04];wire [07:00] WriteData_5 = WrDataByte[05];wire [07:00] WriteData_6 = WrDataByte[06];wire [07:00] WriteData_7 = WrDataByte[07];//******************************************************************* ************************************// ** TIMING CHECKS **//******************************************************************* ************************************wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);wire StopTimingCheckEnable = TimingCheckEnable && SCL;specify`ifdef VCC_1_7V_TO_2_5VspecparamtHI = 4000, // SCL pulse width - hightLO = 4700, // SCL pulse width - lowtSU_STA = 4700, // SCL to SDA setup time tHD_STA = 4000, // SCL to SDA hold time tSU_DAT = 250, // SDA to SCL setup time tSU_STO = 4000, // SCL to SDA setup time tSU_WP = 4000, // WP to SDA setup time tHD_WP = 4700, // WP to SDA hold time tBUF = 4700; // Bus free time`else`ifdef VCC_2_5V_TO_5_5VspecparamtHI = 600, // SCL pulse width - hightLO = 1300, // SCL pulse width - lowtSU_STA = 600, // SCL to SDA setup time tHD_STA = 600, //SCL to SDA hold timetSU_DAT = 100, // SDA to SCL setup time tSU_STO = 600, // SCL to SDA setup time tSU_WP = 600, // WP to SDA setup time tHD_WP = 600, // WP to SDA hold time tBUF = 1300; // Bus free time`elsespecparamtHI = 600, // SCL pulse width - hightLO = 1300, // SCL pulse width - lowtSU_STA = 600, // SCL to SDA setup timetHD_STA = 600, // SCL to SDA hold timetSU_DAT = 100, // SDA to SCL setup timetSU_STO = 600, // SCL to SDA setup timetSU_WP = 600, // WP to SDA setup timetHD_WP = 600, // WP to SDA hold timetBUF = 1300; // Bus free time`endif`endif$width (posedge SCL, tHI);$width (negedge SCL, tLO);$width (posedge SDA &&& SCL, tBUF);$setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);$setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);$setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);$setup (WP, posedge SDA &&& StopTimingCheckEnable, tSU_WP);$hold (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);$hold (posedge SDA &&& StopTimingCheckEnable, WP, tHD_WP);endspecifyendmodule。
I2C协议说明及verilog实现读写I2C器件
1.2 I2C 总线的数据传送
1.2.1 起始和终止信号
如图 1-3 所示,SCL 线为高电平期间,SDA 线由高电平向低电平的变化表示起始信号; SCL 线为高电平期间,SDA 线由低电平向高电平的变化表示终止信号。
产品用户手册
三英卓越科技发展有限公司 1
三英卓越科技发展有限公司
K2FPGA
实验教程
2.2 特性
z 低工作电流:典型值为 0.25μA(VDD=3.0V,Tamb=25℃时)。 z 世纪标志。
1.2.3 数据传送格式...................................................................................................2
1.2.4 I2C 总线寻址 ....................................................................................................4
尔
图 1-6 方式 1
哈 注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。A 表示应答,
A 非表示非应答(高电平)。S 表示起始信号,P 表示终止信号。
b) 主机在第一个字节后,立即从从机读数据,如图 1-7 所示。
图 1-7 方式 2
c) 在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次, 但两次读/写方向位正好反相,如图 1-8 所示。
卓 3. 代码示例...................................................................................................................7
i2c顶层inout 的处理verilog代码
i2c顶层inout 的处理verilog代码什么是I2C 协议?I2C(Inter-Integrated Circuit)是一种串行通信协议,用于在微控制器、传感器、显示屏、存储器和其他外设之间进行数字数据传输。
它于1982年由Philips(现在的NXP半导体)开发,并在全球范围内广泛应用。
I2C 协议中的顶层inout 接口在设计I2C 通信系统时,顶层inout 接口起着核心作用。
I2C 协议中有两条主要的线路,即数据线(SDA)和时钟线(SCL)。
顶层inout 接口是将这两条线路连接到外部设备的关键。
常见的I2C 顶层inout 接口处理方法有几种,下面将逐步介绍这些方法。
第一种方法:Tri-State Buffer(三态缓冲器)Tri-State Buffer 是一种常用的I2C 顶层inout 接口处理方法。
它通过控制一个使能信号来决定输入输出的状态。
当使能信号有效时,内部数据将根据输入信号确定输出;当使能信号无效时,输出则处于高阻态(Hi-Z)。
使用Tri-State Buffer 的I2C 顶层inout 接口处理方法如下:verilogmodule i2c_top (inout wire sda,inout wire scl,input wire enable);TriStateBuffer #(.WIDTH(1)) tri_sda (.i(sda), .o(sda_s), .oe(enable)); TriStateBuffer #(.WIDTH(1)) tri_scl (.i(scl), .o(scl_s), .oe(enable));endmodule在上述代码中,`sda` 和`scl` 是输入输出的I2C 数据线和时钟线,`enable` 是使能输入信号。
通过Tri-State Buffer 控制使能信号,即可实现对I2C 顶层inout 接口的处理。
第二种方法:Assign 语句除了使用Tri-State Buffer,还可以使用Assign 语句来处理I2C 顶层inout 接口。
verilog iic 读写
在Verilog中,实现I2C(Inter-Integrated Circuit)读写操作需要定义I2C的接口信号,包括SCL(Serial Clock Line)和SDA(Serial Data Line),以及根据具体需求定义其他控制信号。
以下是一个简单的Verilog代码示例,实现I2C写操作:verilog复制代码module i2c_write (input wire scl,input wire sda,input wire start,input wire write_bit,input wire [7:0] data,output reg ack);reg [7:0] ack_data;reg ack_flag;always @(posedge scl) beginif (start) beginack_flag <= 1'b0;ack_data <= 8'h00;end else if (write_bit) beginack_data <= data;ack_flag <= 1'b1;endendassign ack = ack_flag & ~sda;endmodule在上述代码中,scl和sda是I2C总线的时钟线和数据线,start信号表示开始信号,write_bit信号表示当前是写操作,data是写入的数据,ack是应答信号。
在时钟上升沿时,根据当前是开始信号还是写操作信号,将相应的数据写入到ack_data寄存器中,并将ack_flag标志位设为1。
最后,将应答信号赋值为ack_flag 和sda的逻辑与。
以下是一个简单的Verilog代码示例,实现I2C读操作:verilog复制代码module i2c_read (input wire scl,input wire sda,input wire start,input wire read_bit,output reg [7:0] data,output reg ack);reg [7:0] ack_data;reg ack_flag;reg read_data;always @(posedge scl) beginif (start) beginack_flag <= 1'b0;ack_data <= 8'h00;end else if (read_bit) beginack_data <= sda;ack_flag <= 1'b1;end else if (!sda) begin// 读取数据时,sda为低电平read_data <= ack_data; // 读取数据到read_data寄存器中endendassign ack = ack_flag & ~sda; // 应答信号为ack_flag和sda的逻辑与取反值assign data = read_data; // 将读取的数据输出到data信号上endmodule在上述代码中,读操作的过程与写操作类似,只是在读取数据时,需要将sda信号拉低,并在时钟上升沿时将读取的数据存储到read_data寄存器中。
fpga 的i2c通信实现方案
fpga 的i2c通信实现方案下载温馨提示:该文档是我店铺精心编制而成,希望大家下载以后,能够帮助大家解决实际的问题。
文档下载后可定制随意修改,请根据实际需要进行相应的调整和使用,谢谢!并且,本店铺为大家提供各种各样类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,如想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by theeditor.I hope that after you download them,they can help yousolve practical problems. The document can be customized andmodified after downloading,please adjust and use it according toactual needs, thank you!In addition, our shop provides you with various types ofpractical materials,such as educational essays, diaryappreciation,sentence excerpts,ancient poems,classic articles,topic composition,work summary,word parsing,copy excerpts,other materials and so on,want to know different data formats andwriting methods,please pay attention!FPGA实现I2C通信的详细方案一、引言Field-Programmable Gate Array(FPGA)是一种可编程逻辑器件,具有高度灵活性和并行处理能力。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
`timescale 1ns / 1psmodule i2c_drive(clk,rst_n,sw1,sw2,scl,sda,dis_data);input clk;// 50MHzinput rst_n;//复位信号,低有效input sw1,sw2;//按键1、2,(1按下执行写入操作,2按下执行读操作)output scl;// 24C02的时钟端口inout sda;// 24C02的数据端口output [7:0] dis_data;//输出指定单元的数据//--------------------------------------------//按键检测reg sw1_r,sw2_r;//键值锁存寄存器,每20ms检测一次键值reg[19:0] cnt_20ms;//20ms计数寄存器always @ (posedge clk or negedge rst_n)if(!rst_n)cnt_20ms <= 20'd0;elsecnt_20ms <= cnt_20ms+1'b1;//不断计数always @ (posedge clk or negedge rst_n)if(!rst_n)beginsw1_r <= 1'b1;//键值寄存器复位,没有键盘按下时键值都为1sw2_r <= 1'b1;endelse if(cnt_20ms == 20'hfffff)beginsw1_r <= sw1;//按键1值锁存sw2_r <= sw2;//按键2值锁存end//---------------------------------------------//分频部分reg[2:0] cnt;// cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间reg[8:0] cnt_delay;//500循环计数,产生iic所需要的时钟reg scl_r;//时钟脉冲寄存器always @ (posedge clk or negedge rst_n)if(!rst_n)cnt_delay <= 9'd0;else if(cnt_delay == 9'd499)cnt_delay <= 9'd0;//计数到10us为scl的周期,即100KHz elsecnt_delay <= cnt_delay+1'b1;//时钟计数always @ (posedge clk or negedge rst_n) beginif(!rst_n)cnt <= 3'd5;elsebegincase (cnt_delay)9'd124:cnt <= 3'd1;//cnt=1:scl高电平中间,用于数据采样9'd249:cnt <= 3'd2;//cnt=2:scl下降沿9'd374:cnt <= 3'd3;//cnt=3:scl低电平中间,用于数据变化9'd499:cnt <= 3'd0;//cnt=0:scl上升沿default: cnt <= 3'd5;endcaseendend`define SCL_POS(cnt==3'd0)//cnt=0:scl上升沿`define SCL_HIG(cnt==3'd1)//cnt=1:scl高电平中间,用于数据采样`define SCL_NEG(cnt==3'd2)//cnt=2:scl下降沿`define SCL_LOW(cnt==3'd3)//cnt=3:scl低电平中间,用于数据变化always @ (posedge clk or negedge rst_n)if(!rst_n)scl_r <= 1'b0;else if(cnt==3'd0)scl_r <= 1'b1;//scl信号上升沿else if(cnt==3'd2)scl_r <= 1'b0;//scl信号下降沿assign scl = scl_r;//产生iic所需要的时钟//---------------------------------------------//需要写入24C02的地址和数据`define DEVICE_READ8'b1010_0001//被寻址器件地址(读操作)`define DEVICE_WRITE8'b1010_0000//被寻址器件地址(写操作)`define WRITE_DATA 8'b0000_0111//写入EEPROM的数据`define BYTE_ADDR 8'b0000_0100//写入/读出EEPROM的地址寄存器reg[7:0] db_r;//在IIC上传送的数据寄存器reg[7:0] read_data;//读出EEPROM的数据寄存器//---------------------------------------------//读、写时序parameter IDLE = 4'd0;parameter START1 = 4'd1;parameter ADD1 = 4'd2;parameter ACK1 = 4'd3;parameter ADD2 = 4'd4;parameter ACK2 = 4'd5;parameter START2 = 4'd6;parameter ADD3 = 4'd7;parameter ACK3= 4'd8;parameter DATA = 4'd9;parameter ACK4= 4'd10;parameter STOP1 = 4'd11;parameter STOP2 = 4'd12;reg[3:0] cstate;//状态寄存器reg sda_r;//输出数据寄存器reg sda_link;//输出数据sda信号inout方向控制位reg[3:0] num;//always @ (posedge clk or negedge rst_n) beginif(!rst_n)begincstate <= IDLE;sda_r <= 1'b1;sda_link <= 1'b0;num <= 4'd0;read_data <= 8'b0000_0000;endelsecase (cstate)IDLE:beginsda_link <= 1'b1;//数据线sda为inputsda_r <= 1'b1;if(!sw1_r || !sw2_r)begin//SW1,SW2键有一个被按下db_r <= `DEVICE_WRITE;//送器件地址(写操作)cstate <= START1;endelsecstate <= IDLE;//没有任何键被按下endSTART1:beginif(`SCL_HIG)begin//scl为高电平期间sda_link <= 1'b1;//数据线sda为outputsda_r <= 1'b0;//拉低数据线sda,产生起始位信号cstate <= ADD1;num <= 4'd0;//num计数清零endelsecstate <= START1; //等待scl高电平中间位置到来endADD1:beginif(`SCL_LOW)beginif(num == 4'd8)beginnum <= 4'd0;//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态(input)cstate <= ACK1;endelsebegincstate <= ADD1;num <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//sda_r <= db_r[4'd7-num];//送器件地址,从高位开始endend//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};//器件地址左移1bitelsecstate <= ADD1;endACK1:beginif(/*!sda*/`SCL_NEG)begin//注:24C01/02/04/08/16器件可以不考虑应答位cstate <= ADD2;//从机响应信号db_r <= `BYTE_ADDR;// 1地址endelsecstate <= ACK1;//等待从机响应endADD2:beginif(`SCL_LOW)beginif(num==4'd8)beginnum <= 4'd0;//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态(input)cstate <= ACK2;endelsebeginsda_link <= 1'b1;//sda作为outputnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//sda_r <= db_r[4'd7-num];//送EEPROM地址(高bit开始)cstate <= ADD2;endend//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};//器件地址左移1bitelsecstate <= ADD2;endACK2:beginif(/*!sda*/`SCL_NEG) begin//从机响应信号if(!sw1_r) begincstate <= DATA; //写操作db_r <= `WRITE_DATA;//写入的数据endelse if(!sw2_r) begindb_r <= `DEVICE_READ;//送器件地址(读操作),特定地址读需要执行该步骤以下操作cstate <= START2;//读操作endendelse cstate <= ACK2;//等待从机响应endSTART2: begin//读操作起始位if(`SCL_LOW) beginsda_link <= 1'b1;//sda作为outputsda_r <= 1'b1;//拉高数据线sdacstate <= START2;endelse if(`SCL_HIG) begin//scl为高电平中间sda_r <= 1'b0;//拉低数据线sda,产生起始位信号cstate <= ADD3;endelse cstate <= START2;endADD3:begin//送读操作地址if(`SCL_LOW) beginif(num==4'd8) beginnum <= 4'd0;//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态(input)cstate <= ACK3;endelse beginnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//sda_r <= db_r[4'd7-num];//送EEPROM地址(高bit开始)cstate <= ADD3;endend//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};//器件地址左移1bitelse cstate <= ADD3;endACK3:beginif(/*!sda*/`SCL_NEG) begincstate <= DATA;//从机响应信号sda_link <= 1'b0;endelse cstate <= ACK3; //等待从机响应endDATA:beginif(!sw2_r) begin//读操作if(num<=4'd7) begincstate <= DATA;if(`SCL_HIG) beginnum <= num+1'b1;case (num)4'd0: read_data[7] <= sda;4'd1: read_data[6] <= sda;4'd2: read_data[5] <= sda;4'd3: read_data[4] <= sda;4'd4: read_data[3] <= sda;4'd5: read_data[2] <= sda;4'd6: read_data[1] <= sda;4'd7: read_data[0] <= sda;default: ;endcase//read_data[4'd7-num] <= sda;//读数据(高bit开始)end//else if(`SCL_NEG) read_data <= {read_data[6:0],read_data[7]};//数据循环右移endelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;//num计数清零cstate <= ACK4;endelse cstate <= DATA;endelse if(!sw1_r) begin//写操作sda_link <= 1'b1;if(num<=4'd7) begincstate <= DATA;if(`SCL_LOW) beginsda_link <= 1'b1;//数据线sda作为outputnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//sda_r <= db_r[4'd7-num];//写入数据(高bit开始)end//else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};//写入数据左移1bitendelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;sda_r <= 1'b1;sda_link <= 1'b0;//sda置为高阻态cstate <= ACK4;endelse cstate <= DATA;endendACK4: beginif(/*!sda*/`SCL_NEG) begin//sda_r <= 1'b1;cstate <= STOP1;endelse cstate <= ACK4;endSTOP1:beginif(`SCL_LOW) beginsda_link <= 1'b1;sda_r <= 1'b0;cstate <= STOP1;endelse if(`SCL_HIG) beginsda_r <= 1'b1;//scl为高时,sda产生上升沿(结束信号)cstate <= STOP2;endelse cstate <= STOP1;endSTOP2:beginif(`SCL_LOW) sda_r <= 1'b1;else if(cnt_20ms==20'hffff0) cstate <= IDLE;else cstate <= STOP2;enddefault: cstate <= IDLE;endcaseendassign sda = sda_link ? sda_r:1'bz;assign dis_data = read_data;//---------------------------------------------endmodule。