用户工具

站点工具


icore4tfpga_18
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
技术论坛http://www.eeschool.org
版本 日期 作者 修改内容
V1.0 2020-03-14 gingko 初次建立

实验十八:I2C实验——基于I2C的ARM与FPGA通信

一、 实验目的与意义

  1. 掌握I2C通信协议。
  2. 掌握I2C时序及使用方法。
  3. 掌握QuartusII的使用方法。

二、 实验设备及平台

  1. iCore4T 双核心板。
  2. Blaster(或相同功能)仿真器。
  3. JLINK(或相同功能)仿真器。
  4. Micro USB线缆。
  5. Keil MDK 开发平台。
  6. Quartus开发平台。
  7. 电脑一台。

三、 实验原理

  • I2C是Inter-Integrated Circuit的缩写,是由Philips公司开发的一种简单的双向二线制同步串行总线。I2C通信的总线信号是双向的,但是对于通信的设备来说,有主从之分,支持一主多从货多主连接模式,这是由通信协议内部的设备地址所决定的。
  • I2C的两条总线:一条SDA(Serial Data Line),另一条SCL(Serial Clock)。其中,SCL总线方向总是由主机输向从机,在每个时钟上升沿到来时将数据写入每个从机设备中;SDA总线的方向是双向的,由主向从或由从向主,其数据的传输方向取决于命令内部的读写标志。其通信时序如图18.1所示:

图18.1  I2C通信时序

  • 如图19.1,SCL一般由主机驱动发向从机。下面对开始标志(START)后的第一个字节进行解析:该字节主要包含器件专用地址码(高7位)和数据方向标志位(低1位),其中,专用地址码的高4位为器件类型,由厂家制定,低3位位器件引脚定义,由使用者定义。数据方向标志位,‘0’表示数据方向由主向从,‘1’表示数据方向由从向主。
  • 本实验是将FPGA自身仿真成一个容量为256Byte的eeprom,按照标准eeprom的标准通信时序进行通信,其写时序如图18.2所示,读时序如图18.3所示。

图18.2 写时许 图18.2 写时许 图18.3 读时序 图18.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;
 
//                      rx_over <= 1'd1;
                        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;
                                        send_state <= send_state + 1'd1;
                                    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

五、 实验步骤及实验结果

图18.4

  1. 将硬件正确连接,如图18.4所示。
  2. 打开putty串口调试工具,打开设备管理器查看对应的端口信息,在putty中打开对应的端口,用于打印串口信息;
  3. 将编写好的FPGA代码进行编译,并下载到开发板中;
  4. 将编写好的ARM代码编译,并下载到开发板中,在putty串口调试工具中输入“test”,开始执行读写操作;
  5. 观察实验现象及putty终端打印信息——FPGA_LED闪烁,putty终端打印如图18.5所示.

图18.5

六、 拓展实验

1.通过Signaltap观察I2C通信时序是否和参考时序一致。

icore4tfpga_18.txt · 最后更改: 2020/03/14 10:46 由 zgf