赞
踩
特别说明:以下所涉及代码参考网络、相关开发板厂家。只用于自己学习、理解和感悟。
I2C总线是由Philips公司开发的一种简单、双向二进制串行总线。只需要两根线即可在连接于总线上的器件之间信息传递。
I2C的标准速率为100kbit/s,快速模式时速率为400kbit/s,支持多机通讯,支持多主控模块,但同一时刻只允许有一个主控。由数据线SDA和时钟线SCL构成串行总线。每个电路和模块都有唯一的地址。
以下以AT24C04为例说明I2C的基本读写操作,I2C设备的操作可分为读写单个存储字节和多个存储字节。读写时序示意图如下所示:
如上图所示,I2C通信分为空闲状态、开始态、信息传输态、停止态。传输信息分为:设备地址、读写标志、应答信号、存储地址、数据。
空闲态:
SDA和SCL同时处于高电平(硬件电路中一般SDA和SCL线通过电阻上拉),进行总线释放。
开始态:
开始态时,SDA和SCL处于高电平状,当数据线SDA由高电平跳变到低电平后进入开始态。开始态代表一次数据传输的开始。
信息传输态:
I2C总线上,每一位数据都与一个时钟脉冲对应,即在每个数据位在相对应的时钟沿进行串行传送。在I2C总线上,时钟的高电平时,数据线上的数据保持稳定;时钟的低电平时,才允许电平改变状态。 数据信息传输过程中伴随着应答信号的发生。应答分位ACK和NACK。I2C总线上的数据都是以8位字节传送的。发送器每发送一个字节后,在第9个时钟脉冲期间释放数据线,同时等待接收器所反馈的应答信号。如果应答信号为低电平时,表示为有效应答(ACK);否则为无效应答(NACK)。无应答表示接收失败。如果主控制器在接收到最后一个字节后,发送一个NACK信号通知从设备结束数据发送,并释放SDA线,以便主设备发送一个停止信号。
停止态:
在时钟线保持高电平期间,数据线返回高电平即数据线被释放,称为I2C总线上的停止信号。接收到停止信号后I2C总线进入空闲状态。
读写时序如下所示:
1、写时序:
2、读时序:
在FPGA中,代码结构如下所示:
代码如下:
1、timescale.v代码
`timescale 1ns / 10ps
2、i2c_master_defines.v代码
`define I2C_CMD_NOP 4'b0000
`define I2C_CMD_START 4'b0001
`define I2C_CMD_STOP 4'b0010
`define I2C_CMD_WRITE 4'b0100
`define I2C_CMD_READ 4'b1000
3、i2c_master_bit_ctrl.v代码
`include "timescale.v" `include "i2c_master_defines.v" //位控制部分 //发送简单命令到SCL/SDA转换器 //每个命令有5个状态,A/B/C/D/idle。 module i2c_master_bit_ctrl( input clk, //系统时钟 input rst, //同步使能高复位 input nReset, //异步使能低复位 input ena, //内核使能信号 input[15:0] clk_cnt, //时钟预分频值 input[3:0] cmd, //从控制器来的命令 output reg cmd_ack, //应答命令 output reg busy, //i2c总线忙 output reg a1, //i2c总线仲裁丢失 input din, //数据输入 output reg dout, //数据输出 input scl_i, //i2c时钟线输入 output scl_o, //i2c时钟线输出 output reg scl_oen, //i2c时钟线输出使能(低使能) input sda_i, //i2c数据线输入 output sda_o, //i2c数据线输出 output reg sda_oen //i2c数据线输出使能(第使能) ); reg[1:0] cSCL,cSDA; //采集SCL,SDA reg[2:0] fSCL,fSDA; //SCL,SDA滤波器输入 reg sSCL,sSDA; //滤波和同步后的SCL,SDA输入 reg dSCL,dSDA; //延时变化后的dSCL,dSDA reg dscl_oen; //延时变化后的时钟使能信号 reg sda_chk; //检查SDA输出(多主机仲裁) reg clk_en; //时钟产生信号 reg slave_wait; //从设备就绪等待 reg[15:0] cnt; //时钟分频计数器(同步) reg[13:0] filter_cnt; //滤波器时钟分频 //状态机变量 reg [17:0] c_state; //逻辑综合枚举状态 //在从设备没有准备好的时候,通过拉低SCL来达到延时的目的 //延时使能SCL always @(posedge clk) dscl_oen <= #1 scl_oen; // slave_wait is asserted when master wants to drive SCL high, //but the slave pulls it low // slave_wait remains asserted until the slave releases SCL always @(posedge clk or negedge nReset) if(!nReset) slave_wait <= 1'b0; else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL); //主设备SCL为高,但另一个主设备为低 //主设备开始倒计时(进行时钟同步) wire scl_sync = dSCL & ~sSCL & scl_oen; //产生时钟使能信号 always @(posedge clk or negedge nReset) if(~nReset) begin cnt <= #1 16'h0; clk_en <= 1'b1; end else if(rst || ~|cnt || !ena || scl_sync) begin cnt <= #1 clk_cnt; clk_en <= #1 1'b1; end else if(slave_wait) begin cnt <= #1 cnt; clk_en <= #1 1'b0; end else begin cnt <= #1 cnt - 16'h1; clk_en <= #1 1'b0; end //信号采集 //降低亚稳态风险 always @(posedge clk or negedge nReset) if(!nReset) begin cSCL <= #1 2'b00; cSDA <= #1 2'b00; end else if(rst) begin cSCL <= #1 2'b00; cSDA <= #1 2'b00; end else begin cSCL <= {cSCL[0],scl_i}; cSDA <= {cSDA[0],sda_i}; end //滤波SCL和SDA信号,(尝试)排除故障 always @(posedge clk or negedge nReset) if(!nReset) filter_cnt <= 14'h0; else if(rst||!ena) filter_cnt <= 14'h0; else if(~|filter_cnt) filter_cnt <= clk_cnt >> 2; else filter_cnt <= filter_cnt - 1; //滤波,filter_cnt频率是fSCLK频率的四倍 always @(posedge clk or negedge nReset) if(!nReset) begin fSCL <= 3'b111; fSDA <= 3'b111; en
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。