FPGA和eeprom通信

本文有参考【精品博文】IIC 通信协议的Verilog实现作者的一些思想,并尝试补充eeprom一端的代码,并不完美,主要是

一eeprom完全按照scl上升沿或下降沿采取动作(写数据或读数据),很难在scl低电平中间点使sda线发生变化(似乎不太符合iic协议要求),

二另外在FPGA放弃sda线控制权和eeprom取得sda线控制权之间会有一小段高阻态(衔接并不连续),以下代码

`timescale 1 ns / 100ps
module exx( clk,rst_n,keywr,keyrd,sda,scl); //fpga端代码
input clk; 
input rst_n; 
input keywr;
input keyrd;
output reg scl;
inout sda; 

reg[3:0] cnt; 
reg[3:0] count;

reg wr;
reg rd;
reg link;
reg sdabuf;
reg[8:1] buffer;
reg[3:0] state;

parameter
idl=0,
st1=1,
wdv=2,
ac1=3,
wwd=4,
ac2=5,
wdt=6,
ac3=7,
st2=8,
rdv=9,
ac4=10,
rdt=11,
nac=12,
stp=13;

assign sda=link?sdabuf:1'bz; //fpga控制或放弃sda线

always @(negedge clk or negedge rst_n)
if(!rst_n) scl<=1;
else if(state>st1)scl<=~scl;

always @(posedge clk or negedge rst_n)
if(!rst_n) begin 
state<=idl;
wr<=0;
rd<=0;
count<=8;
link<=1;
sdabuf<=1;
buffer<=0;
end
else 
case(state)
idl: //空闲状态
if(!keyrd||!keywr) 
begin 
  if(!keywr)wr<=1; 
  if(!keyrd)rd<=1; 
  sdabuf<=0; 
  state<=st1; 
  buffer<=8'b10100000; 
end

st1: state<=wdv; //发送启动信号
        
wdv:if(!scl&&count>0) //发送器件地址8'b10100000;  
      begin 
      sdabuf<=buffer[count]; 
      count<=count-1; 
      end
     else if(!scl&&count==0) 
        begin 
        count<=8; 
        link<=0;   //放弃sda线控制权,等待检测应答信号
        state<=ac1; 
        end
 
ac1:   //接收第一次应答
    begin   
      if(scl)
        begin
          if(!sda) 
            begin 
              buffer<=8'b1000_0001; //准备写字地址8'b1000_0001=d'129;
              sdabuf<=0; 
              link<=1;  //接收到应答信号后重新获取sda线控制权,准备写字地址
              state<=wwd; 
            end
        end
    end

wwd:if(!scl&&count>0) //发送字地址 
      begin 
      sdabuf<=buffer[count]; 
      count<=count-1; 
      end
     else if(!scl&&count==0) 
        begin 
        count<=8; 
        link<=0; 
        state<=ac2; 
        end
 
ac2:     //接收第二次应答
    begin   
      if(scl)
        begin
          if(!sda) 
            begin 
              buffer<=8'b1000_0010; //准备写数据8'b1000_0010=d'130;
              sdabuf<=0; 
              link<=1;
              if(wr) state<=wdt; //如果写信号wr则进入wdt状态
              if(rd) state<=st2; //如果读信号rd则进入st2状态 
            end
        end
    end
    
wdt:if(!scl&&count>0) //发送写数据
      begin 
      sdabuf<=buffer[count]; 
      count<=count-1; 
      end
     else if(!scl&&count==0) 
        begin 
        count<=8; 
        link<=0; 
        state<=ac3; 
        end
 
ac3:  //接收第三次应答
    begin   
      if(scl)
        begin
          if(!sda) 
            begin 
              buffer<=8'b1000_0001; 
              sdabuf<=0; 
              link<=1;
              state<=stp; 
            end
        end
    end
    
st2:  //restart
  begin
    sdabuf<=1;    
    link<=1; 
    if(scl) 
       begin  
       sdabuf<=0;    
       buffer<=8'b10100001;//器件地址8'b10100001赋给buffer;
         state<=rdv; 
         end
    end

rdv:if(!scl&&count>0) //发送器件地址8'b10100001;
      begin 
      sdabuf<=buffer[count]; 
      count<=count-1; 
      end
     else if(!scl&&count==0) 
      begin 
        count<=8; 
        link<=0; 
        state<=ac4; 
      end
 
ac4:    //检测应答信号
    begin   
      if(scl)
        begin
          if(!sda) 
            begin 
            //buffer<=8'b1000_0001; 
              sdabuf<=0; 
              //link<=0;
              state<=rdt; 
            end
        end
    end
        
rdt:    //读取eeprom数据
  if(scl==1&&count>0)  
    begin 
     buffer[count]<=sda; 
      count<=count-1;            
      end
  else if(scl==0&&count==0) 
        begin
          count<=8; 
          sdabuf=1; 
          link<=1;  
          state<=nac;
        end
        
nac:  //发送非应答信号 
  if(scl==0)
    begin   
      sdabuf=0;
      state<=stp; 
    end
    
stp:   //发送停止信号 
  begin
    link<=1; 
    if(scl) 
       begin  
       sdabuf<=1;    
       wr <=0;
         state<=idl; 
         end
    end
 
 default:state<=0; 
 
 endcase
 
 endmodule
 
module eeprom(rst_n,scl,sda); //eeprom端代码
input rst_n; 
input scl;
inout sda; 

reg[8:1] buffer; 
reg[3:0] count;
reg link;
reg sdabuf;
reg wr;
reg[3:0] state;
reg[7:0] addr;   //eeprom内部存储器的地址寄存器
reg[7:0] mema[255:0];  //eeprom内部存储器,其它为控制电路

parameter
idl=0,
st1=1,
wdv=2,
ac1=3,
wwd=4,
ac2=5,
wdt=6,
ac3=7,
st2=8,
rdv=9,
ac4=10,
rdt=11,
nac=12,
stp=13;

assign sda=link?sdabuf:1'bz; //eeprom控制或放弃sda线

always @(scl or negedge rst_n) //eeprom根据scl上升沿或下降沿采取动作
if(!rst_n) begin 
state<=idl;
buffer<=0;
addr<=0;
count<=8;
link<=0;
sdabuf<=0;
end
else 
case(state) 
idl: if(scl==0) state<=wdv; //eeprom检测到scl下降沿直接进入wdv状态
 
wdv:    //eeprom接收器件地址
  if(scl==1&&count>=1) 
    begin 
      buffer[count]<=sda; 
      count<=count-1;   
    end 
  else if(scl==1&&count==0&&buffer[8:1]==8'b10100000) //判断接收到的器件地址是否等于8'b10100000
    begin
      count<=8;  
      link<=1;  //eeprom获取sda控制权以发送应答信号
      state<=ac1;
    end       

ac1:  //eeprom第一次应答 
  if(scl==0)
    begin   
      link<=0;   //eeprom放弃sda控制权
      buffer<=8'b00000000;
      state<=wwd; 
    end
    
wwd:     //eeprom接收写字地址8'b1000_0001=d'129;
  if(scl==1&&count>0) 
    begin 
      buffer[count]<=sda; 
      count<=count-1;            
      end
  else if(scl==1&&count==0) 
        begin
          count<=8;  
          addr<=buffer; //接收到的字地址赋给addr
          link<=1;  
          state<=ac2;
        end
    
ac2:  //eeprom第二次应答
  if(scl==0)
    begin   
      link<=0; 
      buffer<=8'b00000000;     
    end
  else if(scl==1)
    begin   
      if(sda==0)begin buffer[8]<=0; count<=7; state<=wdt; end//scl上升沿发现sda是低电平,说明肯定接下来是写数据信号
      if(sda==1)state<=st2; //scl上升沿发现sda也已经升了,说明有可能是restart信号进入虚假的st2状态待进一步判断
    end
    
wdt:    //eeprom接收写数据8'b1000_0010=d'130;
  if(scl==1&&count>0) 
    begin 
      buffer[count]<=sda; 
      count<=count-1;            
    end
  else if(scl==1&&count==0) 
    begin
      count<=8; 
      mema[addr]<=buffer;  //接收到的buffer数据写入存储器mema addr地址
      link<=1;  
      state<=ac3;
    end
    
ac3:  //eeprom第三次应答
  if(scl==0)
    begin   
      link<=0; 
      buffer<=8'b00000000;
      state<=stp; 
    end
        
st2:   //eeprom检测restart信号
  if(scl==0)
    begin   
      if(sda==1) begin buffer[8]<=1; count<=7; state<=wdt; end//scl下降沿发现sda仍然是高电平,说明是写数据
      else if(sda==0)state<=rdv; //scl下降沿发现sda也降了,说明是restart信号
    end

rdv:   //接收器件地址8'b10100001末位为1
  if(scl==1&&count>0)  
    begin 
      buffer[count]<=sda; 
      count<=count-1;            
      end
  else if(scl==1&&count==0) 
      begin
        count<=8;  
        link<=1;  
        state<=ac4;
      end
    
ac4:  //eeprom读数据的第三次应答
  if(scl==0)
    begin   
      buffer<=mema[addr];
      state<=rdt; 
    end
    
rdt:   //eeprom发送存储器mema在addr 8'b1000_0001(129)地址的数据8'b1000_0010(130);
  if(scl==1&&count>0)  
    begin 
      sdabuf<=buffer[count]; 
      count<=count-1;            
    end
  else if(scl==0&&count==0) 
    begin
      count<=8;  
      link<=0;  
      state<=nac;
    end
        
nac:  //检测非应答信号 
  if(scl==1)
    begin   
      if(sda==1)state<=stp; 
    end
    
stp: //检测停止信号 
begin
  link<=0;
  state<=idl; //回到空闲idl状态 
end

default:state<=idl; 

endcase
 
endmodule

module exx_tst();
reg clk; 
reg rst_n; 
reg keywr;
reg keyrd;
wire scl;
wire sda;

exx myexx( clk,rst_n,keywr,keyrd,sda,scl); 
eeprom myeeprom(rst_n,scl,sda); 

initial                                             
 
begin                                               
   clk=0; 
   rst_n=0; 
   keywr=1; 
   keyrd=1;
   #10;
     rst_n=1; 
     #10;
     keywr=0; //按下写按钮
     #10;
     keywr=1;
     
     #600;
     keyrd=0; //按下读按钮
     #10;
     keyrd=1;
                                
                        
end     
                                               
always #5 clk=~clk;                           
                                                    
   
endmodule

以下FPGA向eeprom写数据

 以下FPGA从eeprom读数据

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA读写EEPROM芯片AT24C02实验Verilog逻辑源码Quartus11.0工程文件, FPGA型号为CYCLONE4E系列中的EP4CE6E22C8,可以做为你的学习设计参考。 module iic_com( clk,rst_n, sw1,sw2, scl,sda, dis_data ); input clk; // 50MHz input rst_n; //复位信号,低有效 input sw1,sw2; //按键1、2,(1按下执行写入操作,2按下执行读操作) output scl; // 24C02的时钟端口 inout sda; // 24C02的数据端口 output[7:0] dis_data; //数码管显示的数据 //按键检测 reg sw1_r,sw2_r; //键值锁存寄存器,每20ms检测一次键值 reg[19:0] cnt_20ms; //20ms计数寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_20ms <= 20'd0; else cnt_20ms <= cnt_20ms+1'b1; //不断计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin sw1_r <= 1'b1; //键值寄存器复位,没有键盘按下时键值都为1 sw2_r <= 1'b1; end else if(cnt_20ms == 20'hfffff) begin sw1_r <= sw1; //按键1值锁存 sw2_r <= sw2; //按键2值锁存 end end //--------------------------------------------- //分频部分 reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间 reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟 reg scl_r; //时钟脉冲寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_delay <= 9'd0; else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz else cnt_delay <= cnt_delay+1'b1; //时钟计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3'd5; else begin case (cnt_delay) 9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样 9'd249: cnt <= 3'd2; //cnt=2:scl下降沿 9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化 9'd499: cnt <= 3'd0; //cnt=0:scl上升沿 default: cnt <= 3'd5; endcase end end `define SCL_POS (cnt==3'd0) //cnt=0:scl上升沿 `define SCL_HIG (cnt==3'd1) //cnt=1:scl高电平中间,用于数据采样 `define SCL_NEG (cnt==3'd2) //cnt=2:scl下降沿 `define SCL_LOW (cnt==3'd3) //cnt=3:scl低电平中间,用于数据变化 always @ (posedge clk or negedge rst_n) begin if(!rst_n) scl_r <= 1'b0; else if(cnt==3'd0) scl_r <= 1'b1; //scl信号上升沿
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值