雾盈 发表于 2016-8-6 13:54:20

雾盈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-8 09:36:42

                              非常棒,继续追贴

zhiweiqiang33 发表于 2016-8-9 09:42:12

感谢分享,持续跟贴中‘

Adamancy 发表于 2016-8-9 15:13:33

               感谢楼主分享

海的自由 发表于 2016-8-9 18:02:59

能不能分享一下整体读写包括按键的代码

雾盈 发表于 2016-8-10 15:06:52

海的自由 发表于 2016-8-9 18:02
能不能分享一下整体读写包括按键的代码

好 我整理一下就发上来

雷磊 发表于 2021-9-6 16:08:32

IIC协议应用之二EEPROM随机读出
页: [1]
查看完整版本: 雾盈FPGA笔记(十二)IIC协议应用之二EEPROM随机读出