一、概述

SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式, 与各种外围设备进行高速数据通信.
     SPI 主要应用在 EEPROM, Flash, 实时时钟(RTC), 数模转换器(ADC), 数字信号处理器(DSP) 以及数字信号解码器之间. 它在芯片中只占用四根管脚 (Pin) 用来控制以及数据传输, 节约了芯片的 pin 数目, 同时为 PCB 在布局上节省了空间. 正是出于这种简单易用的特性, 现在越来越多的芯片上都集成了 SPI技术。

二、 特点
     1. 采用主-从模式(Master-Slave) 的控制方式
       SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). 一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本身不能产生或控制 Clock, 没有 Clock 则 Slave 设备不能正常工作.
     2. 采用同步方式(Synchronous)传输数据
       Master 设备会根据将要交换的数据来产生相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信号(Clock Signal) , 时钟信号通过时钟极性 (CPOL) 和 时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进行采样, 来保证数据在两个设备之间是同步传输的.
     3. 数据交换(Data Exchanges)
       SPI 设备间的数据传输之所以又被称为数据交换, 是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)". 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了.
       一个 Slave 设备要想能够接收到 Master 发过来的控制信号, 必须在此之前能够被 Master 设备进行访问 (Access). 所以, Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上.
       在数据传输的过程中,  每次接收到的数据必须在下一次数据传输之前被采样. 如果之前接收到的数据没有被读取, 那么这些已经接收完成的数据将有可能会被丢弃,  导致 SPI 物理模块最终失效. 因此, 在程序中一般都会在 SPI 传输完数据后, 去读取 SPI 设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的.

  SPI优点:支持全双工通信、通信简单、数据传输速率块。缺点:没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据 ,可靠性上有一定的缺陷。

三、结构

SPI包含四根线:

1、SS(Slave Select):片选信号线,当有多个SPI设备与SPI模块相连时,每个设备的这个片选信号线是与不同的ss相连的,而其他SCK,MOSI,MISO线则为多个设备l连接到相同的SPI总线上,当SS信号线为低电平时,片选有效,开始SPI通信

2、SCK(Serial Clock):时钟信号线,由主通信设备产生,不同的设备支持的时钟频率不一样。

3、MOSI(Master Output,Slave Input):主设备输出、从设备输入引脚

4、MISO(Master Input,Slave Output):主设备输入、从设备输出引脚

SPI内部结构如下图:

上图只是对 SPI 设备间通信的一个简单的描述, 下面就来解释一下图中所示的几个组件(Module):
       SSPBUF, Synchronous Serial Port Buffer, 泛指 SPI 设备里面的内部缓冲区, 一般在物理上是以 FIFO 的形式, 保存传输过程中的临时数据;
       SSPSR, Synchronous Serial Port Register, 泛指 SPI 设备里面的移位寄存器(Shift Regitser), 它的作用是根据设置好的数据位宽(bit-width) 把数据移入或者移出 SSPBUF;
       Controller, 泛指 SPI 设备里面的控制寄存器, 可以通过配置它们来设置 SPI 总线的传输模式.

四、时序

上图通过 Master 设备与 Slave 设备之间交换1 Byte 数据来说明 SPI 协议的工作机制.
        首先,  在这里解释一下两个概念:
        CPOL: 时钟极性, 表示 SPI 在空闲时, 时钟信号是高电平还是低电平. 若 CPOL 被设为 1, 那么该设备在空闲时 SCK 管脚下的时钟信号为高电平. 当 CPOL 被设为 0 时则正好相反.
        CPOL = 0: SCK idle phase is low; 
        CPOL = 1: SCK idle phase is high;
        CPHA: 时钟相位, 表示 SPI 设备是在 SCK 管脚上的时钟信号变为上升沿时触发数据采样, 还是在时钟信号变为下降沿时触发数据采样. 若 CPHA 被设置为 1, 则 SPI 设备在时钟信号变为下降沿时触发数据采样, 在上升沿时发送数据. 当 CPHA 被设为 0 时也正好相反.
        CPHA = 0: Output data at negedge of clock while receiving data at posedge of clock;
        CPHA = 1: Output data at posedge of clock while receiving data at negedge of clock;
        上图里的 "Mode 1, 1" 说明了本例所使用的 SPI 数据传输模式被设置成 CPOL = 1, CPHA = 1. 这样, 在一个 Clock 周期内, 每个单独的 SPI 设备都能以全双工(Full-Duplex) 的方式, 同时发送和接收 1 bit 数据, 即相当于交换了 1 bit 大小的数据. 如果 SPI 总线的 Channel-Width 被设置成 Byte, 表示 SPI 总线上每次数据传输的最小单位为 Byte, 那么挂载在该 SPI 总线的设备每次数据传输的过程至少需要 8 个 Clock 周期(忽略设备的物理延迟). 因此, SPI 总线的频率越快, Clock 周期越短, 则 SPI 设备间数据交换的速率就越快.

五、测试

待测试SPI模块地址及寄存器设置:

实现七路SPI,主模式,基地址如下:

SPI0_F_addr<17:2>:0x1d00;

SPI1_F_addr<17:2>:0x1e00;

SPI2_F_addr<17:2>:0x1f00;

SPI3_F_addr<17:2>:0x2000;

SPI4_F_addr<17:2>:0x2100;

SPI5_F_addr<17:2>:0x2200;

SPI6_F_addr<17:2>:0x2300;

偏移地址F_addr<9:2>

寄存器名称

读写属性

寄存器功能

00H

数据寄存器RBR

R

接收缓冲区,D[15:0],读:接收FIFO数据

00H

数据寄存器TBR

W

发送缓冲区,D[15:0],写:发送FIFO数据

01H

中断控制寄存器ICR

R/W

D[0]=’1’ 使能接收中断

D[1]=’1’ 使能发送缓冲区空中断

02H

中断状态寄存器ISR

R/W

D[0]=’1’ 接收中断有效

D[1]=’1’ 发送缓冲区空中断有效

03H

接收缓冲区数据个数寄存器RBN

R/W

只读,D[7:0]接收缓冲区中的数据个数

04H

发送缓冲区数据个数寄存器TBN

R/W

只读,发送缓冲区中的数据个数

05H

接收中断条件寄存器RIC

R/W

ICR[0]=’1’,且RBN>RIC+1时,ISR[0]会被置位,同时中断输出信号被置位

06H

状态寄存器STR

R

D[0]:’1’表示接收缓冲区空

D[1]:’1’表示接收缓冲区满

D[2]:’1’表示接收缓冲区溢出

D[3]:’1’表示发送缓冲区空

D[4]:’1’表示发送缓冲区满

D[5]:’1’表示发送缓冲区溢出

07H

配置寄存器CFR

R/W

配置寄存器

D[0]:’1’表示使能发送

D[1]:写’1’表示清空接收缓冲区,读为’0’

D[2]:写’1’表示清空发送缓冲区,读为’0’

D[4]:’1’SPI时钟相位寄存器CPHA设置位,‘1’表示在时钟第2个跳变沿采样数据,‘0’表示在时钟第1个跳变沿采样数据

D[7]:‘1’表示拓展配置模式,‘0’表示非拓展配置模式

 //test SPI module 2019.03.11 zhou
//7路 SPI spi_slave u0_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u1_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u2_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u3_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u4_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u5_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]);
spi_slave u6_spi_slave(~F_spi_ss[], F_spi_sdo[], F_spi_sdi[],F_spi_sclk[]); task CPU_READ_SPI_FIFO;
input [:] addr;
output [:] rddata;
begin
#120ns
@(posedge S_cpu_clk)
F_nrd = 'b1;
F_nwr = 'b1;
F_ncs = 'b1;
F_addr =addr;
@(posedge S_cpu_clk)
F_nrd = 'b0;
F_nwr = 'b1;
F_ncs = 'b0;
F_addr =addr;
@(posedge S_cpu_clk)
wait (F_nrdy==);
# //one clock delay then read
rddata = F_data_o;
@(posedge S_cpu_clk)
F_nrd = 'b1;
F_nwr = 'b1;
F_ncs = 'b1;
F_addr =;
end
endtask task TEST_CASE_SPI;
logic [:] spi_reg;
logic [:] spi_rec_data;
logic [:] addr;
int i,spi_rec_fifonum; begin
RST_ASIC();
$display("Testing SPI 0-6..."); for(addr = 'h1d00;addr <= 16'h2300;addr += 'h0100)
begin
//寄存器复位值自检
CPU_READ(addr+'h01,spi_reg); //ICR 0
if(spi_reg != 'h0000) $display("%h:ICR error:%h(0x0000)",addr,spi_reg);
CPU_READ(addr+'h02,spi_reg); //ISR 0
if(spi_reg != 'h0000) $display("%h:ISR error:%h(0x0000)",addr,spi_reg);
CPU_READ(addr+'h03,spi_reg); //RBN 0
if(spi_reg != 'h0000) $display("%h:RBN error:%h(0x0000)",addr,spi_reg);
CPU_READ(addr+'h04,spi_reg); //TBN 0
if(spi_reg != 'h0000) $display("%h:TBN error:%h(0x0000)",addr,spi_reg);
CPU_READ(addr+'h05,spi_reg); //RIC 0
if(spi_reg != 'h0000) $display("%h:RIC error:%h(0x0000)",addr,spi_reg);
CPU_READ(addr+'h06,spi_reg); //STR 09
if(spi_reg != 'h0009) $display("%h:STR error:%h(0x0009)",addr,spi_reg);
CPU_READ(addr+'h07,spi_reg); //CFR 0
if(spi_reg != 'h0000) $display("%h:CFR error:%h(0x0000)",addr,spi_reg); //写SPI的配置寄存器
CPU_WRITE(addr+'h07,8'h06); //clear fifo
CPU_WRITE(addr+'h07,8'h01); //enable sending //向发送缓冲寄存器写数据
CPU_WRITE(addr+'h00,16'h7654);
CPU_WRITE(addr+'h00,16'h3210); #10us; //接收数据 CPU_READ(addr+'h03,spi_rec_fifonum);
for(i = ; i < spi_rec_fifonum;i++)
begin
CPU_READ_SPI_FIFO(addr+'h00,spi_rec_data);
$display("spi_rec_data:%h",spi_rec_data);
end end //end for $display("Testing SPI Sending & Receiving is Finished!");
end endtask

SPI测试代码

`timescale 1ns/10ps

module spi_slave( ncs, mosi, miso, sck);
input ncs, mosi, sck;
output miso; reg nrst; initial
begin
nrst=;
# //related to the number of data
nrst=;
end //SPI接收状态机
reg[:] byte_received;
reg[:] bit_received_cnt;
reg[:] rec_data; always @ (negedge nrst or posedge sck) //每次sck都会接收数据,spi的顶端模块状态机决定是否取用
begin
if(~nrst)
begin
byte_received <= 'h0000;
bit_received_cnt <= 'h0;
end
else
begin
if(~ncs)
begin byte_received = {byte_received[:], mosi};
if(bit_received_cnt == 'hF)
begin
rec_data = byte_received;
$display("spi_test_outer_module received data 0x%h at %t",rec_data,$time);
bit_received_cnt = 'h0;
end
else bit_received_cnt = bit_received_cnt + ; end
end
end //SPI发送状态机
reg miso;
reg[:] byte_sended; //发送移位寄存器
reg[:] bit_sended_cnt; //SPI发送位计数器 always @ (negedge nrst)
begin
if(~nrst)
begin
byte_sended <= 'h89ab;
bit_sended_cnt <= ;
end
end always @ (negedge sck or negedge ncs)
begin
if(~ncs && nrst)
begin
miso = byte_sended[];
byte_sended = {byte_sended[:],'b0}; if(bit_sended_cnt == 'hF)
begin
byte_sended <= 'hcdef;
bit_sended_cnt <= ;
end
else bit_sended_cnt += ;
end
end endmodule

SPI简易从模块代码

测试结果

SPI协议介绍的更多相关文章

  1. [SPI&I2C]I2C和SPI协议介绍

    IIC vs SPI 现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身 ...

  2. SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)

    SPI.I2C.UART三种串行总线协议的区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART( ...

  3. SPI协议及IO模拟

    SPI协议 SPI协议网上资料比较多,但是也比较乱,当初在网上搜集的错误资料导致现在比较混乱. SPI协议资料比较正规的是: 1.SPI的规约协议英文文档,例如<摩托罗拉spi协议规范> ...

  4. FPGA作为从机与STM32进行SPI协议通信---Verilog实现 [转]

    一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...

  5. 模拟SPI协议时序

    SPI是串行外设接口总线,摩托罗拉公司开发的一种全双工,同步通信总线,有四线制和三线制. 在单片机系统应用中,单片机常常是被用来当做主机(MASTER),外围器件被当做从机(SLAVE). 所以,在以 ...

  6. FPGA作为从机与STM32进行SPI协议通信---Verilog实现

    一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...

  7. [SPI]SPI协议详解

    转自:https://my.oschina.net/freeblues/blog/67400 1.SPI协议简介 1.1.SPI协议概括 SPI,是英语Serial Peripheral interf ...

  8. SPI协议及工作原理分析

    说明.文章摘自:SPI协议及其工作原理分析 http://blog.csdn.net/skyflying2012/article/details/11710801 一.概述. SPI, Serial ...

  9. TCP/IP 协议介绍

    转自http://blog.jobbole.com/104886/ 一.TCP/IP 协议介绍 在介绍 HTTP 协议之前,先简单说一下TCP/IP协议的相关内容.TCP/IP协议是分层的,从底层至应 ...

随机推荐

  1. Python的程序结构[3] -> 变量/Variable[0] -> 变量类型

    变量类型 / Variable Type 在 Python 中,变量主要有以下几种,即全局变量,局部变量和内建变量, 全局变量 / Global Variable 通常定义于模块内部,大写变量名形式存 ...

  2. Python的程序结构[4] -> 函数/Function[1] -> 内建函数

    内建函数 / Built-in Function or Method Python中有许多的内建函数(查看内建模块部分),此处将对内建函数进行介绍 内建函数 ord / built-in functi ...

  3. 「kuangbin带你飞」专题二十二 区间DP

    layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...

  4. 洛谷——P1579 哥德巴赫猜想(升级版)

    P1579 哥德巴赫猜想(升级版) 题目背景 1742年6月7日哥德巴赫写信给当时的大数学家欧拉,正式提出了以下的猜想:任何一个大于9的奇数都可以表示成3个质数之和.质数是指除了1和本身之外没有其他约 ...

  5. [BZOJ 3108] 图的逆变换

    Link: BZOJ 3108 传送门 Solution: 样例教你做题系列 观察第三个输出为No的样例,发现只要存在$edge(i,k),edge(j,k)$,那么$i,j$的出边一定要全部相同 于 ...

  6. POJ 2100:Graveyard Design(Two pointers)

    [题目链接] http://poj.org/problem?id=2100 [题目大意] 给出一个数,求将其拆分为几个连续的平方和的方案数 [题解] 对平方数列尺取即可. [代码] #include ...

  7. 在C#中用RX库和await来实现直观的状态机

    在程序的设计过程中,我们经常会遇到一些需要使用状态机的场景,相信状态机的编写和维护是令每一个程序员都非常头大的事情.到了C# 5.0后,由于引进了await语法糖,我们可以通过await和Reacti ...

  8. linux命令详解:jobs命令

    转:http://www.cnblogs.com/lwgdream/p/3413571.html 前言 我们可以将一个程序放到后台执行,这样它就不占用当前终端,我们可以做其他事情.而jobs命令用来查 ...

  9. 【java】Stream的使用

    首先,给大家推荐一个好的地方:http://ifeve.com/stream/ 可以好好学一下 接下来,今天要删除数组中的某些元素,想到了之前用过的这个JDK8的Stream 1.Array转化为St ...

  10. django发送邮箱

    要用django发送邮箱之前需要在setting中配置一下 EMAIL_HOST = 'smtp.qq.com' EMAIL_PORT = 25 EMAIL_HOST_USER = 'xxx@qq.c ...