这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
|
基于spi总线的arm与fpga通信实验 [2020/07/02 18:22] zgf [3.SPI通信指令表] |
基于spi总线的arm与fpga通信实验 [2022/03/22 10:29] (当前版本) sean |
||
|---|---|---|---|
| 行 2: | 行 2: | ||
| |技术支持电话|**0379-69926675-801**||| | |技术支持电话|**0379-69926675-801**||| | ||
| |技术支持邮件|Gingko@vip.163.com||| | |技术支持邮件|Gingko@vip.163.com||| | ||
| - | |技术论坛|http://www.eeschool.org||| | ||
| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ||
| | V1.0 | 2020-07-02 | gingko | 初次建立 | | | V1.0 | 2020-07-02 | gingko | 初次建立 | | ||
| 行 39: | 行 38: | ||
| * SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。如图13-1所示 | * SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。如图13-1所示 | ||
| - | 图13-1 | + | {{ :icore4:icore4_fpga_13_1.png?direct |图13-1}} |
| === 3.SPI通信指令表 === | === 3.SPI通信指令表 === | ||
| 行 59: | 行 58: | ||
| === 4、本实验实现方式 === | === 4、本实验实现方式 === | ||
| * 通过FPGA建立的SPI模块对外提供的SCLK、CS、MOSI、MISO接口与STM32的SPI相连接,Commix串口精灵与STM32通过串口连接,实现三者之间的通信。本实验中,Commix串口精灵向STM32发送数据,STM32的RXD端口接收数据,然后,通过TXD端口把数据发送至FPGA,STM32起到一个桥梁作用。程序运行后,FPGA定时向STM32发送数据,经过STM32发送至串口精灵显示出来。下图为实验原理图。 | * 通过FPGA建立的SPI模块对外提供的SCLK、CS、MOSI、MISO接口与STM32的SPI相连接,Commix串口精灵与STM32通过串口连接,实现三者之间的通信。本实验中,Commix串口精灵向STM32发送数据,STM32的RXD端口接收数据,然后,通过TXD端口把数据发送至FPGA,STM32起到一个桥梁作用。程序运行后,FPGA定时向STM32发送数据,经过STM32发送至串口精灵显示出来。下图为实验原理图。 | ||
| - | + | {{ :icore4:icore4_fpga_13_2.png?direct&700 |实验原理图}} | |
| ==== 四、 代码讲解 ==== | ==== 四、 代码讲解 ==== | ||
| * 本实验基于ARM+FPGA构架,通过ARM首先发送查询ID指令,然后依次通过指令设置写数据的长度、写数据地址、读数据的长度、读数据的地址,然后对比写入数据和读出数据是否一致判断数据是否写入成功,从而基于SPI总线实现ARM与FPGA之间的通信。 | * 本实验基于ARM+FPGA构架,通过ARM首先发送查询ID指令,然后依次通过指令设置写数据的长度、写数据地址、读数据的长度、读数据的地址,然后对比写入数据和读出数据是否一致判断数据是否写入成功,从而基于SPI总线实现ARM与FPGA之间的通信。 | ||
| - | * 1、实现ARM与FPGA通信,对FPGA而言,其关键就在于SPI时序的模拟,实现SPI数据的接收与发送,实现数据与传输信号之间的串并转换。FPGA首先接收ARM指令,然后解析指令,存储相应的信息与数据,并根据指令需求将相应的指令数据放到SIMO总线上,等待ARM读取,从而实现两者之间的数据交互。SPI时序的硬件语言描述如下: | + | * 实现ARM与FPGA通信,对FPGA而言,其关键就在于SPI时序的模拟,实现SPI数据的接收与发送,实现数据与传输信号之间的串并转换。FPGA首先接收ARM指令,然后解析指令,存储相应的信息与数据,并根据指令需求将相应的指令数据放到SIMO总线上,等待ARM读取,从而实现两者之间的数据交互。SPI时序的硬件语言描述如下: |
| <code verilog> | <code verilog> | ||
| ////////////////按字节接收SPI发送过来的数据////////////// | ////////////////按字节接收SPI发送过来的数据////////////// | ||
| 行 72: | 行 71: | ||
| always@(posedge spi_clk or negedge rst_n) | always@(posedge spi_clk or negedge rst_n) | ||
| if(!rst_n) | if(!rst_n) | ||
| - | begin | + | begin |
| - | i <= 4'd0; | + | i <= 4'd0; |
| - | temp_data <= 40'd0; | + | temp_data <= 40'd0; |
| - | data <= 40'd0; | + | data <= 40'd0; |
| - | data_in <= 8'd0; | + | data_in <= 8'd0; |
| - | end | + | end |
| else case(i) //从高位开始接收数据,每8个spi_clk时钟接收一个Byte | else case(i) //从高位开始接收数据,每8个spi_clk时钟接收一个Byte | ||
| 4'd0: | 4'd0: | ||
| - | begin | + | begin |
| - | i <= i + 1'd1; | + | i <= i + 1'd1; |
| - | data_in <= {data_in[6:0],spi_mosi}; | + | data_in <= {data_in[6:0],spi_mosi}; |
| - | temp_data <= {temp_data[31:0],data_in}; | + | temp_data <= {temp_data[31:0],data_in}; |
| - | if(data_in == 8'd13) | + | if(data_in == 8'd13) |
| - | begin | + | begin |
| - | data <= temp_data; | + | data <= temp_data; |
| - | end | + | end |
| - | else | + | else |
| - | begin | + | begin |
| - | data <= data; | + | data <= data; |
| - | end | + | end |
| end | end | ||
| 4'd1,4'd2,4'd3,4'd4,4'd5,4'd6: | 4'd1,4'd2,4'd3,4'd4,4'd5,4'd6: | ||
| begin | begin | ||
| - | i <= i + 1'd1; | + | i <= i + 1'd1; |
| - | data_in <= {data_in[6:0],spi_mosi}; | + | data_in <= {data_in[6:0],spi_mosi}; |
| end | end | ||
| 4'd7:begin | 4'd7:begin | ||
| - | i <= 4'd0; | + | i <= 4'd0; |
| - | data_in <= {data_in[6:0],spi_mosi}; | + | data_in <= {data_in[6:0],spi_mosi}; |
| end | end | ||
| default: i <= 4'd0; | default: i <= 4'd0; | ||
| 行 110: | 行 109: | ||
| always@(posedge clk_25m or negedge rst_n) | always@(posedge clk_25m or negedge rst_n) | ||
| if(!rst_n) | if(!rst_n) | ||
| - | begin | + | begin |
| - | led <= 3'b101; | + | led <= 3'b101; |
| - | end | + | end |
| else if (data == ledr) | else if (data == ledr) | ||
| - | led <= 3'b011; //红灯亮 | + | led <= 3'b011; //红灯亮 |
| else if (data == ledg) | else if (data == ledg) | ||
| - | led <= 3'b101; //绿灯亮 | + | led <= 3'b101; //绿灯亮 |
| else if (data == ledb) | else if (data == ledb) | ||
| - | led <= 3'b110; //蓝灯亮 | + | led <= 3'b110; //蓝灯亮 |
| assign {led_red,led_green,led_blue} = led; | assign {led_red,led_green,led_blue} = led; | ||
| 行 126: | 行 125: | ||
| always@(posedge clk_25m or negedge rst_n) | always@(posedge clk_25m or negedge rst_n) | ||
| if(!rst_n) | if(!rst_n) | ||
| - | begin | + | begin |
| - | spi_clk_r <= 1'd1; | + | spi_clk_r <= 1'd1; |
| - | end | + | end |
| else | else | ||
| spi_clk_r <= spi_clk; | spi_clk_r <= spi_clk; | ||
| 行 140: | 行 139: | ||
| if(!rst_n) | if(!rst_n) | ||
| begin | begin | ||
| - | data_out <= hello; | + | data_out <= hello; |
| - | spi_out <= 1'd0; | + | spi_out <= 1'd0; |
| - | j <= 6'd0; | + | j <= 6'd0; |
| end | end | ||
| else case(j) //连续40个spi_clk_r时钟发送“hello”字符串 | else case(j) //连续40个spi_clk_r时钟发送“hello”字符串 | ||
| 6'd0: | 6'd0: | ||
| - | begin | + | begin |
| - | {spi_out,data_out[39:1]} <= data_out; | + | {spi_out,data_out[39:1]} <= data_out; |
| - | j <= j + 1'd1; | + | j <= j + 1'd1; |
| - | end | + | end |
| 6'd39: | 6'd39: | ||
| - | begin | + | begin |
| - | {spi_out,data_out[39:1]} <= data_out; | + | {spi_out,data_out[39:1]} <= data_out; |
| - | data_out <= hello; | + | data_out <= hello; |
| - | j <= 6'd0; | + | j <= 6'd0; |
| - | end | + | end |
| default: | default: | ||
| - | begin | + | begin |
| - | {spi_out,data_out[39:1]} <= data_out; | + | {spi_out,data_out[39:1]} <= data_out; |
| - | j <= j + 1'd1; | + | j <= j + 1'd1; |
| - | end | + | end |
| endcase | endcase | ||
| </code> | </code> | ||
| ==== 五、 实验步骤 ==== | ==== 五、 实验步骤 ==== | ||
| - | 1、把仿真器与iCore4的SWD调试口连接(直接相连或者通过转换器相连)。 | + | 1、把仿真器与iCore4的SWD调试口连接(直接相连或者通过转换器相连)。\\ |
| - | 2、将USB-Blaster与iCore4的JTAG调试口相连。 | + | 2、将USB-Blaster与iCore4的JTAG调试口相连。\\ |
| - | 3、将跳线帽插在USB_UART。 | + | 3、将跳线帽插在USB_UART。\\ |
| - | 4、把iCore4(USB_UART)通过Micro USB线与计算机相连,为iCore4供电。如图13-2所示: | + | 4、把iCore4(USB_UART)通过Micro USB线与计算机相连,为iCore4供电。如图13-2所示:\\ |
| 图13-2 | 图13-2 | ||
| - | 5、打开串口精灵,找到对应口打开,如下图。 | + | 5、打开串口精灵,找到对应口打开,如下图。\\ |
| - | + | {{ :icore4:icore4_fpga_13_3.png?direct |图13-3}} | |
| - | 图13-3 | + | 6、打开KeilMDK开发环境,并打开实验工程。\\ |
| - | 6、打开KeilMDK开发环境,并打开实验工程。 | + | 7、将ARM程序下载至iCore4。\\ |
| - | 7、将ARM程序下载至iCore4。 | + | 8、打开QuartusII开发环境,并打开实验工程。\\ |
| - | 8、打开QuartusII开发环境,并打开实验工程。 | + | 9、将FPGA程序下载至iCore4\\ |
| - | 9、将FPGA程序下载至iCore4 | + | 10、输入串口命令,观察实验现象。\\ |
| - | 10、输入串口命令,观察实验现象。 | + | |
| ==== 六、 实验现象 ==== | ==== 六、 实验现象 ==== | ||
| * 在Commix上发送命令后,对应的ARM和FPGA的LED灯亮,同时接收显示:hello | * 在Commix上发送命令后,对应的ARM和FPGA的LED灯亮,同时接收显示:hello | ||
| + | |||
| |串口命令发送格式 |ARM_LED现象 |FPGA_LED灯现象| | |串口命令发送格式 |ARM_LED现象 |FPGA_LED灯现象| | ||
| |LEDR\CR\LF |红灯亮 |红灯亮| | |LEDR\CR\LF |红灯亮 |红灯亮| | ||