代码编织梦想

module i2c_ctrl
#(
    parameter DEVICE_ADDR  = 7'b1010_000      , //i2c 设备地址
    parameter SYS_CLK_FREQ = 26'd50_000_000   , //输入系统时钟频率
    parameter SCL_FREQ     = 18'd250_000        //i2c 设备 scl 时钟频率
)
(
    input    wire              sys_clk        ,
    input    wire              sys_rst_n      ,
    input    wire              wr_en          ,
    input    wire              rd_en          ,
    input    wire              addr_num       ,
    input    wire    [15:0]    byte_addr      ,
    input    wire    [7:0]     wr_data        ,
    input    wire              i2c_start      ,
    output   reg               i2c_clk        ,
    output   reg               i2c_end        ,
    output   reg    [7:0]      rd_data        ,
    output   reg               i2c_scl        ,
    inout    wire              i2c_sda      
);
/参数定义//
//这里是为了生成i2c_clk信号,这个信号频率是i2c_scl的4倍,因为从系统时钟/i2c_clk时钟表示一个周期的计数值,
//i2c_clk半个周期翻转一次,所以要÷2,为了生成i2c_clk又要÷4,所以一共是÷8
localparam CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ;
//状态机变量
localparam IDLE          =  'd0  ,
           START_1       =  'd1  ,
           SEND_D_ADDR   =  'd2  ,
           ACK_1         =  'd3  ,
           SEND_B_ADDR_H =  'd4  ,
           ACK_2         =  'd5  ,
           SEND_B_ADDR_L =  'd6  ,
           ACK_3         =  'd7  ,
           WR_DATA       =  'd8  ,
           ACK_4         =  'd9  ,
           STOP          =  'd10 ,
           START_2       =  'd11 ,
           SEND_RD_ADDR  =  'd12 ,
           ACK_5         =  'd13 ,
           RD_DATA       =  'd14 ,
           N_ACK         =  'd15 ;
/线网变量定义//
wire             w_sda_en           ;
wire             w_sda_in           ;
/寄存器变量定义
reg     [7:0]    r_cnt_clk          ;
reg     [3:0]    r_state            ;
reg     [2:0]    r_cnt_i2c_clk      ;
reg     [2:0]    r_cnt_bit          ;
reg              r_cnt_i2c_clk_en   ;
reg              r_ack              ;
reg              r_i2c_sda_reg      ;
reg   [7:0]      r_rd_data_reg      ;
/程序/
always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_clk <= 'd0;
    else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
        r_cnt_clk <= 'd0;
    else
        r_cnt_clk <= r_cnt_clk +'d1;
end

always@(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        i2c_clk <= 'd0;
    else if(r_cnt_clk == CNT_CLK_MAX -1'd1)
        i2c_clk <= ~i2c_clk;
    else
        i2c_clk <= i2c_clk;
end

//状态转移
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_state <= IDLE;
    else
    case(r_state)
        IDLE         : 
            begin
                if(i2c_start == 'd1)
                    r_state <= START_1;
                else 
                    r_state <= IDLE;
            end
        START_1      : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= SEND_D_ADDR;
                else
                    r_state <= START_1;
            end
        SEND_D_ADDR  : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_1;
                else
                    r_state <= SEND_D_ADDR;
            end
        ACK_1        : 
            begin
                if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
                    if(addr_num == 'd1)
                        r_state <= SEND_B_ADDR_H;
                    else
                        r_state <= SEND_B_ADDR_L;
                end
                else
                    r_state <= ACK_1;
            end
        SEND_B_ADDR_H: 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_2;
                else
                    r_state <= SEND_B_ADDR_H;
            end
        ACK_2        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= SEND_B_ADDR_L;
                else
                    r_state <= ACK_2;
            end
        SEND_B_ADDR_L: 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_3;
                else
                    r_state <= SEND_B_ADDR_L;
            end
        ACK_3        : 
            begin
                if(r_ack == 'd0 && r_cnt_i2c_clk == 'd3)begin
                    if(wr_en == 'd1)
                        r_state <= WR_DATA;
                    else if(rd_en == 'd1)
                        r_state <= START_2;
                    else
                        r_state <= r_state;
                end
                else
                    r_state <= ACK_3;
            end
        WR_DATA      : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_4;
                else
                    r_state <= WR_DATA;
            end
        ACK_4        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= STOP;
                else
                    r_state <= ACK_4;
            end
        STOP         : 
             begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd3)
                    r_state <= IDLE;
                else
                    r_state <= STOP;
            end
        START_2      : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= SEND_RD_ADDR;
                else
                    r_state <= START_2;
            end
        SEND_RD_ADDR : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= ACK_5;
                else
                    r_state <= SEND_RD_ADDR;
            end
        ACK_5        : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_ack == 'd0)
                    r_state <= RD_DATA;
                else
                    r_state <= ACK_5;
            end
        RD_DATA      : 
            begin
                if(r_cnt_i2c_clk == 'd3 && r_cnt_bit == 'd7)
                    r_state <= N_ACK;
                else
                    r_state <= RD_DATA;
            end
        N_ACK        : 
            begin
                if(r_cnt_i2c_clk == 'd3)
                    r_state <= STOP;
                else
                    r_state <= N_ACK;
            end
        default      :r_state <= IDLE;
        endcase
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_i2c_clk_en <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        r_cnt_i2c_clk_en <= 'd0;
    else if(i2c_start == 'd1)
        r_cnt_i2c_clk_en <= 'd1;
    else
        r_cnt_i2c_clk_en <= r_cnt_i2c_clk_en;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_i2c_clk <= 'd0;
    else if(r_cnt_i2c_clk == 'd3)
        r_cnt_i2c_clk <= 'd0;
    else if(r_cnt_i2c_clk_en == 'd1)
        r_cnt_i2c_clk <= r_cnt_i2c_clk + 'd1;
    else 
        r_cnt_i2c_clk <= 'd0;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_cnt_bit <= 'd0;
    else if(r_state == START_1 || r_state == ACK_1 || r_state == ACK_2 
            ||r_state == ACK_3 || r_state == ACK_4 || r_state == ACK_5 
            ||r_state == START_2 || r_state == IDLE || r_state == N_ACK
            ) 
        r_cnt_bit <= 'd0;
    else if((r_state == SEND_D_ADDR || r_state == SEND_B_ADDR_H || r_state == SEND_B_ADDR_L
            || r_state == SEND_RD_ADDR || r_state == RD_DATA  || r_state == WR_DATA) 
            && r_cnt_bit == 'd7 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= 'd0;
    else if(r_cnt_i2c_clk_en == 'd1 && r_cnt_i2c_clk == 'd3)
        r_cnt_bit <= r_cnt_bit+'d1;
    else
        r_cnt_bit <= r_cnt_bit;
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        i2c_end <= 'd0;
    else if(r_state == STOP && r_cnt_bit == 'd3 && r_cnt_i2c_clk == 'd3)
        i2c_end <= 'd1;
    else 
        i2c_end <= 'd0;
end

always@(*)begin
    if(!sys_rst_n)
        i2c_scl <=  'd1;
    case(r_state)
        IDLE        :
                i2c_scl <= 'd1;
        START_1       :
            begin
                if(r_cnt_i2c_clk == 'd3)
                    i2c_scl <= 'd0;
                else
                    i2c_scl <= 'd1;
            end
        SEND_D_ADDR , SEND_B_ADDR_H , SEND_B_ADDR_L , WR_DATA 
        , ACK_1 , ACK_2 , ACK_3 , ACK_4 , ACK_5 , RD_DATA
        , START_2 , SEND_RD_ADDR , N_ACK:
            begin
                if(r_cnt_i2c_clk == 'd1 || r_cnt_i2c_clk == 'd2)
                    i2c_scl <= 'd1;
                else
                    i2c_scl <= 'd0;
            end
        STOP          :
            begin
                if(r_cnt_i2c_clk == 'd0 && r_cnt_bit == 'd0)
                    i2c_scl <= 'd0;
                else
                    i2c_scl <= 'd1;
            end
        default : i2c_scl <= 'd1;
        endcase
end

always@(*)begin
    if(!sys_rst_n)
        r_i2c_sda_reg <= 'd0;
    else
    case(r_state)
    IDLE          :r_i2c_sda_reg <= 'd1;
    START_1       :
        begin
            if(r_cnt_i2c_clk <= 'd0)
                r_i2c_sda_reg <= 'd1;
            else
                r_i2c_sda_reg <= 'd0;
        end
    SEND_D_ADDR   :
        begin
            if(r_cnt_bit <= 'd6)
                r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
            else
                r_i2c_sda_reg <= 'd0;
        end
    ACK_1         :r_i2c_sda_reg <= 'd1;
    SEND_B_ADDR_H :r_i2c_sda_reg <= byte_addr[15 - r_cnt_bit];
    ACK_2         :r_i2c_sda_reg <= 'd1;
    SEND_B_ADDR_L :r_i2c_sda_reg <= byte_addr[7 - r_cnt_bit];
    ACK_3         :r_i2c_sda_reg <= 'd1;
    WR_DATA       :r_i2c_sda_reg <= wr_data[7 - r_cnt_bit];
    ACK_4         :r_i2c_sda_reg <= 'd1;
    STOP          :
        begin
            if(r_cnt_bit == 'd0 && r_cnt_i2c_clk < 'd3)
                r_i2c_sda_reg <= 'd0;
            else
                r_i2c_sda_reg <= 'd1;
        end
    START_2       :
        begin
            if(r_cnt_i2c_clk <='d1)
                r_i2c_sda_reg <= 'd1;
            else
                r_i2c_sda_reg <= 'd0;
        end
    SEND_RD_ADDR  :
        begin
            if(r_cnt_bit <= 'd6)
                r_i2c_sda_reg <= DEVICE_ADDR[6 -r_cnt_bit];
            else
                r_i2c_sda_reg <= 'd1;
        end
    ACK_5         :r_i2c_sda_reg <= 'd1;
    RD_DATA       :r_i2c_sda_reg <= 'd1;
    N_ACK         :r_i2c_sda_reg <= 'd1;
    default       :r_i2c_sda_reg <= 'd1;
    endcase
end

assign w_sda_en = ((r_state == ACK_1) || (r_state == ACK_2) || (r_state == ACK_3) || (r_state == ACK_4)
                    || (r_state == ACK_5) || (r_state == RD_DATA))? 'd0 : 'd1;
                    
assign i2c_sda = (w_sda_en == 'd1)? r_i2c_sda_reg : 'bz;

assign w_sda_in = i2c_sda;
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
    if(!sys_rst_n)
        r_ack <= 'd0;
    else 
    case(r_state)
    IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
    WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
        r_ack <= 1'd1;
    ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
        if(r_cnt_i2c_clk == 'd0)
            r_ack <= w_sda_in;
        else 
            r_ack <= r_ack;
    default:r_ack <= 1'd1;
    endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_ack <= 'd0;
    else 
    case(r_state)
    IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
    WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
        r_ack <= 1'd1;
    ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
        if(r_cnt_i2c_clk == 'd1)
            r_ack <= w_sda_in;
        else 
            r_ack <= r_ack;
    default:r_ack <= 1'd1;
    endcase
end
/*用组合逻辑自己赋值自己会锁存
always@(*)begin
    if(!sys_rst_n)
        r_rd_data_reg <= 'd0;
    else 
    case(r_state)
    IDLE:
        r_rd_data_reg <= 'd0;
    RD_DATA:
        begin
            if(r_cnt_i2c_clk == 'd2)
                r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
            else
                r_rd_data_reg <= r_rd_data_reg;
        end
    default :r_rd_data_reg <= r_rd_data_reg;
    endcase
end
*/
always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        r_rd_data_reg <= 'd0;
    else 
    case(r_state)
    IDLE:
        r_rd_data_reg <= 'd0;
    RD_DATA:
        begin
            if(r_cnt_i2c_clk == 'd1)
                r_rd_data_reg[7-r_cnt_bit] <= w_sda_in;
            else
                r_rd_data_reg <= r_rd_data_reg;
        end
    default :r_rd_data_reg <= r_rd_data_reg;
    endcase
end

always@(posedge i2c_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rd_data <= 8'd0;
    else if(r_state == RD_DATA && r_cnt_bit == 3'd7 && r_cnt_i2c_clk == 2'd3)
        rd_data <= r_rd_data_reg;
    else
        rd_data <= rd_data;
end


endmodule

 注意:和野火官方的代码相比我这里做了两处修改,分别是对r_ack,和r_rd_data_reg信号的赋值上。如果用野火的代码,它用的是组合逻辑赋值的方式,但是在组合逻辑中如果把自己的值赋值给自己会产生锁存器,这是我们不想要的,在编译的时候会报警告
 所以在这里,我们r_ack和r_rd_data_reg用时序逻辑的方式进行赋值,效果是一样的。这里讲一下r_ack的赋值,野火的代码逻辑是一到ACK_1,ACK_2,ACK_3,ACK_4,ACK_5,就采样信号的输入,然后保持不变。但是我们在时序逻辑中r_ack的赋值时ACK_1,ACK_2,ACK_3,ACK_4,ACK_5状态下信号稳定的时候再去采样,感觉会更好一点,同时能产生锁存器的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43254508/article/details/129674694

【FPGA——Cyclone Ⅳ学习笔记】七.IIC驱动EEPROM(EP4CE6F17C8)-爱代码爱编程

一.原理图 此开发板的24LC04芯片与之前所用的AT24C02芯片有所不同,此芯片的A2,A1,A0管脚为无效管脚,不能用于器件地址的选择! 二.IIC驱动及代码解释(i2c_dri.v) 由于黑金的代码是使用网上开源的代码,不易于理解,因此使用正点原子的代码进行解释。 IIC的驱动是使用状态机的方式进行编写。 由于iic驱动比较重要,因此单独拆开

OLED 驱动模块程序代码-爱代码爱编程

1、前言 作为嵌入式软件开发,可能经常会使用单片机连接驱动显示屏,实现人机交互的功能,通常可选择的有 OLED 和 LCD 等,其中相关驱动代码例程网上更是数不胜数。 本文介绍的是 OLED, 常见代表有 0.96 寸的OLED,网上也有很多相关的驱动代码,但是大部分代码都是能用即可,不是特别关注驱动代码的整体架构,以下是个人整理的 OLED 驱动代码

9.触摸屏驱动(IIC)移植实战-爱代码爱编程

9.1.触摸屏驱动概览 9.1.1、常用的2种触摸屏 (1)电阻触摸屏。驱动一般分2种:一种是SoC内置触摸屏控制器,一种是外置的专门触摸屏控制芯片,通过I2C接口和SoC通信。 (2)电容触摸屏。驱动只有一种,外接专用的电容式触摸屏控制芯片,I2C接口和SoC通信。9.1.2、学习触摸屏驱动的关键点 (1)input子系统相关知识 (2)中断上下半部 (

FPGA驱动LCD1602(IIC) Verilog代码(一)------ IIC写模块-爱代码爱编程

一、概述         IIC时序参考链接:51单片机 使用IIC转接板驱动LCD1602         基本参考该链接中的IIC时序,使用Verilog代码来实现IIC写模块。只是比较好奇为什么在所有位发完之后,scl还要拉低再拉高了(我没有这样做的时候会一直收不到ack信号)?         需要注意IIC SDA信号的写法,声明为in_o

IIC介绍及驱动编写-爱代码爱编程

1、IIC介绍 IIC是通信协议中的一种,为一主多从的结构,对于主从,所有的数据都是从主机这边发起,从机只能接受,不能主动引起数据传输。它只有两条总线线路:一条串行数据线(SDA),一条串行时钟线(SCL),IIC有硬件IIC和软件IIC,这里简单解释,硬件IIC为硬件构成的IIC,一般只需要操作相关寄存器即可,对于软件IIC,可以由IO口来模拟IIC总

Linux下IIC驱动编写(驱动adxl345传感器)-爱代码爱编程

基于IMX6ULL Mini开发板,硬件连接:SCL->43, SDA->42,CS->3.3V INT1->7 一、搭建基础框架 1、设置私有数据client,linux/i2c.h下其结构体为 struct i2c_client { unsigned short flags; /* 标志 */ unsigned s

正点原子linux阿尔法开发板使用——iic驱动_梅山剑客的博客-爱代码爱编程

I2C设备驱动 i2c_client:表示I2C设备,不需要我们自己创建i2c_client,我们一般在设备树里面添加具体的I2C芯片,比如fxls8471,系统在解析设备树的时候就会知道有这个I2C设备,然后会创建对应的i2c_client。 重点 i2c设备驱动框架,i2c_driver初始化与注册,需要II2C设备驱动编写人员编写的,IIC驱动

硬件iic驱动oled字符显示_ssd1309驱动-爱代码爱编程

OLED显示 OLED的概念 OLED,即有机发光二极管。 OLED具备自发光,不需背光源、对比度高。 LCD 都需要背光,而OLED 不需要,因为它是自发光的。这样同样的显示,OLED 效果要来得好一些。以目前的技术,

iic介绍与代码实现,对照手册驱动aht20温湿度传感器_aht20驱动-爱代码爱编程

  最近项目使用到温湿度模块AHT20,其为IIC协议,看了大佬们的文章后自己参照着写了一下代码,本文章主要是为了加深自己对IIC的理解所需写,可能存在很多不足,还望见谅。   进入正题,这里先对IIC进行简单的介绍,本介绍来自互联网。IIC也称I2C,是一个多主从的串行总线,由飞利浦公司发明的通讯总线,属于半双工同步传输类总线,仅由两条线就能完成

linux 驱动开发之platform设备驱动一(4)_linux 驱动必须用平台总线吗-爱代码爱编程

前言         Linux 设备和驱动通常都需要挂接在一种总线上,例如PCI、USB、I2C、SPI 等的设备存在真实的总线,这自然不是问题,但是SOC上的外设控制器、挂接在SoC内存空间的外设等却不依附于此类总线。基于这一背景,linux形成了一种虚拟的总线,称为platform 总线,相应的设备称为platform_device,而驱动成为pl

【fpga】fpga实现iic协议读写eeprom(一) -爱代码爱编程

IIC读写接口驱动模块 一、功能分析二、输入/输出信号三、IIC接口驱动状态机四、IIC接口驱动实现五、仿真测试 写在前面 FPGA实现IIC协议读写EEPROM相关文章: IIC通信协议 【FPG