标签:
一、IIC总线协议特点及其工作原理
I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
1)I2C总线特点
I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。
I2C总线的另一个优点是,它支持多主控(multimastering),
其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
2)I2C总线工作原理
I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率 100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程
中,I2C总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。CPU发出的控制信号分为地址码和控制
量两部分,地址码用来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对比度、亮度等)及需要调整的量。这样,各控制电路虽然挂
在同一条总线上,却彼此独立,互不相关。
3)总线的构成及信号类型
I2C总线在传送数据过程中共有三种类型信号,
它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单
元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。
4)I2C总线操作
I2C规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。
总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。
SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。
控制字节
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。
写操作
写操作分为字节写和页面写两种操作,对于页面写根据芯片的一次装载的字节不同有所不同。
读操作
读操作有三种基本操作:当前地址读、随机读和顺序读。
5)I2C总线应用
目前有很多半导体集成电路上都集成了I2C接口。带有I2C接口的单片机有:CYGNAL的 C8051F0XX系列,三星的S3C24XX系列,PHILIPSP87LPC7XX系列,MICROCHIP的PIC16C6XX系列等。很多外围器
件如存储器、监控芯片等也提供I2C接口。
二、IIC之EEPROM
1)下面是EEPROM(24LC64)不同封装情况。
数据手册中关于这八个引脚的介绍:
A0, A1,A2 :片选地址输入
SDA : 单bit数据线
SLC : 时钟线(200KHZ)
WP : 接地或者悬空时,可读可写,接电源VCC时,只读不可写。
注意:EEPROM(24LC64)工作的最大时钟为400KHZ,所以我们用系统50M时钟来分频一个400KHZ。
2)I2C总线时序图
控制字节
在起始条件之后,必须是器件的控制字节,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。示意图如下:
读控制字节: 1010_0001
写控制字节: 1010_0000
.
(3) IIC总线读写时序
图 随机写
图 页面写
图 随机读
图 页面读
写操作的时候就要考虑几个问题:
当ENB==1时,数据从FPGA到EEPROM,
当ENB==0时,数据从EEPROM到FPGA。
实现语句: assign sda = (flag==1) ? data :1’bz;
3.写时序的问题,要对照数据手册看到整个过程都有哪些,并捋清高低电平的问题,这里对前面几个状态进行详细阐述。
State0 启动信号,此时要搞清楚高低电平,在前边我介绍数据手册的时候,就说过了,当SCL为高电平的时候,sda此时也为高电平,此时应将flag三态开关打开,迎接数据的到来,其次sda线要拉低进入启动信号,同时给temp(数据缓存总线)赋控制字节
State1 控制字节的输入读取,我们通过阅读数据手册知道当SCL为高电平的时候为数据有效,所以只有在SCL为低电平的时候,对其进行赋值,数据发生改变,因为有8个字节,所以定义一个8位的cnt,进行判断是否数据已经输入完毕,数据的输入采用循环移位拼接的方法来实现,当移位完毕的时候,flag关闭,以迎接ack信号的到来,计数清零。
State2 应答信号(ack)的检测 接受完8bit数据后,会向IIC发送低电平脉冲,表示接收到了数据,此时时钟SCL为高电平的时候,SDA转为只读,表示接收到了ack信号。
State9 发送停止信号 scl为高时,让SDA由低到高表示一个停止信号。
在stop状态之前又加了一状态是为了给下一个状态让sda从低到高的上升沿信号,这样才能发送STOP信号。
其余的状态就是状态1和2的重复
module IIC_rw ( input wire sclk, input wire rst_n, output reg scl, inout wire sda ); reg [23:0] count; reg clk_400k; reg [7:0] state; reg flag; reg data; reg [7:0] temp; reg [7:0] cnt; always @(posedge sclk or negedge rst_n) begin if(!rst_n) begin count <=0; clk_400k <=0; end else begin if(count==62) begin count <=0; clk_400k <=~clk_400k; end else count <= count +1; end end always @(posedge clk_400k or negedge rst_n) begin if(!rst_n) scl <=1; else scl <=~scl; end assign sda = flag ==1 ? data :1‘bz; always @(negedge clk_400k or negedge rst_n) begin if(!rst_n) begin state <= 0; flag <= 1; data <= 1; temp <= 0; cnt <= 0; end else case (state) 0: if(scl) //启动信号 begin flag <=1; state <=1; data <=0; temp <=8‘b1010_0000; end 1: if(scl==0 && cnt<8) //控制信号 begin flag <=1; data <=temp[7]; cnt <= cnt +1; temp <= {temp[6:0],temp[7]};//左移位像最低位移动 end else if(scl==0&&cnt==8) begin flag <=0; state <=2; cnt <=0; end 2: if(scl==1) //ack begin // if(sda==0) begin state<=3; temp<=8‘b0000_0000; //高字节 end // else // state <=0; end 3: if(scl==0&&cnt<8)//高字节 begin flag <=1; data <=temp[7]; cnt <= cnt+1; temp <={temp[6:0],temp[7]}; end else if(scl ==0&&cnt==8) begin flag <=0; cnt <=0; state<=4; end 4: if(scl==1) //ack begin // if(sda==0) begin state<=5; temp<=8‘b0000_0000; //低字节 end // else // state <=0; end 5: if(scl==0&&cnt<8)//低字节输入 begin flag <=1; data <=temp[7]; cnt <= cnt+1; temp <={temp[6:0],temp[7]}; end else if(scl ==0&&cnt==8) begin flag <=0; cnt <=0; state<=6; end 6: if(scl==1) //ACK begin // if(sda==0) begin state<=7; temp<=8‘b0010_0101; //要输入的数据 end // else // state <=0; end 7:if(scl==0&&cnt<8) begin flag <=1; data <=temp[7]; cnt <= cnt+1; temp <={temp[6:0],temp[7]}; end else if(scl ==0&&cnt==8) begin flag <=0; cnt <=0; state<=8; end 8: if(scl==1) //ack begin // if(sda==0) begin state<=9; end // else // state <=0; end 9: if(scl==0)//high to low begin flag <=1; data <=0; state <=10; end 10: if(scl==1)//STOP begin flag <=1; data <=1; state <=10; end default :state<=0; endcase end endmodule
仿真波形:
共13个状态,所以可以通过状态机来书写读操作
涉及重点和难点:
NO ACK(非应答信号):
由上面随机读出的过程示意图就可以看到,NO ACK 信号是个高电平,只能由FPGA通过SDA 数据线向EEPROM 发送。
上一状态当SCL为低电平时,且数据读完后,SDA 开关打开(flag=1)data为高电平,在NOack状态下,SCL为低电平的时候,数据可读写,此时将data拉低,以备后来的STOP信号产生上升沿而用。
我们把NO ACK 和ACK 拉到一块
,就能联想出来,ACK 信号是EEPROM 收到八位数据后反馈给FPGA的信号,那么 NO ACK 信号就是 FPGA 读到八位数据后向EEPROM 发送的反馈信号。
module IIC_read (
input wire sclk,
input wire rst_n,
output reg scl,
inout wire sda,
output reg [7:0] result
);
reg [23:0] count;
reg clk_400k;
reg [7:0] state;
reg flag;
reg data;
reg [7:0] temp;
reg [7:0] cnt;
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
begin
count <=0;
clk_400k <=0;
end
else
begin
if(count==62)
begin
count <=0;
clk_400k <=~clk_400k;
end
else
count <= count +1;
end
end
always @(posedge clk_400k or negedge rst_n)
begin
if(!rst_n)
scl <=1;
else
scl <=~scl;
end
assign sda = flag ==1 ? data :1‘bz;
always @(negedge clk_400k or negedge rst_n)
begin
if(!rst_n)
begin
state <= 0;
flag <= 1;
data <= 1;
temp <= 0;
cnt <= 0;
result <= 0;
end
else
case (state)
0: if(scl) //启动信号
begin
flag <=1;
state <=1;
data <=0;
temp <=8‘b1010_0000;
result<=0;
end
1: if(scl==0 && cnt<8) //控制信号
begin
flag <=1;
data <=temp[7];
cnt <= cnt +1;
temp <= {temp[6:0],temp[7]};
end
else if(scl==0&&cnt==8)
begin
flag <=0;
state <=2;
cnt <=0;
end
2: if(scl==1) //ack
begin
if(sda==0)
begin
state<=3;
temp<=8‘b0000_0000; //高字节
end
else
state <=0;
end
3: if(scl==0&&cnt<8)//高字节
begin
flag <=1;
data <=temp[7];
cnt <= cnt+1;
temp <={temp[6:0],temp[7]};
end
else if(scl ==0&&cnt==8)
begin
flag <=0;
cnt <=0;
state<=4;
end
4: if(scl==1) //ack
begin
if(sda==0)
begin
state<=5;
temp<=8‘b0000_0000; //低字节
end
else
state <=0;
end
5: if(scl==0&&cnt<8)//低字节输入
begin
flag <=1;
data <=temp[7];
cnt <= cnt+1;
temp <={temp[6:0],temp[7]};
end
else if(scl ==0&&cnt==8)
begin
flag <=0;
cnt <=0;
state<=6;
end
6: if(scl==1) //ACK
begin
if(sda==0)
begin
state<=7;
end
else
state <=0;
end
7: if(scl==0) // 拉高sda 备启动信号使用 启动信号是在scl为高电平的同时, sda由高电平到低电平
begin
flag <=1;
state <=8;
data <=1;
temp <=8‘b1010_0001;
result<=0;
end
8: if(scl)//启动信号 高到低
begin
data<=0;
state<=9;
end
9: if(scl==0 && cnt<8) //控制信号
begin
flag <=1;
data <=temp[7];
cnt <= cnt +1;
temp <= {temp[6:0],temp[7]};
end
else if(scl==0&&cnt==8)
begin
flag <=0;
state <=10;
cnt <=0;
end
10: if(scl==1) //ACK
begin
if(sda==0)
begin
state<=11;
end
else
state <=0;
end
11: if(scl==1&&cnt<8)//读 读的时候为高电平
begin
flag <=0;
result <={result[6:0],sda};
cnt <=cnt+1;
end
else if(scl==0&&cnt==8)
begin
flag <=1;
cnt <=0;
state <=12;
data <=1;
end
12: if(scl==0) //NOack
begin
if(sda==1)
begin
state <=13;
data <=0;
end
else
state <=0;
end
13: if(scl==1)//STOP
begin
flag <=1;
data <=1;
state <=13;
end
// 14: if(scl==1)//进入非忙态
// begin
// flag <=1;
// data <=1;
// state <=14;
// end
default :state<=0;
endcase
end
endmodule
读信号时的波形图:
标签:
原文地址:http://www.cnblogs.com/zhouzheng/p/5766789.html