| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-06-29 | gingko | 初次建立 | \\ \\ ===== 实验十八:SPI通信实验——基于SPI总线的ARM与FPGA通信 ===== ==== 一、 实验目的与意义 ==== - 掌握SPI通信协议及实现方法。 - 掌握ISE开发环境的使用。 ==== 二、 实验设备及平台 ==== - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。 - USB CABLE(或相同功能)仿真器。 - JLINK(或相同功能)仿真器。 - Micro USB线缆。 - Keil MDK 开发平台。 - ISE开发平台。 - 电脑一台。 ==== 三、 实验原理 ==== === 1.SPI简介 === * SPI是串行外设接口(Serial PeripheralInterface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB布局节省空间。它以主从方式工作,这种模式通常有一个主设备,和一个或多个从设备;SPI的通信原理很简单,需要至少4根线(事实上单向传输时3根也可以),是所有基于SPI通信的设备共用的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。 * **SPI硬件接口:** * MISO :主设备数据输入,从设备数据输出 * MOSI :主设备数据输出,从设备数据输入 * SCLK :时钟信号,由主设备产生 * CS :从设备片选信号,由主设备控制 === 2.SPI功能说明 === * **SPI时钟极性和相位:** * CPOL决定时钟空闲时的稳定电平,对主/从都有效 * CPOL=0:空闲时低电平 * CPOL=1:空闲时高电平 * CPHA决定数据采样时刻 * CPHA=0:第一个时钟沿开始采样MSBit * CPHA=1:第二个时钟沿开始采样MSBit * SPI总线四种工作方式 * SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。如图18-1所示: {{ :icore4tx:icore4tx_fpga_18_1.png?direct |图18-1}} === 3.SPI通信指令表 === 表18.1 SPI通信指令表 |指令名称 |字节1| 字节2 |字节3 |字节4| |器件ID |01h | | | | |写数据长度 |02h |A15~A8 |A7~A0| | |写数据 |04h |A15~A8 |A7~A0 |数据(直至写完所有数据)| |读数据长度 |05h |A15~A8 |A7~A0 | | |读数据 |07h |A15~A8 |A7~A0 |数据(直至读完所有数据)| |读错误信息 |08h | | | | * ARM与FPGA通信采用的是半双工式通信,FPGA通过识别指令完成与ARM的交互。 * 器件ID指令为01h,接下来为两字节的伪指令,第四字节仍为伪指令读取ID标志。写数据长度指令02h,接下来两个字节为写数据的长度,先发高字节,后发低字节,接下来为一个字节的伪指令00h。 * 写数据指令为04h,接下来为两字节的地址指令,后为要写入的数据,数据写入完毕以伪指令00h结束数据传输。 * 读数据长度指令05h,接下来两个字节为写数据的长度,先发高字节,后发低字节,接下来为一个字节的伪指令00h。 * 读数据指令为07h,接下来为两字节的地址指令,其后为伪指令00h开始读取数据进行数据传输,第五字节以后为要读取的数据。 * 读错误信息指令08h,FPGA接收数据是否出错,先发送两个字节的伪指令,第四字节仍为伪指令读取错误标志信息。 ==== 四、 代码讲解 ==== * 本实验基于ARM+FPGA构架, ARM首先发送查询ID指令,然后依次通过指令设置写数据的长度、写数据地址、读数据的长度、读数据的地址,然后对比写入数据和读出数据是否一致判断数据是否写入成功,从而实现ARM与FPGA之间基于SPI总线的通信。 * 1、更改写数据长度、写数据地址、读数据长度、读数据地址参数时,只需在ARM程序中更改初始化值即可。 这里有个地方需要注意:在设置过程中,地址和数据总长度之和不能大于1024,这是由FPGA内部设置的RAM容量决定的。 * 2、实现ARM与FPGA通信,对FPGA而言,通过模拟SPI时序,实现SPI接收与发送数据,以及数据的串并转换。ARM通过SPI总线发送指令到FPGA,FPGA接受指令后进行解析;然后根据指令需求将相应的数据放到MOSI总线上,等待ARM读取,从而实现两者之间的 * 数据交互。FPGA实现SPI时序的代码如下: //--------------------------接收模块----------------------// reg [3:0]receive_state; reg [7:0]data_in; reg [7:0]receive_byte_r; reg spi_rx_en_r; //按字节接收SPI发送过来的数据 always@(posedge spi_clk or negedge rst_n or posedge cs_delay) begin if((!rst_n)||(cs_delay)) begin receive_state <= 4'd0; receive_byte_r <= 8'd0; spi_rx_en_r <= 1'd0; end else begin //低时钟时利用提取沿的方式,从高位开始接收数据,每8个spi_clk时钟接收一个Byte case(receive_state) 4'd0:begin receive_state <= receive_state + 1'd1; data_in <= {data_in[6:0],spi_mosi}; spi_rx_en_r <= 1'd0; end 4'd1,4'd2:begin receive_state <= receive_state + 1'd1; data_in <= {data_in[6:0],spi_mosi}; end 4'd3:begin receive_state <= receive_state + 1'd1; data_in <= {data_in[6:0],spi_mosi}; spi_rx_en_r <= 1'd1; end 4'd4,4'd5:begin receive_state <= receive_state + 1'd1; data_in <= {data_in[6:0],spi_mosi}; spi_rx_en_r <= 1'd0; end 4'd6:begin receive_state <= receive_state + 1'd1; data_in <= {data_in[6:0],spi_mosi}; end 4'd7:begin receive_state <= 4'd0; data_in <= {data_in[6:0],spi_mosi}; receive_byte_r <= {data_in[6:0],spi_mosi}; end endcase end end //-----------------------发送模块-------------------------// reg [3:0]send_state; reg spi_miso_r; reg spi_tx_en_r; reg [7:0]data_out; always@(negedge spi_clk or negedge rst_n or posedge cs_delay) if((!rst_n) || (cs_delay)) begin send_state <= 4'd0; spi_tx_en_r <= 1'd0; data_out <= 8'd0; end else begin case(send_state) 4'd0:begin spi_miso_r <= data_out[7]; send_state <= send_state + 1'd1; end 4'd1:begin spi_miso_r <= data_out[6]; send_state <= send_state + 1'd1; end 4'd2:begin spi_miso_r <= data_out[5]; send_state <= send_state + 1'd1; end 4'd3:begin spi_miso_r <= data_out[4]; send_state <= send_state + 1'd1; end 4'd4:begin spi_miso_r <= data_out[3]; send_state <= send_state + 1'd1; spi_tx_en_r <= 1'd0; end 4'd5:begin spi_miso_r <= data_out[2]; send_state <= send_state+ 1'd1; spi_tx_en_r <= 1'd1; end 4'd6:begin spi_miso_r <= data_out[1]; send_state <= send_state + 1'd1; spi_tx_en_r <= 1'd0; end 4'd7:begin data_out <= send_byte; spi_miso_r <= data_out[0]; send_state <= 4'd0; spi_tx_en_r <= 1'd0; end endcase end ==== 五、 实验步骤及实验结果 ==== {{ :icore4tx:icore4tx_fpga_18_2.png?direct |图18-2}} - 将硬件正确连接,如图18-2所示。 - 打开putty串口调试工具,打开设备管理器查看对应的端口信息,在putty中打开对应的端口,设置波特率115200,用于打印串口信息; - 将编写好的FPGA代码进行编译,并下载到开发板中; - 将编写好的ARM代码编译,并下载到开发板中,putty工具中会打印相应的SPI通信相关信息; - 观察实验现象及putty终端打印信息——FPGA_LED闪烁,putty终端打印如图18-3所示。 {{ :icore4tx:icore4tx_fpga_18_3.png?direct |图18-3}} ==== 六、 拓展实验 ==== - 通过仿真观察SPI通信的时序是否和参考时序一致。 - 实现错误信息读取的指令功能。