在某些应用场景下,有时候需要 SPI 的参数可配置,例如 SPI 的时钟频率、时钟极性和相位以及 SPI 数据位宽等参数可配置,以兼容更多的应用。这里就以 FPGA 为例简单介绍一下实现原理。
一、测试平台
- Quartus II 13.1 + Keil 5.3
- iCore4
二、可配置的 SPI 通信协议实现原理
2.1 SPI 时钟频率可配置
SPI 时钟频率可配置,主要是通过任意分频器来实现,具体可以参考如下文章:
2.2 SPI 时钟极性相位可配置
关于时钟极性和相位的基础知识可参考之前的文章:
接下来直接给出 FPGA 端的代码分析和仿真流程:
(1)testbench 产生触发信号 start 和数据 SSPI_data_in,DUT 将数据存入 FIFO 中,同时通过跨时钟域处理,将 start 信号转换成 FIFO 读使能信号 spi_valid,不过这里需要延迟一个周期生成 spi_valid_r1,用于读取 FIFO(数据为 SSPI_data_in_q),再延迟一个周期 spi_valid_r2 待数据读出后用于使能 spi 状态机。
(2)根据 SSPI_CPOL_CPHA 的配置信息,执行不同的状态机;需要说明一点,spi 时钟在 CPOL=’1’下,如果用 sys_clk 下降沿采样,最后会额外产生一个毛刺,同样,spi 时钟在 CPOL=’0’下,如果用 sys_clk 上升沿采样,也会产生一个毛刺,所以这部分也根据 SSPI_CPOL_CPHA 的配置,执行不同的状态机。
(3)tb_received_spi_data 为 testbench 收到 DUT 发送过来的 SPI 数据,可以发现和原始信号 SSPI_data_in 相同;tb_send_spi_data 为 testbench 反馈的 SPI 数据,经状态机后,DUT 收到数据 SSPI_data_out,同样,两个数据相同。
下面分别是 4 种 SSPI_CPOL_CPHA 配置下的仿真波形。




2.3 SPI 数据位宽的设置
FPGA 设定的 SPI 数据位宽为 32 位,对应的寄存器也是 32 位,对于小于 32 位的应用,可通过控制状态机的执行过程来实现相应数据位宽的收发。例如 24 位的 SPI 数据位宽,状态机执行到 6’d23 即回归 6’d0 状态,此时 32 位数据中的高 24 位数据发送出去了(即来自 testbench 的数据输入 SSPI_data_in 高 24 位有效),同时接收低 24 位有效。

为了方便操作,使 testbench 的数据输入也变成相应的低 8/16/24 位有效,FPGA 对要发送的数据做一下处理。
assign SSPI_data_in_q = 32'hbbccddeeff; //--------------------------shift bits according to data width configuration----------// reg [31:0] SSPI_data_in_temp; always@(posedge clk_150m or negedge rst_n) if(!rst_n) SSPI_data_in_temp <= 32'b0; else if(SSPI_valid_r2) begin if(SSPI_DATA_WIDTH == 2'b00) SSPI_data_in_temp <= SSPI_data_in_q << 24; else if(SSPI_DATA_WIDTH == 2'b01) SSPI_data_in_temp <= SSPI_data_in_q << 16; else if(SSPI_DATA_WIDTH == 2'b10) SSPI_data_in_temp <= SSPI_data_in_q << 8; else if(SSPI_DATA_WIDTH == 2'b11) SSPI_data_in_temp <= SSPI_data_in_q; else SSPI_data_in_temp <= 32'b0; end
如此,在实际应用中,testbench 或者控制器只需要发送对应位的有效数据即可,例如,24 位的 SPI 通信,发送 32’h00aabbcc 即可(低 24 位有效),FPGA 会将 24’haabbcc 数据配置从机,从机反馈的 SPI 数据假如为 24’hddeeff,FPGA 发给 testbench 或者控制器为 32’h00ddeeff(低 24 位有效)。
三、平台实测
由于这里使用的平台包含了 FPGA 和 ARM 两个控制器,因此就以 FPGA 为 SPI 主机、ARM 为 SPI 从机做简单的分析。这里的 ARM 仅使用了 SPI 从机和 UART 功能,所以 FPGA 的 SPI 时钟配置信息采用手动更改,重点验证各种配置模式下的 SPI 时序正确与否。实际使用时,ARM 一般作为主控芯片通过总线配置 FPGA 的 SPI 时钟参数,FPGA 再发出相应的 SPI 波形,与下游通信。
ARM 为 STM32,采用 Cube 库函数生成相应的代码。先分析一下生成的函数关于 SPI 时钟极性和相位的配置:
hspi4.Init.CLKPolarity = SPI_POLARITY_LOW; hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
对应 SPI 时钟空闲状态低电平,在第一个跳变沿采样,也就是上升沿采样,下降沿发出数据,对应 CPOL = 0, CPHA = 0。
hspi4.Init.CLKPolarity = SPI_POLARITY_LOW; hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;
对应 SPI 时钟空闲状态低电平,在第二个跳变沿采样,也就是下降沿采样,上升沿发出数据,对应 CPOL = 0, CPHA = 1。
hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;
对应 SPI 时钟空闲状态高电平,在第二个跳变沿采样,也就是上升沿采样,下降沿发出数据,对应 CPOL = 1, CPHA = 0。
hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
对应 SPI 时钟空闲状态高电平,在第一个跳变沿采样,也就是下降沿采样,上升沿发出数据,对应 CPOL = 1, CPHA = 1。
根据 STM32 的 SPI 时钟极性相位和数据位宽配置信息,相应的修改 FPGA 端的配置代码:
SSPI_CPOL_CPHA = 2'b11; SSPI_DATA_WIDTH = 2'b01;
以 16 位数据为例,分别测试 SPI 时钟极性和相位 4 种配置情况下的波形。




测试流程:SPI 主机(FPGA)发送 16’heeff,STM32 收到之后传输到 PC;同时 SPI 从机(STM32)反馈 16 位数据 0x0a, 0x0b,SPI 主机收到该数据存入寄存器,并通过串口返回给 STM32,STM32 传输到 PC。如下图所示是串口助手收到的数据。

0xff, 0xee 为 SPI 主机传输过来的 SPI 数据,0x00, 0x00, 0x0b, 0x0a 为 SPI 主机接收到的 SPI 数据并通过串口回传到 PC 的数据(取低 16 位)。
注意一点:串口助手收到的数据与 SPI 主机发送过来的是反的,但是实测波形是与 SPI 主机发送的数据一致,这可能与 STM32 收发 SPI 数据采用的指针型数组有关,待查。
后续可将位宽的功能处理的更完善一些。
扫码关注尚为网微信公众号

原创文章,作者:sunev,如若转载,请注明出处:https://www.sunev.cn/embedded/926.html