本帖最后由 mengyi1989 于 2019-12-7 13:15 编辑
写在前面的话
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时序如下:
由此可以看出,页面读写比随机读写只是多加了几个状态而已,在我们前面设计的基础上加几个状态就可以完成。梦翼师兄把这部分交给大家去发挥。
架构设计
根据原理分析,我们设计出架构图如下:
模块功能介绍
顶层模块端口描述
端口名
| 端口说明
| 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原理中的读写时序图一致,证明我们的设计是正确的。
|