一、IIC的时序很简单,主要有:
(1)起始信号S:是在SCL信号为高电平期间,信号SDA由1变为0,代表着数据开始要传输,注意:SCL信号和SDA信号在空闲的时候为高电平。
(2)停止信号P:是在SCL信号为高电平期间,信号SDA由0变为1,代表着数据传输完成;
(3)数据传输:在 I2C 总线上传送的每一位数据都有一个时钟脉SCL冲相对应。在对数据传送时,在 SCL 高电平期间,SDA 上的电平必项保持稳定,低电平为数据 0,高电平为数据 1。只有在 SCL 为低电平期间,才允讲 SDA 上的电平改变状态。
重点:(4)应答信号ACK与非应答信号NACK:I2C 总线上的所有数据都是以 8 位字节传送的,发送器(FPGA)每发送一个字节,就在时钟脉冲 9 期间释放数据线(也就是将SDA信号的传输方向改变为输入),由接收器(EEPROM)反馈一个应答信号ACK,为低电平时是有效应答位,表示接收器已经成功地接收了该字节,接收器(EEPROM)在第9 个时钟脉冲之前的低电平期间将SDA 线拉低,并且确保在第9个时钟脉的高电平期间为稳定的低电平。
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 如果接收器(EEPROM)是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器(FPGA)结束数据发送,并释放 SDA 线,以便接收器(FPGA)发送一个停止信号。
下面是关于IIC 的时序图,采用的是24LC04B的EEPROM芯片。
图一
二、关于IIC的几种读写
自己主要是采用的是:单字节写,随机读
图二是关于读写的方式:
图二
三、在写代码时所遇到的问题:
1、自己看了特权的关于IIC的代码与黑金资料的IIC的代码,马上就遇到第一个问题:关于IIC的传输的速率问题:在网络上和datasheet上都给了两种传输速率,标准速率100kb/s和快速速率400kb/s。还以为IIC只是有这两种速率而已,但是看了黑金的资料后,发现IIC的速率不仅仅有这两种,原来100kb/s是最高的传输速率,只要比这个小就可以,比如200kb/s。只是刚才说的这两种速率是通用的规定。所以明白了IIC的速率也是可以自己设定的,不止标准速率100kb/s和快速速率400kb/s这两种。
2、端口的信号:
(1)output scl,是采用对SCL信号进行分段写成的,如
reg [9:0] cnt;
reg [2:0] cnt_clk;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt <= 10'd0;
end
else if( cnt == 10'd499)begin
cnt <= 10'd0;
end
else begin
cnt <= cnt + 1'b1;
end
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt_clk <= 3'd0;
end
else begin
case(cnt)
10'd124:cnt_clk <= 3'd1;
10'd249:cnt_clk <= 3'd2;
10'd374:cnt_clk <= 3'd3;
10'd499:cnt_clk <= 3'd0;
default:cnt_clk <= 3'd5;
endcase
end
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
scl_r <= 1'b0;
end
else if(cnt_clk == 3'd0)begin
scl_r <= 1'b1;
end
else if(cnt_clk == 3'd2)begin
scl_r <= 1'b0;
end
assign scl = scl_r;
`define SCL_POS (cnt_clk == 3'd0)
`define SCL_HIG (cnt_clk == 3'd1)
`define SCL_NEG (cnt_clk == 3'd2)
`define SCL_LOW (cnt_clk == 3'd3)
(2)inout sda,由于SDA是双向端口,则使用assign sda = sda_link ? sda_r : 1'bz;来实现数据的传输,其中sda_link 是控制SDA是作为输出(sda_link 为1,输出的是sda_r 的数据)还是输入(sda_link 为0,)。
(3)input [1:0] key;作为控制IIC的写与读的控制信号。自己也在这栽了跟头,由于参考的是特权的代码,自己稍微做了改动,将前后两次的按键值取反相与,为了使检测有按键所按下,输出的信号其实是检验下降沿的一个高脉冲,但是在后面的状态机仍使用该输出的信号,但此时的输出信号以为低电平,所以会返回到初始状态。
(4)output [3:0];作为检验读取信号是否正确的方法。
3、关于ACK信号:
(1)信号的方向:代码写完之后一直发现怎么就是不返回ACK信号呢,一直觉得自己代码没有错,从chipscope所看到的信号就是为低,怎么判断 就是不为低呢,后来经同学指点,才发现,自己在写关于ACK的状态时,没有将sda信号作为判断条件,而是将sda_r作为判断条件,所以总是返回到初始状态。下面就是关于ACK的状态机,红色就是所错误的地方。
ACK1: begin
sda_link <= 1'b0;
if(`SCL_HIG)begin
if(sda== 1'b0)begin //之间将sda写成了sda_r,造成一直返回到IDLE状态
state <= RAM_ADDRESS;
temp_data <= ADDRESS;
end
else begin
state <= IDLE;
end
end
else begin
state <= ACK1;
end
end
(2)ACK谁给的问题:(特权的代码中没有,给忽略了)
在进行写的操作很简单,明白ACK信号都是由EEPROM给FPGA,只要在每发送8位的字节后,在高电平期间,若EEPROM将sda信号拉低,说明就有应答信号。
在进行读操作的时候,在进行读取 数据之前,还有一个ACK信号,别忘记了,自己就把这个ACK信号忘记了,之后读取完数据之后会有个NACK信号,那么这个信号是FPGA给EEPROM的。(很容易理解错误,以为都是EEPROM所给的呢)
4、SCL的时钟
一定要记住:数据的改变只能在SCL信号的低电平期间,在SCL高电平器件保持平稳。如在SCL高电平期间有数据改变很容易被认为是起始信号或是停止信号。
5、代码问题
自己主要采用的特权的IIC时序代码,让我更 容易理解;而黑金资料中的IIC时序代码,自己将时序图在纸上画出来才明白怎么一回事,这个看个人理解吧。
最后将自己的代码上传上来,希望能帮助像我可能遇到问题的 小伙伴。