SPI协议介绍
一、概述
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协议介绍的更多相关文章
- [SPI&I2C]I2C和SPI协议介绍
IIC vs SPI 现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身 ...
- SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)
SPI.I2C.UART三种串行总线协议的区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART( ...
- SPI协议及IO模拟
SPI协议 SPI协议网上资料比较多,但是也比较乱,当初在网上搜集的错误资料导致现在比较混乱. SPI协议资料比较正规的是: 1.SPI的规约协议英文文档,例如<摩托罗拉spi协议规范> ...
- FPGA作为从机与STM32进行SPI协议通信---Verilog实现 [转]
一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...
- 模拟SPI协议时序
SPI是串行外设接口总线,摩托罗拉公司开发的一种全双工,同步通信总线,有四线制和三线制. 在单片机系统应用中,单片机常常是被用来当做主机(MASTER),外围器件被当做从机(SLAVE). 所以,在以 ...
- FPGA作为从机与STM32进行SPI协议通信---Verilog实现
一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...
- [SPI]SPI协议详解
转自:https://my.oschina.net/freeblues/blog/67400 1.SPI协议简介 1.1.SPI协议概括 SPI,是英语Serial Peripheral interf ...
- SPI协议及工作原理分析
说明.文章摘自:SPI协议及其工作原理分析 http://blog.csdn.net/skyflying2012/article/details/11710801 一.概述. SPI, Serial ...
- TCP/IP 协议介绍
转自http://blog.jobbole.com/104886/ 一.TCP/IP 协议介绍 在介绍 HTTP 协议之前,先简单说一下TCP/IP协议的相关内容.TCP/IP协议是分层的,从底层至应 ...
随机推荐
- HDU-6315 Naive Operations//2018 Multi-University Training Contest 2___1007 (线段树,区间除法)
原题地址 Naive Operations Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 502768/502768 K (Java/ ...
- C. Heidi and Library (神奇的网络流)
C. Heidi and Library 题意 有 n 种分别具有价格 b 的书 a ,图书馆里最多同时存放 k 本书,已知接下来 n 天每天都有一个人来看某一本书,如果图书馆里没有则需要购买,问最少 ...
- HNOI2004 郁闷的出纳员(Splay)
郁闷的出纳员 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的 ...
- [BZOJ3237][AHOI2013]连通图(分治并查集)
3237: [Ahoi2013]连通图 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1736 Solved: 655[Submit][Status ...
- PL/SQL分支、循环语句
CREATE OR REPLACE PROCEDURE PR_TEST2 IS V_CASE NUMBER(5) := 100; BEGIN IF 2 < 1 THEN DBMS_OUTPUT. ...
- Java多线程设计模式(2)生产者与消费者模式
1 Producer-Consumer Pattern Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不 ...
- Linux常用网络带宽监控工具(转)
本文介绍了一些可以用来监控网络使用情况的Linux命令行工具.这些工具可以监控通过网络接口传输的数据,并测量目前哪些数据所传输的速度.入站流量和出站流量分开来显示. 一些命令可以显示单个进程所使用的带 ...
- shell中的cut命令
转:http://blog.sina.com.cn/s/blog_5e77c61f0100hqky.html cut是以每一行为一个处理对象的,这种机制和sed是一样的.(关于sed的入门文章将在近期 ...
- What is the purpose of mock objects?
Since you say you are new to unit testing and asked for mock objects in "layman's terms", ...
- Linux命令基本格式
1 起始符td@td-Lenovo-IdeaPad-Y410P:~$ 第一个td表示当前登录管理员名,中间@无实际意义,td-Lenovo-IdeaPad-Y410P表示主机名,-表示当前所在目录(h ...