总线通信协议-IIC

1. 简介

IIC(Inter-Integrated Communication)总线是Philips公司开发的两线串行总线,由数据线SDA和时钟线SCL构成

1.1 特点

同步串行总线,支持多主设备,传输速率为400Kbps

1.2 接口

SDA:数据线
SCL:时钟线

  1. SDA和SCL都是双向线路,都通过一个上拉电阻连接到正的电源电压,当总线空闲时都是高电平。
  2. SCL由主设备产生,SDA的产生与数据传输方向有关。
    image.png

1.3 工作模式

image.png

2. 传输协议

2.1 时序图

起始信号:SCL为高电平,SDA从高电平到低电平
数据传送:只能在SCL为低电平是数据才能变化
终止信号:SCL为高电平,SDA从低电平变为高电平
每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
image.png
image.png
特殊情况:

  1. 从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),主机将数据线置于高电平而产生一个终止信号以结束总线的数据传送。
  2. 如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。与第一种情况类似,都是没有接受到ACK

2.2 数据帧格式

I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
image.png
注意:既然是应答位,因此应答信号总是对方发送的。

2.3 模块实现


module I2C (
	input wire sys_clk,
	input wire sys_rst_n,
	input wire [7:0] data_in,
	input wire data_vld,
	inout wire SDA,
	output reg sck
	);

	reg flag;
	always_ff @(posedge sys_clk or negedge sys_rst_n) begin
		if (~sys_rst_n)
			flag <= 'b0;
		else if (data_vld)
			flag <= 'b1;
		else
			flag <= ~flag;
	end

	reg [7:0] data_reg;

	always_ff @(posedge sys_clk or negedge sys_rst_n) begin : proc_data_reg
		if(~sys_rst_n) begin
			data_reg <= 0;
		end else if (data_vld)begin
			data_reg <= data_in;
		end else begin
			data_reg <= data_reg;
		end
	end

	parameter IDLE = 5'b00001;
	parameter START = 5'b00010;
	parameter TRA_DATA = 5'b00100;
	parameter ACK = 5'b01000;
	parameter STOP = 5'b10000;

	reg [4:0] cur_state;
	reg [4:0] nxt_state;
	reg [3:0] data_cnt;

	always_ff @(posedge sys_clk or negedge sys_rst_n) begin
		if (~sys_rst_n)
			cur_state <= IDLE;
		else
			cur_state <= nxt_state;
	end

	always_latch begin
		case(cur_state)
			IDLE : 
				if (data_vld)
					nxt_state = START;
			START:
				if (flag )
					nxt_state = TRA_DATA;
			TRA_DATA:
				if (data_cnt == 'd7 && flag && sck)
					nxt_state = ACK;
			ACK :
				if (data_cnt == 'd8 && flag && sck)
					nxt_state = STOP;
			STOP :
				if (flag)
					nxt_state = IDLE;
		endcase // cur_state
	end

	always_ff @(posedge sys_clk or negedge sys_rst_n) begin
		if (~sys_rst_n)
			sck <= 'b1;
		else if ((cur_state == START && !flag) || cur_state == STOP)
			sck <= 'b1;
		else if ((cur_state == TRA_DATA || cur_state == START) && flag)
			sck <= ~sck; 
		else if (cur_state == ACK && flag&& !sck)
			sck <= ~sck;
	end


	always_ff @(posedge sys_clk or negedge sys_rst_n) begin
		if (~sys_rst_n)
			data_cnt <= 'd0;
		else if (data_cnt == 'd8 && flag && sck)
			data_cnt <= 'd0;
		else if (cur_state == TRA_DATA && flag == 1'b1 && sck)
			data_cnt <= data_cnt + 1'b1;
		else
			data_cnt <= data_cnt;
	end

	reg data_out;

	always_ff @(posedge sys_clk or negedge sys_rst_n) begin
		if (~sys_rst_n)
			data_out <= 'b1;
		else if (cur_state == START && !flag)
			data_out <= 'b0;
		else if (cur_state == TRA_DATA && !flag)
			data_out <= data_reg[7-data_cnt];
		else if (cur_state == STOP && !flag)
			data_out <= 'b1;
		else
			data_out <= data_out;
	end

	assign SDA = (cur_state == ACK) ? 1'bz : data_out;

endmodule

这里主要注意几个点

  1. 我们不采取时钟上升沿和下降沿分别采样的做法,而采用一个SCK电平持续两个时钟周期,这样我们就可以在SCK为低电平的时候改变输出的值。
  2. 这里我们只进行发送数据的操作,所以ACK信号为高组态。如果是读取数据,那么应该在对应的状态机中返回ACK信号。

2.4 模块测试

module tb_I2C();


reg [7 : 0] data_in;
reg data_vld;

reg sys_clk, sys_rst_n;

wire data_out, sck;

initial begin
	sys_clk = 'b0;
	sys_rst_n = 'b0;

	# 15 
	sys_rst_n = 'b1;
end

always #5 sys_clk = ~sys_clk;

initial begin
	data_vld = 'b0;
	data_in = 'd0;

	repeat(2)
		@ (posedge sys_clk) ;
	data_vld = 'b1;
	data_in = 'b1001_0010;

	@(posedge sys_clk) ;
	data_vld = 'b0;
	data_in = 'd0;

	#10000
	$finish;
end

I2C I2C_dut(
	sys_clk,
	sys_rst_n,
	data_in,
	data_vld,
	

	data_out,
	sck
	);

	initial begin
		$fsdbDumpfile("tb_I2C.fsdb");
		$fsdbDumpvars(0, tb_I2C);
		$fsdbDumpon;
	end

endmodule : tb_I2C

image.png

3. 小结

  1. I2C真正实现的时候第一次发送的是7位设备地址信息,1位方向。然后根据自身的设备型号和读写方向进行对应的操作,我们这里只是根据时序图简单的实现基本的协议。
  2. 如果需要实现完整的,整个核心状态是没有变化的,不过ACK状态有可能再次回到TRA_DATA状态,需要根据数据的传输次数进行配置。
  3. 有一个核心问题是,从设备ACK信号的产生是根据主设备发送的SCL信号作为时钟沿产生的,还是从设备也有一个对应的sys_clk信号。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值