| **银杏科技有限公司旗下技术文档发布平台** ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^
| V1.0 | 2020-03-14 | gingko | 初次建立 |
===== 实验十八:I2C实验——基于I2C的ARM与FPGA通信 =====
==== 一、 实验目的与意义 ====
- 掌握I2C通信协议。
- 掌握I2C时序及使用方法。
- 掌握QuartusII的使用方法。
==== 二、 实验设备及平台 ====
- iCore4T 双核心板。
- Blaster(或相同功能)仿真器。
- JLINK(或相同功能)仿真器。
- Micro USB线缆。
- Keil MDK 开发平台。
- Quartus开发平台。
- 电脑一台。
==== 三、 实验原理 ====
* I2C是Inter-Integrated Circuit的缩写,是由Philips公司开发的一种简单的双向二线制同步串行总线。I2C通信的总线信号是双向的,但是对于通信的设备来说,有主从之分,支持一主多从货多主连接模式,这是由通信协议内部的设备地址所决定的。
* I2C的两条总线:一条SDA(Serial Data Line),另一条SCL(Serial Clock)。其中,SCL总线方向总是由主机输向从机,在每个时钟上升沿到来时将数据写入每个从机设备中;SDA总线的方向是双向的,由主向从或由从向主,其数据的传输方向取决于命令内部的读写标志。其通信时序如图18.1所示:
{{ :icore4t:icore4t_fpga_18_1.png?direct |图18.1 I2C通信时序}}
* 如图19.1,SCL一般由主机驱动发向从机。下面对开始标志(START)后的第一个字节进行解析:该字节主要包含器件专用地址码(高7位)和数据方向标志位(低1位),其中,专用地址码的高4位为器件类型,由厂家制定,低3位位器件引脚定义,由使用者定义。数据方向标志位,‘0’表示数据方向由主向从,‘1’表示数据方向由从向主。
* 本实验是将FPGA自身仿真成一个容量为256Byte的eeprom,按照标准eeprom的标准通信时序进行通信,其写时序如图18.2所示,读时序如图18.3所示。
**图18.2 写时许**
{{ :icore4t:icore4t_fpga_18_2.png?direct |图18.2 写时许}}
**图18.3 读时序**
{{ :icore4t:icore4t_fpga_18_3.png?direct |图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
==== 五、 实验步骤及实验结果 ====
{{ :icore4t:icore4t_ide_1_37.jpg?600 |图18.4}}
- 将硬件正确连接,如图18.4所示。
- 打开putty串口调试工具,打开设备管理器查看对应的端口信息,在putty中打开对应的端口,用于打印串口信息;
- 将编写好的FPGA代码进行编译,并下载到开发板中;
- 将编写好的ARM代码编译,并下载到开发板中,在putty串口调试工具中输入“test”,开始执行读写操作;
- 观察实验现象及putty终端打印信息——FPGA_LED闪烁,putty终端打印如图18.5所示.
{{ :icore4t:icore4t_fpga_18_5.png?direct |图18.5}}
==== 六、 拓展实验 ====
1.通过Signaltap观察I2C通信时序是否和参考时序一致。