当前位置: 首页 > news >正文

fpga iic协议

协议本身就不做介绍了,只介绍一下编写思路,首先这个iic模块具有两个功能,对一个地址进行一个字节读,对一个地址进行一个字节写。由于iic的地址包含设备地址(7位)和寄存器地址(8或16位),因此模块对外提供参数来让用户选择采用哪种位宽的寄存器地址。即REGADDR_2BYTES=1时候采用16位,0时采用8位,而SCLK_FREQ则表示iic的时钟多少频率,而输入的i_clk是50mhz的时钟,这里写了sim是为了仿真用,可以删除

其中本模块核心的寄存器变量如上图,其中状态有如下这几个,除了IDLE,START,STOP,每个状态都会读写完一个字节后进入下一个状态

当start信号拉高,模块开始工作,此时bytecnt开始计数,这个变量和当前读写的字节有关,同时采用sclcnt开始计数,这个计数器是用来给iic做分频时钟用的,本设计采用4分频,其逻辑如下:检验sclcnt的最后两位,那么最后两位有4种数值在反复循环00,01,10,11,那么在11和01处进行SCL时钟的翻转来实现四分频,同时iic协议要求scl低电平时候变化数据,因此在00也就是图上绿色的那一时刻让bytecnt++,并根据当前bytecnt来更新sda信号线。对应代码如下:
bytecnt++:

设备地址信号更新:

寄存器地址信号更新:

要写的字节数据更新和要读的字节数据更新,同时三态线io_sda逻辑如下,后面会说:

从上面时序图可以看到还有一个scl_vld信号,这个是说明只有该信号高电平时候scl才能执行,是为了防止在IDLE,START,STOP这几个状态的时候scl开始变化而设立的,也就是说只有该信号高电平,数据传输才真正开始。

然后ack信号会在bytecnt=0的时候读取,并在bytecnt==0且sclcnt=11的时候进行状态机跳转
 

由于为了适应多个时钟域,因此额外定义了CLK_DIV信号让其作为四分频标志位,当其拉高时scl_cnt++

总体代码如下:

`timescale 1ns / 1ps
// 采用字节写,随机地址读
// 字节写:    	起始位|器件地址|读ACK|寄存器地址|读ACK|数据|读ACK|停止位
// 随机地址读: 	起始位|器件地址|读ACK|寄存器地址|读ACK|停止位|起始位|器件地址|读ACK|数据|写ACK|停止位
`define sim
module I2C_Master#(parameter           SCLK_FREQ      = 400_000, parameter   [0:0]   REGADDR_2BYTES = 0
)(input               i_clk    	,input               i_rst       ,input               i_start     ,input               i_rw_flag   ,input       [6:0]   i_dev_addr  ,input       [15:0]  i_reg_addr  ,input       [7:0]   i_wr_data   ,   inout               io_sda      ,`ifdef simoutput				o_rden		,`endifoutput  reg         o_scl       ,output  reg [7:0]   o_rd_data   ,output  reg         o_vld       ,output	reg			o_err		,output              o_rdy       );localparam  S_IDLE      = 8'b00000001,S_START     = 8'b00000010,S_DADDR     = 8'b00000100,S_RADDR_16  = 8'b00001000,S_RADDR_8   = 8'b00010000,S_WR_DATA   = 8'b00100000,S_RD_DATA   = 8'b01000000,S_STOP      = 8'b10000000;reg         [7:0]       r_cstate    ;reg         [7:0]       r_nstate    ;localparam  CLK_DIV     = 50_000_000 / SCLK_FREQ / 4;//时钟计数器reg         [15:0]      r_clk_cnt   ;wire                    w_div_flag  ;assign  w_div_flag = r_clk_cnt >= CLK_DIV; //分频标志always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_clk_cnt <= 'd0;else if(r_cstate == S_IDLE)r_clk_cnt <= 'd0;else if(r_clk_cnt < CLK_DIV)r_clk_cnt <= r_clk_cnt + 1;elser_clk_cnt <= 'd0;endreg         [6:0]       r_dev_addr  ;reg         [7:0]       r_reg_addr16;reg         [7:0]       r_reg_addr8 ;reg         [7:0]       r_wr_data   ;reg                     r_rw_flag   ;assign  o_rdy = r_cstate == S_IDLE;always@(posedge i_clk or posedge i_rst) beginif(i_rst) beginr_dev_addr  <= 'd0;r_reg_addr16<= 'd0;r_reg_addr8 <= 'd0;r_wr_data   <= 'd0;r_rw_flag   <= 'b0;endelse if(r_cstate == S_IDLE & i_start) beginr_dev_addr  <= i_dev_addr       ;r_reg_addr16<= i_reg_addr[15:8] ;r_reg_addr8 <= i_reg_addr[7:0]  ;r_wr_data   <= i_wr_data        ;  r_rw_flag   <= i_rw_flag        ;endelse beginr_dev_addr  <= r_dev_addr   ;r_reg_addr16<= r_reg_addr16 ;r_reg_addr8 <= r_reg_addr8  ;r_wr_data   <= r_wr_data    ;r_rw_flag   <= r_rw_flag    ;endend//时钟四分频计数器reg     [15:0]  r_scl_cnt   ;reg             r_scl_vld   ; always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_scl_cnt <= 'd0;else if(r_cstate == S_IDLE)r_scl_cnt <= 'd0;else if(r_cstate == S_STOP & (&r_scl_cnt[1:0]) & w_div_flag)//当计数到2‘b11且状态为stop便清零r_scl_cnt <= 'd0;else if(w_div_flag)r_scl_cnt <= r_scl_cnt + 1;//四分频一次加一elser_scl_cnt <= r_scl_cnt;end//iic时钟只有在scl_vld拉高期间才能变化always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_scl_vld <= 'b0;else if(r_cstate == S_STOP & r_scl_cnt[1:0] >= 2)r_scl_vld <= 'b0;else if(r_cstate == S_START & r_scl_cnt[1:0] >= 2)r_scl_vld <= 'b1;elser_scl_vld <= r_scl_vld;endalways@(posedge i_clk or posedge i_rst) beginif(i_rst)o_scl <= 'b1;else if(r_scl_vld & r_scl_cnt[0] & r_clk_cnt == 0)//iic时钟o_scl <= ~o_scl;elseo_scl <= o_scl;endreg             r_dummy_wr  ;wire			w_rw_bit	;assign	w_rw_bit = r_dummy_wr ^ r_rw_flag;always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_dummy_wr <= 'b0;else if(r_cstate == S_IDLE & i_start)r_dummy_wr <= i_rw_flag;else if(r_cstate == S_STOP & (&r_scl_cnt[1:0]) & w_div_flag)r_dummy_wr <= 'b0;elser_dummy_wr <= r_dummy_wr;endreg     [4:0]   r_byte_cnt  ;//根据该计数器去变化sdareg             r_read_en   ;`ifdef simassign o_rden = r_read_en;`endifalways@(posedge i_clk or posedge i_rst) beginif(i_rst)r_byte_cnt <= 'd0;else if(r_scl_vld & r_cstate != S_STOP)if(&r_scl_cnt[1:0] & w_div_flag)//当scl cnt后两位为2‘b11 同时分频标志拉高时,计数器++r_byte_cnt <= r_byte_cnt < 8 ? r_byte_cnt + 1 : 'd0;elser_byte_cnt <= r_byte_cnt;elser_byte_cnt <= 'd0;endalways@(*) beginif(i_rst)r_read_en = 'b0;else if(r_cstate == S_RD_DATA)r_read_en = |r_byte_cnt;else if(|(r_cstate & 8'b10000011))r_read_en = 'b0;elser_read_en <= !r_byte_cnt;endreg             r_daddr_bit ;always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_daddr_bit <= 'b0;else if(r_cstate == S_DADDR)case(r_byte_cnt)1:  r_daddr_bit <= r_dev_addr[6];2:  r_daddr_bit <= r_dev_addr[5];3:  r_daddr_bit <= r_dev_addr[4];4:  r_daddr_bit <= r_dev_addr[3];5:  r_daddr_bit <= r_dev_addr[2];6:  r_daddr_bit <= r_dev_addr[1];7:  r_daddr_bit <= r_dev_addr[0];8:  r_daddr_bit <= w_rw_bit;default:    r_daddr_bit <= 'b0;endcaseelser_daddr_bit <= 'b0;endreg             r_raddr_bit ;always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_raddr_bit <= 'b0;else if(r_cstate == S_RADDR_16)case(r_byte_cnt)1:  r_raddr_bit <= r_reg_addr16[7];2:  r_raddr_bit <= r_reg_addr16[6];3:  r_raddr_bit <= r_reg_addr16[5];4:  r_raddr_bit <= r_reg_addr16[4];5:  r_raddr_bit <= r_reg_addr16[3];6:  r_raddr_bit <= r_reg_addr16[2];7:  r_raddr_bit <= r_reg_addr16[1];8:  r_raddr_bit <= r_reg_addr16[0];default:    r_raddr_bit <= 'b0;endcaseelse if(r_cstate == S_RADDR_8)case(r_byte_cnt)1:  r_raddr_bit <= r_reg_addr8[7];2:  r_raddr_bit <= r_reg_addr8[6];3:  r_raddr_bit <= r_reg_addr8[5];4:  r_raddr_bit <= r_reg_addr8[4];5:  r_raddr_bit <= r_reg_addr8[3];6:  r_raddr_bit <= r_reg_addr8[2];7:  r_raddr_bit <= r_reg_addr8[1];8:  r_raddr_bit <= r_reg_addr8[0];default:    r_raddr_bit <= 'b0;endcaseelser_raddr_bit <= 'b0;endreg             r_wrdata_bit    ;always@(posedge i_clk or posedge i_rst) beginif(i_rst)r_wrdata_bit <= 'b0;else if(r_cstate == S_WR_DATA)case(r_byte_cnt)1:  r_wrdata_bit <= r_wr_data[7];2:  r_wrdata_bit <= r_wr_data[6];3:  r_wrdata_bit <= r_wr_data[5];4:  r_wrdata_bit <= r_wr_data[4];5:  r_wrdata_bit <= r_wr_data[3];6:  r_wrdata_bit <= r_wr_data[2];7:  r_wrdata_bit <= r_wr_data[1];8:  r_wrdata_bit <= r_wr_data[0];default:    r_wrdata_bit <= 'b0;endcaseelser_wrdata_bit <= 'b0;endassign	io_sda = ~r_read_en ? |{~r_scl_vld, r_daddr_bit, r_raddr_bit, r_wrdata_bit} : 1'bz;always@(posedge i_clk or posedge i_rst) beginif(i_rst)o_rd_data <= 'd0;else if(r_cstate == S_RD_DATA && r_scl_cnt[1:0] == 2'b01 && w_div_flag)case(r_byte_cnt)1:  o_rd_data[7] <= io_sda;2:  o_rd_data[6] <= io_sda;3:  o_rd_data[5] <= io_sda;4:  o_rd_data[4] <= io_sda;5:  o_rd_data[3] <= io_sda;6:  o_rd_data[2] <= io_sda;7:  o_rd_data[1] <= io_sda;8:  o_rd_data[0] <= io_sda;default:    o_rd_data <= o_rd_data;endcaseelseo_rd_data <= o_rd_data;end	wire			w_ack_flag	;assign	w_ack_flag = |(r_cstate & 8'b00111100) & r_byte_cnt == 'd0 & r_scl_cnt[1:0] == 2'b01 & w_div_flag;always@(posedge i_clk or posedge i_rst) beginif(i_rst)o_err <= 'b0;else if(r_cstate == S_IDLE)o_err <= 'b0;else if(w_ack_flag)o_err <= io_sda | o_err;else o_err <= o_err;endreg		[7:0]	r_raddr_st ;always@(posedge i_clk) beginif(r_cstate == S_RADDR_16)r_raddr_st <= S_RADDR_8;elser_raddr_st <= REGADDR_2BYTES ? S_RADDR_16 : S_RADDR_8;endwire            w_state_chg	;assign  w_state_chg = (i_start & r_cstate == S_IDLE) | (&(r_scl_cnt[1:0]) && !r_byte_cnt && w_div_flag);always@(posedge i_clk or posedge i_rst) beginif(i_rst)o_vld <= 'b0;else if(r_cstate == S_STOP & r_nstate == S_IDLE)o_vld <= 'b1;elseo_vld <= 'b0;endalways@(posedge i_clk or posedge i_rst) beginif(i_rst)r_cstate <= S_IDLE;elser_cstate <= r_nstate;endalways@(*) beginif(i_rst)r_nstate = S_IDLE;elsecase(r_cstate)S_IDLE:     r_nstate = w_state_chg ? S_START : S_IDLE;S_START:    r_nstate = w_state_chg ? S_DADDR : S_START;S_DADDR:	r_nstate = w_state_chg ? w_rw_bit ? S_RD_DATA : r_raddr_st : S_DADDR; S_RADDR_16: r_nstate = w_state_chg ? S_RADDR_8 : S_RADDR_16;S_RADDR_8:	r_nstate = w_state_chg ? r_dummy_wr ? S_STOP : S_WR_DATA : S_RADDR_8;S_WR_DATA:  r_nstate = w_state_chg ? S_STOP : S_WR_DATA;S_RD_DATA:  r_nstate = w_state_chg ? S_STOP : S_RD_DATA;S_STOP:		r_nstate = w_state_chg ? r_dummy_wr ? S_START : S_IDLE : S_STOP;default:    r_nstate = S_IDLE;endcaseendendmodule

tb文件如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/05/22 11:15:31
// Design Name: 
// Module Name: IIC_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////module IIC_tb();localparam REGADDR_2BYTES = 0;reg             i_clk50m    ;reg             i_rst50m    ;reg             i_start     ;reg				i_rw_flag	;reg     [6:0]   i_dev_addr  ;reg     [15:0]  i_reg_addr  ;reg     [7:0]   i_wr_data   ;wire 			o_rden		;wire            io_sda      ;wire            o_scl       ;wire    [7:0]   o_rd_data   ;wire            o_vld       ;wire			o_err		;wire            o_rdy       ;always#10 i_clk50m = ~i_clk50m;reg    [15:0]  r_random;always#20  r_random = $random()%16'hffff;assign  io_sda = o_rden ? r_random[0] : 1'bz;initial begini_start = 0;i_clk50m = 1;i_rst50m = 1;i_rw_flag = 0;i_dev_addr = 7'b1010101;i_reg_addr = {8'h00, 8'h34};i_wr_data = 8'he3;#100i_rst50m = 0;#10i_start = 1;#30i_start = 0;@(posedge o_vld)#5000i_rw_flag = 1;i_dev_addr = 7'b1110001;i_reg_addr = {8'h00,8'hab};i_start = 1;#30i_start = 0;endI2C_Master#(.REGADDR_2BYTES(REGADDR_2BYTES))
I2C_Master_U (i_clk50m    ,i_rst50m    ,i_start     ,i_rw_flag	,i_dev_addr  ,i_reg_addr  ,i_wr_data  	,   io_sda      ,o_rden		,o_scl       ,o_rd_data	,o_vld       ,o_err		,o_rdy       
);endmodule

讲解视频:

https://www.bilibili.com/video/BV1HUhtzGEFS/?spm_id_from=333.1387.upload.video_card.click&vd_source=69fb997b62efa60ae1add8b53b6a5923

http://www.dtcms.com/a/367564.html

相关文章:

  • 关于嵌入式学习——嵌入式硬件3
  • Function Call实战:用GPT-4调用天气API,实现实时信息查询
  • 2025年热门视频转文字工具测评,助你快速把视频转成文字稿!
  • 基于SpringBoot的家政保洁预约系统【2026最新】
  • C语言中calloc函数
  • flowable基础入门
  • PDF24 Creator:免费的多功能PDF工具
  • 数据可视化大屏精选开源项目
  • rh134第二章复习总结
  • 搭建机器学习模型的数据管道架构方案
  • 富士施乐DocuCentre S2110故障代码01
  • 机器学习 - 使用 ID3 算法从原理到实际举例理解决策树
  • 智能家居芯片:技术核心与创新突破
  • (D题|矿井突水水流漫延模型与逃生方案)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • C#之LINQ
  • [bat-cli] docs | 控制器
  • 你读过哪些深入浅出的(技术)书籍?
  • C++程序员必懂:std::bad_function_call异常的真相与预防秘诀
  • 一篇文章带你彻底搞懂 JVM 垃圾收集器
  • 深度学习之第七课卷积神经网络 (CNN)调整学习率
  • 为什么研发文档总是缺少关键信息
  • Redissson分布式锁
  • C++字符串字符替换程序
  • 2025数学建模国赛A题思路首发!
  • 力扣-二分法想法
  • simple-check-100
  • 自学嵌入式第三十五天:网络编程-网站
  • 分词器详解(二)
  • Webug3.0通关笔记18 中级进阶第06关 实战练习:DisCuz论坛SQL注入漏洞
  • Docker学习记录