目录

银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
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. 电脑一台。

三、 实验原理

图18.1  I2C通信时序

图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通信时序是否和参考时序一致。