写在前面的话
IIC的通信协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行AD,图像处理领域的摄像头配置,工业控制领域的X射线管配置等等。除此之外,由于IIC协议占用的IO资源特别少,连接方便,所以工程中也常选用IIC接口做为不同芯片间的通信协议。
IIC协议的完成靠的是严紧的时序,一个周期都不能错,这也正是梦翼师兄设置本实验的目的。通过IIC的学习,锻炼大家的时序实现技巧和方法,可以说最佳的案例之一。
项目需求
设计IIC接口的驱动电路,可以实现单字节数据的读写。
IIC的原理分析
本实验,我们采用的外部接口芯片为EEPROM 24LC64,其封装图如下:
接下来,我们梳理一下各引脚定义:
A0,A1,A2为24LC64的片选信号,由于IIC总线可以挂载多个IIC接口器件,所以每个器件都应该有自己的“身份标识”,通过对A0,A1,A2输入不同的高低电平,就可以设置该EEPROM的片选信号。
WP为读写使能信号,当WP悬空或者接地,EEPROM可读可写,当WP接电源,EEPROM只能读不能写。
SCL为IIC接口的时钟线(根据不同的配置方式,scl的频率是可以改变的,我们采取200K)
SDA为IIC接口的数据线
得到了上面的配置信息,那么接下来,看一下我们开发板的配置原理图
由此可以看出我们的位选信号为“000”,EEPROM可读写。而我们最需要关心的,就是SCL和SDA两条线的时序关系。
原理图分析完毕,接下来我们学习一下IIC接口的具体时序是什么。IIC读写时序分为随机读写和页面读写,也就是常说的Byte Write/Read 和Page Write/Read。
我们首先来学习Byte Write/Read时序。Byte Write时序如下:
由时序图可以看出,如果我们要向EEPROM写入一个字节,那么必须经过以下步骤:
发送启动信号
发送控制字
接收并检测EEPROM发来的应答信号ACK
发送高字节地址位
接收并检测EEPROM发来的应答信号ACK
发送低字节地址位
接收并检测EEPROM发来的应答信号ACK
发送8bit有效数据
接收并检测EEPROM发来的应答信号ACK
10.发送停止信号
Byte Read时序如下:
由时序图可以看出,如果我们要从EEPROM读出一个字节,那么必须经过以下步骤:
1. 发送启动信号
- 发送控制字1010_0000
- 接收并检测EEPROM发来的应答信号ACK
- 发送高字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送低字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送启动信号
- 发送控制字1010_0001
- 接收并检测EEPROM发来的应答信号ACK
- 读取一个字节数据
- 发送NO ACK信号
- 发送停止信号
那么现在的问题就变成了每个步骤的意义到底是什么呢?各位且听我慢慢道来。
1.启动信号
在SCL保持高电平期间,如果SDA出现由高到低的跳变沿,代表启动信号
2.控制字
我们的控制字为1010_0000或1010_0001,其中1010为EEPROM的型号标识,为一组固定的序列,紧接着A2,A1,A0就是我们的片选信号,最后一位为读写控制位,低电平代表写,高电平代表读,我们这里首先需要对EEPROM写入地址位,所以我们最后一位为0。当我们需要读数据时,最后一位为1。
高/低位地址
由于24LC64有64Kbit的存储空间,所以我们需要13位的地址位宽才能寻址所有的存储空间,由于IIC协议规定只能以字节形式写入,所以必须将13位的地址扩展为16位的地址,分为高八位和低八位,多出来的前三位填充任意数据即可,对我们的寻址地址没有影响。
3.停止信号
在SCL保持高电平期间,如果SDA出现由低到高的跳变沿,代表停止信号
4.应答信号ACK
应答信号是由数据接收方发出的,当SCL为高电平期间,如果监测到SDA为低电平,说明有应答信号。
5.非应答信号NO ACK
非应答信号也是由数据接收方发出的,当SCL为高电平期间,如果SDA为高电平,说明有非应答信号。
由于IIC总线协议启动和停止信号都是在SCL高电平期间发生跳变(当不发送或者接收数据的时候scl一直为高电平),这就决定了我们其他数据的改变只能发生在SCL低电平期间,在SCL为高电平期间,数据必须保持稳定。即在SCL低电平改变数据,在SCL高电平采集数据。
由于读时序和写时序一直到低字节地址的写入之前都是相同的,因此我们设置IIC控制器流程图如下:
接下来,我们来学习Page Write/Read时序
Page Write时序如下:
Page Read时序如下:
由此可以看出,页面读写比随机读写只是多加了几个状态而已,在我们前面设计的基础上加几个状态就可以完成。梦翼师兄把这部分交给大家去发挥。
架构设计
根据原理分析,我们设计出架构图如下:
模块功能介绍
模块名 |
功能描述 |
IIC |
iic总线的驱动 |
顶层模块端口描述
端口名 |
端口说明 |
clk |
系统时钟输入 |
rst_n |
系统复位 |
key_wr |
写信号(低电平有效) |
key_rd |
读信号(低电平有效) |
data_in[7:0] |
输入数据 |
scl |
iic的时钟线 |
sda |
iic的数据线 |
data_out[7:0] |
输出数据 |
代码解释
iic模块代码
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function: 产生iic总线的控制信号 *****************************************************/ 000 module iic( 001 clk, //外部输入时钟 002 rst_n,//系统复位 003 key_wr,//写信号(低电平有效) 004 key_rd, //读信号(低电平有效) 005 data_in, //输入数据 006 scl, //iic的数据线 007 sda, //iic的时钟线 008 data_out//输出数据 009 ); 010 //系统输入 011 input clk;//外部输入时钟 012 input rst_n;//系统复位 013 input key_wr;//写信号(低电平有效) 014 input key_rd;//读信号(低电平有效) 015 input [7:0] data_in;//输入数据 016 //系统输出 017 output reg scl;//iic的时钟线 018 output reg [7:0] data_out;//输出数据 019 020 inout sda; //iic的数据线 021 022 reg sda_buffer;//写入数据的中间寄存器 023 reg flag;//控制系统是否占有总线控制权 024 025 assign sda = (flag) ? sda_buffer : 1'bz;//当flag为高电平时,系统拥有总线控制权 026 //并发送sda_buffer中的数据。当flag为低电平时, 027 //释放总线。 028 029 reg [7:0] count;//计数器 030 reg clk_sys;//系统时钟 031 //-------------------------------clk_sys 032 always @ (posedge clk or negedge rst_n) 033 begin 034 if (!rst_n) 035 begin 036 clk_sys <= 1'b0; 037 count <= 8'd0; 038 end 039 else 040 if (count < 31)//分频成为近800K的时钟 041 count <= count + 1; 042 else 043 begin 044 count <= 8'd0; 045 clk_sys <= ~clk_sys; 046 end 047 end 048 //------------------------------- 049 050 reg [5:0] state;//状态寄存器 051 052 //--------------------------------scl 053 always @ (negedge clk_sys or negedge rst_n) 054 begin 055 if (!rst_n) 056 begin 057 scl <= 1'b1;//复位时,scl为高 058 end 059 else 060 begin 061 if (state > 0)//当总线忙的时候,scl为近400K的时钟 062 scl <= ~scl; 063 else 064 scl <= 1'b1;//空闲时,scl为高 065 end 066 end 067 //---------------------------------- 068 069 reg [1:0] en;//读写使能中间寄存器 070 071 //----------------------------------enable 072 always @ (posedge clk or negedge rst_n) 073 begin 074 if (!rst_n) 075 begin 076 en <= 2'b00;//复位时,将中间寄存器置0; 077 end 078 else 079 begin 080 if (!key_wr)//写有效时 081 en <= 2'b01; 082 else 083 if (!key_rd)//写无效,读有效时 084 en <= 2'b10; 085 end 086 end 087 //--------------------------------- 088 089 reg [3:0] cnt;//发送或者接收数据的个数 090 reg [1:0] temp;//读写使能的中间寄存器 091 reg [7:0] memory;//发送或者接受数据的中间寄存器 092 093 always @ (posedge clk_sys or negedge rst_n) 094 begin 095 if (!rst_n) 096 begin 097 data_out <= 8'd0; 098 flag <= 1'b1; //复位时,系统获得总线的控制权 099 sda_buffer <= 1'b1; //向iic的数据线上发送高电平 100 state <= 0; 101 temp <= 2'b00; 102 end 103 else 104 case(state) 105 0 : begin 106 if(scl) 107 begin 108 if(en != temp)//有按键按下 109 begin 110 sda_buffer <= 1'b0;//发送启动信号 111 state <= 1; 112 temp <= en;//将读写信号保存 113 memory <= 8'b10100000;//控制字 114 end 115 else 116 state <= 0; 117 end 118 else 119 state <= 0; 120 end 121 122 1 : begin 123 if((scl == 0) && (cnt < 8))//发送八位控制字 124 begin 125 sda_buffer <= memory[7]; 126 cnt <= cnt + 1; 127 memory = {memory[6:0],memory[7]}; 128 state <= 1; 129 end 130 else 131 begin 132 if ((scl == 0) && (cnt == 8)) 133 begin 134 cnt <= 0; 135 flag <= 0;//释放总线控制权 136 state <= 2; 137 end 138 else 139 begin 140 state <= 1; 141 end 142 end 143 end 144 2 : begin 145 if(!sda)//检测应答信号 146 begin 147 state <= 3; 148 memory <= 8'd0;//高字节地址 149 end 150 else 151 begin 152 state <= 0; 153 end 154 end 155 3 : begin //发送高字节地址 156 if((scl == 0) && (cnt < 8)) 157 begin 158 flag <= 1;//获得总线控制权 159 sda_buffer <= memory[7]; 160 cnt <= cnt + 1; 161 memory = {memory[6:0],memory[7]}; 162 state <= 3; 163 end 164 else 165 begin 166 if ((scl == 0) && (cnt == 8)) 167 begin 168 cnt <= 0; 169 flag <= 0;//释放总线控制权 170 state <= 4; 171 end 172 else 173 begin 174 state <= 3; 175 end 176 end 177 end 178 4 : begin 179 if(!sda)//检测应答信号 180 begin 181 state <= 5; 182 memory <= 8'h00;//低字节地址 183 end 184 else 185 begin 186 state <= 0; 187 end 188 end 189 5 : begin 190 if((scl == 0) && (cnt < 8))//发送低字节地址 191 begin 192 flag <= 1;//获得总线控制权 193 sda_buffer <= memory[7]; 194 cnt <= cnt + 1; 195 memory = {memory[6:0],memory[7]}; 196 state <= 5; 197 end 198 else 199 begin 200 if ((scl == 0) && (cnt == 8)) 201 begin 202 cnt <= 0; 203 flag <= 0;//释放总线控制权 204 state <= 6; 205 end 206 else 207 begin 208 state <= 5; 209 end 210 end 211 end 212 6 : begin 213 if(!sda)//检测应答信号 214 begin 215 if (temp == 2'b01)//判断是否为写信号 216 begin 217 state <= 7; 218 memory <= data_in[7:0];//发送数据 219 end 220 if (temp == 2'b10)//判断是否为读信号 221 state <= 11; 222 end 223 else 224 begin 225 state <= 0; 226 end 227 end 228 229 7 : begin 230 if((scl == 0) && (cnt < 8))//发送数据 231 begin 232 flag <= 1;//获得总线控制权 233 sda_buffer <= memory[7]; 234 cnt <= cnt + 1; 235 memory <= {memory[6:0],memory[7]}; 236 state <= 7; 237 end 238 else 239 begin 240 if ((scl == 0) && (cnt == 8)) 241 begin 242 cnt <= 0; 243 flag <= 0;//释放总线控制权 244 state <= 8; 245 end 246 else 247 begin 248 state <= 7; 249 end 250 end 251 end 252 8 : begin 253 if(!sda)//检测应答信号 254 begin 255 state <= 9; 256 end 257 else 258 begin 259 state <= 0; 260 end 261 end 262 9 : begin 263 if (scl == 0) 264 begin 265 flag <= 1;//获得总线控制权 266 sda_buffer <= 0;//拉低iic的数据线(为发送停止信号做准备) 267 state <= 10; 268 end 269 else 270 state <= 9; 271 end 272 10 : begin 273 if (scl == 1) 274 begin 275 sda_buffer <= 1;//发送停止信号 276 state <= 0; 277 end 278 else 279 state <= 10; 280 end 281 //----------------------------------------- 282 283 //------- 284 11 : begin 285 flag <= 1;//获得总线控制权 286 sda_buffer <= 1;//拉高iic的数据线(为发送启动信号做准备) 287 state <= 12; 288 end 289 12 : begin 290 sda_buffer <= 0;//发送启动信号 291 state <= 13; 292 memory <= 8'b10100001; //控制字 293 end 294 13 : begin 295 if((scl == 0) && (cnt < 8))//发送八位控制字 296 begin 297 flag <= 1;//获得总线控制权 298 sda_buffer <= memory[7]; 299 cnt <= cnt + 1; 300 memory <= {memory[6:0],memory[7]}; 301 state <= 13; 302 end 303 else 304 begin 305 if ((scl == 0) && (cnt == 8)) 306 begin 307 cnt <= 0; 308 flag <= 0;//释放总线控制权 309 state <= 14; 310 end 311 else 312 begin 313 state <= 13; 314 end 315 end 316 end 317 14 : begin 318 if(!sda)//检测应答信号 319 begin 320 state <= 15; 321 end 322 else 323 begin 324 state <= 0; 325 end 326 end 327 15 : begin 328 if((scl == 1) && (cnt < 8))//接收数据 329 begin 330 cnt <= cnt + 1; 331 memory <= {memory[6:0],sda}; 332 state <= 15; 333 end 334 else 335 begin 336 if ((scl == 0) && (cnt == 8)) 337 begin 338 cnt <= 0; 339 flag <= 1;//获得总线控制权 340 state <= 16; 341 sda_buffer <= 1;//发送应答信号 342 end 343 else 344 state <= 15; 345 end 346 end 347 16 : begin 348 data_out <= memory;//输出数据 349 state <= 17; 350 end 351 17 : begin 352 if (scl == 0) 353 begin 354 sda_buffer <= 0;//拉低iic的数据线(为发送停止信号做准备) 355 state <= 18; 356 end 357 else 358 state <= 17; 359 end 360 18 : begin //发送停止信号 361 if (scl == 1) 362 begin 363 sda_buffer <= 1; 364 state <= 0; 365 end 366 else 367 state <= 18; 368 end 369 370 default : state <= 0 ; 371 endcase 372 end 373 374 endmodule |
第112行,将读写信号保存起来是为了读写的时候只进行一次的读写。(按键按下的时间一般大于20ms,如果不进行处理,系统将重复的向同一个地址中写入或者读出数据)。
我们把13位地址给定了一个确定的数据,并没有通过外部发送给系统,这样可以降低我们理解的难度。有兴趣的同学可以自己尝试一下其他的地址控制方式。
上述代码在下板实测时可用,仿真时则不能用。在我们的设计中,多次检测应答信号,但是仿真过程中没有真实的器件反馈应答信号,就会导致我们的仿真出错。仿真时应把所有的应答信号直接跳过,如下:
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function: 产生iic总线的控制信号 *****************************************************/ 000 module iic( 001 clk, //外部输入时钟 002 rst_n,//系统复位 003 key_wr,//写信号(低电平有效) 004 key_rd, //读信号(低电平有效) 005 data_in, //输入数据 006 scl, //iic的数据线 007 sda, //iic的时钟线 008 data_out//输出数据 009 ); 010 //系统输入 011 input clk;//外部输入时钟 012 input rst_n;//系统复位 013 input key_wr;//写信号(低电平有效) 014 input key_rd;//读信号(低电平有效) 015 input [7:0] data_in;//输入数据 016 //系统输出 017 output reg scl;//iic的时钟线 018 output reg [7:0] data_out;//输出数据 019 020 inout sda; //iic的数据线 021 022 reg sda_buffer;//写入数据的中间寄存器 023 reg flag;//控制系统是否占有总线控制权 024 025 assign sda = (flag) ? sda_buffer : 1'bz;//当flag为高电平时,系统拥有总线控制权 026 //并发送sda_buffer中的数据。当flag为低电平时, 027 //释放总线。 028 029 reg [7:0] count;//计数器 030 reg clk_sys;//系统时钟 031 //-------------------------------clk_sys 032 always @ (posedge clk or negedge rst_n) 033 begin 034 if (!rst_n) 035 begin 036 clk_sys <= 1'b0; 037 count <= 8'd0; 038 end 039 else 040 if (count < 31)//分频成为近800K的时钟 041 count <= count + 1; 042 else 043 begin 044 count <= 8'd0; 045 clk_sys <= ~clk_sys; 046 end 047 end 048 //------------------------------- 049 050 reg [5:0] state;//状态寄存器 051 052 //--------------------------------scl 053 always @ (negedge clk_sys or negedge rst_n) 054 begin 055 if (!rst_n) 056 begin 057 scl <= 1'b1;//复位时,scl为高 058 end 059 else 060 begin 061 if (state > 0)//当总线忙的时候,scl为近400K的时钟 062 scl <= ~scl; 063 else 064 scl <= 1'b1;//空闲时,scl为高 065 end 066 end 067 //---------------------------------- 068 069 reg [1:0] en;//读写使能中间寄存器 070 071 //----------------------------------enable 072 always @ (posedge clk or negedge rst_n) 073 begin 074 if (!rst_n) 075 begin 076 en <= 2'b00;//复位时,将中间寄存器置0; 077 end 078 else 079 begin 080 if (!key_wr)//写有效时 081 en <= 2'b01; 082 else 083 if (!key_rd)//写无效,读有效时 084 en <= 2'b10; 085 end 086 end 087 //--------------------------------- 088 089 reg [3:0] cnt;//发送或者接收数据的个数 090 reg [1:0] temp;//读写使能的中间寄存器 091 reg [7:0] memory;//发送或者接受数据的中间寄存器 092 093 always @ (posedge clk_sys or negedge rst_n) 094 begin 095 if (!rst_n) begin 096 cnt <= 0; 097 data_out <= 8'd0; 098 flag <= 1'b1; //复位时,系统获得总线的控制权 099 sda_buffer <= 1'b1; //向iic的数据线上发送高电平 100 state <= 0; 101 temp <= 2'b00; 102 end 103 else 104 case(state) 105 0 : begin 106 if(scl) 107 begin 108 if(en != temp)//有按键按下 109 begin 110 sda_buffer <= 1'b0;//发送启动信号 111 state <= 1; 112 temp <= en;//将读写信号保存 113 memory <= 8'b10100000;//控制字 114 end 115 else 116 state <= 0; 117 end 118 else 119 state <= 0; 120 end 121 122 1 : begin 123 if((scl == 0) && (cnt < 8))//发送八位控制字 124 begin 125 sda_buffer <= memory[7]; 126 cnt <= cnt + 1; 127 memory = {memory[6:0],memory[7]}; 128 state <= 1; 129 end 130 else 131 begin 132 if ((scl == 0) && (cnt == 8)) 133 begin 134 cnt <= 0; 135 flag <= 0;//释放总线控制权 136 state <= 2; 137 end 138 else 139 begin 140 state <= 1; 141 end 142 end 143 end 144 2 : begin 145 // if(!sda)//检测应答信号 146 // begin 147 state <= 3; 148 memory <= 8'd0;//高字节地址 149 // end 150 // else 151 // begin 152 // state <= 0; 153 // end 154 end 155 3 : begin //发送高字节地址 156 if((scl == 0) && (cnt < 8)) 157 begin 158 flag <= 1;//获得总线控制权 159 sda_buffer <= memory[7]; 160 cnt <= cnt + 1; 161 memory = {memory[6:0],memory[7]}; 162 state <= 3; 163 end 164 else 165 begin 166 if ((scl == 0) && (cnt == 8)) 167 begin 168 cnt <= 0; 169 flag <= 0;//释放总线控制权 170 state <= 4; 171 end 172 else 173 begin 174 state <= 3; 175 end 176 end 177 end 178 4 : begin 179 // if(!sda)//检测应答信号 180 // begin 181 state <= 5; 182 memory <= 8'h00;//低字节地址 183 // end 184 // else 185 // begin 186 // state <= 0; 187 // end 188 end 189 5 : begin 190 if((scl == 0) && (cnt < 8))//发送低字节地址 191 begin 192 flag <= 1;//获得总线控制权 193 sda_buffer <= memory[7]; 194 cnt <= cnt + 1; 195 memory = {memory[6:0],memory[7]}; 196 state <= 5; 197 end 198 else 199 begin 200 if ((scl == 0) && (cnt == 8)) 201 begin 202 cnt <= 0; 203 flag <= 0;//释放总线控制权 204 state <= 6; 205 end 206 else 207 begin 208 state <= 5; 209 end 210 end 211 end 212 6 : begin 213 // if(!sda)//检测应答信号 214 // begin 215 if (temp == 2'b01)//判断是否为写信号 216 begin 217 state <= 7; 218 memory <= data_in[7:0];//发送数据 219 end 220 if (temp == 2'b10)//判断是否为读信号 221 state <= 11; 222 // end 223 // else 224 // begin 225 // state <= 0; 226 // end 227 end 228 229 7 : begin 230 if((scl == 0) && (cnt < 8))//发送数据 231 begin 232 flag <= 1;//获得总线控制权 233 sda_buffer <= memory[7]; 234 cnt <= cnt + 1; 235 memory <= {memory[6:0],memory[7]}; 236 state <= 7; 237 end 238 else 239 begin 240 if ((scl == 0) && (cnt == 8)) 241 begin 242 cnt <= 0; 243 flag <= 0;//释放总线控制权 244 state <= 8; 245 end 246 else 247 begin 248 state <= 7; 249 end 250 end 251 end 252 8 : begin 253 // if(!sda)//检测应答信号 254 // begin 255 state <= 9; 256 // end 257 // else 258 // begin 259 // state <= 0; 260 // end 261 end 262 9 : begin 263 if (scl == 0) 264 begin 265 flag <= 1;//获得总线控制权 266 sda_buffer <= 0;//拉低iic的数据线(为发送停止信号做准备) 267 state <= 10; 268 end 269 else 270 state <= 9; 271 end 272 10 : begin 273 if (scl == 1) 274 begin 275 sda_buffer <= 1;//发送停止信号 276 state <= 0; 277 end 278 else 279 state <= 10; 280 end 281 //----------------------------------------- 282 283 //------- 284 11 : begin 285 flag <= 1;//获得总线控制权 286 sda_buffer <= 1;//拉高iic的数据线(为发送启动信号做准备) 287 state <= 12; 288 end 289 12 : begin 290 sda_buffer <= 0;//发送启动信号 291 state <= 13; 292 memory <= 8'b10100001; //控制字 293 end 294 13 : begin 295 if((scl == 0) && (cnt < 8))//发送八位控制字 296 begin 297 flag <= 1;//获得总线控制权 298 sda_buffer <= memory[7]; 299 cnt <= cnt + 1; 300 memory <= {memory[6:0],memory[7]}; 301 state <= 13; 302 end 303 else 304 begin 305 if ((scl == 0) && (cnt == 8)) 306 begin 307 cnt <= 0; 308 flag <= 0;//释放总线控制权 309 state <= 14; 310 end 311 else 312 begin 313 state <= 13; 314 end 315 end 316 end 317 14 : begin 318 // if(!sda)//检测应答信号 319 // begin 320 state <= 15; 321 // end 322 // else 323 // begin 324 // state <= 0; 325 // end 326 end 327 15 : begin 328 if((scl == 1) && (cnt < 8))//接收数据 329 begin 330 cnt <= cnt + 1; 331 memory <= {memory[6:0],sda}; 332 state <= 15; 333 end 334 else 335 begin 336 if ((scl == 0) && (cnt == 8)) 337 begin 338 cnt <= 0; 339 flag <= 1;//获得总线控制权 340 state <= 16; 341 sda_buffer <= 1;//发送应答信号 342 end 343 else 344 state <= 15; 345 end 346 end 347 16 : begin 348 data_out <= memory;//输出数据 349 state <= 17; 350 end 351 17 : begin 352 if (scl == 0) 353 begin 354 sda_buffer <= 0;//拉低iic的数据线(为发送停止信号做准备) 355 state <= 18; 356 end 357 else 358 state <= 17; 359 end 360 18 : begin //发送停止信号 361 if (scl == 1) 362 begin 363 sda_buffer <= 1; 364 state <= 0; 365 end 366 else 367 state <= 18; 368 end 369 370 default : state <= 0 ; 371 endcase 372 end 373 374 endmodule |
仿真代码
/**************************************************** * Engineer : 梦翼师兄 * QQ : 761664056 * The module function:iic测试模块 *****************************************************/ 00 `timescale 1ns/1ps 01 02 module iic_tb; 03 //系统输入 04 reg clk;//外部输入时钟 05 reg rst_n;//系统复位 06 reg key_wr;//写信号(低电平有效) 07 reg key_rd;//读信号(低电平有效) 08 reg [7:0] data_in;//输入数据 09 //系统输出 10 wire scl;//iic的时钟线 11 wire [7:0] data_out;//输出数据 12 wire sda; //iic的数据线 13 14 iic iic_inst( 15 .clk(clk), //外部输入时钟 16 .rst_n(rst_n), //系统复位 17 .key_wr(key_wr), //写信号(低电平有效) 18 .key_rd(key_rd), //读信号(低电平有效) 19 .scl(scl), //iic的时钟 20 .sda(sda), //iic的数据线 21 .data_in(data_in),//输入数据 22 .data_out(data_out)//输出数据 23 ); 24 25 initial begin 26 clk = 1; 27 rst_n = 0; 28 key_wr = 1; 29 key_rd = 1; 30 data_in = 0; 31 #1000.1 rst_n = 1; 32 # 800 33 #8000 key_wr = 0;//写信号有效 34 data_in = 8'h23;//输入数据为8’h23 35 #40000 key_wr = 1;//写信号释放 36 37 #1000000 38 key_rd = 0;//读信号有效 39 #40000 key_rd = 1;//读写号释放 40 end 41 42 always #10 clk=~clk; //50M的时钟 43 44 endmodule |
仿真分析
- 发送启动信号
- 发送控制字
- 接收并检测EEPROM发来的应答信号ACK
- 发送高字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送低字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送8bit有效数据
- 接收并检测EEPROM发来的应答信号ACK
- 发送停止信号
经过一步一步的查看,我们的设计时序是正确的。
- 发送启动信号
- 发送控制字1010_0000
- 接收并检测EEPROM发来的应答信号ACK
- 发送高字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送低字节地址位
- 接收并检测EEPROM发来的应答信号ACK
- 发送启动信号
- 发送控制字1010_0001
- 接收并检测EEPROM发来的应答信号ACK
- 读取一个字节数据
- 发送NO ACK信号
- 发送停止信号
读写的波形与iic原理中的读写时序图一致,证明我们的设计是正确的。