设计背景:
IIC简单来说,就是一种串行通信协议,IIC 的通信协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行 AD,图像处理领域的摄像头配置,工业控制领域的 X 射线管配置等等。除此之外,由于 IIC协议占用的 IO 资源特别少,连接方便,所以工程中也常选用 IIC 接口做为不同芯片间的通信协议。
设计原理:
IIC电路原理图如下:
24LC64各引脚定义:
1、A0,A1,A2 为 24LC64 的片选信号,由于 IIC 总线可以挂载多个 IIC 接口器件,所以每个器件都应该有自己的“身份标识”,通过对 A0,A1,A2 输入不同的高低电平,就可以设置该 EEPROM 的片选信号。
2、WP 为读写使能信号,当 WP 悬空或者接地,EEPROM 可读可写,当 WP 接电源,EEPROM 只能读不能写。
3、SCL为 IIC 接口的时钟线。
4、SDA为 IIC 接口的数据线。
IIC 接口的读写时序:
IIC接口读写时序分为随机读写(单字节读写)和页面读写(多字节读写),先分析随机读写(Byte Write/Read)时序。
Byte Write 时序如下:
时序解读:如果我们要向 EEPROM 写入一个字节,那么必须经过以下步骤:
1. 发送启动信号
2. 发送控制字
4. 发送高字节地址位
5. 接收并检测 EEPROM 发来的应答信号 ACK
6. 发送低字节地址位
7. 接收并检测 EEPROM 发来的应答信号 ACK
8. 发送 8bit 有效数据
9. 接收并检测 EEPROM 发来的应答信号 ACK
10.发送停止信号
Byte Read 时序如下:
时序解读:如果我们要从 EEPROM 读出一个字节,那么必须经过以下步骤:
1. 发送启动信号
2. 发送控制字 1010_A2A1A0_0
3. 接收并检测 EEPROM 发来的应答信号 ACK
4. 发送高字节地址位
5. 接收并检测 EEPROM 发来的应答信号 ACK
6. 发送低字节地址位
7. 接收并检测 EEPROM 发来的应答信号 ACK
8. 发送启动信号
9. 发送控制字 1010_A2A1A0_1
10. 接收并检测 EEPROM 发来的应答信号 ACK
11. 读取一个字节数据
12. 发送 NO ACK 信号
13. 发送停止信号
接下来则需要分析各步骤具体意义:
1.启动信号
在 SCL 保持高电平期间,如果 SDA 出现由高到低的跳变沿,代表启动信号。
2. 控制字
我们的控制字为 1010_0000,其中 1010 为 EEPROM 的型号标识,为一组固定的序列,紧接着 A2,A1,A0 就是我们的片选信号,最后一位为读写控制位,低电平代表写,高电平代表读,我们这里首先需要对 EEPROM 写入地址位,所以我们最后一位为 0。
3. 高/低位地址
由于 24LC64 有 64Kbit 的存储空间,所以我们需要 13 位的地址位宽才能寻址所有的存储空间,由于 IIC 协议规定只能以字节形式写入,所以必须将 13 位的地址扩展为 16 位的地址,分为高八位和低八位,多出来的前三位填充任意数据即可,对我们的寻址地址没有影响。
4. 停止信号
5. 应答信号 ACK
应答信号是由数据接收方发出的,当 SCL 为高电平期间,如果监测到 SDA为低电平,说明有应答信号。
6. 非应答信号 NO ACK
非应答信号也是由数据接收方发出的,当 SCL 为高电平期间,如果 SDA 为高电平,说明有非应答信号。
说明:由于 IIC 总线协议启动和停止信号都是在 SCL 高电平期间发生跳变,这就决定了我们其他数据的改变只能发生在 SCL 低电平期间,在 SCL 为高电平期间,数据必须保持稳定。即在 SCL 低电平改变数据,在 SCL 高电平采集数据。
相比于单字节读写,页面读写只是增加了几个状态,具体时序如下,这里和后面的设计代码不做详细论述。
Page Write 时序如下:
Page Read 时序如下:
设计架构图:
本设计用两个按键控制 EEPROM 读写,当写按键按下时,向 EEPROM 某一固定地址写入一个字节数据,当读按键按下时,将该地址数据读出,并显示到数码管,LED 灯是一个标志信号,LED 亮说明数据写入完毕。设计架构如下:
设计代码:
iic_wr模块代码:负责进行IIC数据的读写
0moduleiic_wr (
1clk_sys,//系统时钟
2rst_n,//系统复位
3eeprom_scl,//eeprom 串行时钟信号
4eeprom_sda,//eeprom 串行数据信号
5key_wr,//外部写控制按键
6key_rd,//外部读控制按键
7result,//数据采集结果寄存器
8led //led指示灯
9);
10
11inputclk_sys;
12inputrst_n;
13inputkey_wr;
14inputkey_rd;
15
16outputregeeprom_scl;
17inouteeprom_sda;
18outputregled;
19regclk;
20reg[7:0]cnt;//分频计数器
21reg[7:0]state;//状态寄存器
22reg[3:0]counter;//数据移位计数器
23reglink_sda;//总线开关
24regwr;//写标志寄存器
25regrd;//读标志寄存器
26regsda_buf;//总线数据缓存器
27outputreg[7:0]result;
28reg[7:0]data;//待发送控制字、地址、数据寄存器
29assigneeprom_sda=(link_sda)?sda_buf:1'hz;
30
31//-----------------system clk----------------
32//系统时钟分频
33always@(posedgeclk_sys ornegedgerst_n)
34begin
35if(!rst_n)
36begin
37clk<=0;
38cnt<=0;
39end
40else
41begin
42if(cnt<250)
43cnt<=cnt+1'b1;
44else
45begin
46clk<=~clk;
47cnt<=0;
48end
49end
50end
51
52//-----------------eeprom scl-----------------
53//产生eeprom scl信号
54always@(negedgeclk ornegedgerst_n)
55begin
56if(!rst_n)
57begin
58eeprom_scl<=0;
59end
60else
61eeprom_scl<=~eeprom_scl;
62end
63
64//----------------- eeprom contral-----------
65always@(posedgeclk ornegedgerst_n)
66begin
67if(!rst_n)//所有寄存器复位
68begin
69state<=0;
70link_sda<=0;
71sda_buf<=0;
72counter<=0;
73wr<=0;
74led<=1;
75rd<=0;
76result<=0;
77data<=0;
78end
79else
80begin
81case(state)
82//--------------send start singial-------------
830:begin
84if(!key_wr)//检测外部写控制按键是否按下
85wr<=1;
86if(!key_rd)//检测外部读控制按键是否按下
87rd<=1;
88
89if(((rd==1)||(wr==1))&&(!eeprom_scl))
90begin
91link_sda<=1;
92sda_buf<=1;
93state<=1;
94end
95end
961:begin
97if(eeprom_scl)//高电平期间,使sda由高变低,启动串行传输
98begin
99sda_buf<=0;
100state<=2;
101data<=8'b10100000;//写控制字准备
102end
103end
104//--------------send countral word--------------
1052:begin
106if((counter<8)&&(!eeprom_scl))//在scl低电平期间,完成并串转换,发出写控制字
107begin
108counter<=counter+1'b1;
109data<={data[6:0],data[7]};
110sda_buf<=data[7];
111end
112elseif((counter==8)&&(!eeprom_scl))
113begin
114counter<=0;
115state<=3;
116link_sda<=0;//FPGA释放总线控制权
117end
118end
119//--------------receive ack singial--------------
1203:begin
121if(eeprom_scl)//在scl高电平期间,检测是否有应答信号
122begin
123if(!eeprom_sda)
124begin
125state<=4;//有应答则状态继续跳转
126data<=8'b00000000;//高字节地址准备
127end
128end
129end
130//--------------send high Byte address--------------
1314:begin
132link_sda<=1;//FPGA控制总线
133if((counter<8)&&(!eeprom_scl))//在scl低电平期间,完成并串转换,发出高字节地址
134begin
135counter<=counter+1'b1;
136data<={data[6:0],data[7]};
137sda_buf<=data[7];
138end
139elseif((counter==8)&&(!eeprom_scl))
140begin
141counter<=0;
142state<=5;
143link_sda<=0;//FPGA释放总线控制权
144end
145end
146//--------------receive ack singial--------------
1475:begin
148if(eeprom_scl)//在scl高电平期间,检测是否有应答信号
149begin
150if(!eeprom_sda)
151begin
152state<=6;//有应答则状态继续跳转
153data<=8'b00000011;//低字节地址准备
154end
155end
156end
157//--------------send low Byte address--------------
1586:begin
159link_sda<=1;//FPGA控制总线
160if((counter<8)&&(!eeprom_scl))//在scl低电平期间,完成并串转换,发出低字节地址
161begin
162counter<=counter+1'b1;
163data<={data[6:0],data[7]};
164sda_buf<=data[7];
165end
166elseif((counter==8)&&(!eeprom_scl))
167begin
168counter<=0;
169state<=7;
170sda_buf<=1;
171link_sda<=0;//FPGA释放总线控制权
172end
173end
174//--------------receive ack singial--------------
1757:begin
176if(eeprom_scl)//在scl高电平期间,检测是否有应答信号
177begin
178if(!eeprom_sda)
179begin
180if(wr==1)//如果是写的话,跳到状态8,遵循随机写时序
181state<=8;
182if(rd==1)//如果是读的话,跳到状态11,遵循随机读时序
183begin
184state<=11;
185sda_buf<=1;//准备再次发启动信号
186end
187data<=8'b00001111;//准备想要写入的数据
188end
189end
190end
191//--------------send active data-------------
1928:begin
193link_sda<=1;//FPGA控制总线
194if((counter<8)&&(!eeprom_scl))//在scl低电平期间,完成并串转换,发出有效数据
195begin
196counter<=counter+1'b1;
197data<={data[6:0],data[7]};
198sda_buf<=data[7];
199end
200elseif((counter==8)&&(!eeprom_scl))
201begin
202counter<=0;
203state<=9;
204link_sda<=0;//FPGA释放总线控制权
205end
206end
207//--------------receive ack singial--------------
2089:begin
209if(eeprom_scl)//在scl高电平期间,检测是否有应答信号
210begin
211if(!eeprom_sda)//有应答则状态继续跳转
212state<=10;
213end
214end
215//--------------send stop singial-------------
21610:begin
217link_sda<=1;//FPGA控制总线
218sda_buf<=0;//拉低sda,准备发出停止信号
219if(eeprom_scl)//在scl高电平期间,拉高sda,终止串行传输
220begin
221led<=0;//点亮led,说明写操作完毕
222sda_buf<=1;
223if(key_wr &&key_rd)//在按键放开以后才跳转回空闲状态,避免不断循环写入
224state<=0;//状态跳回
225wr<=0;//清除写控制标志
226end
227end
228//------------send start singial-----------
22911:begin
230link_sda<=1;//FPGA控制总线
231if(eeprom_scl)//scl高电平期间拉低sda,发送启动信号
232begin
233sda_buf<=0;
234state<=12;
235data<=8'b10100001;//读控制字准备
236end
237end
238//------------send countral word------------
23912:begin
240if((counter<8)&&(!eeprom_scl))//在scl低电平期间,完成并串转换,发出读控制字
241begin
242counter<=counter+1'b1;
243data<={data[6:0],data[7]};
244sda_buf<=data[7];
245end
246elseif((counter==8)&&(!eeprom_scl))
247begin
248counter<=0;
249state<=13;
250link_sda<=0;//FPGA释放总线控制权
251end
252end
253//--------------receive ack singial--------------
25413:begin
255if(eeprom_scl)//在scl高电平期间,检测是否有应答信号
256begin
257if(!eeprom_sda)//有应答则状态继续跳转
258state<=14;
259end
260end
261//--------------receive input active data--------------
26214:begin
263if((counter<8)&&(eeprom_scl))//在scl高电平期间,完成串并转换,存储接收数据
264begin
265counter<=counter+1'b1;
266result[7-counter]<=eeprom_sda;
267end
268elseif(counter==8)
269begin
270counter<=0;
271state<=15;
272sda_buf<=1;
273link_sda<=1;//接收完毕以后FPGA继续控制总线
274end
275end
276//--------------send NO ACK singial--------------
27715:begin
278if(eeprom_scl)//在scl高电平期间,将sda总线拉高,发出非应答信号
279begin
280sda_buf<=1;
281state<=16;
282end
283end
284//--------------send stop singial-------------
28516:begin
286if(!eeprom_scl)//在scl低电平期间,将sda总线拉低,准备发送停止信号
287sda_buf<=0;
288if(eeprom_scl)//在scl高电平期间,将sda总线拉高,发出停止信号
289begin
290sda_buf<=1;//拉高sda
291state<=0;//状态回转
292rd<=0;//清除读标志信号
293end
294end
295default:state<=0;
296endcase
297end
298end
299endmodule
seg7_lut模块代码,负责数码管显示
0moduleseg7_lut (
1oseg,//数码管段选
2idig,//驱动数据
3clk_sys,//系统时钟
4sel,//数码管位选
5rst_n //系统复位
6);
7//--------输入信号--------
8input[7:0]idig;
9inputclk_sys;
10inputrst_n;
11//--------输出信号--------
12outputreg[7:0]oseg;
13outputreg[2:0]sel;
14//--------中间变量--------
15reg[3:0]cnt;
16//-----------------系统时钟分频-----------------
17always@(posedgeclk_sys ornegedgerst_n)
18begin
19if(!rst_n)
20begin
21sel<=3'd0;//数码管段选定位
22end
23end
24
25//--------数码管显示译码--------
26always@(*)
27case(idig)
288'h0:oseg<=8'hC0;
298'h1:oseg<=8'hF9;
308'h2:oseg<=8'hA4;
318'h3:oseg<=8'hB0;
328'h4:oseg<=8'h99;
338'h5:oseg<=8'h92;
348'h6:oseg<=8'h82;
358'h7:oseg<=8'hF8;
368'h8:oseg<=8'h80;
378'h9:oseg<=8'h90;
388'hA:oseg<=8'h88;
398'hB:oseg<=8'h83;
408'hC:oseg<=8'hC6;
418'hD:oseg<=8'hA1;
428'hE:oseg<=8'h86;
438'hF:oseg<=8'h8E;
44endcase
45
46endmodule
IIC顶层模块代码:
0moduleIIC (
1clk_sys,
2rst_n,
3eeprom_scl,
4eeprom_sda,
5key_wr,
6key_rd,
7oseg,
8sel,
9led
10);
11//--------输入端口--------
12inputclk_sys;
13inputrst_n;
14inputkey_wr;
15inputkey_rd;
16//--------输出端口--------
17outputeeprom_scl;
18output[7:0]oseg;
19output[2:0]sel;
20outputled;
21//--------双向总线--------
22inouteeprom_sda;
23//--------内部连线--------
24wire[7:0]result;
25//--------实例化eeprom控制模块--------
26iic_wr iic_wr (
27.clk_sys(clk_sys),
28.rst_n(rst_n),
29.eeprom_scl(eeprom_scl),
30.eeprom_sda(eeprom_sda),
31.key_wr(key_wr),
32.key_rd(key_rd),
33.result(result),
34.led(led)
35);
36//--------实例化数码管显示驱动模块--------
37seg7_lut seg7_lut (
38.oseg(oseg),
39.idig(result),
40.clk_sys(clk_sys),
41.sel(sel),
42 .rst_n(rst_n)
43);
44
45endmodule
tb顶层测试模块代码:
0`timescale1ns/1ns
1
2moduletb;
3
4regclk_sys;
5regrst_n;
6regkey_wr;
7regkey_rd;
8
9wireeeprom_scl;
10
11wireeeprom_sda;
12wire[7:0]oseg;
13wire[2:0]sel;
14wireled;
15
16initial
17begin
18clk_sys=0;
19key_rd=1;
20rst_n=0;
21#1000rst_n=1;
22#500key_wr=0;
23#3000key_wr=1;
24#400000key_rd=0;
25#300key_rd=1;
26end
27
28always#10clk_sys=~clk_sys;
29
30IIC IIC(
31.clk_sys(clk_sys),
32.rst_n(rst_n),
33.eeprom_scl(eeprom_scl),
34.eeprom_sda(eeprom_sda),
35.key_wr(key_wr),
36.key_rd(key_rd),
37.oseg(oseg),
38.sel(sel),
39.led(led)
40);
41
42endmodule
仿真图:
随机读写,仿真写时序:
随机读写,仿真读时序:
在仿真时,需要将检测应答的状态跳过,直接向下一状态跳转,观察读写时序,当读写按键按下时,都会产生对应的动作。
-
FPGA
+关注
关注
1625文章
21648浏览量
601473
发布评论请先 登录
相关推荐
评论