『FPGA通信接口』串行通信接口-SPI

文章目录

1.SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,通常说SPI接口或SPI协议都是指SPI这一种串行外设接口规范。相对于串口,SPI是一种高速的(可达10Mb\s以上),全双工,同步的通信总线。串口是点对点的全双工的异步通信,因此要通信双方按照相同的约定(指起始、暂停位、波特率)才能在数据上同步,准确通信。而SPI是一种同步通信的总线结构 ,同步是指有专门时钟线用来同步源端和目的端,总线结构是指主从之分,通常是一个设备做主,可以多个设备做从,也正因为如此才有了CS片选线,指定所选从设备。

在实际的应用中,通常也只是一主一从。SPI接口多应用于Flash、ADC、LCD控制器,CMOS寄存器配置接口等场景。SPI在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议。

2.控制时序

SPI有四根线,如下,

(1)MISO-- Master Input Slave Output,主设备数据输入,从设备数据输出;

(2)MOSI-- Master Output Slave Input,主设备数据输出,从设备数据输入;

(3)SCLK -- Serial Clock,时钟信号,由主设备产生;

(4)CS -- Chip Select,从设备使能信号,由主设备控制。

SPI通信一般包括主设备(Master)和从设备(Slave),主设备负责发起通信并控制通信的时序,从设备则响应主设备的指令。SPI通信有四种模式,分别是模式0、模式1、模式2和模式3,不同的模式在时钟极性和相位上有所不同。这四种数据传输的时序模式,是由设备的属性的决定的,用CPOL和CPHA标识这四种时序。CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0(1),空闲电平为低电平(高电平)CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,CPHA=1,则在每个周期的第二个时钟沿采样。 通常使用的是MODE0模式,即CPOL=CPHA=0,空闲时为低电平,在第一个时钟沿采样。其他几种模式如果需要可以上网搜索,资料很多。

使用SPI接口时需要注意,很多SPI接口的芯片并不是标准的SPI协议,可能是稍有变化,例如对哪个沿采样会有明确的说明。此外,还有一种可能是在SPI协议的基础上进一步做限制,例如要求一次读写过程结束后CS必须拉高等。SPI要求是高位在前,MSB的方式。还有就是SPI一个时钟周期必然要执行接收一位数据和发送一位数据。

3.Dual、Qual模式

上面描述的就是标准SPI,有4根引脚信号:clk , cs, mosi, miso。针对SPI Flash而言,有Dual SPI和Qual SPI两种模式,这是因为,Flash器件的全双工并不常用,因此扩展mosi和miso的用法,让它们工作在半双工,用以加倍数据传输。也就是对于Dual SPI Flash,可以发送一个命令字节进入dual mode,这样mosi变成SIO0(serial io 0),mosi变成SIO1(serial io 1),这样一个时钟周期内就能传输2个bit数据,加倍了数据传输。与Dual SPI类似,Qual SPI Flash增加了两根I/O线(SIO2,SIO3),目的是一个时钟内传输4个bit。

4.例程设计与代码解读

如图所示,把Spi_driver模块的输入输出分成两个部分,一个部分为图示左侧的与其他模块交互的接口,另一部分为图示右侧与物理管脚相连。Spi_en信号为外部的脉冲信号,收到该脉冲执行一次8Bit的发送动作,和8Bit的接收动作。通过VIO产生spi_en信号和要发送的数据信号data[7:0],将板卡上的mosi和miso管脚相连,判断发送数据和接收数据是否一致,若一致则点亮led。由于缺乏带SPI设备的板卡,故设计此例程。一次发送动作结束后会产生spi_done的脉冲信号,在发送动作执行过程中,spi_busy持续为拉高状态。另外,spi_sck作为同步时钟直接决定传输数据的速度,通常在10Mb/s以下,或者依据对端设备来指定。

在使用SPI设备时可以修改此驱动模块以满足需求,左侧信号与外部设备交互,当busy拉高的时候不允许发送spi_en信号;可以对data信号采用多路选择器,以完成某些固定的指令或者读写操作;同样,数据的位宽也可以扩展。另外,实际使用的时候应该使用cs管脚,按照相关设备的要求灵活运用该管脚。

下面代码在硬件板卡上完成测试。该代码使用MODE0,即CPOL=CPHA=0的模式。程序分为以下四步,第一步产生sck同步时钟,第二步依据en指示信号写数据,第三步依据en指示信号并发读数据,第四步产生ui交互信号。

bash 复制代码
module spi_wr(
    input        clk_i       ,
    //user interface
    // input        spi_en     ,//其他模块的spi使能信号
    // input    [7:0]data      ,
    output       spi_busy      ,//指示spi的状态,表示SPI过程
    output       spi_done      ,//指示spi结束一次动作
    output       led           ,
    // output       spi_cs        ,//SPI从机的片选信号,低电平有效。
    output       spi_clk       ,//主从机之间的数据同步时钟。
    output       spi_mosi      ,//数据引脚,主机输出,从机输入。
    input        spi_miso      //数据引脚,主机输入,从机输出。 
    );
// assign spi_cs = 0;
//1.同步时钟产生模块 用50MHz分频为50KHz
assign spi_clk = m_clk;
parameter [9:0] SPI_DIV    = 10'd499;//分频定为50KHz,50Mhz/50K/2- 1'b1=499
reg [9:0] clk_cnt = 10'd0;//分频计数器
always@(posedge clk_i)begin
    if(clk_cnt < SPI_DIV&&spi_busy)
        clk_cnt <= clk_cnt + 1'b1;
    else 
        clk_cnt <= 10'd0;
end
reg m_clk = 1'b0;
always@(posedge clk_i)begin
    if(spi_en)
        m_clk <= 1'b0;
    else if(clk_cnt==SPI_DIV) 
        m_clk <= ~m_clk;
end
//2.接收en信号和数据,执行spi mosi操作(写数据)
//计数发送个数
reg [3:0]tx_cnt = 0;
always@(posedge clk_i)begin
    if(spi_en)begin
        tx_cnt <= 0; 
    end else if((clk_cnt == SPI_DIV)&&(tx_cnt==4'd8))begin
        tx_cnt <= 0;
    end else if((clk_cnt == SPI_DIV)&&spi_clk==0)
        tx_cnt<=tx_cnt+1;
end
//移位寄存器发送
wire [7:0]data;
reg  [7:0]data_reg=8'b0;//数据源
always@(posedge clk_i)begin
    if(spi_en)
        data_reg <= data;
    else if(spi_done)
        data_reg <=8'b0;
    else if(clk_cnt == SPI_DIV&&spi_clk==1)
        data_reg[7:0] <= {data_reg[6:0],data_reg[7]};
end
assign spi_mosi = data_reg[7];
//3.接收en信号和数据,执行spi miso操作(读数据)
reg  [7:0]data_fifo=8'b0;
always@(posedge clk_i)begin
    if((clk_cnt == SPI_DIV)&&spi_clk==0)begin
        case (tx_cnt)
           0 : data_fifo[7] <= spi_miso;
           1 : data_fifo[6] <= spi_miso;
           2 : data_fifo[5] <= spi_miso;
           3 : data_fifo[4] <= spi_miso;
           4 : data_fifo[3] <= spi_miso;
           5 : data_fifo[2] <= spi_miso;
           6 : data_fifo[1] <= spi_miso;
           7 : data_fifo[0] <= spi_miso;
           default: ;
        endcase
    end else 
        data_fifo <= data_fifo;
end
//4.发出user interface相关指示信号
wire spi_en;
reg spi_busy =0;
assign spi_done = ((clk_cnt == SPI_DIV)&&(tx_cnt==4'd8)) ? 1'b1 : 1'b0;
assign led = (data_fifo==8'b11010111) ? 1'b1 : 1'b0;
//产生busy信号,指示spi进程
always@(posedge clk_i)begin
    if(spi_en)
        spi_busy<=1'b1;
    else if(spi_done)
        spi_busy<=1'b0;
    else
        spi_busy<=spi_busy;
end
vio_0 use_vio (
  .clk(clk_i),                // input wire clk
  .probe_out0(spi_en),  // output wire [0 : 0] probe_out0
  .probe_out1(data)  // output wire [7 : 0] probe_out1
);
ila_0 your_instance_name (
    .clk(clk_i), // input wire clk
    .probe0({data_reg,data_fifo}), // input wire [15:0]  probe0  
    .probe1(tx_cnt), // input wire [3:0]  probe1 
    .probe2({spi_en,spi_mosi,spi_miso,spi_busy}) // input wire [3:0]  probe2
);

5.SPI接口实战应用

给一款以SPI寄存器接口作为接口的芯片写一个SPI控制器大概需要三步,首先搞清楚芯片datasheet对于SPI接口的时序要求,每个芯片都可能有特殊的一面。然后,根据芯片要求设计程序完成仿真验证。最后,RTL设计,布局布线上板测试。

5.1时序要求

  1. 系统对于SPI读写发生的时隙做了规定,即上电之后经过一系列步骤等reset拉高之后的10us之后。

  2. 在使用PLL时钟的情况下,最大的SPI的时钟为10MHz,如下。为了精准的控制每一个上升沿和下降沿的数据,使用手动的方式生成这个时钟,用户时钟为50MHz,因此分频的时钟周期为20ns。

  3. 根据datasheet提及的SPI时序,首先应该在按照上电时序要求的情况下把ss_n拉低。

  4. 手册要求,ss_n拉低之后需要保持一个spi的时钟周期才能给spi时钟。

  5. 该芯片的SPI的时序是,第一个时钟边沿是上升沿,直接在第一个边沿就加载数据了。此处加载的是写入的9bit宽的地址值,注意此处是,MSB.

  6. 注意读写之间的切换,与标准的SPI的时序不同,是在spiclk的下降沿抓取数据。

5.2仿真时序图

5.3代码设计


源码工程见文末

init模块 完成系统对于上电时序的要求,上电后10us给时钟,时钟给到后的10us才开始复位拉高,控制SPI Upload在reset 10us之后开始;

②形成四个模块,各司其职。spi_ctrl模块 控制读写寄存器的顺序以满足配置顺序要求;LUT模块 将每个寄存器的将要写入的内容提前编辑好,配合ctrl完成读写配置;spi_driver模块 根据ctrl模块的读写命令指示和lut的寄存器地址和写入数据驱动spi接口写入数据;top模块 将三个子模块例化保持与design_top的通信。

③当开始spi upload后,spi_ctrl模块将按照预先编辑好的查找表,按顺序写入寄存器,其中需要注意有一个环节要读时钟管理寄存器的状态,状态正常后才能继续写,这点尤为重要。

6.传送门

|-----|
| END |

🔈文章原创,首发于CSDN论坛,我的主页

🔈欢迎点赞❤❤收藏⭐⭐打赏💴💴!

🔈欢迎评论区或私信指出错误❌,提出宝贵意见或疑问❓。

相关推荐
9527华安1 天前
Artix7系列FPGA实现SDI视频解码转CameraLink,基于GTP高速收发器+OSERDES2原语架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
!chen1 天前
自适应滤波算法FPGA实现思路
算法·fpga开发
华舞灵瞳1 天前
学习FPGA(七)正弦信号合成
学习·fpga开发
葡萄杨1 天前
【软件使用】Icarus Verilog仿真
fpga开发
s09071361 天前
常用FPGA实现的图像处理算法
图像处理·算法·fpga开发
s09071361 天前
FPGA实现Gamma校正的系统性指南
图像处理·fpga开发·gama校正
读书点滴1 天前
FPGA中如何获取任何一条路径的延时
fpga开发
minglie11 天前
嵌入式协程AlarmProtothread
mcu·fpga开发
Godspeed Zhao1 天前
自动驾驶中的传感器技术79——Sensor Fusion(2)
人工智能·fpga开发·自动驾驶
ShiMetaPi1 天前
GM-3568JHF丨ARM+FPGA异构开发板系列教程:外设教程 07 音频
arm开发·fpga开发·音视频·fpga·rk3568