使用Verilog设计I2C控制器

本文介绍了如何在FPGA中实现I2C协议,包括I2C的基本时序、状态机设计和Verilog代码实现。同时,给出了Zynq7010平台上的C语言驱动代码,用于控制I2C接口,进行读写操作。通过案例测试验证了I2C通信的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

最近想用FPGA数字图像处理加速,Camera采集到图像后直接在硬件上处理后再交给软件做二次处理。所以首先是要把Sensor给跑起来,目前也没有太多要求,采用OV2640 Sensor,IIC+DVP的接口,所以第一步要把I2C调通。虽然FPAG上也带有I2C IP,但自己写一个应该会更有乐趣,所以就有了本文。

IIC时序介绍

IIC其实时序非常简单,就俩根线SCL和SDA,逻辑0时电平为GND,逻辑高时为高阻态,一个完整时序如下:

一个完整的时序可以拆分为三个步骤:

(1)开始信号S:保证SCL为逻辑1时,SDA从逻辑1跳变为逻辑0。(后文标识位[S])

(2)数据位+ACK位: SCL下降沿时SDA准备好数据,SCL上升沿时采样。(后文标志[DA])

(3)停止信号P:保证SCL为逻辑1时,SDA从逻辑0跳变为逻辑1。(后文标志[P])

电路设计思想

分析IIC, 一般有如下几种组合:

1. 单byte读/写: [S]开始  +  [DA]器件地址  +  [DA]数据  +  [P]停止

2. 多byte读/写: [S]开始  +  [DA]器件地址  +  [DA]数据*n个  +  [P]停止

3. 单byte复合读/写: [S]开始  +  [DA]器件地址  +  [DA]片内地址 + [DA]数据 + [P]停止

4. 单byte复合读/写: [S]开始  +  [DA]器件地址  +  [DA]片内地址 + [DA]数据*n个 + [P]停止

5. 寻址扫描: [S]开始  +  [DA]器件地址  +  [P]停止

根据上述分析,可以设计一个状态机,状态图如下:

在软件上,只需要控制 [S]开始信号、[DA]数据和ACK位、[P]停止信号 状态的切换即可即可实现常用组合

Verilog实现

/**
 * qinhuqiang @2022-3-20
 * 后续需要考虑到的问题:
 *  (2) DMA 自动传输
 *  (3) 考虑10bit iic器件地址
 *  (4) 增加slave模式
 */

module iic (
    inout wire              iic_sda,        /* serial date line */
    inout wire              iic_scl,        /* serial clock line */
    input wire              mod_rst,        /* i2c module reset signal */
    input wire              mod_clk,        /* i2c module clock signal */
    input wire [15:0]       mod_div,        /* iic dividing frequency signal */
    output wire [7:0]       mod_sta,        /* iic status signal */
    /*
     * mod_sta [only read] 位分布:
     *  [0]: 1:iic总线空闲          0:iic总线忙
     *  [1]: 1:slave被寻址          0:slave没有被寻址
     *  [2]: 1:收到ACK              0:没有收到ACK
     *  [3]: 1:mod_cmd[1]未完成     0:mod_cmd[1]操作完成
     *  [4]: 1:mod_cmd[3]未完成     0:mod_cmd[3]操作完成
     *  [5]: 1:mod_cmd[5]未完成     0:mod_cmd[5]操作完成
     *  [6]: 1:mod_cmd[6]未完成     0:mod_cmd[6]操作完成
     */
    input wire [7:0]        mod_wdata,      /* iic write date */
    output wire [7:0]       mod_rdata,      /* iic read date */
    input wire [7:0]        mod_cmd         /* iic command input */
    /*
     * mod_cmd [only write] 位分布:
     *  [0]: 0->1 跳变时将mod_div设置到reg_div
     *  [1]: 0->1 跳变时发送start信号, 开始占用总线 (自动从slave模式切换到master模式)
     *  [2]: 0->1 跳变时将mod_wdata内容锁存到移位寄存器
     *  [3]: 0->1 跳变时将产生9bit写总线时钟 (8bit 数据 + 1bit ACK)
     *  [4]: 0->1 跳变时, 设置读总线时, stop信号前不需要发送ACK (master模式和slave模式都可用)
     *  [5]: 0->1 跳变时将产生9bit读总线时钟 (8bit 数据 + 1bit ACK)
     *  [6]: 0->1 跳变时发送stop信号, 释放总线 (自动从master模式切换到slave模式)
     */
);

/* iic输出参考状态, iic标准是高阻态, 如果设置wei1, 就只是为了调试方便, 仅此而已 */
localparam IIC_OUTPUT_VER = 1'bz;
// localparam IIC_OUTPUT_VER = 1'b1;

/* 配置分频寄存器 */
reg [15:0] reg_div;
always @(posedge mod_cmd[0] or posedge mod_rst) begin
    if (mod_rst)
        reg_div <= 16'd0;
    else
        reg_div <= mod_div;
end

/* 分频器比较逻辑 */
reg [15:0] reg_div_cmp;
wire wire_scl_filp;
assign wire_scl_filp = reg_div == reg_div_cmp;
always @(posedge mod_clk or posedge mod_rst) begin
    if (mod_rst)
        reg_div_cmp <= 16'd0;
    else if(wire_scl_filp)
        reg_div_cmp <= 16'd0;
    else
        reg_div_cmp <= reg_div_cmp + 1'b1;
end

/* 当前iic模式 */
reg reg_iic_mode;
localparam IIC_MODE_SLAVE = 1'b0;         /* slave模式 */
localparam IIC_MODE_MASTER = 1'b1;        /* master模式 */

/* 构建iic master时钟 */
reg reg_master_scl;
always @(posedge mod_clk or posedge mod_rst) begin
    if (mod_rst)
        reg_master_scl <= 1'b1;
    else if (wire_scl_filp)
        reg_master_scl <= ~reg_master_scl;
    else;
end

/* iic master状态机 */
reg [3:0] reg_iic_state;
localparam IIC_STATE_HSCL = 4'd0;         /* 发送停止位2, 拉高SCL */
localparam IIC_STATE_LALL = 4'd1;         /* 发送停止位1, 俩跟线拉低 */
localparam IIC_STATE_ACK  = 4'd2;         /* 操作ACK位 */
localparam IIC_STATE_BIT0 = 4'd3;         /* 操作第0位 */
localparam IIC_STATE_BIT1 = 4'd4;         /* 操作第1位 */
localparam IIC_STATE_BIT2 = 4'd5;         /* 操作第2位 */
localparam IIC_STATE_BIT3 = 4'd6;  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值