前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

SPI

作者头像
瓜大三哥
发布2020-07-24 13:02:40
1.7K0
发布2020-07-24 13:02:40
举报
文章被收录于专栏:瓜大三哥瓜大三哥

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 先行的方式。

代码语言:javascript
复制
                        _   _   _   _   _   _         _   _   _
              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 程序,很健壮,小编抛出来。

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 瓜大三哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SPI 简介
    • Standard-SPI
      • Dual-SPI
        • Quad-SPI
        • SPI 总线四种工作方式
        • SPI通信协议
        • FPGA 程序实现
        • 参考链接
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档