FPGA学习笔记05——I2C通信

it2025-01-28  15

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观察波形

 

最新回复(0)