FPGA之旅设计99例之第五例-----IIC通信

本文涉及的产品
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介: 笔记

一. 简介


这是FPGA之旅设计的第五例啦!今天给大家带来的是IIC通信,IIC协议应用非常广泛,例如与MPU6050进行通信,配置OV5640摄像头、驱动OLED屏幕等等,都需要使用到IIC协议,所以掌握它是非常必要的,废话不多说,接着往下看。文末获取完整代码。


二. IIC简介


IIC协议分为主机和从机,所有的请求都是由主机发出,从机进行响应,从机是没有办法对主机进行读或写的。IIC协议共有两根线,数据线SDA和时钟线SCL,两根线就可以完成所以的通信请求,简直是太给力了。


三. IIC协议


终于到了IIC协议的部分。IIC协议简单来说,共有五种状态,这五种状态的有序组合就组成了完整的IIC通信,学习IIC协议,就是学习这五种状态。


空闲态: SCL 和 SDA 都为高电平,不进行通信的时候。

起始态: 在SCL为高电平的时候,将SDA拉低,主机通知从机,开始进行通信。

数据传输态:数据传输态,又可以分为读和写两个部分,过程都是一样的,就合在一起了,都是在SCL为低电平的时候,SDA将数据发送,在SCL为高电平的时候,将数据接收。

(非)应答态:数据传输态完成后,必须接一个应答态或者非应答态,为了确定对方接收到了数据。在SCL为高电平的时候,检测到SDA为低电平,则为应答,否则为非应答。

停止态:一次数据传输完成,由主机发起,在SCL为高电平的时候,SDA由低电平变成高电平。

了解了这五种状态后,接下来就要学习如何使用这五种状态来进行读写操作了。


(一) IIC写操作

下面就是一个完整的写操作,共包含三次数据传输态,第一次发送的是从机地址 + 0,第二次发送的是寄存器的地址,第三次写的是数据,写入寄存器中的数据。从机地址一般为7bit,与另外一bit共同组成8bit,0表示写,1表示读。


14.png15.png

(二)IIC读操作

读操作要比写操作复杂一点,需要的状态多一些。一共有五个数据传输态,状态图如下了。


16.png


17.png


上面的流程图都是对从机的地址为7位以及从机的寄存器地址为8位的操作。


四. Verilog代码实现


有了上面的各个状态中,SDA和SCL的变换关系,以及读写的序列,就可以很方便的来写程序啦。


首先,当然离不开状态机,根据上面叙述的五种状态,编写状态机,状态机中,将数据传输态分成了读和写两种状态。有了各个状态,操作SDA和SCL两根线不是易如反掌嘛!


/*IIC 状态*/
localparam IIC_IDLE       =   6'b000_001;  /*空闲态*/
localparam IIC_START      =   6'b000_010;  /*起始态*/
localparam IIC_WRDATA     =   6'b000_100;  /*写数据态*/
localparam IIC_RDDATA     =   6'b001_000;  /*读数据态*/
localparam IIC_ACK        =   6'b010_000;  /*应答态*/
localparam IIC_STOP       =   6'b100_000;  /*停止态*/


状态机的跳转条件如下,跳转条件和上面叙述的一样。单独看这个有点难懂,有些变量不明白其具体含义,可以结和仿真图形和完整代码进行理解。


/*状态机*/
always @(*)
begin
    case(state)
    IIC_IDLE: 
        if(IICWriteReq == 1'b1 || IICReadReq == 1'b1)
            next_state <= IIC_START;
        else
            next_state <= IIC_IDLE;
    IIC_START:
        if(IICCnt == (IIC_Pre * 'd2))
            next_state <= IIC_WRDATA;
        else
            next_state <= IIC_START;
    IIC_WRDATA:
        if(IICBitCnt == 'd8 && IICCnt == IIC_Pre /4 && iicCLK == 1'b0)
            next_state <= IIC_ACK;
        else
            next_state <= IIC_WRDATA;
    IIC_RDDATA:
        if(IICBitCnt == 'd8 && IICCnt == IIC_Pre /4 && iicCLK == 1'b0)
            next_state <= IIC_ACK;
        else
            next_state <= IIC_RDDATA;
    IIC_ACK:
        if(IICACKStopCnt == 'd1 && IICCnt == IIC_Pre /4 && iicCLK == 1'b0)
            if(IICSendBytes == 'd3) 
                if(IICWriteReq == 1'b1)         /*三个字节发送完成,进入停止态*/
                    next_state <= IIC_STOP;
                else 
                    next_state <= IIC_RDDATA;
            else if(IICSendBytes == 'd2 && IICReadReq == 1'b1)
                next_state <= IIC_START;
            else if(IICSendBytes == 'd4)
                next_state <= IIC_STOP;
            else
                next_state <= IIC_WRDATA;
        else
            next_state <= IIC_ACK;
    IIC_STOP:
        if(IICACKStopCnt == 'd1 && IICCnt == IIC_Pre/4 && iicCLK == 1'b1)
            next_state <= IIC_IDLE;
        else
            next_state <= IIC_STOP;
    default:  next_state <= IIC_IDLE;
    endcase
end

各个部分实现的详细代码,就不列举出来啦,代码总计280多行,也不算多。通过本IIC模块,可以驱动OV5640摄像头,MPU6050模块和0.96寸OLED屏幕等等,后续会基于此模块,来驱动这些外设。


五. testbeach编写


还是按照流程走,编写完模块后,进行一下仿真,还真有错误,幸亏仿真了,哈哈哈。


`timescale 1ns/1ps
module testbench();
    reg  clk;
    reg  rst;
    wire  SDA;
    wire  SCL;
    reg IICWriteReq;
    reg IICReadReq;
    wire IICWriteDone;
    wire IICReadDone;
    always # 50 clk = ~clk;
    initial begin
        clk = 1'b1;
        rst = 1'b1;
        IICWriteReq = 1'b0;
        IICReadReq = 1'b1;
        #100   /*手动复位*/
        rst = 1'b0;
        #100
        rst = 1'b1;
    end
    always@(posedge clk)
        if(IICReadDone == 1'b1)   /*读完成后,readReq为0,只进行一次读写操作*/
            IICReadReq <= 1'b0;
        else
            IICReadReq <= IICReadReq;
IIC_Driver  IIC_DriverHP(
    .sys_clk            (clk),           /*系统时钟*/
    .rst_n              (rst),             /*系统复位*/
    .IICSCL             (SCL),            /*IIC 时钟输出*/
    .IICSDA             (SDA),             /*IIC 数据线*/
    .IICSlave           ('h1234),
    .IICWriteReq        (IICWriteReq),       /*IIC写寄存器请求*/
    .IICWriteDone        (IICWriteDone),      /*IIC写寄存器完成*/
    .IICWriteData        ('h5a), /*IIC发送数据 8bit的从机地址 + 8bit的寄存器地址 + 8bit的数据(读忽略,后默认为0)*/
    .IICReadReq         (IICReadReq),        /*IIC读寄存器请求*/
    .IICReadDone        (IICReadDone),       /*IIC读寄存器完成*/
    .IICReadData        ()/*IIC读取数据*/
);
endmodule


需要完整代码的可以关注微信公众号 FPGA之旅 回复 :FPGA之旅设计99例之第五例

公众号:FPGA之旅

目录
相关文章
|
2月前
|
人工智能 物联网 5G
|
10月前
|
异构计算
【乌拉喵.教程】IIC总线介绍及FPGA编程(二)
【乌拉喵.教程】IIC总线介绍及FPGA编程(二)
【乌拉喵.教程】IIC总线介绍及FPGA编程(二)
|
10月前
|
算法 安全 芯片
【乌拉喵.教程】IIC总线介绍及FPGA编程(一)
【乌拉喵.教程】IIC总线介绍及FPGA编程(一)
【乌拉喵.教程】IIC总线介绍及FPGA编程(一)
|
11月前
|
传感器 5G 数据处理
ZYNQ(FPGA)与DSP之间GPIO通信
基于 TI KeyStone 架构 C6000 系列 TMS320C6657双核C66x 定点/浮点 DSP以及 Xilinx Zynq-7000 系列 XC7Z035/045 SoC 处理器设计的高端异构多核评估板,由核心板与评估底板组成。 DSP采用 TMS320C6657 双核C66x 定点/浮点,每核主频1GHz/1.25GHz。 Xilinx Zynq SoC处理器采用的XC7Z035/045集成PL端Kintex-7架构+PS 端双核ARM Cortex-A9 ,28nm可编程逻辑资源。
ZYNQ(FPGA)与DSP之间GPIO通信
|
11月前
|
算法 数据安全/隐私保护 芯片
m基于FPGA的通信数据帧加扰解扰verilog实现,包含testbench
m基于FPGA的通信数据帧加扰解扰verilog实现,包含testbench
263 0
|
算法 异构计算
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
270 0
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
|
数据采集 数据处理 异构计算
ZYNQ(FPGA)与DSP之间SRIO通信实现
XQ6657Z35-EVM多核开发板通过SPI、EMIF16、uPP、SRIO 通信接口将DSP 与Zynq 结合在一起,组成DSP+Zynq 架构,实现了需求独特、灵活、功能强大的DSP+Zynq 高速数据采集处理系统。
ZYNQ(FPGA)与DSP之间SRIO通信实现
|
芯片
FPGA-(OV摄像头SCCB协议)IIC驱动代码
FPGA-(OV摄像头SCCB协议)IIC驱动代码
241 0
|
Go C语言 异构计算
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写
1. int main() 2. { 3. int A; 4. A = 16; 5. } 代码17.1 话题为进入之前,首先让我们来聊聊一些题外话。那些学过软核NIOS的朋友可曾记得,软核NIOS可利用片上内存作为储存资源,而且它也能利用SDRAM作为储存资源,然而问题是在这里 ... 如代码17.1所示,笔者先建立变量A,然后变量A赋值16。
953 0

热门文章

最新文章