FPGA学习笔记05——I2C通信

1.介绍

I2C总线是Philips半导体公司(现在的NXP半导体公司)开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出.需通过上拉电阻接电源VCC.当总线空闲时.两根线都是高电平。

在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原因处于高电平状态,此时I2C总线处于空闲状态。如果主机(此处指FPGA)想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在SCL为高电平时, SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。

在串行时钟线SCL为低电平状态时, SDA允许改变传输的数据位( 1为高电平, 0为低电平),在SCL为高电平状态时, SDA要求保持稳定,相当于一个时钟周期传输1bit数据,经过8个时钟周期后,传输了8bit数据,即一个字节。第8个时钟周期末,主机释
放SDA以使从机应答,在第9个时钟周期,从机将SDA拉低以应答;如果第9个时钟周期, SCL为高电平时, SDA未被检测到为低电平,视为非应答,表明此次数据传输失败。第9个时钟周期末,从机释放SDA以使主机继续传输数据,如果主机发送停止信号,此次传输结束。数据以8bit即一个字节为单位串行发出,其最先发送的是字节的最高位。

eeprom选用at24c02,at24c64

器件地址

写数据

读数据

2.硬件

FPGA管脚配置

信号名方向管教端口说明
sys_clkinputE1

系统时钟50M

sys_rst_ninputM1系统复位,低电平有效
led0outputF3 
led1outputF5 
key0inputG5 
key1inputL7 
key2inputJ6 
i2c_scloutputD5I2C时钟
i2c_sdkinputC3I2C数据

3.程序设计

always @(posedge i2c_clk or negedge rst) begin
	if(!rst)begin
		state 	   <= s0_idle_start;
		r_rd_sel   <= SDA_WRITE;
		sda_buffer <= 1'd1;
		buffer	   <= 8'd0;
		bit_cnt    <= 4'd0;
		rd_data	   <= 8'd0;
		I2C_WorR   <= I2C_READ;
	end
	else begin
		case(state)
			s0_idle_start:begin
				if	   ((key_flag == 1'd1) && (key_value == 3'b110))begin	// read
					r_rd_sel   <= SDA_WRITE;		// system control the iic sda write
					sda_buffer <= 1'd0;				// start signal
					state      <= s1_device_addr;	// state change
					buffer	   <= 8'b1010_0000;
					I2C_WorR   <= I2C_READ;
					rd_data    <= 8'd0;
				end
				else if((key_flag == 1'd1) && (key_value == 3'b101))begin	// write
					r_rd_sel   <= SDA_WRITE;		// system control the iic sda write
					sda_buffer <= 1'd0;				// start signal
					state      <= s1_device_addr;	// state change
					buffer	   <= 8'b1010_0000;
					I2C_WorR   <= I2C_WRITE;
				end
				else begin
					state <= s0_idle_start;
				end
			end
			s1_device_addr 	 	 :begin
				if(!scl)begin	// if scl is low level
					if(bit_cnt < 4'd8)begin
						r_rd_sel   <= SDA_WRITE;
						sda_buffer <= buffer[7];
						buffer     <= {buffer[6:0],buffer[7]};
						bit_cnt    <= bit_cnt + 1'b1;
					end
					else begin
						state    <= s2_device_addr_ack;
						bit_cnt  <= 4'd0;
						r_rd_sel <= SDA_READ;		// sda receive data
					end
				end
			end
			s2_device_addr_ack 	 :begin
				if(scl)begin		// if scl is high level
					r_rd_sel <= SDA_WRITE;
					if(sda == 1'd0)begin
						if(DATA_ADDR_WIDTH)begin	// 16bit data 
							state  <= s3_word_addr_high;
							buffer <= 8'h00;
						end
						else begin
							state <= s5_word_addr_low;
							buffer <= 8'h01;
						end
					end
					else begin
						//state <= s5_word_addr_low;
						state <= s0_idle_start;
					end
				end
			end
			s3_word_addr_high	 :begin
				if(!scl)begin	// if scl is low level
					if(bit_cnt < 4'd8)begin
						sda_buffer <= buffer[7];
						buffer     <= {buffer[6:0],buffer[7]};
						bit_cnt	   <= bit_cnt + 1'd1;
					end
					else begin
						state	 <= s4_word_addr_high_ack;
						bit_cnt  <= 4'd0;
						r_rd_sel <= SDA_READ;	// sda receive data
					end
				end
			end
			s4_word_addr_high_ack:begin
				if(scl)begin
					r_rd_sel <= SDA_WRITE;
					if(sda == 1'd0)begin
						state  <= s5_word_addr_low;
						buffer <= 8'h01;
					end
					else begin
						state <= s0_idle_start;
						//state  <= s5_word_addr_low;
					end
				end
			end
			s5_word_addr_low	 :begin
				if(!scl)begin	// if scl is low level
					if(bit_cnt < 4'd8)begin
						sda_buffer <= buffer[7];
						buffer     <= {buffer[6:0],buffer[7]};
						bit_cnt	   <= bit_cnt + 1'd1;
					end
					else begin
						state	 <= s6_word_addr_low_ack;
						bit_cnt  <= 4'd0;
						sda_buffer <= 1'd1;
						r_rd_sel <= SDA_READ;	// sda receive data
					end
				end
			end
			s6_word_addr_low_ack :begin
				if(scl)begin
					r_rd_sel   <= SDA_WRITE;
					if(sda == 1'd0)begin
						buffer <= 8'h53;	// character "S"
						if(I2C_WorR == I2C_WRITE)begin	// write iic
							state <= s7_data_write;
							sda_buffer <= 1'd0;		// put down sda in order to prevent the generation of stop signal

						end
						else begin				// iic read
							state <= s9_read_start;
							sda_buffer <= 1'd1;		// put down sda in order to generate a start signal
						end
					end
					else begin
						state <= s0_idle_start;
//						if(rw_en == 2'b01)begin
//							state <= s7_data_write;
//						end
//						else begin
//							state <= s9_read_start;
//						end
					end
				end
			end
			s7_data_write		 :begin
				if(!scl)begin // scl low level
					if(bit_cnt < 4'd8)begin
						bit_cnt 	<= bit_cnt +1'd1;
						sda_buffer	<= buffer[7];
						buffer  	<= {buffer[6:0],buffer[7]};
					end
					else begin
						state	 <= s8_data_write_ack;
						bit_cnt  <= 4'd0;
						r_rd_sel <= SDA_READ;
						sda_buffer <= 1'd0;
					end
				end
			end
			s8_data_write_ack	 :begin
				if(scl)begin
					r_rd_sel   <= SDA_WRITE;
					sda_buffer <= 1'd0;
					if(sda == 1'd0)begin
						state <= s13_stop;
					end
					else begin
//						state <= s13_stop;
						state <= s0_idle_start;
					end
				end
			end
			s9_read_start		 :begin
				if(scl)begin	// scl high level
					sda_buffer <= 1'd0;
					state	   <= s10_read_device_addr;
					buffer	   <= 8'b1010_0001;
				end
			end
			s10_read_device_addr :begin
				if(!scl)begin	// if scl is low level
					if(bit_cnt < 4'd8)begin
						r_rd_sel   <= SDA_WRITE;
						sda_buffer <= buffer[7];
						buffer     <= {buffer[6:0],buffer[7]};
						bit_cnt    <= bit_cnt + 1'b1;
					end
					else begin
						state    <= s11_read_decice_ack;
						bit_cnt  <= 4'd0;
						r_rd_sel <= SDA_READ;		// sda receive data
					end
				end
			end
			s11_read_decice_ack	 :begin
				if(scl)begin
					r_rd_sel   <= SDA_READ;
					if(sda == 1'd0)begin
						state <= s12_data_read;
					end
					else begin
//						state <= s12_data_read;
						state <= s0_idle_start;
					end
				end
			end
			s12_data_read		 :begin
				if(scl)begin
					if(bit_cnt < 4'd8)begin
						bit_cnt <= bit_cnt + 1'd1;
						rd_data[7-bit_cnt] <= sda;
					end
					else begin
						bit_cnt  <= 4'd0;
						state 	 <= s13_stop;
						r_rd_sel <= SDA_WRITE;
						sda_buffer <= 1'd0;
					end
				end
			end
			s13_stop			 :begin
				if(scl)begin
					state	   <= s0_idle_start;
					sda_buffer <= 1'd1;
				end
			end
		default:;
		endcase
	end
end	

用sigaltap观察波形

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值