【学习笔记】I2C协议

         I2C 通讯协议(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制 同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。

        (1) 它是一个支持多设备的总线。在一个 I2C 通讯 总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。

         (2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。

        (3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备 之间的访问。

        (4) 总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而当所有设备都 空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

         (5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占 用总线。

         (6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s。 

功能实现:

        按下数据写操作按键,写触发信号传入数据收发模块,模块接收到有效的写触发信号后,生成写使能信号、待写入数据、数据地址传入 I2C 驱动模块,I2C 驱动模块按照 I2C 协议将数据写入 EEPROM 存储芯片;数据写入完成后,按下数据读操作按键,读触发信号传入数据收发模块,模块接收到有效的读触发信号后,生成读使能信号、数据地址传入 I2C 驱动模块,I2C 驱动模块自 EEPROM 存储芯片读取数据,将读取到的数据回传给数据收发模块,数据收发模块将数据暂存。

总体框图:

 驱动模块:

 

 

图1

 

 图2

 图3

图4

图5

图6

图7

 

 

module iic_wr_rd_ctrl
	#(
	parameter DEVICE_ADDR 	= 7'b1010_000 		, 	//iic 设备地址
	parameter CLK_SYS_FREQ 	= 26'd50_000_000	,	//输入系统时钟频率
	parameter SCL_FREQ 		= 18'd250_000 			//iic 设备 scl 时钟频率		
	)
	(
	input wire clk_sys 				, 		//输入系统时钟,50MHz
	input wire rst_n_sys 			, 		//输入复位信号,低电平有效
	input wire wr_en 					, 		//输入写使能信号
	input wire rd_en 					, 		//输入读使能信号
	input wire iic_start 			, 		//输入 iic 触发信号
	input wire addr_num 				, 		//输入 iic 字节地址字节数
	input wire [15:0] byte_addr 	, 		//输入 iic 字节地址
	input wire [7:0] wr_data 		,		//输入 iic 设备数据

	output reg iic_clk 				, 		//iic 驱动时钟
	output reg iic_end 				, 		//iic 一次读/写操作完成
	output reg [7:0] rd_data		, 		//输出 iic 设备读取数据
	output reg scl						, 		//输出至 iic 设备的串行时钟信号 scl
	
	inout wire sda								//输出至 iic 设备的串行数据信号 sda
);


	wire sda_in ; 					//sda 输入数据寄存
	wire sda_en ; 					//sda 数据写入使能信号
	
	reg [7:0] cnt_clk 	; 	
	reg [3:0] state 		; 	
	reg iic_clk_en 		; 	
	reg [1:0] cnt_iic_clk; 	
	reg [2:0] cnt_bit 	; 	
	reg ack 					; 	
	reg sda_reg 			; 	
	reg [7:0] rd_data_reg; 	
	
parameter CNT_CLK_MAX = (CLK_SYS_FREQ/SCL_FREQ) >> 2'd3 ; 

parameter	IDLE 				= 4'd00		, 		//初始状态
				START_1 			= 4'd01		,		//开始状态 1
				SEND_D_ADDR 	= 4'd02		, 		//设备地址写入状态 + 控制写
				ACK_1 			= 4'd03		, 		//应答状态 1
				SEND_B_ADDR_H 	= 4'd04		, 		//字节地址高八位写入状态
				ACK_2 			= 4'd05		, 		//应答状态 2
				SEND_B_ADDR_L 	= 4'd06		, 		//字节地址低八位写入状态
				ACK_3 			= 4'd07		, 		//应答状态 3
				SEND_WR_DATA 	= 4'd08		, 		//写数据状态
				ACK_4 			= 4'd09		, 		//应答状态 4
				START_2 			= 4'd10		, 		//开始状态 2
				SEND_RD_ADDR	= 4'd11		, 		//设备地址写入状态 + 控制读
				ACK_5 			= 4'd12		, 		//应答状态 5
				RECEIVE_RD_DATA= 4'd13		, 		//读数据状态
				N_ACK 			= 4'd14		, 		//非应答状态
				STOP 				= 4'd15		; 		//结束状态
					
always@(posedge clk_sys or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		cnt_clk <= 8'd0;
	else if(cnt_clk == CNT_CLK_MAX - 1'b1)
		cnt_clk <= 8'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;
		
always@(posedge clk_sys or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		iic_clk <= 1'b1;
	else if(cnt_clk == CNT_CLK_MAX - 1'b1)
		iic_clk <= ~iic_clk;	
		
always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		iic_clk_en <= 1'b0;
	else if(iic_start == 1'b1)
		iic_clk_en <= 1'b1;	
	else if(((state == STOP) && (cnt_bit == 3'd3) &&(cnt_iic_clk == 2'd3))||state==IDLE)
		iic_clk_en <= 1'b0;
	else	
		;
		
always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		cnt_iic_clk <= 2'd0;
	else if(iic_clk_en == 1'b1)
		cnt_iic_clk <= cnt_iic_clk + 1'b1;
	else	if(state==IDLE)
		cnt_iic_clk	<=	2'd0;
	else
		;
	
always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		cnt_bit<=3'b0				;
	else	if	(state==IDLE)
		cnt_bit<=3'd0				;
	else	if((state==SEND_D_ADDR||state==SEND_B_ADDR_H||state==SEND_B_ADDR_L||state==SEND_RD_ADDR||state==SEND_WR_DATA||state==RECEIVE_RD_DATA||state==STOP) && cnt_iic_clk==2'd3)
		cnt_bit<=1'b1+cnt_bit	;
	else
		cnt_bit<=cnt_bit			;

//状态转移
always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		state<=IDLE		;
	else	begin
		case(state)
			IDLE 				:
				if(iic_start==1'b1 && (wr_en==1'b1||rd_en==1'b1))
					state<=START_1			;
				else
					;
			START_1 			:
				if(cnt_iic_clk==2'd3)
					state<=SEND_D_ADDR	;
				else
					;
			SEND_D_ADDR 	:
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=ACK_1			;
				else
					;
			ACK_1 			:
				if (ack==1'b1 && cnt_iic_clk==2'd1)
					state<=STOP	;
				else if(cnt_iic_clk==2'd3 && ack==1'b0)
					state<=SEND_B_ADDR_H	;
				else
					;
			SEND_B_ADDR_H 	:
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=ACK_2			;
				else
					;
			ACK_2 			:
				if (ack==1'b1 && cnt_iic_clk==2'd1)
					state<=STOP	;
				else if(cnt_iic_clk==2'd3 && ack==1'b0)
					state<=SEND_B_ADDR_L	;
				else
					;
			SEND_B_ADDR_L 	:
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=ACK_3			;
				else
					;
			ACK_3 			:
				if (ack==1'b1 && cnt_iic_clk==2'd1)
					state<=STOP	;
				else if(cnt_iic_clk==2'd3 && ack==1'b0)
					if(wr_en==1'b1)
						state<=SEND_WR_DATA	;
					else if(rd_en==1'b1)
						state<=START_2			;
					else
						state<=IDLE				;
				else	
					;
			SEND_WR_DATA 	:				
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=ACK_4				;
				else
					;
			ACK_4 			:
				if(cnt_iic_clk==2'd3)
					state<=STOP					;
				else
					;
			START_2 			:
				if(cnt_iic_clk==2'd3)
					state<=SEND_RD_ADDR		;
				else
					;
			
			SEND_RD_ADDR	:
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=ACK_5				;
				else
					;
			ACK_5 			:
				if (ack==1'b1 && cnt_iic_clk==2'd1)
					state<=STOP	;
				else if(cnt_iic_clk==2'd3 && ack==1'b0)
					state<=RECEIVE_RD_DATA	;
				else
					;
			RECEIVE_RD_DATA:
				if(cnt_bit==3'd7 && cnt_iic_clk==2'd3)
					state<=N_ACK				;
				else
					;
			N_ACK 			:
				if(cnt_iic_clk==2'd3)
					state<=STOP	;
				else
					;
			STOP 				:
				if(cnt_bit==3'd3 && cnt_iic_clk==2'd3)
					state<=IDLE	;
				else
					;
			default:	state<=IDLE	;
		endcase
	end
		
//always@(*)
//	case(state)
//		ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
//			if(cnt_iic_clk==2'd0)
//				ack<=sda_in	;
//			else
//				;
//		default:
//			ack<=1'b1	;
//		endcase	

always@(*)
	case (state)
		IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
		SEND_WR_DATA,START_2,SEND_RD_ADDR,RECEIVE_RD_DATA,N_ACK:
			ack <= 1'b1;
		ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
			if(cnt_iic_clk == 2'd0)
				ack <= sda_in;
			else
				ack <= ack;
	default: ack <= 1'b1;
	endcase



		
always@(posedge iic_clk or negedge rst_n_sys)
	if (~rst_n_sys)
		scl<=1'b1	;
	else if(state==STOP || state==IDLE)
		scl<=1'b1	;
	else if(cnt_iic_clk==2'd2)
		scl<=1'b0	;
	else if(cnt_iic_clk==2'd0)
		scl<=1'b1	;
	else
		;

always@(*)
	begin
		case(state)
			IDLE 				:
				sda_reg<=1'b1		;

			START_1 			:
				if	(cnt_iic_clk==2'd0)
					sda_reg<=1'b1	;
				else	
					sda_reg<=1'b0	;

			SEND_D_ADDR 	:
				if(cnt_bit==3'd7)
					sda_reg<=1'b0	;
				else
					sda_reg<=DEVICE_ADDR[6-cnt_bit]	;

			ACK_1 			:
				sda_reg<=1'b1	;

			SEND_B_ADDR_H 	:
				sda_reg<=byte_addr[15-cnt_bit]	;
				
			ACK_2 			:
				sda_reg<=1'b1	;
				
			SEND_B_ADDR_L 	:
				sda_reg<=byte_addr[7-cnt_bit]	;
				
			ACK_3 			:
				sda_reg<=1'b1	;
				
			SEND_WR_DATA 	:				
				sda_reg<=wr_data[7-cnt_bit]	;
				
			ACK_4 			:
				sda_reg<=1'b1	;
				
			START_2 			:
				if(cnt_iic_clk <= 2'd1)
					sda_reg <= 1'b1;
				else
					sda_reg <= 1'b0;

				
			SEND_RD_ADDR	:
				if(cnt_bit==3'd7)
					sda_reg<=1'b1							;
				else
					sda_reg<=DEVICE_ADDR[6-cnt_bit]	;
			ACK_5 			:
				sda_reg<=1'b1	;
				
			RECEIVE_RD_DATA:
				sda_reg<=1'b1	;
			
			N_ACK 			:
				sda_reg<=1'b1	;
				
			STOP 				:
				if(cnt_bit==3'd0)
					sda_reg<=1'b0	;
				else
					sda_reg<=1'b1	;

			default:	state<=IDLE	;
		endcase
	end
				
always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		rd_data_reg<=8'd0								;
	else	if(state==RECEIVE_RD_DATA && cnt_iic_clk==2'b1)
		rd_data_reg<={rd_data_reg[6:0],sda_in}	;
	else
		;

always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		rd_data <= 8'd0;
	else if((state == RECEIVE_RD_DATA)&&(cnt_bit == 3'd7)&&(cnt_iic_clk == 2'd3))
		rd_data <= rd_data_reg;
	else
		;

always@(posedge iic_clk or negedge rst_n_sys)
	if(rst_n_sys == 1'b0)
		iic_end <= 1'b0;
	else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_iic_clk == 2'd3))
		iic_end <= 1'b1;
	else
		iic_end <= 1'b0;
		
assign sda_in = sda	;

assign sda_en = ((state == RECEIVE_RD_DATA)||(state == ACK_1)||(state == ACK_2)|| (state == ACK_3)||(state == ACK_4)||(state == ACK_5))? 1'b0 : 1'b1	;

assign sda =(sda_en==1'b1)?sda_reg:1'bz	;

endmodule
				
				

 

收发模块:

 

 

 

module iic_wr_rd_data(
	input		wire	clk_sys				,
	input		wire	rst_n_sys			,			
	input		wire	iic_clk 				, 	
	input		wire	iic_end 				, 	
	input		wire	[7:0] rd_data		, 				
	input		wire	rd_flag				,
	input		wire	wr_flag				,
	
	output	reg	wr_en					,
	output	reg	rd_en					,
	output	reg	iic_start			,
	output	wire	[7:0]fifo_rd_data	,
	output	reg	[15:0]byte_addr	,
	output	reg	[7:0]wr_data					
	
	);
	
	reg	start_vaild				;
	reg	[7:0]cnt_start_vaild	;
	reg	[12:0]cnt_wr_rd_start;
	reg	[7:0]cnt_byte			;
	reg	fifo_in_en				;
	reg	fifo_out_en				;
	
	
always@(posedge clk_sys or negedge rst_n_sys)	
	if(~rst_n_sys)begin	
		wr_en<=1'b0		;
		rd_en<=1'b0		;
		end
	else	if(cnt_byte==3'd2 && iic_end==1'b1)begin	
		wr_en<=1'b0		;
		rd_en<=1'b0		;
		end
	else	if(wr_flag==1'b1)begin
		wr_en<=1'b1		;
		rd_en<=1'b0		;
		end
	else	if(rd_flag==1'b1)begin
		wr_en<=1'b0		;
		rd_en<=1'b1		;
		end
	else
		;
		
always@(posedge clk_sys or negedge rst_n_sys)	
	if(~rst_n_sys)
		start_vaild<=1'b0	;
	else	if(cnt_start_vaild==8'd200)
		start_vaild<=1'b0	;
	else	if(wr_flag==1'b1 || rd_flag==1'b1)
		start_vaild<=1'b1	;
	else
		;
		
always@(posedge clk_sys or negedge rst_n_sys)	
	if(~rst_n_sys)
		cnt_start_vaild<=8'b0	;
	else if(cnt_start_vaild==8'd200)
		cnt_start_vaild<=8'd0	;
	else	if(start_vaild==1'b1)
		cnt_start_vaild<=cnt_start_vaild+1'b1	;
	else	
		cnt_start_vaild<=8'd0	;

always@(posedge iic_clk or negedge rst_n_sys)	
	if(~rst_n_sys)
		iic_start<=1'b0	;
	else	if(start_vaild==1'b1 || cnt_wr_rd_start==13'd5_000)
		iic_start<=1'b1	;
	else	
		iic_start<=1'b0	;
	
always@(posedge iic_clk or negedge rst_n_sys)	
	if(~rst_n_sys)	
		cnt_wr_rd_start<=13'd0	;
	else	if ((wr_en==1'b0 && rd_en==1'b0) || cnt_wr_rd_start==13'd5000)
		cnt_wr_rd_start<=13'd0	;
	else	
		cnt_wr_rd_start<=cnt_wr_rd_start+1'b1	;
				
always@(posedge iic_clk or negedge rst_n_sys)	
	if(~rst_n_sys)	begin
		wr_data	<=8'd89		;
		byte_addr<=8'd18		;
		cnt_byte	<=8'b0		;
		end
	else	if(iic_end==1'b1 && cnt_byte==8'd2)begin
		wr_data	<=8'd89		;	
		byte_addr<=8'd18		;	
		cnt_byte	<=8'b0		;	
		end
	else	if(iic_end==1'b1)begin
		wr_data	<=wr_data	+1'b1	;
	   byte_addr<=byte_addr	+1'b1	;
		cnt_byte	<=cnt_byte	+1'b1	;
		end
	else	
		;
		
always@(posedge iic_clk or negedge rst_n_sys)	
	if(~rst_n_sys)	begin
		fifo_in_en	<=1'b0		;
		fifo_out_en	<=1'b0		;
		end
	else	begin
		fifo_in_en	<=iic_end	;
		fifo_out_en	<=fifo_in_en;
		end
	
	
iic_fifo	iic_fifo_inst (
	.clock 	( iic_clk 		),
	.data 	( rd_data 		),
	.rdreq 	( fifo_in_en  	),
	.wrreq 	( fifo_out_en && rd_en	),
	.q 		( fifo_rd_data )
	);	
	
	
endmodule

说实话挺长的,参考了好几次视频的代码,还花了挺长时间检查,还帮一个群里实习生检查了一次(笑哭.jpg)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值