| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-07-01 | gingko | 初次建立 | ===== 实验十九:I2C实验——基于I2C的ARM与FPGA通信 ===== ==== 一、 实验目的与意义 ==== - 掌握I2C通信协议。 - 掌握I2C时序及通信流程。 - 掌握ISE软件的使用方法。 ==== 二、 实验设备及平台 ==== - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。 - USB-CABLE(或相同功能)仿真器。 - JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 - Micro USB线缆。 - Keil MDK 开发平台。 - ISE开发平台。 - 电脑一台。 ==== 三、 实验原理 ==== * I2C是Inter-Integrated Circuit的缩写,是由Philips公司开发的一种简单的双向二线制同步串行总线。I2C通信的总线信号是双向的,但是对于通信的设备来说,有主从之分,支持一主多从货多主连接模式,这是由通信协议内部的设备地址所决定的。 * I2C的两条总线:一条SDA(Serial Data Line),另一条SCL(Serial Clock)。其中,SCL总线方向总是由主机输向从机,在每个时钟上升沿到来时将数据写入每个从机设备中;SDA总线的方向是双向的,由主向从或由从向主,其数据的传输方向取决于命令内部的读写标志。其通信时序如图19.1所示: {{ :icore4tx:icore4tx_fpga_19_1.png?direct |图19-1 I2C通信时序}} * 如图19-1,SCL一般由主机驱动发向从机。下面对开始标志(START)后的第一个字节进行解析:该字节主要包含器件专用地址码(高7位)和数据方向标志位(低1位),其中,专用地址码的高4位为器件类型,由厂家制定,低3位位器件引脚定义,由使用者定义。数据方向标志位,‘0’表示数据方向由主向从,‘1’表示数据方向由从向主。 * 本实验是将FPGA自身仿真成一个容量为256Byte的eeprom,按照标准eeprom的标准通信时序进行通信,其写时序如图19-2所示,读时序如图19-3所示。 {{ :icore4tx:icore4tx_fpga_19_2.png?direct&700 |图19-2 写时序}} {{ :icore4tx:icore4tx_fpga_19_3.png?direct&700 |图19-3 读时序}} ==== 四、 代码讲解 ==== 1、正常情况下,数据的变化都发生在SCL的低电平,只有起始标志时,SDA的变化发生在SCL的高电平。根据这个特点,提取每次命令交互的开始标志,其代码如下: always@(negedge i2c_sda or negedge rst_n or posedge cmd_over) begin if(!rst_n) begin rx_start_r <= 1'd0; end else if(cmd_over) begin rx_start_r <= 1'd0; end else if(i2c_scl) begin rx_start_r <= 1'd1; end end 2、在每个SDL的上升沿锁存数据并将数据拼接成完整的字节,其代码如下: always@(posedge i2c_scl or negedge rst_n or posedge rx_start) begin if((!rst_n)||(rx_start)) begin receive_byte_r <= 8'd0; rx_state <= 8'd0; rx_over <= 1'd0; data_over <= 1'd0; end else begin case(rx_state) 8'd0:begin rx_state <= rx_state + 1'd1; receive_byte_r[7] <= i2c_sda; end 8'd1:begin rx_state <= rx_state + 1'd1; receive_byte_r[6] <= i2c_sda; end 8'd2:begin rx_state <= rx_state + 1'd1; receive_byte_r[5] <= i2c_sda; end 8'd3:begin rx_over <= 1'd0; rx_state <= rx_state + 1'd1; receive_byte_r[4] <= i2c_sda; end 8'd4:begin rx_state <= rx_state + 1'd1; receive_byte_r[3] <= i2c_sda; end 8'd5:begin rx_state <= rx_state + 1'd1; receive_byte_r[2] <= i2c_sda; end 8'd6:begin rx_state <= rx_state + 1'd1; receive_byte_r[1] <= i2c_sda; end 8'd7:begin rx_state <= rx_state + 1'd1; receive_byte_r[0] <= i2c_sda; data_over <= 1'd1; end 8'd8:begin rx_state <= 8'd0; rx_over <= 1'd1; data_over <= 1'd0; end default:begin end endcase end end 3、在给个数据拼接完成后,对该数据进行二级存储,并根据接收的自己数目产生RAM的读写时钟信号和命令完成标志,其代码下: always@(posedge rx_over or negedge rst_n or posedge rx_start) begin if(!rst_n) begin ack_state <= 4'd0; address_r <= 8'd0; rx_data <= 8'd0; wr_clk_r <= 1'd0; rd_clk_r <= 1'd0; cmd_over_r <= 1'd1; end else if(rx_start) begin ack_state <= 4'd0; wr_clk_r <= 1'd0; cmd_over_r <= 1'd0; end else begin case(ack_state) 4'd0:begin if(receive_byte_r==8'ha0) begin ack_state <= ack_state + 1'd1; end else if(receive_byte_r==8'ha1) begin rd_clk_r <= 1'd1; ack_state <= 4'd3; end else begin ack_state <= ack_state; end end 4'd1:begin//锁存地址信息 address_r <= receive_byte_r; ack_state <= ack_state + 1'd1; cmd_over_r <= 1'd1; end 4'd2:begin//接收写入RAM的数据 rx_data <= receive_byte_r; wr_clk_r <= 1'd1; end 4'd3:begin rd_clk_r <= 1'd0; cmd_over_r <= 1'd1; end default:begin end endcase end end 4、在每个读命令到来时发送,读取的数据,其代码如下: always@(negedge i2c_scl or negedge rst_n or posedge rx_start) begin if((!rst_n)||(rx_start)) begin sda_r <= 1'hz; send_state <= 8'd0; end else begin case(send_state) 8'd0:begin if(data_over) begin if(receive_byte_r==8'ha0) begin sda_r <= 1'd0; end else if(receive_byte_r==8'ha1) begin sda_r <= 1'd0; send_state <= 8'd3; end else begin sda_r <= 1'hz; end end else begin send_state <= send_state; end end 8'd1:begin if(data_over) begin sda_r <= 1'd0; send_state <= send_state + 1'd1; end else begin sda_r <= 1'hz; end end 8'd2:begin if(data_over) begin sda_r <= 1'd0; end else begin sda_r <= 1'hz; end end 8'd3:begin {sda_r,send_data[7:1]} <= data_out; send_state <= send_state + 1'd1; end 8'd4,8'd5,8'd6,8'd7,8'd8,8'd9,8'd10,:begin {sda_r,send_data[7:1]} <= send_data; send_state <= send_state + 1'd1; end 8'd11:begin sda_r <= 1'hz; end default:begin end endcase end end ==== 五、 实验步骤及实验结果 ==== {{ :icore4tx:icore4tx_fpga_19_4.png?direct |图19-4}} - 将硬件正确连接,如图19-4所示。 - 打开putty串口调试工具,打开设备管理器查看对应的端口信息,在putty中打开对应的端口,设置波特率115200,用于打印串口信息; - 将编写好的FPGA代码进行编译,并下载到开发板中; - 将编写好的ARM代码编译,并下载到开发板中,在putty串口调试工具中输入“test”,开始执行读写操作; - 观察现象及putty终端打印信息——FPGA_LED闪烁,putty终端打印如下图所示: {{ :icore4tx:icore4tx_fpga_19_5.png?direct&650 |图19-5}} ==== 六、 拓展实验 ==== 1.通过chipscope观察I2C通信时序是否和参考时序一致。