雾盈FPGA笔记(十二)IIC协议应用之二EEPROM随机读出
本帖最后由 雾盈 于 2016-8-28 13:41 编辑雾盈FPGA笔记(十二)
IIC协议应用之二EEPROM随机读出(random read)
雾盈 2016-8-5
雾盈FPGA笔记汇总目录
写在前面:
这是我写的第二篇关于IIC协议应用的帖子。在第一篇贴子里,我略详细的介绍了IIC 总线协议及其工作原理,并把IIC协议的基础应用—EEPROM 字写入(byte write)讲解了一下。
我把第一篇帖子的链接挂在下面,初学者或者不了解EEPROM 字写入(byte write)的读者必须看一下,因为这篇文章就是在那一篇的基础上来讲的。
雾盈FPGA笔记(十二)IIC协议应用之二EEPROM字写入(byte write)链接:
雾盈FPGA笔记(十一)IIC总线协议应用之一EEPROM字节写入
一、 EEPROM 随机读出(random read)
随机读出(random read)是相对于顺序读出(sequential read)来说的,这种读方式允许控制可以以随机的方式按照地址来读出IIC器件里对应地址的数据。这就好比,你拿着某一个号码的停车牌,去停车场里去取回你之前停在那里的车。
EEPROM 随机读出(random read)的工作过程 和字写入(byte write)的工作过程,有一大部分是相同的。
我把这两个工作过程的示意图贴出来对比一下就清楚了。示意图如下:1)
看过示意图对比之后,我们发现随机读出(random write)和字写入(byte write) 只在后面一小部分过程不一样,一个读,一个写。
那样我们只要理解了字写入的工作过程和时序,就很容易把字写入(random write)前面部分的代码移植过来。
所以,我只把剩余几个不同的地方着重说一下。
1. NO ACK(非应答信号):
由上面随机读出的过程示意图就可以看到,NO ACK 信号是个高电平,只能由FPGA通过SDA 数据线向EEPROM 发送。
当SCL为高电平时,SDA 开关打开输出为高电平之后,就表示发送了NO ACK 信号。
我们把NO ACK 和ACK 拉到一块 ,就能联想出来,ACK 信号是EEPROM 收到八位数据后反馈给FPGA的信号,那么 NO ACK 信号是不是就是 FPGA 读到八位数据后向EEPROM 发送的反馈信号。
2. 读数据过程
随机读出的读数据过程和字写入的写数据过程是相反的。
对比一下代码,立刻能体会出来。
写数据:
if( scl == 1'b0 && cnt < 8 )
begin
data_out <= temp;
flag1 <= 1'b1;
temp <= { temp,temp };
cnt <= cnt + 1'b1;
state <= 4'd7;
end
else if( scl == 1'b0 && cnt == 8 )
begin
flag1 <= 1'b0;
cnt <= 1'b0;
state <= 4'd8;
end
读数据:
if( scl == 1'b1 && cnt < 8 ) // scl high and flag1=0
begin
flag1 <= 1'b0;
temp <= { temp,sda }; // remember !!!
cnt <= cnt + 1'b1;
state <= 4'd10;
end
else if( scl == 1'b1 && cnt == 8 )
begin
flag1 <= 1'b1;
data_out <= 1'b0;
data_rd <= temp;
cnt <= 1'b0;
state <= 4'd11;
end
这两段代码中,多位数据与单位数据依靠寄存器左移进行传递的方法应该熟记下来,之前在写到这段代码时,总是卡壳,就是因为我对这种方法理解的懵懵懂懂似是而非,希望你们也能注意一下。
还有,在读数据那段,我之前写的是这样的:
代码
if( scl == 1'b1 && cnt < 8 ) // scl high and flag1=0
begin
flag1 <= 1'b0;
temp <= sda;
temp <= { temp,temp }; // error!!!
cnt <= cnt + 1'b1;
state <= 4'd10;
end
else if( scl == 1'b1 && cnt == 8 )
begin
flag1 <= 1'b1;
data_out <= 1'b0;
data_rd <= temp;
cnt <= 1'b0;
state <= 4'd11;
end
后来仿真波形出来后,发现读到的数据并没有左移,经过陈老师的指点才改正过来。
问题出在temp=sda,这样是左移不出来的。
而其他的步骤,几乎和字写入一毛一样了,而遇到的问题大概也就是之前字写入时碰到的问题了,我就不讲了= = 。
明天我会再发一个帖子,在一个模块里糅合写入和读出两个过程,并通过按键进行控制。
你可以给我的帖子提一些建议的,这样才能写的越来越成熟。
后面贴一下,仿真波形图。
仿真图里读出数据为高阻态,是因为仿真的时候在接收ack时把sda 屏蔽 且 没有连接eeprom 所以sda数据线读出的为高组态。
源代码:
//=====================================================================
// module name: iic_rd
// function:
// create data: from 2016-8-4 11:26:13to
// editor: miao
// Tool versions: quartus 13.0
//=====================================================================
module iic_rd(
// system signal
input clk, //50MHZ
input rst_n,
// output signal
output reg scl, // eeprom scl 200K
output reg data_rd,// read from eeprom
inout sda // eeprom sda
);
//=========================================================================================================
//************************** Define parameter and internal signals *********************************
//=========================================================================================================
reg clk_400k;
reg count;
parameter COUNT_400K = 62;
reg flag1;//for sda
reg state;
reg temp; // register for sda
reg cnt;// use for data transfer
reg data_out;
//=========================================================================================================
//********************************* 400KHZ & scl & flag1 ***********************************
//=========================================================================================================
// frequency division 400KHZ , count 62 times
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
count <= 1'b0;
clk_400k <= 1'b0;
end
elsebegin
if( count == COUNT_400K )
begin
count <= 1'b0;
clk_400k <= ~clk_400k;
end
else begin
count <= count + 1'b1;
end
end
end
//400KHZcould produce 200KHZ named scl of eeprom
always @ (negedge clk_400k or negedge rst_n)
begin
if(!rst_n)
begin
scl <= 1'b1;
end
else if( state == 13)
begin
scl <= 1'b1;
end
else
begin
scl <= ~scl;
end
end
//=========================================================================================================
//********************************* state machine ***********************************
//=========================================================================================================
assign sda = ( flag1 == 1'b1) ? data_out : 1'bz;
always @ (posedge clk_400k or negedge rst_n)
begin
if(!rst_n)
begin // initial , both scl and sda high
state <= 4'd0;
temp <= 8'd0; // register
data_out <= 1'b1;
flag1 <= 1'b1;
cnt <= 1'b0;
data_rd <= 8'd0;
end
elsebegin
case(state)
0:begin // send start signal
if( scl == 1'b1 ) //scl high while sda low , producethe"start" signal
begin
data_out <= 1'b0;
flag1 <= 1'b1;
temp <= 8'b1010_0000;
state <= 1'b1;
end
end
1:begin // send contrl_word
if( scl == 1'b0 && cnt < 8 ) //while scl is low ,change data
begin
data_out <= temp; //cycle 0-8
flag1 <= 1'b1;
temp <= { temp,temp };
cnt <= cnt + 1'b1;
state <= 1'b1;
end
else if( scl == 1'b0 && cnt == 8 )
begin
flag1 <= 1'b0; // flag = 0 ,sda begin receive data
state <= 4'd2;
cnt <= 1'b0;
end
end
2:begin // receive ack
if ( scl == 1'b1 )
begin
if( sda == 1'b0 ) // scl high and sda low ,receive ACK . meanwhile switch to "state3"
begin
temp <= 8'd0000_0000; // put 8 bit address into the register "temp"
state <= 4'd3;
end
else begin //if couldn't receive ACK ,switch to "state1" and transfer contrl_word again
state <= 4'd1;
end
end
end
3:begin // send high address
if( scl == 1'b0 && cnt < 8 ) // scl low could change data , similar with "state1"
begin
data_out <= temp;
flag1 <= 1'b1;
temp <= { temp,temp };
cnt <= cnt + 1'b1;
state <= 4'd3;
end
else if( scl == 1'b0 && cnt == 8 )
begin
flag1 <= 1'b0; // sda = data_out
state <= 4'd4;
cnt <= 1'b0;
end
end
4:begin // receive ack
if ( scl == 1'b1 )
begin
if( sda == 1'b0 )
begin
temp <= 8'd0000_0000;
state <= 4'd5;
end
else begin
state <= 4'd3;
end
end
end
5:begin // send low address
if( scl == 1'b0 && cnt < 8 )
begin
data_out <= temp;
flag1 <= 1'b1;
temp <= { temp,temp };
cnt <= cnt + 1'b1;
state <= 4'd5;
end
else if( scl == 1'b0 && cnt == 8 )
begin
flag1 <= 1'b0;
cnt <= 1'b0;
state <= 4'd6;
end
end
6:begin // receive ack
if ( scl == 1'b1 )
begin
if( sda == 1'b0 )
begin
temp <= 8'd0000_0000;
state <= 4'd7;
end
else begin
state <= 4'd5;
end
end
end
7:begin // send start signal againsecond
if( scl == 1'b1 ) //scl high while sda low , producethe"start" signal
begin
data_out <= 1'b0;
flag1 <= 1'b1;
temp <= 8'b1010_0001;// differentfirst
state <= 4'd8;
end
end
8:begin // send contrl_word again second
if( scl == 1'b0 && cnt < 8 ) //while scl is low ,change data
begin
data_out <= temp; //cycle 0-8
flag1 <= 1'b1;
temp <= { temp,temp };
cnt <= cnt + 1'b1;
state <= 4'd8;
end
else if( scl == 1'b0 && cnt == 8 )
begin
flag1 <= 1'b0; // flag = 0 ,sda begin receive data
state <= 4'd9;
cnt <= 1'b0;
end
end
9:begin // receive ack
if ( scl == 1'b1 )
begin
if( sda == 1'b0 )
begin
temp <= 8'd0000_0000;
state <= 4'd10;
end
else begin
state <= 4'd8;
end
end
end
///***====================read state=========================*********///
10:begin // read data // note: if error ,add a state to makesclhigh
if( scl == 1'b1 && cnt < 8 ) // scl high and flag1=0
begin
flag1 <= 1'b0;
temp <= { temp,sda }; // remember !!!
cnt <= cnt + 1'b1;
state <= 4'd10;
end
else if( scl == 1'b1 && cnt == 8 )
begin
flag1 <= 1'b1;
data_out <= 1'b0;
data_rd <= temp;
cnt <= 1'b0;
state <= 4'd11;
end
end
11:begin // wirte noack
if ( scl == 1'b0 )
begin
flag1 <= 1'b1;
data_out <= 1'b1;
state <= 4'd12;
end
end
12:begin // order tomake sdalow tohigh
if( scl == 1'b0 ) // scl low and sda low ,produce "stop" signal
begin
data_out <= 1'b0;
flag1 <= 1'b1;
state <= 4'd13;
end
end
13:begin // "stop" flag
if( scl == 1'b1)
begin
flag1 <= 1'b1;
data_out <= 1'b1;
state <= 4'd13;
end
end
default: state <= 4'd0;
endcase
end
end
endmodule
非常棒,继续追贴 感谢分享,持续跟贴中‘ 感谢楼主分享 能不能分享一下整体读写包括按键的代码
海的自由 发表于 2016-8-9 18:02
能不能分享一下整体读写包括按键的代码
好 我整理一下就发上来 IIC协议应用之二EEPROM随机读出
页:
[1]