首页 > 其他分享 >FPGA常用通信协议——I2C(二)

FPGA常用通信协议——I2C(二)

时间:2024-03-16 11:05:00浏览次数:20  
标签:wire FPGA clk 通信协议 flag sda input I2C reg

一、时序

  上一篇中我们已经了解了I2C的基本时序,这里我们只考虑I2C的写数据,具体时序见下图

a715b948426d43e39558fc2b8e34dc03.png

 二、信号列表

sta_flag开始信号,拉高时开始一次读或写
sda_en三态门控制信号,控制SDA是输出还是输入
ready_flag结束标志,传入下一个模块,提醒下一模块可继续发送
write总线正在工作信号,这里只考虑写信号
sda_out作为SDA输出时的数据

 三、代码

module IIC_ctrl (
        input           wire            sys_clk         ,
        input           wire            sys_rst_n       ,
        input           wire            sta_flag        ,//开始标志,由外部传入
        input           wire    [7:0]   ins             ,
        input           wire    [15:0]  addr            ,
        input           wire    [7:0]   data            ,
        inout           wire            sda             ,
        output          wire            scl             ,
        output          wire            sda_en          ,
        output          reg             ready_flag      );//结束标志,高电平时可以继续传输数据
        
        
        reg     [6:0]       current_state       ;
        reg     [6:0]       next_state          ;
        reg     [3:0]       bit_cnt             ;//数据传输位数
        reg     [1:0]       clk_cnt             ;//用于产生4分频时钟
        reg                 sda_out             ;//产生sda
        reg                 stop_flag           ;//停止信号
        reg                 start_flag          ;//开始信号
        wire                iic_clk             ;
        reg                 write               ;//写信号,有效时表示正在写
        reg                 sda_en_2            ;
        
        parameter   IDLE=7'b0000001,
                    START=7'b0000010,
                    INS=7'b0000100,
                    ADDR_1=7'b0001000,
                    ADDR_2=7'b0010000,
                    DATA_W=7'b0100000,
                    STOP=7'b1000000;
        
        
        assign sda=sda_en?sda_out:1'bz;
        assign sda_en=((bit_cnt==4'd0)&&(current_state!=IDLE)&&(current_state!=START)&&(current_state!=STOP)&&(write==1'b1))?1'b0:1'b1;
        assign iic_clk=((clk_cnt>=2'd2)&&(clk_cnt<=2'd3))?1'b1:1'b0;
         assign scl=((current_state==IDLE)||(current_state==START)||(current_state==STOP&&sda_en_2==1'b1))?1'b1:iic_clk; 
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        sda_en_2<=1'b1;
        else
        sda_en_2<=sda_en;
        end
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        write<=1'b0;
        else if(bit_cnt==4'd1)
        write<=1'b1;
        else if(stop_flag==1'b1)
        write<=1'b0;
        else
        write<=write;
        end
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        start_flag<=1'b0;
        else if((sda==1'b0)&&(scl==1'b1))
        start_flag<=1'b1;
        else
        start_flag<=1'b0;
        end
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        stop_flag<=1'b0;
        else if(scl==1'b1&&sda==1'b1&&current_state==STOP)
        stop_flag<=1'b1;
        else
        stop_flag<=1'b0;
        end
        
        
        
        
        
        always@(negedge scl or negedge sys_rst_n) begin
        if(!sys_rst_n)
        bit_cnt<=4'b0;
        else if(bit_cnt==4'd8)
        bit_cnt<=4'b0;
        else if(current_state!=STOP)
        bit_cnt<=bit_cnt+1'b1;
        else
        bit_cnt<=bit_cnt;
        end
        
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        clk_cnt<=2'b0;
        else if(clk_cnt==2'b11)
        clk_cnt<=2'b0;
        else
        clk_cnt<=clk_cnt+1'b1;
        end
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        current_state<=IDLE;
        else
        current_state<=next_state;
        end
        
        
        
        
        always@(*) begin//状态机
        if(!sys_rst_n)
        next_state<=IDLE;
        
        else  
        begin
        case(current_state)
        
        IDLE: begin
        if(sta_flag==1'b1)
        next_state<=START;
        else
        next_state<=IDLE;
        end
        
        START:  begin
        if(start_flag==1'b1)
        next_state<=INS;
        else
        next_state<=START;
        end
        
        
        INS:  begin
        if(bit_cnt==4'd0&&clk_cnt==2'd3&&write==1'b1)
        next_state<=ADDR_1;
        else
        next_state<=INS;
        end
        
        ADDR_1: begin
        if(bit_cnt==4'd0&&clk_cnt==2'd3&&write==1'b1)
        next_state<=ADDR_2;
        else
        next_state<=ADDR_1;
        end
        
        
        ADDR_2: begin
        if(bit_cnt==4'd0&&clk_cnt==2'd3&&write==1'b1)
        next_state<=DATA_W;
        else
        next_state<=ADDR_2;
        end
        
        DATA_W: begin
        if(bit_cnt==4'd0&&clk_cnt==2'd3&&write==1'b1)
        next_state<=STOP;
        else
        next_state<=DATA_W;
        end

        STOP: begin
        if(stop_flag==1'b1)
        next_state<=IDLE;
        else
        next_state<=STOP;
        end
        
        default:next_state<=IDLE;
        
        endcase
        end
        end
        
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        ready_flag<=1'b0;
        else if(current_state==IDLE)
        ready_flag<=1'b1;
        else
        ready_flag<=1'b0;
        end
        
        
        always@(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
        sda_out<=1'b1;
        else
        begin
        case(current_state)
        
        
        IDLE:
        sda_out<=1'b1;
        
        
        START:
        sda_out<=1'b0;
        
        
        INS:
        begin
        case(bit_cnt)
        4'd0:
        sda_out<=1'b0;
        4'd1:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[7];
        end
        4'd2:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[6];
        end
        4'd3:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[5];
        end
        4'd4:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[4];
        end
        4'd5:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[3];
        end
        4'd6:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[2];
        end
        4'd7:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[1];
        end
        4'd8:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=ins[0];
        end
        default:sda_out<=1'b1;
        
        endcase
        end
        
        ADDR_1:begin
        case(bit_cnt)
        4'd0:sda_out<=1'b0;
        4'd1:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[15];
        end
        4'd2:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[14];
        end
        4'd3:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[13];
        end
        4'd4:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[12];
        end
        4'd5:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[11];
        end
        4'd6:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[10];
        end
        4'd7:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[9];
        end
        4'd8:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[8];
        end
        default:sda_out<=1'b1;
        endcase
        end
        
        
        ADDR_2:begin
        case(bit_cnt)
        4'd0:sda_out<=1'b0;
        4'd1:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[7];
        end
        4'd2:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[6];
        end
        4'd3:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[5];
        end
        4'd4:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[4];
        end
        4'd5:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[3];
        end
        4'd6:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[2];
        end
        4'd7:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[1];
        end
        4'd8:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=addr[0];
        end
        default:sda_out<=1'b1;
        endcase
        end
        
        DATA_W:begin
        case(bit_cnt)
        4'd0:sda_out<=1'b0;
        4'd1:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[7];
        end
        4'd2:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[6];
        end
        4'd3:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[5];
        end
        4'd4:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[4];
        end
        4'd5:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[3];
        end
        4'd6:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[2];
        end
        4'd7:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[1];
        end
        4'd8:begin
        if(clk_cnt==2'd0||clk_cnt==2'd3)
        sda_out<=data[0];
        end
        default:sda_out<=1'b1;
        endcase
        end
        
        STOP:
        begin
        if(scl==1'b1)
        sda_out<=1'b1;
        else
        sda_out<=1'b0;
        end
        
        
	endcase
	end
	end

	endmodule

四、代码简要分析

  代码主体比较简单,按照时序来写就行了,有几点要说明一下,这里输入的sta_flag来自另一个模块,只要IIC_Ctrl模块的ready_flag拉高,sta_flag就会拉高,写数据就会开始,因为那个模块是在配置摄像头的时候写的,之后讲OV5640的时候再贴代码。兄弟们可以自己写一个简单的开始信号发送的模块。

  还有就是SDA的赋值这里,常用的双向信号赋值就是向我写的那样用一个assign语句,一个sda_out做跳板。实际的写法有很多,这种比较简单,他的写法也可以很复杂,感兴趣的兄弟可以自行了解。

  状态机那一部分,I2C的应答ACK必须是低电平才可以接着下一步,这里是按照SCCB来写的,基本是一样,但是SCCB的ACK不关心是什么状态,也就是1或0都可以。如果要验证I2C的兄弟这里得注意一下。

五、一些话

  本来打算把常用的三个协议写完了再写其他东西的,前两天翻看我很久之前写的SPI代码,有点惨不忍睹,虽然能正常跑起来,但是可读性很差。我最近也没心思去重写一遍了,网上资料很多,兄弟们自己去看吧。接下来会跟新OV5640的配置内容。

标签:wire,FPGA,clk,通信协议,flag,sda,input,I2C,reg
From: https://blog.csdn.net/weixin_62966975/article/details/136727925

相关文章

  • 14. I2C读取EEPROM
    一、AT24C02简介  AT24C02是一个2Kbit的串行EEPROM存储器,内部含有256个字节。在24C02里面还有一个8字节的页写缓冲器。该设备的通信方式I2C,通过其SCL和SDA与其他设备通信,芯片的引脚图如下图所示。  上图中有一个WP,这个是写保护引脚,接高电平只读,接地允许......
  • (笔记)FPGA多周期路径及set_multicycle_path详解
    默认情况下综合工具会把每条路径定义为单周期路径,即源触发器在时钟的任一边沿启动(launch)的数据都应该由目的触发器在时钟的下一上升沿捕获(capture)。有的设计可能存在时序例外(timingexceptions),如多周期路径、虚假路径等。数据从起点到终点的传输时间需要一个时钟周期以上才能稳定......
  • FPGA交通信号灯设计报告(VHDL语言)
    FPGA的大作业我选择了交通灯控制系统的设计,此课程只有2个学分,因此只需要在相应软件仿真出结果即可。以下是我的设计报告,当时写的匆忙,没有对代码进行优化改进,但仿真结果是正确的,可以给大家提供一下思路。一、任务分析1.输入和输出2.多进程3.特殊情况4.注意二、quartus......
  • m基于FPGA的Alamouti编码verilog实现,包含testbench测试文件
    1.算法仿真效果 本系统进行了Vivado2019.2平台的开发,结果如下:   2.算法涉及理论知识概要        在无线通信领域,多天线技术是提高系统容量和可靠性的关键手段之一。Alamouti编码是空时编码(STC)的一种,它为两发射天线的系统提供了一种全速率、全分集的简单编码方......
  • FPGA的FIFO部分的知识点
    看的小梅哥的新视频,FIFO这边讲的不太清楚,换正点原子的fifo听一下。后面的以太网和HDMI有空也看一下正点原子的,主要是想快速看zynq的知识,而且现在学的很多都是模仿抄代码,真正自己来还是得工程中实际应用的时候才会使用学习FIFO,先入先出,像队列。常用于数据的缓存,因为数据的读写带......
  • 13. I2C通信协议
    一、I2C通信协议简介  I2C(Inter-IntegratedCircuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据,在CPU与被控IC之间、IC与IC之间进行双向传送。  I2C总线有如下特......
  • 从零开始利用MATLAB进行FPGA设计(一):建立脉冲检测模型的Simulink模型2
    目录1.模块的总体结构1.1从工作空间导入输入信号1.2FIR滤波器2.Subsystem3.MATLABFunction文章灵感来源于MATLAB官方免费教程:HDLCoderSelf-GuidedTutorial考虑到MATLAB官网的英文看着慢,再加上视频讲解老印浓浓的咖喱味,我决定记录利用MATLAB&Simulink&SystemGenerat......
  • FPGA的时钟IP核知识点
    IP核在我看来就跟stm32中的一些驱动的库函数一样,可以调用快速使用。不用一步一步的自己写底层原理。可以加速设计,快速设计代码。IP核的PLL还有一个MMCM。PLL是锁相环,对时钟进行管理。也是后面使用中很重要的IP核。不同器件需要不同的时钟。时钟管理单元CMT=PLL+MMCM混合时钟管......
  • 基于FPGA各种视频接口转换的国产化设计
    随着国产化进程推进,现在许多项目需要实现国产化设计,本博主通过器件选型/原理图设计,到视频接口输入,DDR3缓存,再到图像输出,使用者可在此基础实现二次开发,功能实现通过verilog,操作简单,添加功能方便。接口包含lvds/camelink/bt1120/hdmi/sdi等等常用视频接口,也可定制其他接口,带......
  • OMAP-L138 + FPGA开发板(Pin-To-Pin国产FPGA)
    XQ138F-EVM是广州星嵌电子科技有限公司基于TIOMAP-L138(定点/浮点DSPC674x+ARM9)+FPGA处理器研制的开发板;OMAP-L138选用TI德州仪器的TMS320C6748+ARM926EJ-S异构双核处理器,主频456MHz,高达3648MIPS和2746MFLOPS的运算能力;FPGA采用中科亿海微eHiChip6家族EQ6......