上一章介绍的SPI比较详细,这篇文章将会简化讲解SPI的时序,并且联系代码(VHDL)来详细讲解SPI(PS:下次一定是Verilog,不出意外应该讲QSPI控制器,或者FIFO,也可能是I2C,总之基础的接口都会有计划,以后会加深比较有难度的项目,大家看到错误或者有不懂的可以Q群反馈或公众号反馈),可能大家在网上都看过很多的关于SPI的教程和代码,不知道大家有没有在实际的项目中进行数据传输的测试。我看过很多的代码,都基本实现了功能,但兼容性都不算很好,甚至可以说局限性非常大,仅能面向某个特殊的应用,一个好的代码需要考虑能否通过调整参数来适应不同的应用场景,比如能否通过调整参数实现主从模式的切换,能否通过调整参数来实现四种基本模式的切换,能否通过调整参数来适应不同从机数的改变等等,下面废话不多说直接进入主题。
一、SPI的介绍
1.1基本四线介绍
图1-1基本的接线图
SPI外部一共有四根线:SCLK、MOSI、MISO、SS(CS)
SCLK:SPI的时钟线,由主机提供。
MOSI:主机输出/从机输入数据
MISO:主机输入/从机输出
SS/CS:从器件选择引脚
图1-2 使用拓扑结构的主器件和三个独立的从器件
1.2 时序的介绍
图1-3时序图
图1-3是显示时钟极性和相位的时序图,红色表示时钟前沿,蓝色线表示时钟后沿,下面具体介绍
CPOL确定时钟极性
CPOL = 0 表示SPI时钟为低电平(SCLK=0)为空闲状态,那么有效状态就是SCLK处于高电平时。
CPOL = 1 表示SPI时钟为高电平(SCLK=1)为空闲状态,那么有效状态就是SCLK处于低电平时。
CPHA确定相对于时钟脉冲的数据位的时序
CPHA=0数据采样在时钟周期的第一个时钟边缘,数据发送在时钟周期的第二个时钟边缘
CPHA=0数据采样在时钟周期的第二个时钟边缘,数据发送在时钟周期的第一个时钟边缘
模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿发送
模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿发送
模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿发送
模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿发送
图1-4 模式总结表格
图1-5数据传输方式(以8位数据为例)
如图1-5所示的,主机在发送数据时是从高位传输到从机的低位,而从机的高位数据则是传输到主机的低位。
二、代码的详解
将上一章的SPI介绍精简化再介绍一下,如果想详细了解SPI可以看前一章的文章。看完这些基本的介绍,大家有没有想过要怎么写代码呢?下面将以FPGA为从机的SPI代码作为例子来详细讲解如何写SPI的代码。
我们了解时序后首先要有个基本概念和整体的逻辑架构,我们需要做什么才能写出代码?那么需要思考以下的一些问题:
(1)根据时序需要知道SPI的采样是在时钟的前沿和后沿,但如何捕获SPI时钟的前后沿?时钟的前后沿标志位怎么得出?
(2)传输的数据位(比如8位)怎么知道传输数据已经有8位了?如何确定一个字节最后一位字的数据标志位。
(3)在传输数据过程中移位寄存器忙的标志怎么确定?
(4)移位寄存器又应该怎么移位?
如果知道这些,其它的一些信号的设置都是一些细枝末节。
下面我们根据每一段代码(VHDL)来进行讲解
以SPI的模式0为例讲解,上图是实体声明,基本的系统时钟以及复位信号,其它的两个就是SPI标准的四线接口以及用户接口,如果是入门的朋友,可以先不关注信号口的定义,下面的模块会更加清晰的讲清楚各个信号的内涵。
上图是模块的结构体声明,这些信号还是一样后续会做详细的讲解
SPI的寄存器是为了获取SPI时钟(SCLK)的边沿,因为有了时钟的边沿才能获得传输数据的标志,这个模块将捕获SPI时钟的上升沿和下降沿,原理也很简单,当系统时钟上升沿来时,将SCLK给spi_clk_reg寄存器,那么SCLK和spi_clk_reg寄存器恰好相差一个系统时钟周期(CLK),如此将SCLK和not spi_clk_reg取个逻辑与便可以得到SCLK的上升沿,下降沿同理。
这个模块主要是为了计算传输了多少位的数据,这里是每计数到8位就把bit_cnt_max赋值为1,而bit_cnt赋值为0。
上面的模块是获得一个字节最后一个字传输完成的标志位,这个标志就是计数寄存器最大值为1时,而数据有效的标志则是捕捉到SCLK的下降沿以及获得最后一位数据传输完成的标志信息。
这个模块主要功能就是获得移位寄存器的忙标志,以及从机准备好接受数据的标志信号和加载数据使能信号。
这两段代码主要利用移位寄存器来传输数据,这个逻辑用语言描述的话可能很累赘,所以我把前面的主从数据传输的示意图再次贴到下面,理解了这个图,这个移位寄存器的原理基本也就懂了,当然这个只是一种传输数据的方式,也可以通过其它方式
三、从机的仿真图
主机的仿真图也贴上(具体的仿真请大家获取代码自行用modelsim仿真)
总结:讲解的是基于VHDL的从机的代码,包含测试代码,大家获得代码可以先看从机的代码,相对简单,获取完整(包含主机从机)代码: