IIC通信方式的详解

Inter-IntegratedCircuit(集成电路总线)

IIC是一种多向控制总线,由飞利浦半导体公司在八十度年代初设计,主要是用来连接整体电路(ICS)。在IIC中多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实施数据传输的控制源,这种方式简化了信号传容输总线。

  1. IIC的物理层
    典型电路如下:
    在这里插入图片描述

  2. IIC协议层
    在这里插入图片描述
    在这里插入图片描述
    每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答。

  3. 数据帧格式
    在这里插入图片描述
    每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
    在这里插入图片描述

  4. 传输信号时序图及时序参数
    在这里插入图片描述在这里插入图片描述

  5. AT24CXX系列的介绍
    (1)写操作
    AT24CXX系列的EEPROM为了提高写效率,提供了页写功能,内部有个一页大小的写缓冲RAM,地址范围当然就是从00到一页大小,发生写操作时,开始送入的地址对应的页被选中,并将其内容映像到缓冲RAM,数据从低端地址对应的缓冲RAM地址开始修改,超过这个地址范围就回到00,写完后,就会把开始确定的EEPROM页擦除,再把一整页RAM数据写入。所有写数据都发生在开始写地址时确定的页上。
    24C01、24C02 这两个型号是 8 个字节一个页,而 24C04、24C08、24C16 是 16 个字节一页。以页容量128为例,一页都是从00开始按128字节分成一个个的页,0页就是0—7F,1页就是80—FF,类推,边界就是128字节的整数倍地址。页RAM的地址范围为7位00—7F,写入时高端地址就是页号。发生写操作,开始送入的地址对应的页被锁存,后续不论写多少,都在这个页中,只是一个页内的地址进行加一,超过就归零开始。从F0开始写32个字节,那么开始送入的地址为F0,就会锁定在1号页(第2个页)上,底端7位页内部地址开始从70H开始写,到达7F时回到00再到10H,也就是写在了F0—FF,80—8F。也就是,从01开始写也只能到7F,再往80写就跑到00上去了,这就是写操作的翻卷,datasheet上都有说明。就是从边界前写两个字节也要分两次写。页是绝对的,按整页大小排列,不是从开始写入的地址开始算。
    (2)读操作
    读没有页的问题,可以从任意地址开始读取任意大小数据,只是超过整个存储器容量时地址才回卷。但一次性访问的数据长度也不要太大。

  6. Verliog代码设计
    在正点原子的三段式IIC驱动代码的基础上进行修改,实现对AT24C64的多字节读写操作。这里需要格外注意的有两点:
    (1)写操作完成最后一个字节数据后是从机产生应答;读操作完成一个字节数据后是主机产生应答。
    (2)分页的存储器要做好存储器管理,尽量将同时读写的数据放在一个页上。
    代码如下:

    `timescale 1ns/1ps
    
    module iic_driver
    #(// slave address(器件地址),放此处方便参数传递
    	parameter   SLAVE_ADDR =  7'b1010000   ,
    	parameter   CLK_FREQ   = 26'd50_000_000,   // iic_dri模块的驱动时钟频率(CLK_FREQ)
    	parameter   IIC_FREQ   = 18'd250_000       // IIC的SCL时钟频率
    )
    (
    	input 				CLK,      		// iic_dri模块的驱动时钟(CLK_FREQ)
    	input 				RST_N,     		// 复位信号
    	input				iic_exec,		// IIC触发执行信号
    	input				bit_ctrl,		// 字地址位控制(16b/8b)
    	input				iic_rh_wl,		// IIC读写控制信号(1:read 0:write)
    	input 		[15:0] 	iic_addr,     	// IIC器件内地址
    	input 		[ 7:0] 	iic_data_w,    	// IIC要写的数据
    	
    	output reg 	[ 7:0] 	iic_data_r,		// IIC读出的数据
    	output wire 		done,    		// IIC一次操作完成
    	output reg 			SCL,     		// IIC的SCL时钟信号
    	inout 				SDA,     		// IIC的SDA信号
    	
    	input 		[ 7:0] 	wdata_num,    	// IIC要写数据的个数
    	input 		[ 7:0] 	rdata_num,    	// IIC要读数据的个数
    	output wire			wr_data_vaild,  // IIC写入数据有效
    	output wire			rd_data_vaild,  // IIC读出数据有效
    	output reg 			dri_clk       	// 驱动IIC操作的驱动时钟
    
    );
    
    //localparam define
    localparam  st_idle     = 8'b0000_0001;          // 空闲状态						1							
    localparam  st_sladdr   = 8'b0000_0010;          // 发送器件地址(slave address)	2
    localparam  st_addr16   = 8'b0000_0100;          // 发送16位字地址					4
    localparam  st_addr8    = 8'b0000_1000;          // 发送8位字地址					8
    localparam  st_data_wr  = 8'b0001_0000;          // 写数据(8 bit)				16
    localparam  st_addr_rd  = 8'b0010_0000;          // 发送器件地址读					32
    localparam  st_data_rd  = 8'b0100_0000;          // 读数据(8 bit)				64
    localparam  st_stop     = 8'b1000_0000;          // 结束IIC操作					128
    
    //reg define
    reg 			sda_dir;				// I2C数据(SDA)方向控制
    reg 			sda_out;				// SDA输出信号
    reg 			st_done;				// 状态结束
    reg				iic_exec_r;				// 起始信号寄存器
    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 [9:0] 		clk_cnt;			// 分频时钟计数
    reg 	 		ack;				// SDA应答
    reg	[7:0]		wdata_cnt;			// 写数据计数器
    reg	[7:0]		rdata_cnt;			// 读数据计数器
    reg	[1:0]		wr_data_vaild_r;  	// IIC写入数据有效
    reg	[1:0]		rd_data_vaild_r; 	// IIC写入数据有效
    reg				iic_done;
    reg	[1:0]		iic_done_r;
    //wire define
    wire 			sda_in;				// SDA输入信号
    wire [7:0] 		data_wr_t;			// IIC需写的数据保存
    wire [8:0] 		clk_divide;			// 模块驱动时钟的分频系数
    
    
    //SDA控制
    assign  SDA = sda_dir ?  (sda_out ? 1'bz :1'b0 ) : 1'bz;	
    assign  sda_in = SDA ;							// SDA数据输入
    assign  clk_divide = (CLK_FREQ/IIC_FREQ) >> 3;	// 模块驱动时钟的分频系数
    
    //寄存1个时钟周期的起始信号
    always @(posedge CLK or negedge RST_N) 
    begin
    	if(!RST_N)
    		iic_exec_r <= 1'b0;
    	else if(iic_exec)
    		iic_exec_r <= 1'b1;
    	else if(iic_done)
    		iic_exec_r <= 1'b0;
    	else 
    		iic_exec_r <= iic_exec_r;
    end
    
    //生成IIC的SCL的四倍频率(1M)的驱动时钟用于驱动i2c的操作
    always @(posedge CLK or negedge RST_N) 
    begin
        if(!RST_N) 
    		begin
    			dri_clk <=  1'b1;
    			clk_cnt <= 10'd0;
    		end
        else if(clk_cnt == clk_divide - 1'd1) 	//	F=50M/(25*2)=1M
    		begin
    			clk_cnt <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值