本帖最后由 雾盈 于 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[7];
- flag1 <= 1'b1;
- temp <= { temp[6:0],temp[7] };
- 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[6:0],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[7] <= sda ;
- temp <= { temp[6:0],temp[7] }; // 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[7]=sda,这样是左移不出来的。
而其他的步骤,几乎和字写入一毛一样了,而遇到的问题大概也就是之前字写入时碰到的问题了,我就不讲了= = 。
明天我会再发一个帖子,在一个模块里糅合写入和读出两个过程,并通过按键进行控制。
你可以给我的帖子提一些建议的,这样才能写的越来越成熟。
后面贴一下,仿真波形图。
仿真图里读出数据为高阻态,是因为仿真的时候在接收ack时把sda 屏蔽 且 没有连接eeprom 所以sda数据线读出的为高组态。
源代码: 
- //=====================================================================
- // module name: iic_rd
- // function:
- // create data: from 2016-8-4 11:26:13 to
- // 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 [7:0] data_rd, // read from eeprom
- inout sda // eeprom sda
- );
- //=========================================================================================================
- //************************** Define parameter and internal signals *********************************
- //=========================================================================================================
- reg clk_400k;
- reg [7:0] count;
- parameter COUNT_400K = 62;
- reg flag1; //for sda
- reg [3:0] state;
- reg [7:0] temp; // register for sda
- reg [7:0] 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
- else begin
- if( count == COUNT_400K )
- begin
- count <= 1'b0;
- clk_400k <= ~clk_400k;
- end
- else begin
- count <= count + 1'b1;
- end
-
- end
- end
- // 400KHZ could 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
- else begin
- case(state)
- 0:begin // send start signal
- if( scl == 1'b1 ) // scl high while sda low , produce the "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[7]; //cycle 0-8
- flag1 <= 1'b1;
- temp <= { temp[6:0],temp[7] };
- 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[7];
- flag1 <= 1'b1;
- temp <= { temp[6:0],temp[7] };
- 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[7];
- flag1 <= 1'b1;
- temp <= { temp[6:0],temp[7] };
- 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 again second
- if( scl == 1'b1 ) // scl high while sda low , produce the "start" signal
- begin
- data_out <= 1'b0;
- flag1 <= 1'b1;
- temp <= 8'b1010_0001; // different first
- 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[7]; //cycle 0-8
- flag1 <= 1'b1;
- temp <= { temp[6:0],temp[7] };
- 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 make scl high
- if( scl == 1'b1 && cnt < 8 ) // scl high and flag1=0
- begin
- flag1 <= 1'b0;
- temp <= { temp[6:0],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 to make sda low to high
- 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
复制代码
|