SPI

2020/07/21 00:00
阅读数 37

SPI 简介

SPI全称为Seriel Peripheral Interface (串行外设接口),是 MCU 中常用的外设接口。SPI 通信原理很简单,它是以主从方式进行工作,通常有一个主设备和一个或多个从设备,至少需要4根线(支持全双工)工作,分别为 MISO(主入从出),MOSI(主出从入),SCLK(时钟),SS(片选)。

Standard-SPI

基本的 SPI 协议也被称为 Standard-SPI,Standard-SPI 是串行通信协议,数据是逐位进行传输,在 SCLK 的边沿进行 MOSI 和 MISO 的传输。数据输出通过 MOSI 线进行传输,数据在时钟上升沿或者下降沿改变,在紧接着的下降沿或者上升沿被采样完成一位数据传输,输入同理。

Dual-SPI

由于在实际应用中较少使用全双工模式,因此为了能够充分利用数据线,引入了 Dual-SPI 和 Quad-SPI ,在 Dual-SPI 协议中,MOSI、MISO 数据线被重命名为 SD0、SD1 ,变为既可以输入也可以输出的 inout 信号。

Dual-SPI 由于同时使用两根数据线进行传输,一个时钟周期可以传输2 bit数据,因此可以将 Standard-SPI的吞吐率提高一倍。

Quad-SPI

Quad-SPI 是在 Dual-SPI 的基础上再添加了两根数据线,所以数据线变为了SD0、SD1、SD2、SD3。

Quad-SPI 由于同时使用四根数据线进行传输,一个时钟周期可以传输4 bit数据,因此可以在 Dual-SPI 基础上将吞吐率提高一倍。

SPI 总线四种工作方式

SPI 在数据传输的时候,需要确定两件事情

  1. 数据是在时钟的上升沿采集还是下降沿采集

  2. 时钟的初始(空闲)状态是为高电平还是低电平

CPOL:时钟极性, 表示 SPI 在空闲时, 时钟信号是高电平还是低电平。

CPHA:时钟相位, 表示 SPI 设备是在 SCK 管脚上的时钟信号变为上升沿时触发数据采样, 还是在时钟信号变为下降沿时触发数据采样。

那么CPOL 有两种可能,CPHA 有两种可能,则 SPI 传输就有四种模式。

传输方式 描述
方式1 CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
方式2 CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
方式3 CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
方式4 CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换

SPI通信协议

通讯的起始信号:SS 信号线由高变低,是 SPI 通讯的起始信号。SS 是每个从机各自独占的信号线,当从机在自己的 SS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。

通讯的停止信号:SS 信号由低变高,是 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

数据有效性:SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCLK 信号线进行数据同步。MOSI 及 MISO 数据线在 SCLK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般采用 MSB 先行的方式。

                        _   _   _   _   _   _         _   _   _
              inClk  _/ \_/ \_/ \_/ \_/ \_/ \.....\_/ \_/ \_/ \_
                     ___                                   _____
    inReset_EnableB     \_________________________________/
                     _____                                   ___
          outSpiCsB       \_________________________________/
                     _______ ___
   inData8Send[7:0]  _______XD8SXXX...
                                 ___ ___ ___       ___ _________
          inSpiMosi             XD7_XD6_XD5_X.....XD0_X_________
                     ___________   _   _   _    _    ___________
          outSpiClk             \_/ \_/ \_/ \.....\_/
                                                     ___________
outData8Receive[7:0]           XD8SX               XXXD8R*D8SX___
image

FPGA 程序实现

一个用了好多年的 SPI 程序,很健壮,小编抛出来。

library ieee;
--Library UNISIM;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
--use UNISIM.vcomponents.all;

entity SpiSerDes is
  port 
  (
    -- SerDes clock and control signals
    inClk           : in  std_logic;  -- System clock. Fmax <= SPI peripheral Fmax.
    inReset_EnableB : in  std_logic;  -- Active-high, synchronous reset.
    inStartTransfer : in  std_logic;  -- Active-high, initiate transfer of data
    outTransferDone : out std_logic;  -- DONE='1' when transfer is done

    -- Parallel data ports
    inData8Send     : in  std_logic_vector(7 downto 0); -- Sent to SPI device
    outData8Receive : out std_logic_vector(7 downto 0); -- Received from SPI device

    -- SPI ports and tristate control - Connect these to the SPI bus
    outSpiCsB       : out std_logic;  -- SPI chip-select to SPI device 
                                      -- or all SPI outputs control enable
    outSpiClk       : out std_logic;  -- SPI clock to SPI device
    outSpiMosi      : out std_logic;  -- SPI master-out, slave-in to SPI device
    inSpiMiso       : in  std_logic   -- SPI master-in, slave-out from SPI device
  );
end SpiSerDes;

architecture behavioral of SpiSerDes is

  -- Constants
  constant  cShiftCountInit : std_logic_vector(8 downto 0)  := B"000000001";

  -- Registers
  signal    regShiftCount   : std_logic_vector(8 downto 0)  := cShiftCountInit;
  signal    regShiftData    : std_logic_vector(7 downto 0)  := B"00000000";
  signal    regSpiCsB       : std_logic                     := '1';
  signal    regSpiMosi      : std_logic                     := '1';
  signal    regTransferDoneDelayed  : std_logic             := '1';

  -- Signals
  signal    intTransferDone : std_logic;

  -- Attributes
  attribute clock_signal    : string;
  attribute clock_signal    of inClk : signal is "yes";

begin
  -- Internal signals
  intTransferDone <= regShiftCount(0);

  -- TransferDone delayed by half clock cycle
  processTransferDone : process (inClk)
  begin
    if (falling_edge(inClk)) then
      regTransferDoneDelayed  <= intTransferDone;
    end if;
  end process processTransferDone;

  -- SPI chip-select (active-Low) is always inverse of inReset_EnableB.
  processSpiCsB : process (inClk)
  begin
    if (rising_edge(inClk)) then
      regSpiCsB <= inReset_EnableB;
    end if;
  end process processSpiCsB;

  -- Track transfer of serial data with barrel shifter.
  processShiftCount : process (inClk)
  begin
    if (rising_edge(inClk)) then
      if (inReset_EnableB='1'then
        regShiftCount <= cShiftCountInit;
      elsif ((intTransferDone='0'or (inStartTransfer='1')) then
        -- Barrel shift (rotate right)
        regShiftCount <= regShiftCount(0) & regShiftCount(8 downto 1);
      end if;
    end if;
  end process processShiftCount;

  -- Simultaneous serialize outgoing data & deserialize incoming data. MSB first
  processShiftData : process (inClk)
  begin
    if (rising_edge(inClk)) then
      if (intTransferDone='0'then
        -- SHIFT-left while not outTransferDone
        regShiftData  <= regShiftData(6 downto 0) & inSpiMiso;
      elsif (inStartTransfer='1'then
        -- Load data to start a new transfer sequence from a done state
        regShiftData  <= inData8Send;
      end if;
    end if;
  end process processShiftData;

  -- SPI MOSI register outputs on falling edge of inClk.  MSB first.
  processSpiMosi : process (inClk)
  begin
    if (falling_edge(inClk)) then
      if (inReset_EnableB='1'then
        regSpiMosi  <= '1';
      elsif (intTransferDone='0'then
        regSpiMosi  <= regShiftData(7);
      end if;
    end if;
  end process processSpiMosi;

  -- Assign outputs
      outSpiClk       <= (inClk or intTransferDone or regTransferDoneDelayed);
      outSpiCsB       <= regSpiCsB;
      outSpiMosi      <= regSpiMosi;
      outTransferDone <= intTransferDone;
      outData8Receive <= regShiftData;

end behavioral;

假设上升沿发送、下降沿接收、高位先发送。

假设主机8位寄存器装的是待发送的数据10101010

参考链接

https://blog.csdn.net/weiqifa0/article/details/82765892

https://mp.weixin.qq.com/s/h1xco58oRDbIq8z3zaP_pA

工程源码获取

获取资料方法一:集赞

关注小编公众号后,将本文转发至朋友圈,集齐3个赞,截图发送到后台,小编会在24小时之内回复。备注:【领取SPI源码】即可领取资料

获取资料方法二:转发群

关注小编公众号后,将本文转发至不低于100人的群(FPGA相关行业),截图发送到后台,小编会在24小时之内回复。备注:【领取SPI源码】即可领取资料


 

FPGA   

本文分享自微信公众号 - 瓜大三哥(xiguazai_tortoise)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部