(带有源代码)基于FPGA的模拟I2C接口设计与实现

发布时间: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(InterIntegrated Circuit,内置集成电路总线)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。IIC总线产生于在80年代,最初为音频和视频设备开发,如今主要在服务器管理中使用,其中包括单个组件状态的通信。例如管理员可对各个组件进行查询,以管理系统的配置或掌握组件的功能状态,如电源和系统风扇。可随时监控内存、硬盘、网络、系统温度等多个参数,增加了系统的安全性,方便了管理。

11 IIC总线特点

IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10 Kbps的最大传输速率支持40个组件。IIC总线的另一个优点是,它支持多主控(multimastering) 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。[1]

12 IIC总线工作原理

121总线的构成及信号类型

1 具有多主机的IIC总线的系统结构

IIC总线的系统结构如图1所示。IIC总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、ICIC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,IIC总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关。[2]

IIC总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

  开始信号: SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。 

结束信号SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

  应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

   目前有很多半导体集成电路上都集成了IIC接口。带有IIC接口的单片机有:CYGNAL C8051F0XX系列,PHILIPSP87LPC7XX系列,MICROCHIPPIC16C6XX系列等。很多外围器件如存储器、监控芯片等也提供IIC接口。

122 总线基本操作

   IIC规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。参见图2

2 串行总线上的数据传送顺序

13 控制字节

   在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。如图3所示。

3 控制字节配置

14 写操作

写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同。关于页面写的地址、应答和数据传送的时序参见图4   

4 页面写

15 读操作

   读操作有三种基本操作:当前地址读、随机读和顺序读。图5给出的是顺序读的时序图。应当注意的是:最后一个读操作的第9个时钟周期不是不关心。为了结束读操作,主机必须在第9个周期间发出停止条件或者在第9个时钟周期内保持SDA为高电平、然后发出停止条件。

5 顺序读

IIC总线系统由两根总线即SCL(串行时钟)线和 SDA(串行数据)线构成。这种总线可以设计成很多种通讯配置,但本文只讨论主从系统的应用。主器件控制总线通讯,开始/结束传送、发送信息并产生IIC系统时钟

在写操作过程中,从器件一旦被主控器件寻址,就执行特定的相应功能。在读操作过程中,主控器件从从器件那里获得数据。在整个主从传送过程中,所有的事件都通过主控器件的SCL 时钟线达到同步。连到总线上的器件的接口形式必须是漏极开路或集电极开路输出状态。通过上拉电阻,使得两根总线在空闲的状态下都为高电平状态。因此IIC 总线上具有线与功能,即总线上的所有器件都达到高电子状态时,IIC总线才能达到高电平状态,从而使总线上的高速器件和慢速器件工作同步。

16 7位的地址格式介绍

数据的传输遵循图7所示的格式 在起始条件S 发送了一个从机地址这个地址共有7 紧接着的第8 位是数据方向位R/ W 0 表示发送写 1 表示请求数据读。数据传输一般由主机产生的停止(P终止但是如果主机仍希望在总线上通讯它可以产生重复起始条件( S r)和寻址另一个从机而不是首先产生一个停止条件在这种传输中可能有不同的读写格式结合

6 完整的数据传输

IIC 协议中,从器件地址是一个唯一的7 位地址。接下来是一个读写方向标志位,读状态是高电平、写状态是低电子。然后是CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。[2]

7主机-发送器7 位地址寻址从机接收器传输的方向不变

8在第一个字节后主机立即读从机

2 IIC模块的硬、软件设计

IIC模块总体硬件设计流程:先在QuartusII90里建立Verilog HDL文件后,然后由Verilog HDL文件生成原理图子模块文件,再将生成的原理图子模块进行简单的导线连接后就建立了本次系统所需的整体原理图,然后进行编译、调试生成sof文件后,通过JTAG口将文件下载到目标板里,进行在系统测试,实物测试图见附录部分

21 IIC模块硬件设计

211 按键消抖模块设计(KeyFilter_Check)

在总体硬件设计中,按键消抖模块建立的Verilog HDL文件名为KeyFilter_Check1按键消抖:分为硬件和软件;软件消抖延时时间一般为10ms按键触发的时间一般为几百毫秒采样时钟一般为几个毫秒)。此模块的Verilog HDL文件使用的是非阻塞赋值语句。

word/media/image10_1.png

9 按键消抖模块

212 分频模块设计(Division_1_500HZ)

在总体硬件设计中,分频模块建立的Verilog HDL文件名为Division_1_500HZ,其实现的功能是将输入的初始时钟clk进行分频,通过分频获取串行总线器件所需要的时钟信号(即达到输出1HZ50HZ500HZ频率的目的)。偶分频具体过程为:计数到n/2-1的时候时钟进行翻转,然后计数复位

213 IIC总线接口模块设计(IIC_Interface_Bus)

在总体硬件设计中,IIC总线接口模块建立的Verilog HDL文件名为IIC_Interface_Bus,然后将文件名为IIC_Interface_BusVerilog 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

214 显示模块设计(Led_Seg_Display)

在总体硬件设计中,显示模块建立的Verilog HDL文件名为Led_Seg_Display,然后将文件名为Led_Seg_DisplayVerilog HDL文件生成名为Led_Seg_Display的原理图文件。50MHz的时钟输入端口clk;复位端口reset_n端口ackflag8位数据输入端口datain数码管段码输出seg_data数码管位选输出端口seg_com

word/media/image12_1.png

11 显示模块

215 硬件模块总体设计思想及总体电路原理图

介绍下硬件模块总体设计思想:本IIC模块实现的主要功能是完成并行数据与串行数据的转换在转换过程中串行数据的输入与输出必须满足IIC 总线规范。图12IIC总线接口框图主要包括以下几个单元。

分频器FPGA的锁相环输出的稳定时钟信号由于频率很高所以必须经过分频模块输出满足IIC总线要求的数据传输速率。

IIC总线接口控制时序逻辑块IIC总线数据传输的所有时序控制逻辑都由它产生是这个IIC 模块的核心。

数据锁存器根据读写使能信号(r/ w)存储IIC 己接收的或待发送的数据。

移位寄存器在时序控制逻辑模块的控制下根据读写使能信号(r/w)对数据进行正确地处理。

就本次IIC模块设计而言,其硬件电路图如图13

12IIC总线接口框图

word/media/image14_1.png

13硬件模块总体电路图

22 IIC模块的verilog HDL代码设计

IIC 模块软件设计是基于QuartusII90建立Division_1_500HZ IIC_Interface_Bus KeyFilter_Check Led_Seg_DisplayVerilog HDL文件

由于IIC 总线传输协议可知 IIC 在传输过程中存在着几个固定的状态因此我们采用同步状态机来设计IIC 模块。主状态机从的大的方面共有5个状态:空闲(Idle) 开始(Start) 发送数据(Tx)接收数据(Rx)停止(Stop)。但是具体细分后的状态有17个,它们的具体功能及地址分别如下。

IDLE17'b0_0000_0000_0000_0001;// IIC总线处在空闲状态

START117'b0_0000_0000_0000_0010;//scl信号为高电平sdl由高电平到低电平变化时开始运作IIC 模块且根据(r/ w)判断进入下一状态

ADD117'b0_0000_0000_0000_0100;//写入器件地址

ACK117'b0_0000_0000_0000_1000;// 写入器件地址后的应答

ADD217'b0_0000_0000_0001_0000;//写入字节地址

ACK217'b0_0000_0000_0010_0000;// 写入字节地址后的应答

START217'b0_0000_0000_0100_0000;//读操作开始前的起始信号

ADD317'b0_0000_0000_1000_0000;//写入器件地址

ACK317'b0_0000_0001_0000_0000;//应答状态

ACKR 17'b1_0000_0000_0000_0000;//fpga给应答状态

DATA17'b0_0000_0010_0000_0000;//字节读写状态

PAGER17'b0_0000_0100_0000_0000;//页读状态状态

PAGEW17'b0_0000_1000_0000_0000;//页写状态

ACK4 17'b0_0001_0000_0000_0000;//应答状态

HIGH 17'b0_0010_0000_0000_0000;//高电平状态

STOP117'b0_0100_0000_0000_0000;//停止位状态(当数据传输完毕跳入STOP1状态。

STOP217'b0_1000_0000_0000_0000;//延时同步

由上面的17状态组成的状态转移图如图14所示。

14 IIC模块主状态机状态转移图

3 IIC接口模块的功能实物测试(基EP2C20Q240C8

根据IIC 协议中传输过程的特点,IIC模块可以划分为字节发送模块、字节接收模块、开始条件模块、停止条件模块。其中,字节发送模块、字节接收模块和停止条件模块为基本模块。在开始条件模块中,因为需要发送从器件地址,所以要调用字节发送模块。

下面给出用 Verilog HDL语言实现字节发送模块的关键程序。相关变量的声明在此略去。程序QuartusII9.0环境下编译、调试、仿真。然后将生成的.sof文件下载到EP2C20Q240C8目标板上进行实物测试,得到如下的测试图。

15写一个字节为十进制78的字节操作

16连续写2个字节 (十进制2632)的页操作

4 IIC接口模块设计的改进

由于24C01/02/04/08/16器件可以不考虑应答位, 所以本次设计中没有设定应答接口,因此没有进行仿真测试,所编写的IIC 接口模块的Verilog HDL代码是可综合的,仅仅是将此代码直接下载到altera公司EP2C20Q240C8器件中后,进行了实物模拟IIC总线接口功能,基本实现项目要求。

为此我们将IIC 模块设计进行改进,可以达到直观地利用时序图来观察IIC总线接口协议的目的,可以观察本设计是否满足IIC总线接口功能。

具体的操作是:可以实例化了两个模块master(主机)和slave(从机) ,并设计了顶层模块调用masterslave ,使用了QuartusII9.0进行了仿真。从图17和图18明显可知,masterdrive线置高电平且r/w线为低电平时, IIC 模块运作,产生起始信号Start ,且在sda线发送8位数据,且在scl 线发送9个时钟脉冲信号,等待相应信号mcf ,slave 响应,mcf为高电平,继续发送下一字节数据,直到产生终止信号Stop ,sda线和scl 线置高电平,发送数据终止,等待下一个起始信号Start。同样的,slave drive线置高电平时, IIC 模块运作,r/w线为1’时接收数据功能启动。当mcf1’,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_WriteByte_ReadPage_WritePage_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; //计数到10usscl的周期,即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:数据线sdainput1:数据线sdaoutput

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;//数据线sdainput

sda_r <= 1'b1;

read_data <= 8'b0000_0000;

//ackflag <= 3'd0;

if( (!Byte_Write) || (!Byte_Read) || (!Page_Write) || (!Page_Read))

begin //Byte_WriteByte_ReadPage_WritePage_Read键有一个被按下

db_r <= `DEVICE_WRITE;//送器件地址(写操作)

cstate <= START1; //

end

else cstate <= IDLE;//没有任何键被按下

end

START1://起始信号

begin

if(`SCL_HIG)

begin //scl为高电平期间拉低数据线sda,产生起始位信号

sda_link<= 1'b1; //数据线sdaoutput

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;

(带有源代码)基于FPGA的模拟I2C接口设计与实现

相关推荐