协议——IIC
I²C即Inter-Integrated Circuit(集成电路总线),它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。I²C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可接收数据。在主控与被控IC之间可进行双向数据传送,数据的传输速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s,各种被控器件均并联在总线上,通过器件地址(SLAVE ADDR,具体可查器件手册)识别。I²C总线物理拓扑结构图如下所示:
图中的IIC_SCL是串行时钟线,IIC_SDA是串行数据线,由于I2C器件一般采用开漏结构与总线相连,所以IIC_SCL和IIC_SDA均需接上拉电阻,也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的SDA及SCL都是“线与”关系。IIC总线支持多主和主从两种工作方式,通常工作在主从工作方式,我们的开发板就采用主从工作方式。在主从工作方式中,系统中只有一个主机,其它器件都是具有I2C总线的外围从机。在主从工作方式中,主机启动数据的发送(发出启动信号)并产生时钟信号,数据发送完成后,发出停止信号。I2C总线结构虽然简单,使用两线传输,然而要实现器件间的通信,需要通过控制SCL和SDA的时序,使其满足I2C的总线传输协议,方可实现器件间的数据传输。
一、起始和结束
在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原因处于高电平状态,此时I2C总线处于空闲状态。
如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在SCL为高电平时,SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传输状态,主机可以向从机写数据,也可以读取从机输出的数据,数据的传输由双向数据线(SDA)完成。停止信号产生后,总线再次处于空闲状态。
起始:SCL为高时,SDA由高拉低。
停止:SCL为高时,SDA由低拉高。
二、数据传输和应答期
先看看数据是怎么通过两根线传过来的,除了起始和结束比较特殊,中间的数据传输所遵循的规律如下所示:
1.数据传输
SCL为低时,SDA运行变化。
SCL为高时,SDA数据锁存。
2.应答期,SDA总线是三态门
①在第8个时钟周期末,主机释放SDA以使从机应答
②在第9个时钟周期,从机将SDA拉低以应答
③若第9个时钟周期,SCL为高电平时,SDA未被检测到为低电平,视为非应答,表明此次数据传输失败。
④在第9个时钟周期末,从机释放SDA以使主机继续传输数据,如果主机发送停止信号,此次传输结束。
三、器件地址
每个I2C器件都有一个器件地址,有些I2C器件的器件地址是固定的,而有些I2C器件的器件地址由一个固定部分和一个可编程的部分构成,这是因为很可能在一个系统中有几个同样的器件,器件地址的可编程部分能最大数量的使这些器件连接到I2C总线上,例如为了增加系统的EEPROM容量,可能需要多个EEPROM。器件可编程地址位的数量由它可使用的管脚决定,比如EEPROM器件一般会留下3个管脚用于可编程地址位,当硬件电路上分别将这3个管脚连接到GND或VCC时,就可以设置不同的可编程地址。但有些I2C器件在出厂时器件地址就设置好了,用户不可以更改(如实时时钟PCF8563的器件地址为固定的7’h51)。所以当主机想给某个器件发送数据时,只需向总线上发送接收器件的器件地址即可。
进行数据传输时,主机首先向总线上发出开始信号,对应开始位S,然后按照从高到低的位序发送器件地址,一般为7bit,第8bit位为读写控制位R/W,该位为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作,然后接收从机响应。对于AT24C64来说,其传输器件地址格式如下图所示。
四、存储器地址(字地址)
一般而言,每个兼容I2C协议的器件,内部总会有可供读写的寄存器或存储器,对于我们本次实验用到的EEPROM存储器,内部就是一系列顺序编址的存储单元。所以,当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度,具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示。
1.单字节的字地址
2.双字节的字地址
五、IIC写
主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“0”即写命令,从机就处于接收数据的状态,此时,主机就开始写数据了。写数据分为单字节写(对于EEPROM而言,称为字节写)和连续写(对于EEPROM而言,称为页写)。
不管单字节写和连续写,都可概括为:start + 器件地址 + 写命令(0) + 字地址 + 数据 + stop
1.单字节写
单字节写:发送完一字节数据后发送结束信号。
2.连续写
连续写:发送完一字节数据后继续发送下一字节数据,最后发送的是结束信号。
六、IIC读
主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“1”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。读数据分为当前地址读、单字节读和连续读。
不管是单字节读还是连续读,都可以概括为:start + 器件地址 + 写命令(0) + 字地址 + start + 器件地址 + 读命令(1) + 接收从机的数据 + 主机非应答(1) + stop
1.单字节读
发送完器件地址和字地址后又发送起始信号和器件地址,而且第一次发送器件地址时后面的读写控制位为“0”,也就是写命令,第二次发送器件地址时后面的读写控制位为“1”,也就是读。为什么会有这样奇怪的操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次Dummy Write也就是虚写操作,只所以称为虚写,是因为我们并不是真的要写数据,而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以按当前地址读的方式读数据了。因此也可以理解为:没有发送数据的单次写操作 + 当前地址的读操作
单字节读:读取完一字节数据后,主机发送非应答信号。
2.连续读
连续读:读取完一字节数据后,主机发送应答信号,读取完最后一个字节数据后,主机发送非应答信号。
七、IIC控制器Verilog代码设计
(修改自正点原子FPGA)
1 //************************************************************************** 2 // *** 名称 : iic.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : IIC控制器 7 //************************************************************************** 8 9 module iic 10 //========================< 参数 >========================================== 11 #( 12 parameter DEVICE_ID = 7'b1010000 , //器件ID 13 parameter CLK = 26'd50_000_000 , //本模块的时钟频率 14 parameter SCL = 18'd250_000 //输出的SCL时钟频率 15 ) 16 //========================< 端口 >========================================== 17 ( 18 input clk , //时钟 19 input rst_n , //复位,低电平有效 20 //IIC control --------------------------------------- 21 input iic_en , //IIC触发信号 22 input addr16_en , //16位地址使能 23 input addr8_en , //8位地址使能 24 input write_en , //IIC写使能 25 input read_en , //IIC读使能 26 input [15:0] iic_addr , //IIC器件内地址 27 input [ 7:0] iic_data_wr , //IIC要写的数据 28 //IIC output ---------------------------------------- 29 output reg [ 7:0] iic_data_rd , //IIC读出的数据 30 output reg iic_done , //IIC一次操作完成 31 output reg iic_scl , //IIC的SCL时钟信号 32 inout iic_sda , //IIC的SDA数据信号 33 //dri_clk ------------------------------------------- 34 output reg iic_dri_clk //驱动IIC操作的驱动时钟,1Mhz 35 ); 36 //========================< 参数 >========================================== 37 localparam IDLE = 8'b0000_0001 ; //空闲状态 38 localparam DEVICE = 8'b0000_0010 ; //写器件地址 39 localparam ADDR_16 = 8'b0000_0100 ; //写字地址高8位 40 localparam ADDR_8 = 8'b0000_1000 ; //写字地址低8位 41 localparam DATA_WR = 8'b0001_0000 ; //写数据 42 localparam DEVICE_RD = 8'b0010_0000 ; //虚写器件地址 43 localparam DATA_RD = 8'b0100_0000 ; //读数据 44 localparam STOP = 8'b1000_0000 ; //结束 45 //========================< 信号 >========================================== 46 reg sda_dir ; //IIC数据(SDA)方向控制 47 reg sda_out ; //SDA输出信号 48 wire sda_in ; //SDA输入信号 49 reg state_done ; //状态结束 50 reg [ 6:0] cnt ; //计数 51 reg [ 7:0] state_c ; //状态机当前状态 52 reg [ 7:0] state_n ; //状态机下一状态 53 reg [15:0] iic_addr_t ; //地址 54 reg [ 7:0] iic_data_rd_t ; //读取的数据 55 reg [ 7:0] iic_data_wr_t ; //IIC需写的数据的临时寄存 56 reg [ 9:0] clk_cnt ; //分频时钟计数 57 wire [ 8:0] clk_divide ; //模块驱动时钟的分频系数 58 59 //========================================================================== 60 //== sda控制 61 //========================================================================== 62 assign iic_sda = sda_dir ? sda_out : 1'bz; //SDA数据输出或高阻 63 assign sda_in = iic_sda ; //SDA数据输入 64 65 //========================================================================== 66 //== 生成SCL的4倍时钟来驱动后面IIC的操作,生成1Mhz的iic_dri_clk 67 //========================================================================== 68 assign clk_divide = (CLK/SCL) >> 3; // >>3即除以8 69 70 always @(posedge clk or negedge rst_n) begin 71 if(!rst_n) begin 72 iic_dri_clk <= 1'b1; 73 clk_cnt <= 10'd0; 74 end 75 else if(clk_cnt == clk_divide - 1'd1) begin 76 clk_cnt <= 10'd0; 77 iic_dri_clk <= ~iic_dri_clk; 78 end 79 else 80 clk_cnt <= clk_cnt + 1'b1; 81 end 82 83 //========================================================================== 84 //== 状态机 85 //========================================================================== 86 always @(posedge iic_dri_clk or negedge rst_n) begin 87 if(!rst_n) 88 state_c <= IDLE; 89 else 90 state_c <= state_n; 91 end 92 93 always @(*) begin 94 case(state_c) 95 IDLE: begin //空闲状态 96 if(iic_en) begin 97 state_n = DEVICE; 98 end 99 else 100 state_n = IDLE; 101 end 102 DEVICE: begin //写器件ID 103 if(state_done) begin 104 if(addr16_en) 105 state_n = ADDR_16; 106 else if(addr8_en) 107 state_n = ADDR_8 ; 108 end 109 else 110 state_n = DEVICE; 111 end 112 ADDR_16: begin //写地址高8位 113 if(state_done) 114 state_n = ADDR_8; 115 else 116 state_n = ADDR_16; 117 end 118 ADDR_8: begin //写地址低8位 119 if(state_done) begin 120 if(write_en) 121 state_n = DATA_WR; 122 else if(read_en) 123 state_n = DEVICE_RD; 124 end 125 else 126 state_n = ADDR_8; 127 end 128 DATA_WR: begin //写数据 129 if(state_done) 130 state_n = STOP; 131 else 132 state_n = DATA_WR; 133 end 134 DEVICE_RD: begin //虚写器件ID 135 if(state_done) 136 state_n = DATA_RD; 137 else 138 state_n = DEVICE_RD; 139 end 140 DATA_RD: begin //读数据 141 if(state_done) 142 state_n = STOP; 143 else 144 state_n = DATA_RD; 145 end 146 STOP: begin //结束 147 if(state_done) 148 state_n = IDLE; 149 else 150 state_n = STOP ; 151 end 152 default:state_n= IDLE; 153 endcase 154 end 155 156 //========================================================================== 157 //== 设计各路信号 158 //========================================================================== 159 always @(posedge iic_dri_clk or negedge rst_n) begin 160 if(!rst_n) begin 161 iic_scl <= 1'b1; 162 sda_out <= 1'b1; 163 sda_dir <= 1'b1; 164 iic_done <= 1'b0; 165 cnt <= 1'b0; 166 state_done <= 1'b0; 167 iic_addr_t <= 1'b0; 168 iic_data_rd <= 1'b0; 169 iic_data_rd_t <= 1'b0; 170 iic_data_wr_t <= 1'b0; 171 end 172 else begin 173 state_done <= 1'b0 ; 174 cnt <= cnt +1'b1 ; 175 case(state_c) 176 //--------------------------------------------------- 空闲状态 177 IDLE: begin 178 iic_scl <= 1'b1; 179 sda_out <= 1'b1; 180 sda_dir <= 1'b1; 181 iic_done <= 1'b0; 182 cnt <= 7'b0; 183 if(iic_en) begin 184 iic_addr_t <= iic_addr; 185 iic_data_wr_t <= iic_data_wr; 186 end 187 end 188 //--------------------------------------------------- 写器件ID 189 DEVICE: begin 190 case(cnt) 191 7'd1 : sda_out <= 1'b0; 192 7'd3 : iic_scl <= 1'b0; 193 7'd4 : sda_out <= DEVICE_ID[6]; 194 7'd5 : iic_scl <= 1'b1; 195 7'd7 : iic_scl <= 1'b0; 196 7'd8 : sda_out <= DEVICE_ID[5]; 197 7'd9 : iic_scl <= 1'b1; 198 7'd11: iic_scl <= 1'b0; 199 7'd12: sda_out <= DEVICE_ID[4]; 200 7'd13: iic_scl <= 1'b1; 201 7'd15: iic_scl <= 1'b0; 202 7'd16: sda_out <= DEVICE_ID[3]; 203 7'd17: iic_scl <= 1'b1; 204 7'd19: iic_scl <= 1'b0; 205 7'd20: sda_out <= DEVICE_ID[2]; 206 7'd21: iic_scl <= 1'b1; 207 7'd23: iic_scl <= 1'b0; 208 7'd24: sda_out <= DEVICE_ID[1]; 209 7'd25: iic_scl <= 1'b1; 210 7'd27: iic_scl <= 1'b0; 211 7'd28: sda_out <= DEVICE_ID[0]; 212 7'd29: iic_scl <= 1'b1; 213 7'd31: iic_scl <= 1'b0; 214 7'd32: sda_out <= 1'b0; //0:写 215 7'd33: iic_scl <= 1'b1; 216 7'd35: iic_scl <= 1'b0; 217 7'd36: begin 218 sda_dir <= 1'b0; //从机应答 219 sda_out <= 1'b1; 220 end 221 7'd37: iic_scl <= 1'b1; 222 7'd38: state_done <= 1'b1; //状态结束 223 7'd39: begin 224 iic_scl <= 1'b0; 225 cnt <= 1'b0; 226 end 227 default : ; 228 endcase 229 end 230 //--------------------------------------------------- 写字地址高8位 231 ADDR_16: begin 232 case(cnt) 233 7'd0 : begin 234 sda_dir <= 1'b1 ; 235 sda_out <= iic_addr_t[15]; 236 end 237 7'd1 : iic_scl <= 1'b1; 238 7'd3 : iic_scl <= 1'b0; 239 7'd4 : sda_out <= iic_addr_t[14]; 240 7'd5 : iic_scl <= 1'b1; 241 7'd7 : iic_scl <= 1'b0; 242 7'd8 : sda_out <= iic_addr_t[13]; 243 7'd9 : iic_scl <= 1'b1; 244 7'd11: iic_scl <= 1'b0; 245 7'd12: sda_out <= iic_addr_t[12]; 246 7'd13: iic_scl <= 1'b1; 247 7'd15: iic_scl <= 1'b0; 248 7'd16: sda_out <= iic_addr_t[11]; 249 7'd17: iic_scl <= 1'b1; 250 7'd19: iic_scl <= 1'b0; 251 7'd20: sda_out <= iic_addr_t[10]; 252 7'd21: iic_scl <= 1'b1; 253 7'd23: iic_scl <= 1'b0; 254 7'd24: sda_out <= iic_addr_t[9]; 255 7'd25: iic_scl <= 1'b1; 256 7'd27: iic_scl <= 1'b0; 257 7'd28: sda_out <= iic_addr_t[8]; 258 7'd29: iic_scl <= 1'b1; 259 7'd31: iic_scl <= 1'b0; 260 7'd32: begin 261 sda_dir <= 1'b0; //从机应答 262 sda_out <= 1'b1; 263 end 264 7'd33: iic_scl <= 1'b1; 265 7'd34: state_done <= 1'b1; //状态结束 266 7'd35: begin 267 iic_scl <= 1'b0; 268 cnt <= 1'b0; 269 end 270 default : ; 271 endcase 272 end 273 //--------------------------------------------------- 写字地址低8位 274 ADDR_8: begin 275 case(cnt) 276 7'd0: begin 277 sda_dir <= 1'b1 ; 278 sda_out <= iic_addr_t[7]; 279 end 280 7'd1 : iic_scl <= 1'b1; 281 7'd3 : iic_scl <= 1'b0; 282 7'd4 : sda_out <= iic_addr_t[6]; 283 7'd5 : iic_scl <= 1'b1; 284 7'd7 : iic_scl <= 1'b0; 285 7'd8 : sda_out <= iic_addr_t[5]; 286 7'd9 : iic_scl <= 1'b1; 287 7'd11: iic_scl <= 1'b0; 288 7'd12: sda_out <= iic_addr_t[4]; 289 7'd13: iic_scl <= 1'b1; 290 7'd15: iic_scl <= 1'b0; 291 7'd16: sda_out <= iic_addr_t[3]; 292 7'd17: iic_scl <= 1'b1; 293 7'd19: iic_scl <= 1'b0; 294 7'd20: sda_out <= iic_addr_t[2]; 295 7'd21: iic_scl <= 1'b1; 296 7'd23: iic_scl <= 1'b0; 297 7'd24: sda_out <= iic_addr_t[1]; 298 7'd25: iic_scl <= 1'b1; 299 7'd27: iic_scl <= 1'b0; 300 7'd28: sda_out <= iic_addr_t[0]; 301 7'd29: iic_scl <= 1'b1; 302 7'd31: iic_scl <= 1'b0; 303 7'd32: begin 304 sda_dir <= 1'b0; //从机应答 305 sda_out <= 1'b1; 306 end 307 7'd33: iic_scl <= 1'b1; 308 7'd34: state_done <= 1'b1; //状态结束 309 7'd35: begin 310 iic_scl <= 1'b0; 311 cnt <= 1'b0; 312 end 313 default : ; 314 endcase 315 end 316 //--------------------------------------------------- 写数据 317 DATA_WR: begin 318 case(cnt) 319 7'd0: begin 320 sda_out <= iic_data_wr_t[7]; 321 sda_dir <= 1'b1; 322 end 323 7'd1 : iic_scl <= 1'b1; 324 7'd3 : iic_scl <= 1'b0; 325 7'd4 : sda_out <= iic_data_wr_t[6]; 326 7'd5 : iic_scl <= 1'b1; 327 7'd7 : iic_scl <= 1'b0; 328 7'd8 : sda_out <= iic_data_wr_t[5]; 329 7'd9 : iic_scl <= 1'b1; 330 7'd11: iic_scl <= 1'b0; 331 7'd12: sda_out <= iic_data_wr_t[4]; 332 7'd13: iic_scl <= 1'b1; 333 7'd15: iic_scl <= 1'b0; 334 7'd16: sda_out <= iic_data_wr_t[3]; 335 7'd17: iic_scl <= 1'b1; 336 7'd19: iic_scl <= 1'b0; 337 7'd20: sda_out <= iic_data_wr_t[2]; 338 7'd21: iic_scl <= 1'b1; 339 7'd23: iic_scl <= 1'b0; 340 7'd24: sda_out <= iic_data_wr_t[1]; 341 7'd25: iic_scl <= 1'b1; 342 7'd27: iic_scl <= 1'b0; 343 7'd28: sda_out <= iic_data_wr_t[0]; 344 7'd29: iic_scl <= 1'b1; 345 7'd31: iic_scl <= 1'b0; 346 7'd32: begin 347 sda_dir <= 1'b0; //从机应答 348 sda_out <= 1'b1; 349 end 350 7'd33: iic_scl <= 1'b1; 351 7'd34: state_done <= 1'b1; //状态结束 352 7'd35: begin 353 iic_scl <= 1'b0; 354 cnt <= 1'b0; 355 end 356 default : ; 357 endcase 358 end 359 //--------------------------------------------------- 虚写器件ID 360 DEVICE_RD: begin 361 case(cnt) 362 7'd0 : begin 363 sda_dir <= 1'b1; 364 sda_out <= 1'b1; 365 end 366 7'd1 : iic_scl <= 1'b1; 367 7'd2 : sda_out <= 1'b0; //重新开始 368 7'd3 : iic_scl <= 1'b0; 369 7'd4 : sda_out <= DEVICE_ID[6]; 370 7'd5 : iic_scl <= 1'b1; 371 7'd7 : iic_scl <= 1'b0; 372 7'd8 : sda_out <= DEVICE_ID[5]; 373 7'd9 : iic_scl <= 1'b1; 374 7'd11: iic_scl <= 1'b0; 375 7'd12: sda_out <= DEVICE_ID[4]; 376 7'd13: iic_scl <= 1'b1; 377 7'd15: iic_scl <= 1'b0; 378 7'd16: sda_out <= DEVICE_ID[3]; 379 7'd17: iic_scl <= 1'b1; 380 7'd19: iic_scl <= 1'b0; 381 7'd20: sda_out <= DEVICE_ID[2]; 382 7'd21: iic_scl <= 1'b1; 383 7'd23: iic_scl <= 1'b0; 384 7'd24: sda_out <= DEVICE_ID[1]; 385 7'd25: iic_scl <= 1'b1; 386 7'd27: iic_scl <= 1'b0; 387 7'd28: sda_out <= DEVICE_ID[0]; 388 7'd29: iic_scl <= 1'b1; 389 7'd31: iic_scl <= 1'b0; 390 7'd32: sda_out <= 1'b1; //1:读 391 7'd33: iic_scl <= 1'b1; 392 7'd35: iic_scl <= 1'b0; 393 7'd36: begin 394 sda_dir <= 1'b0; //从机应答 395 sda_out <= 1'b1; 396 end 397 7'd37: iic_scl <= 1'b1; 398 7'd38: state_done <= 1'b1; //状态结束 399 7'd39: begin 400 iic_scl <= 1'b0; 401 cnt <= 1'b0; 402 end 403 default : ; 404 endcase 405 end 406 //--------------------------------------------------- 读数据 407 DATA_RD: begin 408 case(cnt) 409 7'd0 : sda_dir <= 1'b0; 410 7'd1 : begin 411 iic_data_rd_t[7] <= sda_in; 412 iic_scl <= 1'b1; 413 end 414 7'd3 : iic_scl <= 1'b0; 415 7'd5 : begin 416 iic_data_rd_t[6] <= sda_in; 417 iic_scl <= 1'b1; 418 end 419 7'd7 : iic_scl <= 1'b0; 420 7'd9 : begin 421 iic_data_rd_t[5] <= sda_in; 422 iic_scl <= 1'b1; 423 end 424 7'd11: iic_scl <= 1'b0; 425 7'd13: begin 426 iic_data_rd_t[4] <= sda_in; 427 iic_scl <= 1'b1; 428 end 429 7'd15: iic_scl <= 1'b0; 430 7'd17: begin 431 iic_data_rd_t[3] <= sda_in; 432 iic_scl <= 1'b1; 433 end 434 7'd19: iic_scl <= 1'b0; 435 7'd21: begin 436 iic_data_rd_t[2] <= sda_in; 437 iic_scl <= 1'b1; 438 end 439 7'd23: iic_scl <= 1'b0; 440 7'd25: begin 441 iic_data_rd_t[1] <= sda_in; 442 iic_scl <= 1'b1; 443 end 444 7'd27: iic_scl <= 1'b0; 445 7'd29: begin 446 iic_data_rd_t[0] <= sda_in; 447 iic_scl <= 1'b1 ; 448 end 449 7'd31: iic_scl <= 1'b0; 450 7'd32: begin 451 sda_dir <= 1'b1; //非应答 452 sda_out <= 1'b1; 453 end 454 7'd33: iic_scl <= 1'b1; 455 7'd34: state_done <= 1'b1; //状态结束 456 7'd35: begin 457 iic_scl <= 1'b0; 458 cnt <= 1'b0; 459 iic_data_rd <= iic_data_rd_t; 460 end 461 default : ; 462 endcase 463 end 464 //--------------------------------------------------- 结束 465 STOP: begin 466 case(cnt) 467 7'd0 : begin 468 sda_dir <= 1'b1; 469 sda_out <= 1'b0; 470 end 471 7'd1 : iic_scl <= 1'b1; 472 7'd3 : sda_out <= 1'b1; 473 7'd15: state_done <= 1'b1; //状态结束 474 7'd16: begin 475 cnt <= 1'b0; 476 iic_done <= 1'b1; //IIC配置完成 477 end 478 default : ; 479 endcase 480 end 481 endcase 482 end 483 end 484 485 486 487 488 endmodule
参考资料:
[1]正点原子FPGA教程
[2]小梅哥FPGA教程