差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
book_excise_i2c_if [2020/11/01 14:00]
gongyu 创建
book_excise_i2c_if [2021/08/20 16:06] (当前版本)
zili
行 1: 行 1:
-### I2C接口实验+## I2C接口实验 
 + 
 +### 1. 实验内容 
 +本实验要求了解I2C通信协议,掌握I2C总线的特征,编程访问I2C接口。本程序要求通过I2C访问扩展板上的ADV7181视频编解码芯片,要求写入的数据和读取的数据一致。 
 + 
 +\\ 
 +\\ 
 +### 2. 实验原理 
 +#### 2.1 I2C 总线的特点 
 + 
 +I2C是目前广泛应用的低速总线接口,主要用于对芯片的配置,其主要特点如下:\\ 
 +1) 只有两个总线信号:一个串行数据线 SDA, 一个串行时钟线 SCL。\\ 
 +2) 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/​从机关系软件设定地址;主机可以作为主机发送器或主机接收器。 ​ \\ 
 +3) 它是一个真正的多主机总线,如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁,防止数据被破坏。\\ 
 +4) 串行的 8 位双向数据传输位速率在标准模式下可达100kbit/​s,快速模式下可达 400kbit/​s,高速模式下可达3.4Mbit/​s。 
 + 
 +\\ 
 +#### 2.2 I2C总线协议 
 +I2C是目前广泛应用的低速总线接口,主要用于对芯片的配置,其主要特点如下:\\ 
 +1) 只有两个总线信号:一个串行数据线 SDA, 一个串行时钟线 SCL。\\ 
 +2) 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/​从机关系软件设定地址;主机可以作为主机发送器或主机接收器。 ​ \\ 
 +3) 它是一个真正的多主机总线,如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁,防止数据被破坏。\\ 
 +4) 串行的 8 位双向数据传输位速率在标准模式下可达100kbit/​s,快速模式下可达 400kbit/​s,高速模式下可达3.4Mbit/​s。\\ 
 +5 I2C只能在总线空闲时才能启动数据传输。\\ 
 +6) 在数据传输过程中,当时钟线为高电平时,数据线必须保持稳定。如果时钟线为高电平期间数据线电平发生变化,会被认为是控制信号。相应地定义了如下的总线条件: 
 +  ➢ 总线空闲:数据线和时钟线均保持高电平。 
 +  ➢ 启动数据传输:在时钟线(SCL)是高电平时,数据线(SDA)从高电平向低电平切换,定义为START条件,如图 15-1所示。 
 +  ➢ 停止数据传输:在时钟线(SCL)是高电平时,数据线(SDA)从低电平向高电平切换,定义为STOP条件,如图 15-1所示。 
 +{{ :​图15-1.png |图15-1 I2C的START和STOP条件}} 
 +<WRAP centeralign>​ 
 +**图15-1 I2C的START和STOP条件** 
 +</​WRAP>​ 
 + 
 +  ➢ 数据有效:产生START条件后,若在时钟信号为高电平期间数据线保持稳定,则此时数据线的状态代表有效数据。线上数据必须在时钟信号为低电平期间改变。每个时钟脉冲传送一位数据。使用START条件启动每次数据传输,并由STOP条件终止传输。在START和STOP条件之间传输的数据字节没有限制,仅有主设备决定。信息传输以字节为单位,首先传输的是数据的最高位 MSB,每个接收器使用第9位进行应答,如图 15-2所示。 
 +  ➢ 应答:被寻址的接收设备必须在收到每个字节后发出应答信号。相关的响应时钟脉冲由主机产生,用于该应答位,在响应的时钟脉冲期间,发送器释放SDA 线(高)。应答设备必须在应答时钟脉冲期间拉低SDA线,因此在应答时钟的高电平期间,SDA线保持稳定的低电平。对从设备同步输出的最后字节产生应答位,主设备向从设备指示数据的结尾。这样,从设备必须保持数据线为高电平以使主设备能够产生STOP条件,如图 15-2所示。 
 +{{ :​图15-2.png |图15-2 I2C传输时序图}} 
 +<WRAP centeralign>​ 
 +**图15-2 I2C传输时序图** 
 +</​WRAP>​ 
 + 
 +#### 2.2 从设备ADV7181的I2C接口 
 +ADV7181是美国ADI公司生产的一款低功耗的多功能数字视频解码芯片。它可以自动检测 NTSC、 PAL和 SECAM等标准的复合电视信号并将其转换为 16位或 8位的 IT U656 YUV 4:​2:​2的视频数据输出格式。 
 + 
 +ADV7181内部共有 240个控制寄存器用来对该芯片的功能和状态进行设置和查询。这些寄存器的取值对ADV7181的功能和状态的影响的详细内容可以参考 ADV7181的数据手册。ADV7181的寄存器的取值分为默认值和设置值。它对应芯片设计的初始化功能状态;默认值是芯片复位后的寄存器缺省取值,设置值是通过I2C总线配置方式对这些控制寄存器的默认值进行修改,以达到重新设定 ADV7181的各种功能的目的。具体的向ADV7181的读写时序如图 15-3所示,其中A(s)是传输8bit数据后的ACK信号,此时该信号应该被拉低。 
 +{{ :​图15-3.png |图15-3 ADV7181的读写时序}} 
 +<WRAP centeralign>​ 
 +**图15-3 ADV7181的读写时序** 
 +</​WRAP>​ 
 + 
 +\\ 
 +\\ 
 +### 3. 程序设计 
 +#### 3.1 总体架构 
 +程序的总体架构如15 4所示,程序的顶层模块为I2C,由两部分组成:\\ 
 +1. I2C_config实现对I2C的SCL和SDL的控制,产生读写时序。\\ 
 +2. INS_SDL和INS_SCL完成对SCL和SDL的三态逻辑控制,实现对外的接口。 
 + 
 +{{ :​图15-4.png |图15-4 程序总体架构}} 
 +<WRAP centeralign>​ 
 +**图15-4 程序总体架构** 
 +</​WRAP>​ 
 + 
 +#### 3.2 顶层模块I2C(I2C_config.v) 
 +顶层模块I2C连接各子模块,其接口如下: 
 +<code verilog>  
 + 
 +input iclk;​ //​时钟信号,50MHZ 
 +input reset;​ //​异步复位信号  
 +output scl;​ //​ I2C串行时钟线 
 +inout sda;​ //​ I2C串行数据线 
 +output [7:0] regvalue;​ //​读操作时,从从设备寄存器中读取的寄存器值。 
 + 
 +</​code>​ 
 + 
 +#### 3.3 INS_SDL和INS_SCL模块 
 +INS_SDL和INS_SCL完成I2C接口的物理控制,是一个三态控制模块trigate,由Mega wizard所定制,其接口如图15-5所示,真值表如表15 1所示。其中最关键的是两个控制信号enabletr和enabledt,其它模块根据访问需要来控制这两个信号。 
 +{{ :​图15-5.png |图15-5 trigate模块示意图}} 
 +<WRAP centeralign>​ 
 +**图15-5 trigate模块示意图** 
 +</​WRAP>​ 
 + 
 +<WRAP centeralign>​ 
 +**表15-1 ​ Trigate真值表** 
 +</​WRAP>​ 
 +|...|输入|双向接口|输出| 
 +|enabledt|enabletr|tridata[]|result[]| 
 +|0|0|Z (input)|Z| 
 +|0|1|Z (input)|tridata[]| 
 +|1|0|data[]|Z| 
 +|1|1|data[]|data[]| 
 + 
 +#### 3.4 I2C_config模块(I2C_config.v) 
 +I2C_config产生访问外设所需要的SDL和SCL信号,其内部结构如图 15-6所示。 
 +{{ :​图15-6.png |图15-6 I2C_config模块结构}} 
 +<WRAP centeralign>​ 
 +**图15-6 I2C_config模块结构** 
 +</​WRAP>​ 
 + 
 +1. SignalTap_clk产生SignalTap所需要的时钟,为1200kHz。\\ 
 +2. I2C_clk产生I2C接口所需要的时钟,为160KHz。\\ 
 +3. Register_operation模块中存储了可以进行的操作,由LUT_INDEX决定要访问的数据。\\ 
 +4. Control模块产生访问I2C的时序。\\ 
 +5. I2C_access_control控制对外设的访问,决定要访问的操作。\\ 
 +I2C_config接口如下: 
 +<code verilog>  
 + 
 +input iclk;​ //​时钟信号,50MHZ 
 +input reset;​ //​异步复位信号  
 +input sdai;​ //​I2C读操作数据线。 
 +input scli; ​        ​ //​暂时无用。 
 +output sclo; ​      ​ //​I2C写操作时钟线。 
 +output sdao; ​      ​ //​I2C写操作数据线。 
 +output enable_scli;​ //​下面4根信号线为控制i2c是向从设备写入还是读取。 
 +output enable_sclo;​ 
 +output enable_sdao;​ 
 +output enable_sdai;​ 
 +output ​ [7:​0]regvalue;/​ /​向从设备读取数据所读取出来的信号 
 + 
 +</​code>​ 
 + 
 +#### 3.5 Register_operation模块 
 +Register_operation存储了可以进行的操作,由LUT_INDEX决定了要进行的操作: 
 +<code verilog>  
 + 
 +always @(posedge counter_clk) begin : register_operation 
 + case(LUT_INDEX) 
 + 5'​h0 ​ : I2C_DATA <= 24'​h400131;​ //​写寄存器,地址1,写入31 
 + 5'​h1 ​ : I2C_DATA <= 24'​h410100;​ //​读寄存器,读出应该是31. 
 + 5'​h2 ​ : I2C_DATA <= 24'​h400212;​ //​寄存器地址为2的参数 
 + 5'​h3 ​ : I2C_DATA <= 24'​h400306;​ //​寄存器地址为3的参数 
 + .... 
 + Endcase 
 +end 
 + 
 +</​code>​ 
 + 
 +I2C_DATA表示通过I2C接口对外设完成的操作,由3个字节X1X2Y1Y2Z1Z2组成。第一个字节X1X2表示从设备的地址,X2的最低一位为0时表示写操作,为1时表示读操作,本实验中从设备的写地址为40,从设备的读地址为41。Y1Y2表示要访问的寄存器地址。对于写操作,Z1Z2表示要写入的数据,对于读操作Z1Z2没有意义。例如:400131对从设备40的寄存器1写入数据31;410100表示对从设备41的寄存器1进行读操作。 
 + 
 +#### 3.6 I2C_access_control 
 +I2C_access_control决定要访问的寄存器及其操作: 
 + 
 +<code verilog>  
 + 
 +always @(negedge reset or posedge counter_clk) begin : i2c_access_control 
 + if (!reset) begin 
 + ST <​= 0;​ 
 + GO <​= 0;​ 
 + LUT_INDEX <​= 0;​ // 
 + end 
 + else begin 
 + if(LUT_INDEX<​5'​d19) begin 
 + case(ST) //​ST由两个状态组成。 
 + 0: begin  
 + GO <​= 1;​ //​GO指示I2C_master开始读写操作。 
 + ST <​= 1; ​ //​转移至状态1. 
 + end 
 + 1:​ begin 
 + if(END) begin //​I2C_master通过End指示读写操作结束,高有效。 
 + if(!ACK) //​I2C_master通过ack指示读写操作是否正确完成, 
 +                                                         //​低电平有效。 
 + //​如果操作正常结束则进行下一个操作,否则重试上一 
 + //​个操作。 
 +  ​   LUT_INDEX <= LUT_INDEX + 1'b1;  
 +  ​   GO <= 0;​ //​回到状态0。 
 +  ​   ST <= 0; 
 + end 
 + end 
 + default : begin 
 + ST <= 0; 
 + GO <= 0;  
 + end  
 + endcase 
 + end 
 + end 
 + end 
 + 
 + 
 +</​code>​ 
 + 
 +#### 3.7 I2C_Master模块(I2C_Master.v) 
 +I2C_Master模块完成I2C读写时序的产生。I2C读写时序的产生由状态机counter控制,将I2C_config模块给出的24bit I2C操作码I2C_DATA转化为正确的I2C的时序。 
 + 
 +I2C_Master的接口定义如下: 
 +<code verilog>  
 + 
 +input clock;​ //​I2C工作时钟,为160KHz。 
 +input reset;​ //​异步复位信号 
 +input sdai; ​          ​ //​I2C读操作数据输入 
 +input GO; ​            //​指示开始进行I2C操作,高电平有效 
 +input [23:​0]I2C_DATA;​ //​I2C操作码 
 +output scl;          //I2C SCL输出 
 +output sdao;        // I2C SDO输出 
 +output ACK;          //​接收设备响应信号 
 +output END;          //​发送24bit信号结束 
 +output ​ [7:​0]regvalue;​ //​为从从设备读取的信号 
 + 
 +</​code>​ 
 + 
 +I2C串行时序最终实现时是以bit为最小传输单位,将24位宽的数据以比特为单位进行传输。在向从设备写入1bit数据时,需要在scl上升沿前使该bit输出到sda信号线上,而在从从设备读取数据,也需要在scl上升沿后保存sda信号线上的1bit数据一段时间,具体时序如图 15-7所示。 
 +{{ :​图15-7.png |图15-7 1bit数据传输时序}} 
 +<WRAP centeralign>​ 
 +**图15-7 1bit数据传输时序** 
 +</​WRAP>​ 
 + 
 +ADV7181芯片I2C接口读时序要求:输出从设备的地址(最低位为0)=>​ 输出要读取的寄存器地址=>​输出从设备的地址(最低位为1)=>​ 读取寄存器值。 
 + 
 +ADV7181芯片I2C接口写时序要求:输出从设备的地址(最地位为0)=>​输出要写入的寄存器地址=>​输出要写入的数据。 
 + 
 +I2C接口的实现代码如下: 
 +<code verilog>  
 + 
 +always @ (negedge reset or posedge clock) begin 
 +if(!reset) begin 
 + ACK1<​=0;​ ACK2<=0; ACK3<=0; END<=0; SDO<=0; scl<=0; regvalue<​=0;​ read_buf<​=0;​  
 + end 
 + else begin 
 +END <= 0; 
 + if(I2C_DATA[16]==1)begin //​如果I2C_DATA的第16比特为1,则表示读操作。 
 + case(counter) 
 +8'​d0 ​ : begin ACK1<=1 ;ACK2<=1 ;​ACK3<​=0;​ END<=0; SDO<=1; scl<​=1; ​  
 +end 
 + //​8'​d1 : 
 + 8'​d2 ​ : begin SD<​=I2C_DATA;​ SDO<=0; end 
 + 8'​d3 ​ : scl<=0; //​SDO先拉低,SCL再拉低。 
 + //​开始输出从设备地址。 
 + 8'​d4 ​ : SDO<​=SD[23];​ //​第1 bit 
 + 8'​d5 ​ : scl<​=1;​ 
 + //​8'​d6 ​ :  
 + 8'​d7 ​ : scl<​=0;​ 
 + 8'​d8 ​ : SDO<​=SD[22];​ //​第2 bit 
 + 8'​d9 ​ : scl<​=1;​ 
 + //​8'​d10 ​ :  
 + 8'​d11 ​ : scl<​=0;​  
 + 8'​d12 ​ : SDO<​=SD[21];​ //​第3 bit 
 + 8'​d13 ​ : scl<​=1;​ 
 + //​8'​d14 ​ :  
 + 8'​d15 ​ : scl<​=0;​ 
 + 8'​d16 ​ : SDO<​=SD[20];​ //​第4 bit 
 + 8'​d17 ​ : scl<​=1;​ 
 + //​8'​d18 ​ :  
 + 8'​d19 ​ : scl<​=0;​  
 + 8'​d20 ​ : SDO<​=SD[19];​ //​第5 bit 
 + 8'​d21 ​ : scl<​=1;​ 
 + //​8'​d22 ​ :  
 + 8'​d23 ​ : scl<​=0;​ 
 + 8'​d24 ​ : SDO<​=SD[18];​ //​第6 bit 
 + 8'​d25 ​ : scl<​=1;​ 
 + //​8'​d26 ​ :  
 + 8'​d27 ​ : scl<​=0;​  
 + 8'​d28 ​ : SDO<​=SD[17];​ //​第7 bit 
 + 8'​d29 ​ : scl<​=1;​ 
 + //​8'​d30 ​ :  
 + 8'​d31 ​ : scl<​=0;​ 
 + 8'​d32 ​ : SDO<​=0; ​       //第8 bit 输出0,在读之前先写寄存器地址。 
 + 8'​d33 ​ : scl<​=1;​ 
 + //​8'​d34 ​ :  
 + 8'​d35 ​ : scl<​=0;​  ​     //​从器件响应ack 信号,​输出置高阻关闭输出 。 
 + 8'​d36 ​ : SDO<​=1;​ //ack。 
 + 8'​d37 ​ : begin scl<=1; ACK1<​=sdai;​ end //​主器件读取从器件响应 
 + //​8'​d38 ​ :  
 + 8'​d39 ​ : scl<=0; //​打开输出 
 + 8'​d40 ​ : SDO<​=SD[15];​ //​主器件写寄存器地址第 1 bit  
 + 8'​d41 : scl<​=1;​ 
 + //​8'​d42 ​ :  
 + 8'​d43 ​ : scl<​=0;​ //​寄存器地址第 2 bit 
 + 8'​d44 ​ : SDO<​=SD[14];​  
 + 8'​d45 ​ : scl<​=1;​  
 + //​8'​d46 ​ :  
 + 8'​d47 ​ : scl<​=0;​ //​寄存器地址第 3 bit 
 + 8'​d48 ​ : SDO<​=SD[13];​ 
 + 8'​d49 ​ : scl<​=1;​ 
 + //​8'​d50 ​ : 
 + 8'​d51 ​ : scl<​=0;​ //​寄存器地址第 4 bit 
 + 8'​d52 ​ : SDO<​=SD[12];​ 
 + 8'​d53 ​ : scl<​=1;​ 
 + //​8'​d54 ​ :  
 + 8'​d55 ​ : scl<​=0;​ //​寄存器地址第 5 bit 
 + 8'​d56 ​ : SDO<​=SD[11];​ 
 + 8'​d57 ​ : scl<​=1;​ 
 + //​8'​d58 ​ : 
 + 8'​d59 ​ : scl<​=0;​ //​寄存器地址第 6 bit 
 + 8'​d60 ​ : SDO<​=SD[10];​ 
 + 8'​d61 ​ : scl<​=1;​ 
 + //​8'​d62 ​ :  
 + 8'​d63 ​ : scl<​=0;​ //​寄存器地址第 7 bit 
 + 8'​d64 ​ : SDO<​=SD[9];​ 
 + 8'​d65 ​ : scl<​=1;​ 
 + //​8'​d66 ​ : 
 + 8'​d67 ​ : scl<​=0;​ //​寄存器地址第 8 bit 
 + 8'​d68 ​ : SDO<​=SD[8];​ 
 + 8'​d69 ​ : scl<​=1;​ 
 + //​8'​d70 ​ : 
 + 8'​d71 ​ : scl<=0; //​从器件响应ack 信号,​关闭输出  
 + 8'​d72 ​ : SDO<​=1; ​    ​ //​ack 
 + 8'​d73 ​ : begin scl<=1; ACK2<​=sdai;​ end     ​ //​主器件上升沿检测是否有响应,有则另外发送一个开始信号,继续发数据。 
 + //​8'​d74 ​ :  
 + 8'​d75 ​ : scl<​=0;​ 
 + 8'​d76 ​ : SDO<​=1;​ 
 + 8'​d77 ​ : scl<​=1;​ 
 + 8'​d78 ​ : begin SD<​=I2C_DATA;​ SDO<​=0;​end ​   //​再次发送从设备地址 
 + //​8'​d79 : 
 + 8'​d80 ​ : scl<​=0;​ 
 + //SLAVE ADDR 
 + 8'​d81 ​ : SDO<​=SD[23];​ //​第1 bit 
 + 8'​d82 ​ : scl<​=1;​ 
 + //​8'​d83 ​ :  
 + 8'​d84 ​ : scl<​=0;​ 
 + 8'​d85 ​ : SDO<​=SD[22];​ //​第2 bit 
 + 8'​d86 ​ : scl<​=1;​ 
 + //​8'​d87 ​ :  
 + 8'​d88 ​ : scl<​=0;​  
 + 8'​d89 ​ : SDO<​=SD[21];​ //​第3 bit 
 + 8'​d90 ​ : scl<​=1;​ 
 + //​8'​d91 ​ :  
 + 8'​d92 ​ : scl<​=0;​ 
 + 8'​d93 ​ : SDO<​=SD[20];​ //​第4 bit 
 + 8'​d94 ​ : scl<​=1;​ 
 + //​8'​d95 ​ :  
 + 8'​d96 ​ : scl<​=0;​  
 + 8'​d97 ​ : SDO<​=SD[19];​ //​第5 bit 
 + 8'​d98 ​ : scl<​=1;​ 
 + //​8'​d99 ​ :  
 + 8'​d100 ​ : scl<​=0;​ 
 + 8'​d101 ​ : SDO<​=SD[18];​ //​第6 bit 
 + 8'​d102 ​ : scl<​=1;​ 
 + //​8'​d103 ​ :  
 + 8'​d104 ​ : scl<​=0;​  
 + 8'​d105 ​ : SDO<​=SD[17];​ //​第7 bit 
 + 8'​d106 ​ : scl<​=1;​ 
 + //​8'​d107 ​ :  
 + 8'​d108 ​ : scl<​=0;​ 
 + 8'​d109 ​ : SDO<​=1; ​                    ​ //​第8 bit 1, 表示读操作 
 + 8'​d110 ​ : scl<​=1;​ 
 + //​8'​111 ​ : 
 + 8'​d112 : scl<​=0;​ //​从器件响应ack 信号,​输出置高阻关闭输出  
 + 8'​d113 : SDO<​=1;​ //​ack 
 + 8'​d114 : begin scl<=1; ACK1<​=sdai;​ end //​主器件上升沿检测是否有响应,有则读数据 
 + //​8'​d115 : 
 + 8'​d116 : scl<​=0;​  
 + //​8'​d117 ​ :     
 + 8'​d118 ​ : begin scl<=1; read_buf[7]<​=sdai;​ end    //​主器件读第 1 bit 
 + //​8'​d119 ​ :  
 + 8'​d120 : scl<​=0;​  
 + //​8'​d121 ​ :  
 + 8'​d122 ​ : begin scl<=1; read_buf[6]<​=sdai;​ end   //第 2 bit 
 + //​8'​d123 ​ :  
 + 8'​d124 ​ : scl<​=0;​  
 + //​8'​d125 ​ :  
 + 8'​d126 ​ : begin scl<=1; read_buf[5]<​=sdai;​ end //第 3 bit 
 + //​8'​d127 ​ :  
 + 8'​d128 ​ : scl<​=0;​  
 + //​8'​d129 ​ :  
 + 8'​d130 ​ : begin scl<=1; read_buf[4]<​=sdai;​ end //第 4 bit 
 + //​8'​d131 ​ :  
 + 8'​d132 ​ : scl<​=0;​  
 + //​8'​d133  
 + 8'​d134 ​ :  begin scl<=1; read_buf[3]<​=sdai;​ end //第 5 bit 
 + //​8'​d135 
 + 8'​d136 : scl<​=0;​  
 + //​8'​d137 ​ :  
 + 8'​d138 ​ : begin scl<=1; read_buf[2]<​=sdai;​ end //第 6 bit 
 + //​8'​d139 ​ :  
 + 8'​d140 : scl<​=0;​  
 + //​8'​d141 ​ :  
 + 8'​d142 ​ : begin scl<=1; read_buf[1]<​=sdai;​ end //第 7 bit 
 + //​8'​d143 :  
 + 8'​d144 ​ : scl<​=0;​  
 + //​8'​d145 ​ :  
 + 8'​d146 ​ : begin scl<=1; read_buf[0]<​=sdai;​ end //第 8 bit 
 + //​8'​d147 ​ :   
 + //ack 
 + 8'​d148 ​ : scl<​=0;​ //​从器件写入 1,进入高阻态,释放数据线, 等待主器件相应,​打开输出  
 + 8'​d149 ​ : SDO<​=1;​ //​主器件响应 noacknowledge信号结束读取 
 + 8'​d150 ​ : scl<​=1;​ //​从器件上升沿检测是否有响应,若是noack就停止发送 
 + //​8'​d151 ​ :  
 + //​结束 
 + 8'​d152 ​ : scl<​=0; ​    ​  ​  ​ //​关闭输出  ​   8'​d153 ​ : SDO<​=0;​ //​准备进入结束信号 
 + 8'​d154 ​ : scl<​=1;​ 
 + 8'​d155 ​ : begin SDO<​=1;​ END<​=1;​ regvalue<​=read_buf;​ end//​产生结束信号 
 + endcase //​读操作结束。 
 + end 
 +else begin //​写操作 
 +case(counter) 
 + 8'​d0 ​ : begin ACK1=0 ;ACK2=0 ;ACK3=0 ; END=0; SDO=1; scl=1; end 
 + //​start 
 + 8'​d1 ​ : begin SD=I2C_DATA;​SDO=0;​end 
 + 8'​d2 ​ : scl=0; 
 + 8'​d3 ​ : SDO=SD[23];​ //​第1 bit 
 + 8'​d4 ​ : scl=1; 
 + //​8'​d5 ​ :  
 + 8'​d6 ​ : scl=0; 
 + 8'​d7 ​ : SDO=SD[22];​ //​第2 bit 
 + 8'​d8 ​ : scl=1; 
 + //​8'​d9 ​ :  
 + 8'​d10 : scl=0;  
 + 8'​d11 ​ : SDO=SD[21];​ //​第3 bit 
 + 8'​d12 ​ : scl=1; 
 + //​8'​d13 ​ :  
 + 8'​d14 ​ : scl=0; 
 + 8'​d15 ​ : SDO=SD[20];​ //​第4 bit 
 + 8'​d16 ​ : scl=1; 
 + //​8'​d17 ​ :  
 + 8'​d18 ​ : scl=0;  
 + 8'​d19 ​ : SDO=SD[19];​ //​第5 bit 
 + 8'​d20 ​ : scl=1; 
 + //​8'​d21 ​ :  
 + 8'​d22 ​ : scl=0; 
 + 8'​d23 ​ : SDO=SD[18];​ //​第6 bit 
 + 8'​d24 ​ : scl=1; 
 + //​8'​d25 ​ :  
 + 8'​d26 ​ : scl=0;  
 + 8'​d27 ​ : SDO=SD[17];​ //​第7 bit 
 + 8'​d28 ​ : scl=1; 
 + //​8'​d29 ​ :  
 + 8'​d30 ​ : scl=0; 
 + 8'​d31 ​ : SDO=SD[16]; ​                    ​ //​第8 bit  写0 
 + 8'​d32 ​ : scl=1; 
 + //​8'​d33 ​ :  
 + 8'​d34 ​ : scl=0; //​从器件响应ack 信号,​关闭输出  
 + 8'​d35 ​ : SDO=1;​  
 + 8'​d36 ​ : begin scl=1;ACK1= sdai; end //​主器件读取从器件的ACK响应 
 + //​8'​d37 ​ :  
 + 8'​d38 ​ : scl=0; //​寄存器地址第 1 bit,​打开输出 
 + 8'​d39 ​ : SDO=SD[15];​  
 + 8'​d40 ​ : scl=1; 
 + //​8'​d41 ​ :  
 + 8'​d42 ​ : scl=0;​ //​寄存器地址第 2 bit 
 + 8'​d43 ​ : SDO=SD[14];​  
 + 8'​d44 ​ : scl=1;  
 + //​8'​d45 ​ :  
 + 8'​d46 ​ : scl=0;​ //​寄存器地址第 3 bit 
 + 8'​d47 ​ : SDO=SD[13];​ 
 + 8'​d48 ​ : scl=1; 
 + //​8'​d49 ​ : 
 + 8'​d50 ​ : scl=0;​ //​寄存器地址第 4 bit 
 + 8'​d51 ​ : SDO=SD[12];​ 
 + 8'​d52 ​ : scl=1; 
 + //​8'​d53 ​ :  
 + 8'​d54 ​ : scl=0;​ //​寄存器地址第 5 bit 
 + 8'​d55 ​ : SDO=SD[11];​ 
 + 8'​d56 ​ : scl=1; 
 + //​8'​d57 ​ : 
 + 8'​d58 ​ : scl=0;​  
 + 8'​d59 ​ : SDO=SD[10]; ​                  //​寄存器地址第 6 bit 
 + 8'​d60 ​ : scl=1; 
 + //​8'​d61 ​ :  
 + 8'​d62 ​ : scl=0;​ //​寄存器地址第 7 bit 
 + 8'​d63 ​ : SDO=SD[9];​ 
 + 8'​d64 ​ : scl=1; 
 + //​8'​d65 ​ : 
 + 8'​d66 ​ : scl=0;​ //​寄存器地址第 8 bit 
 + 8'​d67 ​ : SDO=SD[8];​ 
 + 8'​d68 ​ : scl=1; 
 + //​8'​d69 ​ : 
 + 8'​d70 ​ : scl=0; //​从器件响应ack 信号,​关闭输出  
 + 8'​d71 ​ : SDO=1; ​    ​  
 + 8'​d72 ​ : begin scl=1; ACK2=sdai; end  //​主器件读取从器件的ACK响应 
 + //​8'​d73 ​ :  
 + 8'​d74 ​ : scl=0; //​从器件置高阻,释放数据线 ,​打开输出  
 + 8'​d75 ​ : SDO=SD[7]; ​   //​写数据第 1 bit 
 + 8'​d76 : scl=1; ​    ​ //​主器件读第 9 bit 
 + //​8'​d77 ​ :  
 + 8'​d78 ​ : scl=0;​  
 + 8'​d79 ​ : SDO=SD[6];​ //​写数据第 2 bit 
 + 8'​d80 ​ : scl=1; 
 + //​8'​d81 ​ :  
 + 8'​d82 ​ : scl=0;​  
 + 8'​d83 ​ : SDO=SD[5];​ //​写数据第 3 bit 
 + 8'​d84 ​ : scl=1; 
 + //​8'​d85 ​ :  
 + 8'​d86 ​ : scl=0;​  
 + 8'​d87 ​ : SDO=SD[4];​ //​写数据第 4 bit 
 + 8'​d88 ​ : scl=1; 
 + //​8'​d89 ​ :  
 + 8'​d90 ​ : scl=0;​  
 + 8'​d91 ​ : SDO=SD[3];​ //​写数据第 5 bit 
 + 8'​d92 ​ : scl=1; 
 + //​8'​d93 ​ :  
 + 8'​d94 ​ : scl=0;​  
 + 8'​d95 ​ : SDO=SD[2];​ //​写数据第 6 bit 
 + 8'​d96 ​ : scl=1; 
 + //​8'​d101 ​ :  
 + 8'​d97 ​ : scl=0;​  
 + 8'​d98 ​ : SDO=SD[1];​ //​写数据第 7 bit 
 + 8'​d99 ​ : scl=1; 
 + //​8'​d100 ​ :  
 + 8'​d101 ​ : scl=0;​  
 + 8'​d102 ​ : SDO=SD[0];​ //​写数据第 8 bit 
 + 8'​d103 ​ : scl=1; 
 + //​8'​d104 ​ : 
 + 8'​d105 ​ : scl=0;​ //​ 从器件响应,​关闭输出 
 + 8'​d106 ​ : SDO=1;  
 + 8'​d107 ​ : begin scl=1; ACK3=sdai; end //​主器件读取响应信号并判断 
 + //​8'​d108 ​ : 
 + 8'​d109 ​ : scl=0;​ //​从器件释放数据线,置高阻 
 + 8'​d110 ​ : SDO=0;​ //​准备进入结束 
 + 8'​d111 ​ : scl=1; 
 + 8'​d112 ​ : begin SDO=1; END=1; ​ end //​结束信号 
 +            endcase 
 +        end 
 +    end 
 +end 
 + 
 +</​code>​ 
 + 
 +\\ 
 +\\ 
 +### 4. 实验运行结果 
 +图 15-8和图15-9分别给出了I2C的写时序和读时序。写入码是400131,即向从设备40的第一个寄存器写入数据31。读取码是410100,即从从设备40(读地址为41)的第一个寄存器中读取数据,读取的结果应该是写入的数据31。 
 +{{ :​图15-8.png |图15-8 I2C写时序}} 
 +<WRAP centeralign>​ 
 +**图15-8 I2C写时序序** 
 +</​WRAP>​ 
 + 
 +写时序分析如下(图 15 8):\\ 
 +1) START信号(第1条坐标线)\\ 
 +首先发送的是START信号,该信号的表征为:在scl为高期间,sda由高变低。\\ 
 +2) 第一个8bit数据(第2条坐标线到第3条坐标线)\\ 
 +发送第一个8bit的从设备地址40h,各个bit为1、为0的判别依据为:在scl为高电平期间,sda的高低。当scl为高期间,sda为高此时发送1;当scl为高期间,sda为低此时发送0。\\ 
 +3) 8bit数据后的ACK信号(第4条坐标线)\\ 
 +发送完成8bit数据后,从设备发出ACK信号。\\ 
 +4) 第二个8bit数据(第5条坐标线到第6条坐标线)\\ 
 +发送第二个8bit的寄存器地址01h。\\ 
 +5) 8bit数据后的ACK信号(第7条坐标线)\\ 
 +发送完成8bit数据后,从设备发出ACK信号。\\ 
 +6) 第三个8bit数据(第8条到第9条坐标线)\\ 
 +发送第一个8bit的数据30h。\\ 
 +7)  8bit数据后的ACK信号(第10条坐标线)\\ 
 +发送完成8bit数据后,从设备发出ACK信号。\\ 
 +8) 24位宽数据发送完成后的END信号\\ 
 +在24bit数据发送完后,主设备FPGA给出END信号,该信号的表征为:在scl为高电平期间,sda从低电平变为高电平。 
 + 
 +{{ :​图15-9.png |图15-9 I2C读时序}} 
 +<WRAP centeralign>​ 
 +**图15-9 I2C读时序** 
 +</​WRAP>​ 
 + 
 +读时序分析如下(图15 9):\\ 
 +1) 发送START信号(第1条坐标线)。\\ 
 +2) 发送从设备地址40h(第2条坐标线到第3条坐标线)。\\ 
 +3) 发送完8bit的从设备地址后,从设备发出ACK信号(第4条坐标线)。\\ 
 +4) 发送读取的寄存器地址01h(第5条坐标线到第6条坐标线)。\\ 
 +5) 发送完8bit的寄存器地址后,从设备发出ACK信号(第7条坐标线)。\\ 
 +6) 再次发送从设备地址,此次发送的地址是41h(第8条坐标线到第9条坐标线)。\\ 
 +7) 发送完8bit的从设备地址后,从设备发出ACK信号(第10条坐标线)。\\ 
 +8) 从设备发送寄存器01h中的数据31h(第11条坐标线到第12条坐标线)。\\ 
 +9) 接收完数据后,主设备发送NACK(第13条坐标线)。\\ 
 +10) 读操作完成,发送停止信号(第14条坐标线)。 
 + 
 +\\ 
 +\\ 
 +### 5. 演示程序文件说明 
 +<WRAP centeralign>​ 
 +**图15-2 演示程序文件说明** 
 +</​WRAP>​ 
 + 
 +|文件名|功能| 
 +|I2C.v|I2C顶层模块| 
 +|I2C_config.v|I2C_config模块| 
 +|I2C_master.v|I2C_master模块| 
 +|Trigate.v|Trigate模块| 
 + 
 +\\ 
 +\\ 
 +### 6. 演示程序使用 
 +演示设备:核心板、扩展板。 
 + 
 +演示方法:在下载程序之前先打开SignalTap,设置触发条件为SCL下降沿触发。然后下载程序,由于该程序是下载后很快就运行完成,为了观测到信号,首先运行SignalTap,然后按核心板上的reset键,则会看到I2C的访问信号。 
 + 
 + 
  
-#### 实验内容 
-#### 实验原理 
-#### 程序设计 
-#### 仿真结果 
-#### 演示程序文件说明 
-#### 演示程序使用