您的位置:首页 > 汽车 > 新车 > 天津营销类网站设计_网站设计的公司排名_济南新闻头条最新事件_seo点击排名软件哪里好

天津营销类网站设计_网站设计的公司排名_济南新闻头条最新事件_seo点击排名软件哪里好

2025/4/15 2:26:12 来源:https://blog.csdn.net/TommiWei/article/details/147091132  浏览:    关键词:天津营销类网站设计_网站设计的公司排名_济南新闻头条最新事件_seo点击排名软件哪里好
天津营销类网站设计_网站设计的公司排名_济南新闻头条最新事件_seo点击排名软件哪里好

0 简介

IIC的物理层


IIC一共有只有两个总线: 一条是双向的串行数据线SDA,一条是串行时钟线SCL.

SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据;

SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序。

通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。[1]

I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和uart通信为全双工。

1 时序

AT24C02_IDCHIP(英锐芯)_AT24C02中文资料_PDF手册_价格-立创商城

下面的时序图均截取自AT24C02 datasheet

数据和时钟线都为高则称总线处在空闲状态。当SCL为高电平时SDA的下降沿(高到低
叫做起始条件(START,简写为S),SDA的上升沿(低到高)则叫做停止条件(STOP,简
写为P)。

IIC的起始和停止条件的定义

IIC的位传输 

 IIC总线的应答

  

应答信号
当 I2C 主机发送完 8 位数据以后会将 SDA 设置为输入状态,等待 I2C 从机应答,也就是等到 I2C 从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完 8 位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将 SDA 拉低来表示发出应答信号,表示通信成功,否则表示通信失败。[2]

 期间地址

 IIC字节写 

当前地址读

 

随机读

顺序读 

本文要涉及到的单字节写时序和随机读时序。

 单字节写时序

 随机读时序 

2完整代码

top层代码:

`timescale 1ns / 1psmodule iic_top(clk,rst_n,sw1,sw2,scl,sda,sm_cs1_n,sm_cs2_n,sm_db);input clk;		// 50MHz
input rst_n;	//复位信号,低有效
input sw1,sw2;	//按键1、2,(1按下执行写入操作,2按下执行读操作)
output scl;		// 24C02的时钟端口
inout sda;		// 24C02的数据端口output sm_cs1_n,sm_cs2_n;	//数码管片选信号,低有效
output[6:0] sm_db;	//7段数码管(不包括小数点)wire[7:0] dis_data;		//在数码管上显示的16进制数iic_com		iic_com(.clk(clk),.rst_n(rst_n),.sw1(sw1),.sw2(sw2),.scl(scl),.sda(sda),.dis_data(dis_data));led_seg7	led_seg7(.clk(clk),.rst_n(rst_n),.dis_data(dis_data),.sm_cs1_n(sm_cs1_n),.sm_cs2_n(sm_cs2_n),.sm_db(sm_db)	);endmodule		

iic_com代码

`timescale 1ns / 1ps// Company: 
// Engineer:
//
// Create Date:
// Design Name:    
// Module Name:    iic_top
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// module iic_com(clk,rst_n,sw1,sw2,scl,sda,dis_data);input clk;		// 50MHz
input rst_n;	//复位信号,低有效
input sw1,sw2;	//按键1、2,(1按下执行写入操作,2按下执行读操作)
output scl;		// 24C02的时钟端口
inout sda;		// 24C02的数据端口
output[7:0] dis_data;	//数码管显示的数据//--------------------------------------------//按键检测
reg sw1_r,sw2_r;	//键值锁存寄存器,每20ms检测一次键值 
reg[19:0] cnt_20ms;	//20ms计数寄存器always @ (posedge clk or negedge rst_n)if(!rst_n) cnt_20ms <= 20'd0;else cnt_20ms <= cnt_20ms+1'b1;	//不断计数always @ (posedge clk or negedge rst_n)if(!rst_n) beginsw1_r <= 1'b1;	//键值寄存器复位,没有键盘按下时键值都为1sw2_r <= 1'b1;endelse if(cnt_20ms == 20'hfffff) beginsw1_r <= sw1;	//按键1值锁存sw2_r <= sw2;	//按键2值锁存end//---------------------------------------------//分频部分
reg[2:0] cnt;	// cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg[8:0] cnt_delay;	//500循环计数,产生iic所需要的时钟
reg scl_r;		//时钟脉冲寄存器always @ (posedge clk or negedge rst_n)if(!rst_n) cnt_delay <= 9'd0;else if(cnt_delay == 9'd499) cnt_delay <= 9'd0;	//计数到10us为scl的周期,即100KHzelse cnt_delay <= cnt_delay+1'b1;	//时钟计数always @ (posedge clk or negedge rst_n) beginif(!rst_n) cnt <= 3'd5;else begincase (cnt_delay)9'd124:	cnt <= 3'd1;	//cnt=1:scl高电平中间,用于数据采样9'd249:	cnt <= 3'd2;	//cnt=2:scl下降沿9'd374:	cnt <= 3'd3;	//cnt=3:scl低电平中间,用于数据变化9'd499:	cnt <= 3'd0;	//cnt=0:scl上升沿default: cnt <= 3'd5;endcaseend
end`define SCL_POS		(cnt==3'd0)		//cnt=0:scl上升沿
`define SCL_HIG		(cnt==3'd1)		//cnt=1:scl高电平中间,用于数据采样
`define SCL_NEG		(cnt==3'd2)		//cnt=2:scl下降沿
`define SCL_LOW		(cnt==3'd3)		//cnt=3:scl低电平中间,用于数据变化always @ (posedge clk or negedge rst_n)if(!rst_n) scl_r <= 1'b0;else if(cnt==3'd0) scl_r <= 1'b1;	//scl信号上升沿else if(cnt==3'd2) scl_r <= 1'b0;	//scl信号下降沿assign scl = scl_r;	//产生iic所需要的时钟
//---------------------------------------------//需要写入24C02的地址和数据`define	DEVICE_READ		8'b1010_0001	//被寻址器件地址(读操作)
`define DEVICE_WRITE	8'b1010_0000	//被寻址器件地址(写操作)
`define	WRITE_DATA		8'b0001_0001	//写入EEPROM的数据
`define BYTE_ADDR		8'b0000_0011	//写入/读出EEPROM的地址寄存器	
reg[7:0] db_r;		//在IIC上传送的数据寄存器
reg[7:0] read_data;	//读出EEPROM的数据寄存器//---------------------------------------------//读、写时序
parameter 	IDLE 	= 4'd0;
parameter 	START1 	= 4'd1;
parameter 	ADD1 	= 4'd2;
parameter 	ACK1 	= 4'd3;
parameter 	ADD2 	= 4'd4;
parameter 	ACK2 	= 4'd5;
parameter 	START2 	= 4'd6;
parameter 	ADD3 	= 4'd7;
parameter 	ACK3	= 4'd8;
parameter 	DATA 	= 4'd9;
parameter 	ACK4	= 4'd10;
parameter 	STOP1 	= 4'd11;
parameter 	STOP2 	= 4'd12;reg[3:0] cstate;	//状态寄存器
reg sda_r;		//输出数据寄存器
reg sda_link;	//输出数据sda信号inout方向控制位		
reg[3:0] num;	//always @ (posedge clk or negedge rst_n) beginif(!rst_n) begincstate <= IDLE;sda_r <= 1'b1;sda_link <= 1'b0;num <= 4'd0;read_data <= 8'b0000_0000;endelse 	  case (cstate)IDLE:	beginsda_link <= 1'b1;			//数据线sda为inputsda_r <= 1'b1;if(!sw1_r || !sw2_r) begin	//SW1,SW2键有一个被按下			db_r <= `DEVICE_WRITE;	//送器件地址(写操作)cstate <= START1;		endelse cstate <= IDLE;	//没有任何键被按下endSTART1: beginif(`SCL_HIG) begin		//scl为高电平期间sda_link <= 1'b1;	//数据线sda为outputsda_r <= 1'b0;		//拉低数据线sda,产生起始位信号cstate <= ADD1;num <= 4'd0;		//num计数清零endelse cstate <= START1; //等待scl高电平中间位置到来endADD1:	beginif(`SCL_LOW) beginif(num == 4'd8) begin	num <= 4'd0;			//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;		//sda置为高阻态(input)cstate <= ACK1;endelse begincstate <= ADD1;num <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//		sda_r <= db_r[4'd7-num];	//送器件地址,从高位开始endend//		else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};	//器件地址左移1bitelse cstate <= ADD1;endACK1:	beginif(/*!sda*/`SCL_NEG) begin	//注:24C01/02/04/08/16器件可以不考虑应答位cstate <= ADD2;	//从机响应信号db_r <= `BYTE_ADDR;	// 1地址		endelse cstate <= ACK1;		//等待从机响应endADD2:	beginif(`SCL_LOW) beginif(num==4'd8) begin	num <= 4'd0;			//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;		//sda置为高阻态(input)cstate <= ACK2;endelse beginsda_link <= 1'b1;		//sda作为outputnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase//		sda_r <= db_r[4'd7-num];	//送EEPROM地址(高bit开始)		cstate <= ADD2;					endend//		else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};	//器件地址左移1bitelse cstate <= ADD2;				endACK2:	beginif(/*!sda*/`SCL_NEG) begin		//从机响应信号if(!sw1_r) begincstate <= DATA; 	//写操作db_r <= `WRITE_DATA;	//写入的数据							end	else if(!sw2_r) begindb_r <= `DEVICE_READ;	//送器件地址(读操作),特定地址读需要执行该步骤以下操作cstate <= START2;		//读操作endendelse cstate <= ACK2;	//等待从机响应endSTART2: begin	//读操作起始位if(`SCL_LOW) beginsda_link <= 1'b1;	//sda作为outputsda_r <= 1'b1;		//拉高数据线sdacstate <= START2;endelse if(`SCL_HIG) begin	//scl为高电平中间sda_r <= 1'b0;		//拉低数据线sda,产生起始位信号cstate <= ADD3;end	 else cstate <= START2;endADD3:	begin	//送读操作地址if(`SCL_LOW) beginif(num==4'd8) begin	num <= 4'd0;			//num计数清零sda_r <= 1'b1;sda_link <= 1'b0;		//sda置为高阻态(input)cstate <= ACK3;endelse beginnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase									//	sda_r <= db_r[4'd7-num];	//送EEPROM地址(高bit开始)		cstate <= ADD3;					endend//	else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};	//器件地址左移1bitelse cstate <= ADD3;				endACK3:	beginif(/*!sda*/`SCL_NEG) begincstate <= DATA;	//从机响应信号sda_link <= 1'b0;endelse cstate <= ACK3; 		//等待从机响应endDATA:	beginif(!sw2_r) begin	 //读操作if(num<=4'd7) begincstate <= DATA;if(`SCL_HIG) begin	num <= num+1'b1;	case (num)4'd0: read_data[7] <= sda;4'd1: read_data[6] <= sda;  4'd2: read_data[5] <= sda; 4'd3: read_data[4] <= sda; 4'd4: read_data[3] <= sda; 4'd5: read_data[2] <= sda; 4'd6: read_data[1] <= sda; 4'd7: read_data[0] <= sda; default: ;endcase																		//				read_data[4'd7-num] <= sda;	//读数据(高bit开始)end//				else if(`SCL_NEG) read_data <= {read_data[6:0],read_data[7]};	//数据循环右移endelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;			//num计数清零cstate <= ACK4;endelse cstate <= DATA;endelse if(!sw1_r) begin	//写操作sda_link <= 1'b1;	if(num<=4'd7) begincstate <= DATA;if(`SCL_LOW) beginsda_link <= 1'b1;		//数据线sda作为outputnum <= num+1'b1;case (num)4'd0: sda_r <= db_r[7];4'd1: sda_r <= db_r[6];4'd2: sda_r <= db_r[5];4'd3: sda_r <= db_r[4];4'd4: sda_r <= db_r[3];4'd5: sda_r <= db_r[2];4'd6: sda_r <= db_r[1];4'd7: sda_r <= db_r[0];default: ;endcase									//	sda_r <= db_r[4'd7-num];	//写入数据(高bit开始)end//					else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};	//写入数据左移1bitendelse if((`SCL_LOW) && (num==4'd8)) beginnum <= 4'd0;sda_r <= 1'b1;sda_link <= 1'b0;		//sda置为高阻态cstate <= ACK4;endelse cstate <= DATA;endendACK4: beginif(/*!sda*/`SCL_NEG) begin
//						sda_r <= 1'b1;cstate <= STOP1;						endelse cstate <= ACK4;endSTOP1:	beginif(`SCL_LOW) beginsda_link <= 1'b1;sda_r <= 1'b0;cstate <= STOP1;endelse if(`SCL_HIG) beginsda_r <= 1'b1;	//scl为高时,sda产生上升沿(结束信号)cstate <= STOP2;endelse cstate <= STOP1;endSTOP2:	beginif(`SCL_LOW) sda_r <= 1'b1;else if(cnt_20ms==20'hffff0) cstate <= IDLE;else cstate <= STOP2;enddefault: cstate <= IDLE;endcase
endassign sda = sda_link ? sda_r:1'bz;
assign dis_data = read_data;//---------------------------------------------endmodule

3 状态机

主机要向从机写数据时:

主机首先产生START信号
然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方 向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)
主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
这时候主机等待从机的应答信号(A)
当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号
当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号,
主机产生停止信号,结束传送过程。

主机要从从机读数据时

主机首先产生START信号
然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令,
这时候主机等待从机的应答信号(ACK)
当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,
当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设 置成接收模式开始读取数据,
这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,表示不在接收数据
主机进而产生停止信号,结束传送过程。

4 总结

代码中已经说明了一切,这篇文章不仅仅是说IIC的时序,也想告诉大家,状态机的写法。掌握了状态机的写法,等于成功了一半。虽然本篇文章是一段式写法,但重要的是先实现功能。欢迎大家批评指正。

5 参考文献

[1]IIC原理超详细讲解---值得一看-CSDN博客

[2]IIC 通信协议详解_i2c协议-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com