(带有源代码)基于FPGA的模拟I2C接口设计与实现
发布时间:2013-01-26 12:04:44
发布时间:2013-01-26 12:04:44
研究生课程设计
论文题目: 基于FPGA的模拟IIC接口设计与实现
课程名称: FPGA及片上系统SOPC应用
任课教师: 宋树祥(教授) 殷严刚(讲师)
学 院: 电子工程学院
班 级: 12 级电子与通信工程
学 号: 2012011xxxx
姓 名: xxx
2012 年 12 月 30 日
基于FPGA的模拟IIC接口设计与实现
摘要:本文简述了IIC总线的特点;介绍了基于FPGA 的模拟IIC总线接口模块的设计思想;设计并编写了基于Verilog HDL语言来实现部分IIC总线接口功能的程序代码,同时给出了基于目标板的硬件实物测试图。
关键词:IIC 总线接口 FPGA Verilog HDL EP2C20Q240C8
在进行FPGA的开发时,利用EDA 工具设计芯片实现系统的功能已经成为支撑电子设计的通用平台,并逐步向支持系统级的设计方向发展。模块化的设计思想在软件设计过程中越来越被重视。IIC总线是Philips 公司推出的双向两线串行通讯标准,具有接口线少、通讯效率高等特点。目前针对IIC总线的相关开发的资料都是利用VHDL语言或AHDL语言实现的。本文设计的IIC 总线模块是利用Verilog HDL 语言来实现的。
1 IIC 总线特点及工作原理概述
IIC(Inter-Integrated Circuit,内置集成电路总线)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。IIC总线产生于在80年代,最初为音频和视频设备开发,如今主要在服务器管理中使用,其中包括单个组件状态的通信。例如管理员可对各个组件进行查询,以管理系统的配置或掌握组件的功能状态,如电源和系统风扇。可随时监控内存、硬盘、网络、系统温度等多个参数,增加了系统的安全性,方便了管理。
1.1 IIC总线特点
IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10 Kbps的最大传输速率支持40个组件。IIC总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。[1]
1.2 IIC总线工作原理
1.2.1总线的构成及信号类型
图1 具有多主机的IIC总线的系统结构
IIC总线的系统结构如图1所示。IIC总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,IIC总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关。[2]
IIC总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
开始信号: SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
目前有很多半导体集成电路上都集成了IIC接口。带有IIC接口的单片机有:CYGNAL的 C8051F0XX系列,PHILIPSP87LPC7XX系列,MICROCHIP的PIC16C6XX系列等。很多外围器件如存储器、监控芯片等也提供IIC接口。
1.2.2 总线基本操作
IIC规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。参见图2。
图2 串行总线上的数据传送顺序
1.3 控制字节
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。如图3所示。
图3 控制字节配置
1.4 写操作
写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同。关于页面写的地址、应答和数据传送的时序参见图4。
图4 页面写
1.5 读操作
读操作有三种基本操作:当前地址读、随机读和顺序读。图5给出的是顺序读的时序图。应当注意的是:最后一个读操作的第9个时钟周期不是“不关心”。为了结束读操作,主机必须在第9个周期间发出停止条件或者在第9个时钟周期内保持SDA为高电平、然后发出停止条件。
图5 顺序读
IIC总线系统由两根总线即SCL(串行时钟)线和 SDA(串行数据)线构成。这种总线可以设计成很多种通讯配置,但本文只讨论主从系统的应用。主器件控制总线通讯,开始/结束传送、发送信息并产生IIC系统时钟.
在写操作过程中,从器件一旦被主控器件寻址,就执行特定的相应功能。在读操作过程中,主控器件从从器件那里获得数据。在整个主从传送过程中,所有的事件都通过主控器件的SCL 时钟线达到同步。连到总线上的器件的接口形式必须是漏极开路或集电极开路输出状态。通过上拉电阻,使得两根总线在空闲的状态下都为高电平状态。因此IIC 总线上具有线与功能,即总线上的所有器件都达到高电子状态时,IIC总线才能达到高电平状态,从而使总线上的高速器件和慢速器件工作同步。
1.6 7位的地址格式介绍
数据的传输遵循图7所示的格式 在起始条件S 后,发送了一个从机地址,这个地址共有7 位,紧接着的第8 位是数据方向位R/ W, 0 表示发送写 ,1 表示请求数据读。数据传输一般由主机产生的停止(P)终止。但是,如果主机仍希望在总线上通讯。它可以产生重复起始条件( S r)和寻址另一个从机。而不是首先产生一个停止条件,在这种传输中可能有不同的读写格式结合 。
图6 完整的数据传输
在IIC 协议中,从器件地址是一个唯一的7 位地址。接下来是一个读写方向标志位,读状态是高电平、写状态是低电子。然后是CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。[2]
图7主机-发送器用7 位地址寻址从机接收器(传输的方向不变)
图8在第一个字节后主机立即读从机
2 IIC模块的硬、软件设计
IIC模块总体硬件设计流程:先在QuartusII9.0里建立Verilog HDL文件后,然后由Verilog HDL文件生成原理图子模块文件,再将生成的原理图子模块进行简单的导线连接后,就建立了本次系统所需的整体原理图,然后进行编译、调试生成.sof文件后,通过JTAG口将文件下载到目标板里,进行在系统测试,实物测试图见附录部分。
2.1 IIC模块硬件设计
2.1.1 按键消抖模块设计(KeyFilter_Check)
在总体硬件设计中,按键消抖模块建立的Verilog HDL文件名为KeyFilter_Check,1.按键消抖:分为硬件和软件;软件消抖延时时间一般为10ms,按键触发的时间一般为几百毫秒(采样时钟一般为几个毫秒)。此模块的Verilog HDL文件使用的是非阻塞赋值语句。
word/media/image10_1.png
图9 按键消抖模块
2.1.2 分频模块设计(Division_1_500HZ)
在总体硬件设计中,分频模块建立的Verilog HDL文件名为Division_1_500HZ,其实现的功能是将输入的初始时钟clk进行分频,通过分频获取串行总线器件所需要的时钟信号(即达到输出1HZ,50HZ,500HZ频率的目的)。偶分频具体过程为:计数到n/2-1的时候时钟进行翻转,然后计数复位
2.1.3 IIC总线接口模块设计(IIC_Interface_Bus)
在总体硬件设计中,IIC总线接口模块建立的Verilog HDL文件名为IIC_Interface_Bus,然后将文件名为IIC_Interface_Bus的Verilog HDL文件生成名为IIC_Interface_Bus的原理图文件。
word/media/image11_1.png
图10 IIC总线接口模块
输入信号有50MHz的时钟clk,复位信号rst_n,低电平有效的同步信号Syn_Sign,控制字节写操作的按键的响应信号Byte_Write, Byte_Read,控制页写操作的按键的响应信号Page_Write,控制页读操作按键的响应信号Page_Read;按键1按下执行写字节入操作,按键2按下执行读操作,按键3按下执行连写操作,按键4按下执行连读操作。AT24C08的时钟端口scl; AT24C08的数据端口sda;后面显示接收到数据的标志ackflagIIC;数码管所显示的数据输出端口outdata。
2.1.4 显示模块设计(Led_Seg_Display)
在总体硬件设计中,显示模块建立的Verilog HDL文件名为Led_Seg_Display,然后将文件名为Led_Seg_Display的Verilog HDL文件生成名为Led_Seg_Display的原理图文件。50MHz的时钟输入端口clk;复位端口reset_n;端口ackflag;8位数据输入端口datain;数码管段码输出seg_data;数码管位选输出端口seg_com。
word/media/image12_1.png
图11 显示模块
2.1.5 硬件模块总体设计思想及总体电路原理图
介绍下硬件模块总体设计思想:本IIC模块实现的主要功能是完成并行数据与串行数据的转换,在转换过程中串行数据的输入与输出必须满足IIC 总线规范。图12为IIC总线接口框图,主要包括以下几个单元。
分频器:由FPGA的锁相环输出的稳定时钟信号,由于频率很高所以必须经过分频模块输出满足IIC总线要求的数据传输速率。
IIC总线接口控制时序逻辑块:IIC总线数据传输的所有时序控制逻辑都由它产生,是这个IIC 模块的核心。
数据锁存器:根据读写使能信号(r/ w)存储IIC 己接收的或待发送的数据。
移位寄存器:在时序控制逻辑模块的控制下根据读写使能信号(r/w)对数据进行正确地处理。
就本次IIC模块设计而言,其硬件电路图如图13。
图12为IIC总线接口框图
word/media/image14_1.png
图13硬件模块总体电路图
2.2 IIC模块的verilog HDL代码设计
本IIC 模块的软件设计是基于QuartusII9.0建立Division_1_500HZ ,IIC_Interface_Bus ,KeyFilter_Check ,Led_Seg_Display等Verilog HDL文件。
由于IIC 总线传输协议可知, IIC 在传输过程中存在着几个固定的状态,因此我们采用同步状态机来设计IIC 模块。主状态机从的大的方面共有5个状态:空闲(Idle) ,开始(Start) ,发送数据(Tx),接收数据(Rx),停止(Stop)。但是具体细分后的状态有17个,它们的具体功能及地址分别如下。
IDLE:17'b0_0000_0000_0000_0001;// IIC总线处在空闲状态
START1:17'b0_0000_0000_0000_0010;//当scl信号为高电平,sdl由高电平到低电平变化时开始运作IIC 模块,且根据(r/ w)判断进入下一状态
ADD1:17'b0_0000_0000_0000_0100;//写入器件地址
ACK1:17'b0_0000_0000_0000_1000;// 写入器件地址后的应答
ADD2:17'b0_0000_0000_0001_0000;//写入字节地址
ACK2:17'b0_0000_0000_0010_0000;// 写入字节地址后的应答
START2:17'b0_0000_0000_0100_0000;//读操作开始前的起始信号
ADD3:17'b0_0000_0000_1000_0000;//写入器件地址
ACK3:17'b0_0000_0001_0000_0000;//应答状态
ACKR: 17'b1_0000_0000_0000_0000;//fpga给应答状态
DATA:17'b0_0000_0010_0000_0000;//字节读写状态
PAGER:17'b0_0000_0100_0000_0000;//页读状态状态
PAGEW:17'b0_0000_1000_0000_0000;//页写状态
ACK4: 17'b0_0001_0000_0000_0000;//应答状态
HIGH: 17'b0_0010_0000_0000_0000;//高电平状态
STOP1:17'b0_0100_0000_0000_0000;//停止位状态(当数据传输完毕,跳入STOP1状态。)
STOP2:17'b0_1000_0000_0000_0000;//延时同步
由上面的17个状态组成的状态转移图如图14所示。
图14 IIC模块主状态机状态转移图
3 IIC接口模块的功能实物测试(基于EP2C20Q240C8)
根据IIC 协议中传输过程的特点,IIC模块可以划分为字节发送模块、字节接收模块、开始条件模块、停止条件模块。其中,字节发送模块、字节接收模块和停止条件模块为基本模块。在开始条件模块中,因为需要发送从器件地址,所以要调用字节发送模块。
下面给出用 Verilog HDL语言实现字节发送模块的关键程序。相关变量的声明在此略去。程序在QuartusII9.0环境下编译、调试、仿真。然后将生成的.sof文件下载到EP2C20Q240C8目标板上进行实物测试,得到如下的测试图。
图 15写一个字节为十进制78的字节操作
图 16连续写2个字节 (十进制26、32)的页操作
4 IIC接口模块设计的改进
由于24C01/02/04/08/16器件可以不考虑应答位, 所以本次设计中没有设定应答接口,因此没有进行仿真测试,所编写的IIC 接口模块的Verilog HDL代码是可综合的,仅仅是将此代码直接下载到altera公司EP2C20Q240C8器件中后,进行了实物模拟IIC总线接口功能,基本实现项目要求。
为此我们将IIC 模块设计进行改进,可以达到直观地利用时序图来观察IIC总线接口协议的目的,可以观察本设计是否满足IIC总线接口功能。
具体的操作是:可以实例化了两个模块master(主机)和slave(从机) ,并设计了顶层模块调用master和slave ,使用了QuartusII9.0进行了仿真。从图17和图18明显可知,当master的drive线置高电平且r/w线为低电平时, IIC 模块运作,产生起始信号Start ,且在sda线发送8位数据,且在scl 线发送9个时钟脉冲信号,等待相应信号mcf ,若slave 响应,则mcf为高电平,继续发送下一字节数据,直到产生终止信号Stop ,sda线和scl 线置高电平,发送数据终止,等待下一个起始信号Start。同样的,当slave 的drive线置高电平时, IIC 模块运作,r/w线为’1’时接收数据功能启动。当mcf为’1’时,slave 响应master ,且在这时刻才能保证接收数据的准确性,当出现终止信号Stop时数据接收终止,等待下一个起始信号Start。图17和图18准确反映了master发送3字节数据和slave 准确接收3字节数据的功能。
图17 master时序仿真图
图18 slave 时序仿真图
参考文献
[1] 夏宇闻. Verilog数字系统设计教程[ M ] .北京航空航天大学出版社,2005.
[2] 何立民. IIC总线应用系统设计[ M ] .北京航空航天大学出版社,2004.
[3] 吴继华,王诚. Altera FP GA/ CPLD 设计(高级篇) [ M ] .人民邮电出版社,2005.
[4] 路永坤.用Verilog HDL 实现IIC 总线功能[J].:电子技术应用,2005.
[5] IIC 总线规范[E].:电子技术论坛,2000.
附录 部分源代码
/************************************************************************************/
//IIC接口时序模拟部分
module IIC_Interface_Bus(
clk,
rst_n,
Syn_Sign,
Byte_Write,
Byte_Read,
Page_Write,
Page_Read,
scl,
sda,
ackflag,
outdata
);
/************************************************************************************/
//AT24C08的地址和数据,根据硬件进行更改.
`define DEVICE_READ 8'b1010_0001//被寻址器件地址(读操作)
`define DEVICE_WRITE 8'b1010_0000//被寻址器件地址(写操作)
/************************************************************************************/
//写入EEPROM的数据
`define WRITE_DATA0 8'd78//8'b0110_0010 0x62
`define WRITE_DATA1 8'd32//8'b0010_0001 0x21
`define WRITE_DATA2 8'd26//8'b0100_0011 0x43
`define WRITE_DATA3 8'd79//8'b0110_0101 0x45
`define WRITE_DATA4 8'd43//8'b1000_0111 0x87
/************************************************************************************/
`define BYTE_ADDR 8'b0000_0100 //要写入/读出EEPROM的地址寄存器
/************************************************************************************/
/************************************************************************************/
input clk; //50MHz
input rst_n; //复位信号,低电平有效
input Syn_Sign;//同步信号
input Byte_Write,Byte_Read,Page_Write,Page_Read;//按键1按下执行写入操作,2按下执行读操作,3按下执行连写操作,4按下执行连读操作
output scl; //AT24C08的时钟端口
inout sda; //AT24C08的数据端口
output [2:0]ackflag; //后面显示接收到数据的标志
output [7:0] outdata; //数码管显示的数据
/************************************************************************************/
//分频部分,通过分频获取串行总线器件的时钟信号
reg[2:0] cnt; //cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟 100khz - Max:400KHZ
reg scl_r; //时钟脉冲寄存器
parameter pre_cnt = 500;//
/************************************************************************************/
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
else
cnt_delay <= cnt_delay+1'b1;//时钟计数
/************************************************************************************/
always @ (posedge clk or negedge rst_n)
if(!rst_n)
scl_r <= 1'b0;
else if(cnt_delay==9'd499) scl_r <= 1'b1;//scl信号上升沿
else if(cnt_delay==9'd249) scl_r <= 1'b0;//scl信号下降沿
assign scl = scl_r; //产生iic所需要的时钟100KHZ
/************************************************************************************/
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 3'd5;
else
begin
case (cnt_delay)
9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样
9'd255: cnt <= 3'd2; //cnt=2:scl下降沿后面点
9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化
9'd495: cnt <= 3'd0; //cnt=0:scl上升沿前面点
default: cnt <= 3'd5;
endcase
end
end
/************************************************************************************/
`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低电平中间,用于数据变化
/************************************************************************************/
reg[7:0] db_r; //在IIC上传送的数据寄存器
reg[7:0] read_data; //读出EEPROM的数据寄存器
reg[7:0] outdata_r; //输出数据贮存器
parameter PAGEDATA_NUM = 3'd4;//页写数据个数
/************************************************************************************/
//IIC读、写时序
parameter IDLE = 17'b0_0000_0000_0000_0001;//初始态
parameter START1 = 17'b0_0000_0000_0000_0010;//起始信号
parameter ADD1 = 17'b0_0000_0000_0000_0100;//写入器件地址
parameter ACK1 = 17'b0_0000_0000_0000_1000;//应答
parameter ADD2 = 17'b0_0000_0000_0001_0000;//写入字节地址
parameter ACK2 = 17'b0_0000_0000_0010_0000;//应答
parameter START2 = 17'b0_0000_0000_0100_0000;//读操作开始前的起始信号
parameter ADD3 = 17'b0_0000_0000_1000_0000;//写入器件地址
parameter ACK3 = 17'b0_0000_0001_0000_0000;//应答
parameter ACKR = 17'b1_0000_0000_0000_0000;//fpga给应答
parameter DATA = 17'b0_0000_0010_0000_0000;//字节读写
parameter PAGER = 17'b0_0000_0100_0000_0000;//页读
parameter PAGEW = 17'b0_0000_1000_0000_0000;//页写
parameter ACK4 = 17'b0_0001_0000_0000_0000;//应答
parameter HIGH = 17'b0_0010_0000_0000_0000;//高电平
parameter STOP1 = 17'b0_0100_0000_0000_0000;//停止位
parameter STOP2 = 17'b0_1000_0000_0000_0000;//延时同步
/************************************************************************************/
reg[16:0] cstate; //状态寄存器
reg sda_r; //输出数据寄存器
reg sda_link; //输出数据sda信号inout方向控制位
reg [3:0] num; //读写的字节计数
reg [2:0] ackflag; //连读时的数据标志
reg [2:0] pagecnt; //连读连写时的数据计数器
reg [7:0] pagedata_r;//连读储存器
/************************************************************************************/
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
begin
pagedata_r <= 8'd0;
end
else
begin
case(pagecnt)
3'd0: pagedata_r <= `WRITE_DATA1;
3'd1: pagedata_r <= `WRITE_DATA2;
3'd2: pagedata_r <= `WRITE_DATA3;
3'd3: pagedata_r <= `WRITE_DATA4;
default:;
endcase
end
end
/************************************************************************************/
//FSM状态机
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cstate <= IDLE; //
sda_r <= 1'b1; //
sda_link <= 1'b0; //0:数据线sda为input,1:数据线sda为output
num <= 4'd0; //
ackflag <= 3'd0; //
pagecnt <= 3'd0; //
read_data <= 8'b0000_0000;//
outdata_r <= 8'b0000_0000;//
end
else
case (cstate)
IDLE://初始态
begin
sda_link <= 1'b1;//数据线sda为input
sda_r <= 1'b1;
read_data <= 8'b0000_0000;
//ackflag <= 3'd0;
if( (!Byte_Write) || (!Byte_Read) || (!Page_Write) || (!Page_Read))
begin //Byte_Write,Byte_Read,Page_Write,Page_Read键有一个被按下
db_r <= `DEVICE_WRITE;//送器件地址(写操作)
cstate <= START1; //
end
else cstate <= IDLE;//没有任何键被按下
end
START1://起始信号
begin
if(`SCL_HIG)
begin //scl为高电平期间,拉低数据线sda,产生起始位信号
sda_link<= 1'b1; //数据线sda为output
sda_r <= 1'b0; //拉低数据线sda,产生起始位信号
cstate <= ADD1;
ackflag <= 1'b0;
num <= 4'd0; //num计数清零
end
else
cstate <= START1; //等待scl高电平中间位置到来
end
ADD1://写入器件地址
begin
if(`SCL_LOW)
begin
if(num==4'd8)
begin
num <= 4'd0;//num计数清零
sda_r <= 1'b1;//shi fang shiju xian
sda_link<= 1'b0;//sda置为高阻态(input)
cstate <= ACK1;
end
else
begin
cstate <= ADD1;
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];//gao dao di
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
end
end
else
cstate <= ADD1;
end
ACK1://应答信号都是由接收方发送的,第九个时钟脉冲拉低数据线.发送方需检测数据线电平以获取应答信号.
begin//注:24C01/02/04/08/16器件可以不考虑应答位if(/*!sda*/`SCL_NEG)
if(`SCL_NEG)
begin
cstate <= ADD2; //从机响应信号
db_r <= `BYTE_ADDR;//1地址
end
else
cstate <= ACK1; //等待从机响应
end
ADD2://写入需要读或写的存储地址\字节地址
begin
if(`SCL_LOW)
begin
if(num==4'd8) begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
sda_link<= 1'b0; //sda置为高阻态(input)
cstate <= ACK2;
end
else
begin
sda_link <= 1'b1; //sda作为output
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'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
cstate <= ADD2;
end
end
else
cstate <= ADD2;
end
ACK2://
begin
if(`SCL_NEG)
begin//从机响应信号
if(!Byte_Write)//字节写按键
begin
cstate <= DATA; //写操作
db_r <= `WRITE_DATA0;//写入的数据1
end
else if( (!Byte_Read) || (!Page_Read))//字节读或者页读按键
begin//读或着是连读
db_r <= `DEVICE_READ;//送器件地址(读操作),特定地址读需要执行该步骤以下操作
cstate <= START2; //读操作
end
else if(!Page_Write)//页写按键
begin//连写
//db_r <= pagedata_r;
//ackflag <= ackflag + 1'd1;
cstate <= PAGEW;
end
else
cstate<= ACK2;//等待从机响应
end
end
START2:
begin//读操作起始位
if(`SCL_LOW)
begin
sda_link <= 1'b1;//sda作为output
sda_r <= 1'b1; //拉高数据线sda
cstate <= START2;
end
else if(`SCL_HIG)
begin //scl为高电平中间
sda_r <= 1'b0;//拉低数据线sda,产生起始位信号
cstate <= ADD3;
end
else
cstate <= START2;
end
ADD3:
begin //送读操作地址
if(`SCL_LOW)
begin
if(num==4'd8)
begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
sda_link <= 1'b0;//sda置为高阻态(input)
cstate <= ACK3;