基于FPGA的IIC总线协议的实现(代码精简)

I2C(Inter-Integrated Circuit)中文名称“集成电路总线”,是一种串行同步半双工总线。

I2C总线是由Philips公司开发的一种简单,双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传达信息,一根是数据线(SDA),一根是时钟线(SCL)。

I2C总线可实现多主机,多从机之间的互联,具有低功耗,抗干扰,电源电压范围宽,工作温度范围广的特点。其通讯速率为:标准模式0-100kbit/s,快速模式0-400kbit/s,和高速模式0-3.4Mbit/s。可连接很多设备,总电容在400pF。

I2C结构:1,  设备之间互联只需要两根线(SDA,SCL)

         2,  I2C总线支持任何IC生产过程(NMOS  CMOS双极性)

         3,  SDA双向数据线

         4,  SCL双向时钟线

         5,  每个设备有且只有一个设备地址

         6,  设备分为主机(master)和从机,主机允许输出时钟信号

         7,  主机发送信号时,发送时钟和数据,当主机发送结束后,数据线高阻,主机继续发送时钟,从机发送数据给主机

 

I2C硬件连接:1, I2C只使用两条漏极开路(Open Drain)SDA及SCL并利用电阻将电平上拉。

                     2, VDD通常为+5V或者是+3.3V

                     3, 逻辑0 = 低电平

                     4, 逻辑1 = 高点平

 

 

I2C总线协议:

写格式:1,主机发start位

              2,主机发从机地址和写命令

              3,从机回ack

              4,主机发寄存器地址

              5,从机回ack

             6,主机发数据

             7,从机回ack

             8,主机发stop位

 

 

 

读格式:1,主机发atart位                                    

              2,主机发从机地址和写命令

              3,从机回ack

              4,主机发寄存器地址

              5,从机回ack

              6,主机发restart位

              7,主机发从机地址和读命令

              8,从机回ack

              9,从机回数据

            10,主机发nack

            11,主机发stop位

 

 

I2C驱动模块:

`define     IIC_FREQ100K    500            //频率100k
`define     IIC_FREQ400K    125            //频率400k
module i2c_driver   #(
parameter   IIC_CNTMAX = `IIC_FREQ100K
)
(
input           clk,
input           rst_n,

input   wire    [1:0]   cmd,                //2'b00-无操作     2'b01-写操作       2'b10-读操作
input   wire    [6:0]   device_addr,        //从机地址
input   wire    [7:0]   reg_addr,           //寄存器地址
input   wire    [7:0]   wr_data,            //写入的数据

output  reg     [7:0]   rd_data,            //读出的数据
output  reg             done,               //结束脉冲
output  reg             err,                //报错信号
output  reg             scl,                //时钟线
inout                   sda                 //数据线
    );
reg             sda_sel;                
reg             sda_reg; 

assign  sda = sda_sel ? sda_reg : 1'bz;
    
localparam  IIC_CNTMAX_4_1 = IIC_CNTMAX/4-1,                            
            IIC_CNTMAX_4_2 = IIC_CNTMAX/2-1,           
            IIC_CNTMAX_4_3 = (IIC_CNTMAX/4)*3-1,      
            IIC_CNTMAX_4_4 = IIC_CNTMAX-1;              


reg     [25:0]  cnt;                    //时钟计数器
reg     [25:0]  delay_cnt;              //延迟计数器
reg             ack;                    //寄存一个从机回的ack信号
reg     [7:0]   data_reg;               //定义一个八位的数据寄存器 
reg     [3:0]   num;                    //定义个计数num
reg     [3:0]   state;                  
reg     [3:0]   back_state;
localparam  FSM_START               = 0,                //主机发start位
            FSM_DEVICE_ADDR_WR_RD   = 1,                //主机发从机地址和写命令
            FSM_ACK                 = 2,                //从机回ack
            FSM_WR_REG_ADDR         = 3,                //主机发寄存器地址
            FSM_WR_DATA             = 4,                //主机发数据
            FSM_RESTART             = 5,                //主机发restart位
            FSM_RD_DATA             = 6,                //从机回数据
            FSM_NACK                = 7,                //主机发nack 
            FSM_STOP                = 8,                //主机发stop位
            FSM_DELAY               = 9;                //延迟20us

                                    
reg         [1:0]   scl_state;          //定义个时钟线状态
                    

always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        cnt <= 0;
    else    if  (state == FSM_DELAY)
        cnt <= 0;
    else    if  (cmd == 2'b01 || cmd == 2'b10)
        begin
            if  (cnt == IIC_CNTMAX_4_4)
                cnt <= 0;
            else    cnt <= cnt + 1;
        end
    else    cnt <= 0; 


always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        begin
            scl <= 1;
            scl_state <= 0;
        end
    else    if  (cmd == 2'b01 || cmd == 2'b10)
        case    (scl_state)
            0   :   if  (cnt == 0)
                        scl <= 1;
                    else    if  (cnt == IIC_CNTMAX_4_3)
                        scl <= 0;
                    else    if  (cnt == IIC_CNTMAX_4_4) 
                        scl_state <= 1;
                    else    scl_state <= 0;
                    
            1   :   if  ((back_state == FSM_STOP && state == FSM_ACK && cnt == IIC_CNTMAX_4_4) || (state == FSM_NACK && cnt == IIC_CNTMAX_4_4))
                        scl_state <= 2;
                    else    if  (cnt == 0)
                        scl <= 0;
                    else    if  (cnt == IIC_CNTMAX_4_1)
                        scl <= 1;
                    else    if  (cnt == IIC_CNTMAX_4_3)
                        scl <= 0;
                    else    scl_state <= 1;
                    
            2   :   if  (cnt == 0)                     
                        scl <= 0;                      
                    else    if  (cnt == IIC_CNTMAX_4_1)
                        scl <= 1;
                    else    if  (cnt == IIC_CNTMAX_4_4)
                        scl_state <= 3;
                    else    scl_state <= 2;
                    
            3   :   if  (delay_cnt == 999)
                        scl_state <= 0;
                    else    scl_state <= 3;
                    
        default :   begin   scl <= 1; scl_state <= 0;   end
        endcase
    else    begin
        scl <= 1;      
        scl_state <= 0;
    end
    
    
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        begin
            state <= FSM_START;    
            back_state <= 0;
            rd_data <= 0;
            done <= 0;
            sda_sel <= 1;
            sda_reg <= 1;
            delay_cnt <= 0;
            ack <= 1;
            data_reg <= 0;
            num <= 0;
            err <= 0;
        end
     
    else    if  (cmd == 2'b01 || cmd == 2'b10)
        case    (state)
            FSM_START               :   begin
                                            done <= 0;
                                            sda_sel <= 1;
                                            data_reg <= {device_addr,1'b0};
                                            back_state <= FSM_WR_REG_ADDR;
                                            if  (cnt == 0)
                                                sda_reg <= 1;
                                            else    if  (cnt == IIC_CNTMAX_4_2)
                                                sda_reg <= 0;
                                                
                                            if  (cnt == IIC_CNTMAX_4_4)
                                                state <= FSM_DEVICE_ADDR_WR_RD;
                                            else    state <= FSM_START;
                                        end
               
            FSM_DEVICE_ADDR_WR_RD   :   begin
                                            sda_sel <= 1;  
                                            if  (cnt == 0)
                                                sda_reg <= data_reg[7-num];
                                                
                                            if  (cnt == IIC_CNTMAX_4_4 && num == 7)
                                                begin
                                                    num <= 0;
                                                    state <= FSM_ACK;
                                                end
                                            else    if  (cnt == IIC_CNTMAX_4_4)
                                                    num <= num + 1;
                                            else    state <= FSM_DEVICE_ADDR_WR_RD;
                                        end
                                        
            FSM_ACK                 :   begin
                                            sda_sel <= 0;
                                            sda_reg <= 1;
                                            if  (cnt == IIC_CNTMAX_4_2)
                                                ack <= sda;
                                            
                                            if  (cnt == IIC_CNTMAX_4_4)
                                                state <= back_state;   
                                            else    state <= FSM_ACK; 
                                        end
                                        
            FSM_WR_REG_ADDR         :   begin
                                            if  (!ack)
                                                begin
                                                    sda_sel <= 1;
                                                    if  (cmd == 2'b01)
                                                        back_state <= FSM_WR_DATA;
                                                    else    if  (cmd == 2'b10)
                                                        back_state <= FSM_RESTART;
                                                        
                                                    if  (cnt == 0)
                                                        sda_reg <= reg_addr[7-num];
                                                        
                                                    if  (cnt == IIC_CNTMAX_4_4 && num == 7)
                                                        begin                                                 
                                                            num <= 0;                      
                                                            state <= FSM_ACK;      
                                                        end                                
                                                    else    if  (cnt == IIC_CNTMAX_4_4)                             
                                                            num <= num + 1;                                                   
                                                    else    state <= FSM_WR_REG_ADDR;                       
                                                end
                                            else    begin
                                                err <= 1;
                                                state <= FSM_START;
                                            end
                                        end    

            FSM_WR_DATA             :   begin
                                            if  (!ack)
                                                begin
                                                    sda_sel <= 1;
                                                    back_state <= FSM_STOP;
                                                    if  (cnt == 0)
                                                        sda_reg <= wr_data[7-num];
                                                        
                                                    if  (cnt == IIC_CNTMAX_4_4 && num == 7)
                                                        begin                                                  
                                                            num <= 0;                      
                                                            state <= FSM_ACK;      
                                                        end                                
                                                    else    if  (cnt == IIC_CNTMAX_4_4)                           
                                                            num <= num + 1;                                                        
                                                    else    state <= FSM_WR_DATA;                      
                                                end
                                            else    begin
                                                err <= 1;
                                                state <= FSM_START;
                                            end
                                        end        
            
            FSM_RESTART             :   begin
                                            if  (!ack)
                                                begin
                                                    sda_sel <= 1;
                                                    data_reg <= {device_addr,1'b1};
                                                    back_state <= FSM_RD_DATA;
                                                    if  (cnt == 0)
                                                        sda_reg <= 1;
                                                    else    if  (cnt == IIC_CNTMAX_4_2)
                                                        sda_reg <= 0;
                                                        
                                                    if  (cnt == IIC_CNTMAX_4_4)
                                                            state <= FSM_DEVICE_ADDR_WR_RD;
                                                    else    state <= FSM_RESTART;  
                                                end
                                            else    begin
                                                err <= 1;                
                                                state <= FSM_START;
                                            end
                                        end
            
            FSM_RD_DATA             :   begin
                                            if  (!ack)
                                                begin
                                                    sda_sel <= 0;
                                                    if  (cnt == IIC_CNTMAX_4_2)
                                                        rd_data[7-num] <= sda;
                                                    
                                                    if  (cnt == IIC_CNTMAX_4_4 && num == 7)
                                                        begin
                                                            num <= 0;
                                                            state <= FSM_NACK;
                                                        end
                                                    else    if  (cnt == IIC_CNTMAX_4_4)
                                                            num <= num + 1;
                                                    else    state <= FSM_RD_DATA; 
                                                end
                                            else    begin
                                                err <= 1;                
                                                state <= FSM_START;
                                            end
                                        end
                                        
            FSM_NACK                :   begin
                                            sda_sel <= 1;
                                            sda_reg <= 1;
                                            if  (cnt == IIC_CNTMAX_4_4)
                                                    state <= FSM_STOP;   
                                            else    state <= FSM_NACK;
                                        end
                                        
            FSM_STOP                :   begin                                  
                                            sda_sel <= 1;                      
                                            if  (cnt == 0)                     
                                                sda_reg <= 0;                  
                                            else    if  (cnt == IIC_CNTMAX_4_2)
                                                sda_reg <= 1;                  
            
                                            if  (cnt == IIC_CNTMAX_4_4)                        
                                                    state <= FSM_DELAY;  
                                            else    state <= FSM_STOP;           
                                        end                                    

            FSM_DELAY               :   if  (delay_cnt == 999)              //延迟20us   
                                            begin                                      
                                                delay_cnt <= 0;                        
                                                done <= 1;                             
                                                state <= FSM_START;              
                                            end                                        
                                        else    delay_cnt <= delay_cnt + 1;            

            default :   state <= FSM_START;
        endcase
         
    else
        begin                        
            state <= FSM_START; 
            back_state <= 0;    
            rd_data <= rd_data;            
            done <= 0;                              
            sda_sel <= 1;            
            sda_reg <= 1;                            
            delay_cnt <= 0;          
            ack <= 1;                
            data_reg <= 0;           
            num <= 0;                
            err <= 0;                
        end                              
       
endmodule

 

  • 4
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值