北京至芯科技FPGA创新中心01062670708通信协议的编写与调试,下面介绍一下我从一开始理解夏老师的程序,修改程序,直到下板调试整个的学习过程,希望对大家学习I2C有一定的帮助。一、分析源代码学习I2C,首先我们要知道I2C是一种串行总线协议。目前几种常用的串行总线有UART、SPI和I2C,先来简单了解一下这三种串行总线。1.5@128kbps33总线类型线数通信类型多主支持数据率总线上器件的数量线缆长度(米)UARTSPII2C242异步同步同步不支持不支持支持3Kbps到4Mbps1Mbps3.4Mbps21010图1.UART、SPI、I2C总线对比如图1所示,UART的总线数只有两条,分别是TX(发送)和RX(接收),没有时钟信号,所以UART需要固定的波特率,也就是两位数据的间隔要相等。而SPI的四条总线分别为SCLK(时钟)、MISO(主器件数据输入,从器件数据输出)、MOSI(主器件数据输出,从器件数据输入)、SS(从器件使能信号),SPI总线由主机提供时钟,为同步通信,并且SPI总线有两条总线进行数据传输,可以同时进行收发数据,为双工模式。SPI有3线、4线两种模式,3线模式一般代表没有SS信号,具体情况还要视器件手册而定。I2C有两条总线SCL(时钟)和SDA(数据),I2C用的线更少,因为I2C需要有双向IO的支持,而且使用上拉电阻,抗干扰能力没有其他两种强,一般用于同一板卡上芯片之间的通信,较少用于远距离通信。具体到FPGA设计这一环节,我们需要搞清这三种总线上的信号是怎么走的,UART很简单,使用发送数据线TXD和接收数据线RXD来传送数据,接收和发送可以单独进行也可以同时进行。它传送数据的格式有严格的规定,每个数据以相同的位串形式传送,每个串行数据由起始位,数据位,奇偶校验位和停止位组成。从起始位到停止位为一个字符的完整通信格式。SPI情况就相对多一些,根据时钟极性(CPOL)和时钟相位(CPHA)两个参数的不同有四种基本情况,传送数据的格式与UART差不多。而I2C总线的协议要比UART和SPI复杂,能掌握I2C,也就能掌握UART和SPI。言归正传,回到我们的I2C设计实例。第一步首先了解,这个I2C实例的功能。这个实例实现了通过I2C总线对EEPROM写入数据,再将写入EEPROM中的数据读取出来的一个过程。实例的重点在于对I2C总线协议时序的掌握,即用I2C总线要求的格式将数据写入到EEPROM中,再读取出来。什么是EEPROM?EEPROM(ElectricallyErasableProgrammableRead-OnlyMemory),电可擦可编程只读存储器,一种掉电后数据不丢失的存储芯片。EEPROM可以在电脑上或专用设备上擦除已有信息,重新编程。所以,EEPROM是可以写入数据也可以读取数据的,并且EEPROM掉电数据并不会丢失,在后面将程序烧写到开发板的过程中可以验证这一点。第二步,简单地了解一下这个实例的各个模块。北京至芯科技FPGA创新中心01062670708实例中,为了实现两个器件的通信,我们需要一个主机和一个从机,如下图2所示,它们之间采用I2C协议进行串行通信,设计实例中的signal模块和EEPROM_WR模块模拟主机,EEPROM模块模拟从机。当写入数据时,EEPROM_WR模块从signal模块中调取数据,然后通过I2C的两条总线SDA(数据总线)和SCL(时钟总线)向EEPROM器件写入数据。当读出数据时,EEPROM_WR仍然通过SDA和SCL总线读出EEPROM中的数据,然后输入给signal模块,比较读出的数据与当时写入的数据是否相同。验证读写的正确性。在至芯科技EP2C8-2010开发板上,是有EEPROM器件的,而signal模块我们可以通过创建一个RAM的IP核来实现其功能,所以图2中唯有EEPROM_WR模块是要写入FPGA中的可综合模块,所谓可综合模块就是可以转化为实际电路的模块,而signal和EEPROM模块则是为了仿真而存在的,所以其代码不必可综合,只编写其行为级模型即可。读者在后面会发现这三个模块的源代码编写风格不一样,这就是因为EEPROM_WR模块是可综合的,要求比较严格,不能用一些initial、#50之类的语句,而signal与EEPROM模块则没有这个限制。第三步,了解I2C总线特征。在I2C总线中,只有在总线处于“非忙”状态时,才能开始数据传输。在数据传输期间,只要时钟线为高电平,数据线都必须保持稳定,否则数据线上的任何变化都被当作“启动”或“停止”信号。图3是总线状态的定义。eeprom_wrSignaleepromDATA[7:0]DATA[7:0]ADDR[10:0]ADDR[10:0]CLKCLKRDRDWRWRRESETRESETACKACKSDASCLSCLSDA图2.EEPROM读写电路和它的测试电路SCLSDA(A)(B)(D)(D)(C)(A)停止信号数据的电平稳定数据才有效数据在此期间变化启动信号图3.I2C双向二线制串行总线数据在此期间变化数据在此期间变化1/0北京至芯科技FPGA创新中心01062670708(1)总线非忙状态(A段):数据线SDA和时钟线SCL都保持高电平。(2)启动数据传输(B段):当时钟线(SCL)为高电平状态时,数据线(SDA)由高电平变为低电平的下降沿被认为是“启动”信号。只有出现“启动”信号后,其它的命令才有效。(3)停止数据传输(C段):当时钟线(SCL)为高电平状态时,数据线(SDA)由低电平变为高电平的上升沿被认为是“停止”信号。随着“停在”信号出现,所有的外部操作都结束。(4)数据有效(D段):在出现“启动”信号以后,在时钟线(SCL)为高电平状态时数据线是稳定的,这时数据线的状态就要传送的数据。数据线(SDA)上的数据的改变必须在时钟线为低电平期间完成,每位数据占用一个时钟脉冲。每个数传输都是由“启动”信号开始,结束于“停止”信号。(5)应答信号:每个正在接收数据的从机EEPROM在接到一个字节的数据后,通常需要发出一个应答信号。而每个正在发送数据的EEPROM在发出一个字节的数据后,通常需要接收一个应答信号。EEPROM读写控制器必须产生一个与这个应答位相联系的额外的时钟脉冲。在EEPROM的读操作中,EEPROM读写控制器对EEPROM完成的最后一个字节产生一个高的应答位,这叫做非应答信号,随后给EEPROM一个结束信号。第四步,理解各个模块的代码。首先介绍核心模块——EEPROM_WR模块,这个模块就干一件事,严格控制SDA与SCL总线上的信号,使其满足I2C总线时序要求。这里要用到状态机来控制SDA与SCL上的信号,所以难点就在于对状态机的编写。因为EP2C8-2010开发板采用的EEPROM是AT24C02,所以该状态机控制的SCL与SDA时序就要满足AT24C02的写入和读取格式。图4、5分别是AT24C02/4/8/16字节写入帧格式和读指定地址存储单元的数据帧格式,简单分析一下字节写入格式,如图4所示:第1位启动信号,接下来的第2-9位是控制字节写入,其中2-5位是固定的机器码1010,6-8位是页地址,第10位是EEPROM给出的应答信号0,第11-18位是存储单元地址,19位是EEPROM给出的应答信号0,第20-27位是写入的数据,28位应答信号,29位停止信号。而AT24C02/4/8/16的字节读取格式也是大同小异,先写入控制字和存储地址,然后是启动信号与控制字节信号,这时,控制字节的第8位变为了1(读取),最后读取数据,并且在读取完毕后,主机将SDA拉高作为非应答信号。最后是停止位。可能有人还是对这种字节写入与读取格式不明白,其实这种格式是别人定好的,我们所需要的就是要让SDA与SCL的信号满足这种格式要求即可,下面我们来看夏老师的EEPROM_WR程序。图4.AT24C02/4/8/16字节写入帧格式图5.AT24C02/4/8/16读指定地址存储单元的数据帧格式启动控制字节EEPROM存储单元地址数据停止写SDA线S1010XXX应答应答应答启动控制字节EEPROM存储单元指定地址(n)写SDA线S1010XXX应答应答启动S1010XXX停止读控制字节数据(n)应答非应答P北京至芯科技FPGA创新中心01062670708首先是一堆输入输出、寄存器的定义,我们读程序的时候大可先不看这些,等到后面有需要的时候再回过头来看这些定义,这里需要注意的是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总线的示意图。EEPROM_WRlink_sdaout_flagSDAEEPROM图6.sda总线示意图link_sda和out_flag分别为EEPROM_WR和EEPROM控制三态门输出的开关,当link_sda打开,out_flag关闭时,EEPROM_WR向SDA传输数据,EEPROM接收SDA的数据,此时,SDA对于EEPROM_WR来说就是output,对于EEPROM来说就是input。反之,当link_sda关闭,out_flag打开时,EEPROM_WR通过SDA从从EEPROM中读取数据。当link_sda与out_flag都关闭时,SDA就被置为高阻状态。下面来看程序中对于SDA和DATA三态门的描述。assignsda1=(link_head)?head_buf[1]:1'b0;assignsda2=(link_write)?sh8out_buf[7]:1'b0;assignsda3=(link_stop)?stop_buf[1]:1'b0;assignsda4=(sda1|sda2|sda3);assignSDA=(link_sda)?sda4:1'bz;assignDATA=(link_read)?data_from_rm:8'hzz;这是程序中应用三态门对双向口的实现,我们可以看到引入了多个开关,link_head、link_write、link_stop、link_sda、link_read,其中link_sda和link_read这两个开关是最主要的,当link_sda=1,即link_sda打开时,将sda4输出,SDA=sda4。当link_sda=0时,将SDA置为高阻。同时sda4也是由三个开关控制的,为sda1、sda2、sda3三个信号相或的结果,这里是将EEPROM_WR的状态分为了三个基本的状态,分别是启动状态、写入状态以及停止状北京至芯科技FPGA创新中心01062670708态,这是根据EEPROM对SDA总线输出的值区分的。对照图3、