一、概述

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. Dfs+Spfa【p1606】[USACO07FEB]荷叶塘Lilypad Pond

    Description 为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘.这个长方形的池子被分成了M行N列个方格(1≤M,N≤30).一些格子是坚固得令人惊讶的莲花,还有一些格子是岩石,其余的只是 ...

  2. Java面向对象内测

    功能要求 开发基于控制台的试题信息管理系统.具体要求如下: (1)显示试题信息管理系统主菜单,包括: 1)列出所有试题信息 2)按科目查询 3)按题干查询 4)添加试题 5)删除试题 6)退出系统 p ...

  3. Spring项目搭建

    1,新建web项目 2,导入jar包 3,编写配置文件 <?xml version="1.0" encoding="UTF-8"?> <bea ...

  4. 初学Django:创建第一个项目+使用模板

    1. 创建一个项目 之前在Anaconda 3里面用命令行安装了Django之后,有了可用的管理工具django-admin.py (1)用django.admin.py来创建一个项目Hellowor ...

  5. 将本地jar包安装进入maven仓库

    实际项目中pom.xml依赖写法: <dependency> <groupId>org.springframework</groupId> <artifact ...

  6. win10 创建安卓模拟器及启动的方法

    一打开 安卓 studio 然后点击AVD manager 创建一个模拟器 二 通过命令行快速启动模拟器 D:\Android\sdk\tools\emulator.exe -netdelay non ...

  7. docker下载ubuntu并进行修改后生成新的镜像提交

    一  docker pull ubuntu ,先下载下来一个镜像, 或者 从本地启动一个镜像 docker run -i -t ubuntu /bin/bash 二 进入一定更新操作 # shell ...

  8. 解压缩报错tar: Error is not recoverable: exiting now

    [root@Gris-11140 FMIS2600bak]# tar -zxvf /home/oradata/FMIS2600DMP.tar.gzgzip: stdin: not in gzip fo ...

  9. zabbix自定义触发器

    zabbix中监控项仅负责收集数据,而通常收集数据的目的还包括在某指标对应的数据超出合理范围时给相关人员发送告警信息,"触发器"正式 用于为监控项所收集的数据定义阈值,每一个触发器 ...

  10. Python Requests post并将得到结果转换为json

    Python Requests post并将得到结果转换为json 学习了:https://blog.csdn.net/sinat_28680819/article/details/70940325  ...