zynq之fpga学习----iic协议驱动模块仿真实验(代码片段)

鲁棒最小二乘支持向量机 鲁棒最小二乘支持向量机     2022-12-15     309

关键词:

1 IIC通信协议简介

IIC通信协议基础知识学习:硬件设计基础----通信协议IIC

2 实验任务

设计IIC驱动模块,并进行仿真验证,观察仿真波形

3 实验设计

3.1 创建工程

新建工程,操作如图所示:

输入工程名和路径,如图:

选择创建RTL工程,如图:

直接点击Next:

继续点击Next:

添加芯片型号,操作如图:

工程创建完成:

3.2 设计输入

创建工程文件,操作如图所示:

创建iic_drive文件:

创建完成:

双击打开,输入代码如下:

module iic_drive(
//系统接口
    input               clk,      //时钟信号,50MHZ
    input               rst_n,    //复位信号,低电平有效
 //IIC时序控制接口                   
    input                i2c_exec   ,  //IIC触发执行信号
    input                bit_ctrl   ,  //字地址位控制(16b/8b)
    input                i2c_rh_wl  ,  //IIC读写控制信号
    input        [15:0]  i2c_addr   ,  //IIC器件内地址
    input        [ 7:0]  i2c_data_w ,  //IIC要写的数据
    output  reg  [ 7:0]  i2c_data_r ,  //IIC读出的数据
    output  reg          i2c_done   ,  //IIC一次操作完成
    output  reg          i2c_ack    ,  //IIC应答标志 0:应答 1:未应答
//IIC物理接口
    output  reg          scl        ,  //IIC的SCL时钟信号
    inout                sda        ,  //IIC的SDA信号                                 
    //user interface                   
    output  reg          dri_clk       //驱动IIC操作的驱动时钟
    );
parameter   SLAVE_ADDR = 7'b1010000   ;  //IIC从机地址    
parameter   CLK_FREQ   = 26'd50_000_000; //模块输入的时钟频率
parameter   I2C_FREQ   = 18'd250_000;     //IIC_SCL的时钟频率 ,250k   
//状态机定义
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'b0001_0000; //写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000; //发送器件地址读
localparam  st_data_rd  = 8'b0100_0000; //读数据(8 bit)
localparam  st_stop     = 8'b1000_0000; //结束IIC操作
//reg 定义
reg            sda_dir   ; //IIC数据(SDA)方向控制
reg            sda_out   ; //SDA输出信号
reg            st_done   ; //状态结束
reg            wr_flag   ; //写标志
reg    [ 6:0]  cnt       ; //计数
reg    [ 7:0]  cur_state ; //状态机当前状态
reg    [ 7:0]  next_state; //状态机下一状态
reg    [15:0]  addr_t    ; //地址
reg    [ 7:0]  data_r    ; //读取的数据
reg    [ 7:0]  data_wr_t ; //IIC需写的数据的临时寄存
reg    [ 9:0]  clk_cnt   ; //分频时钟计数

//wire 定义
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数
//SDA控制
assign  sda        = sda_dir ?  sda_out : 1'bz   ;  //SDA数据输出或高阻
assign  sda_in     = sda                         ;  //SDA数据输入
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2 ;  //模块驱动时钟的分频系数

//生成IIC的SCL的四倍频率的驱动时钟用于驱动IIC的操作
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dri_clk <=  1'b0;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
        clk_cnt <= 10'd0;
        dri_clk <= ~dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle: begin                          //空闲状态
           if(i2c_exec) begin
               next_state = st_sladdr;
           end
           else
               next_state = st_idle;
        end
        st_sladdr: begin
            if(st_done) begin
                if(bit_ctrl)                    //判断是16位还是8位字地址
                   next_state = st_addr16;
                else
                   next_state = st_addr8 ;
            end
            else
                next_state = st_sladdr;
        end
        st_addr16: begin                        //写16位字地址
            if(st_done) begin
                next_state = st_addr8;
            end
            else begin
                next_state = st_addr16;
            end
        end
        st_addr8: begin                         //8位字地址
            if(st_done) begin
                if(wr_flag==1'b0)               //读写判断
                    next_state = st_data_wr;
                else
                    next_state = st_addr_rd;
            end
            else begin
                next_state = st_addr8;
            end
        end
        st_data_wr: begin                       //写数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_wr;
        end
        st_addr_rd: begin                       //写地址以进行读数据
            if(st_done) begin
                next_state = st_data_rd;
            end
            else begin
                next_state = st_addr_rd;
            end
        end
        st_data_rd: begin                       //读取数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_rd;
        end
        st_stop: begin                          //结束I2C操作
            if(st_done)
                next_state = st_idle;
            else
                next_state = st_stop ;
        end
        default: next_state= st_idle;
    endcase
end

//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
    //复位初始化
    if(!rst_n) begin
        scl       <= 1'b1;
        sda_out   <= 1'b1;
        sda_dir   <= 1'b1;                          
        i2c_done  <= 1'b0;                          
        i2c_ack   <= 1'b0;                          
        cnt       <= 1'b0;                          
        st_done   <= 1'b0;                          
        data_r    <= 1'b0;                          
        i2c_data_r<= 1'b0;                          
        wr_flag   <= 1'b0;                          
        addr_t    <= 1'b0;                          
        data_wr_t <= 1'b0;                          
    end                                              
    else begin                                       
        st_done <= 1'b0 ;                            
        cnt     <= cnt +1'b1 ;                       
        case(cur_state)                              
             st_idle: begin                          //空闲状态
                scl     <= 1'b1;                     
                sda_out <= 1'b1;                     
                sda_dir <= 1'b1;                     
                i2c_done<= 1'b0;                     
                cnt     <= 7'b0;               
                if(i2c_exec) begin                   
                    wr_flag   <= i2c_rh_wl ;         
                    addr_t    <= i2c_addr  ;         
                    data_wr_t <= i2c_data_w;  
                    i2c_ack   <= 1'b0;                      
                end                                  
            end                                      
            st_sladdr: begin                         //写地址(器件地址和字地址)
                case(cnt)                            
                    7'd1 : sda_out <= 1'b0;          //开始IIC
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= SLAVE_ADDR[5]; 
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= SLAVE_ADDR[4]; 
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= SLAVE_ADDR[3]; 
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= SLAVE_ADDR[2]; 
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= SLAVE_ADDR[1]; 
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= SLAVE_ADDR[0]; 
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: sda_out <= 1'b0;          //0:写
                    7'd33: scl <= 1'b1;              
                    7'd35: scl <= 1'b0;              
                    7'd36: begin                     
                        sda_dir <= 1'b0;             
                        sda_out <= 1'b1;                         
                    end                              
                    7'd37: scl     <= 1'b1;            
                    7'd38: begin                     //从机应答 
                        st_done <= 1'b1;
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位     
                    end                                          
                    7'd39: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_addr16: begin                         
                case(cnt)                            
                    7'd0 : begin                     
                        sda_dir <= 1'b1 ;            
                        sda_out <= addr_t[15];       //传送字地址
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= addr_t[14];    
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= addr_t[13];    
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= addr_t[12];    
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= addr_t[11];    
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= addr_t[10];    
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= addr_t[9];     
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= addr_t[8];     
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;             
                        sda_out <= 1'b1;   
                    end                              
                    7'd33: scl  <= 1'b1;             
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end        
                    7'd35: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_addr8: begin                          
                case(cnt)                            
                    7'd0: begin                      
                       sda_dir <= 1'b1 ;             
                       sda_out <= addr_t[7];         //字地址
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= addr_t[6];     
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= addr_t[5];     
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= addr_t[4];     
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= addr_t[3];     
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= addr_t[2];     
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= addr_t[1];     
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= addr_t[0];     
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;         
                        sda_out <= 1'b1;                    
                    end                              
                    7'd33: scl     <= 1'b1;          
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end   
                    7'd35: begin                     
                        scl <= 1'b0;                 
                        cnt <= 1'b0;                 
                    end                              
                    default :  ;                     
                endcase                              
            end                                      
            st_data_wr: begin                        //写数据(8 bit)
                case(cnt)                            
                    7'd0: begin                      
                        sda_out <= data_wr_t[7];     //IIC写8位数据
                        sda_dir <= 1'b1;             
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= data_wr_t[6];  
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= data_wr_t[5];  
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= data_wr_t[4];  
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= data_wr_t[3];  
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= data_wr_t[2];  
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= data_wr_t[1];  
                    7'd25: scl <= 1'b1;              
                    7'd27: scl <= 1'b0;              
                    7'd28: sda_out <= data_wr_t[0];  
                    7'd29: scl <= 1'b1;              
                    7'd31: scl <= 1'b0;              
                    7'd32: begin                     
                        sda_dir <= 1'b0;           
                        sda_out <= 1'b1;                              
                    end                              
                    7'd33: scl <= 1'b1;              
                    7'd34: begin                     //从机应答
                        st_done <= 1'b1;     
                        if(sda_in == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end          
                    7'd35: begin                     
                        scl  <= 1'b0;                
                        cnt  <= 1'b0;                
                    end                              
                    default  :  ;                    
                endcase                              
            end                                      
            st_addr_rd: begin                        //写地址以进行读数据
                case(cnt)                            
                    7'd0 : begin                     
                        sda_dir <= 1'b1;             
                        sda_out <= 1'b1;             
                    end                              
                    7'd1 : scl <= 1'b1;              
                    7'd2 : sda_out <= 1'b0;          //重新开始
                    7'd3 : scl <= 1'b0;              
                    7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址
                    7'd5 : scl <= 1'b1;              
                    7'd7 : scl <= 1'b0;              
                    7'd8 : sda_out <= SLAVE_ADDR[5]; 
                    7'd9 : scl <= 1'b1;              
                    7'd11: scl <= 1'b0;              
                    7'd12: sda_out <= SLAVE_ADDR[4]; 
                    7'd13: scl <= 1'b1;              
                    7'd15: scl <= 1'b0;              
                    7'd16: sda_out <= SLAVE_ADDR[3]; 
                    7'd17: scl <= 1'b1;              
                    7'd19: scl <= 1'b0;              
                    7'd20: sda_out <= SLAVE_ADDR[2]; 
                    7'd21: scl <= 1'b1;              
                    7'd23: scl <= 1'b0;              
                    7'd24: sda_out <= SLAVE_ADDR[1]; 
                    7'd25: scl <= 1'b1;              
                    7查看详情  

zynq之fpga学习----uart串口实验(代码片段)

1UART串口简介UART串口基础知识学习:硬件设计基础----通信协议UART2实验任务上位机通过串口调试助手发送数据给Zynq,ZynqPL端通过RS232串口接收数据并将接收到的数据发送给上位机,完成串口数据环回,管脚分配如... 查看详情

zynq之fpga学习----ramip核使用实验(代码片段)

1RAMIP核介绍RAM的英文全称是RandomAccessMemory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据,其读写速度由时钟频率决定Xilinx7系列器件具有嵌入式存储... 查看详情

zynq之fpga学习----vivado功能仿真(代码片段)

1Vivado功能仿真阅读本文需先学习:FPGA学习----Vivado软件使用典型的FPGA设计流程,如图所示:图片来自《领航者ZYNQ之FPGA开发指南》Vivado设计套件内部集成了仿真器VivadoSimulator,能够在设计流程的不同阶段运行设计的功... 查看详情

zynq之fpga学习----fifoip核使用实验(代码片段)

1FIFOIP核介绍FIFO的英文全称是FirstInFirstOut,即先进先出。与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,缺点就是不能像RAM和ROM那样可以由地... 查看详情

sylixos下基于zynq-7000加载fpga程序

...GA程序的在线加载。2、使用方法介绍2.1工程所在目录devcfg驱动模块及应用程序位于本地git仓库中,暂不提供源码,如有需要可自行移植。2.2使用方法2.2.1部署驱 查看详情

zynq之fpga学习----mmcm/pllip核使用实验(代码片段)

1MMCM/PLLIP核介绍PLL的英文全称是PhaseLockedLoop,即锁相环,是一种反馈控制电路。PLL对时钟网络进行系统级的时钟管理和偏移控制,具有时钟倍频、分频、相位偏移和可编程占空比的功能Xilinx7系列器件中的时钟资源包含... 查看详情

zynq之hls学习----开篇实验(代码片段)

1VivadoHLS简介Xilinx推出的VivadoHLS工具可以直接使用C、C++或SystemC来对Xilinx系列的FPGA进行编程FPGA设计中从底层向上一共存在着四种抽象层级,依次为:结构性的、RTL、行为性的和高层VivadoHLS的功能简单地来说就是把C、... 查看详情

zynq之嵌入式学习----开篇实验helloworld(代码片段)

1ZYNQ嵌入式系统的开篇实验HelloWorld阅读本文需先学习:FPGA学习----Vivado软件使用1.1ZYNQ嵌入式系统开发流程创建Vivado工程使用IPIntegrator创建ProcessorSystem生成顶层HDL生成比特流,导出到SDK在SDK中创建应用工程板级验证开篇实验任务... 查看详情

一视频处理fpga驱动ov7725摄像头模块(代码片段)

...fifo的摄像头,开发板是正点原子的开拓者。一、SCCB协议与IIC协议的不同摄像头采用的是SCCB协议,这个协议与I2C协议很像,但是有细微的区别。差别在于SCCB传输协议中,第9位为不必关心位,而IIC写传输协议位... 查看详情

如何学习zynq以太网控制器及协议栈

参考技术A说到学习ZYNQ+SOC+Linux开发,我认为主要应该细分为lian两大点:zynq,soc合为一个点,linux为一个点。下面我就给大家介绍学习的流程和路线。一,学习zynq+soc的FPGA开发部分和片上ARM核的寄存器,裸奔应用开发,我推荐大... 查看详情

fpga基础设计:iic协议

...ADC、串行DAC)都是通过IIC总线来和控制器通信的。不过IIC协议仍然是一种慢速的通信方式,标准IIC速率为100kbit/s,快速模式速率为400kbit/s。本文致力于讲述如何用计数器控制和分频时钟控制两种方式完成IIC的读写操作。IIC协议??I... 查看详情

zynq之fpga学习----vivado软件使用(代码片段)

...以完成从设计输入到硬件配置的完整FPGA设计流程。Vivado学习使用版本为Vivadov18.3Vivado软件使用流程:新建工程设计输入分析与综合约束输入设计实现生成和下载比特流1.1新建工程Vivado软件启动界面如图所示,点击CreateProje... 查看详情

fpga的学习:基于spi协议的flash驱动写操作控制(代码片段)

系统整体框图如图:顶层模块的设计:flash页写模块的设计:时序图:页写的时序:各个代码块如下:`timescale1ns/1nsmodulekey_filter#(parameterCNT_MAX=20'd999_999//计数器计数最大值)(inputwiresys_clk,//系统时钟50... 查看详情

zynq开发之hls

Zynq开发之HLS由 FPGA菜鸟 于星期三,06/28/2017-11:53发表HLS简介HLS(HighLevelSynthesis)即高层次综合,不同于以往的FPGA逻辑开发,是用HDL编写的,开发周期长、难度大。而HLS可以使用C,C++,SystemC以及OPenCL等编写,通过高层次综合,... 查看详情

基于stm8的iic协议--实例篇--时钟模块(ds3231)读取(代码片段)

1.综述  由上篇博客可知道IIC协议如何用代码实现,本篇博客就不涉及协议内容,只讲解如何使用。  本次的实验传感为:DS3231(时钟模块),对于时钟模块的具体信息我也就不多介绍大家可以自行度娘,具体功能无非就是... 查看详情

linux下iic驱动编写,介绍iic子系统框架的使用

一、IIC协议介绍说起IIC,搞单片机,嵌入式的那肯定是接触的比较多的。串口、IIC、SPI这3个协议在单片机阶段应该是用比较多的,很多的外设模块,芯片都是串口、IIC、SPI等协议与主控芯片进行通信,完成逻辑开发。在Linux系统... 查看详情

linux下iic驱动编写,介绍iic子系统框架的使用

一、IIC协议介绍说起IIC,搞单片机,嵌入式的那肯定是接触的比较多的。串口、IIC、SPI这3个协议在单片机阶段应该是用比较多的,很多的外设模块,芯片都是串口、IIC、SPI等协议与主控芯片进行通信,完成逻辑开发。在Linux系统... 查看详情