1. 设计 SPI 模块,并将其添加入 SOPC 工程
硬件 SPI 模块的设计 SPI(Serial peripheral interface,即串行外围设备接口) SPI 是一种很常见的总线,是一种高速、同步、全双工的通讯总线。 SPI 传输速率受 限与系统时钟频率,一般最大为时钟频率的一半。 “总线”指多个设备共用的信号线。在一个 SPI 总线中,支持多个从设备。 区别于 I2C 协议中不同的从设备有不同的器件地址,这样 SPI 通过片选线 CS/NSS (SS1、SS2、SS3)选择与哪一个从机通信,如下所示,
SPI信号线 NSS: 片选信号线,主机通过拉低从机 NSS 引脚来选择从机,才能和从机通 信。 SCLK: 时钟信号线,由主机产生。SPI协议需要时钟来保证发送端和接收端的 同步传输。 MOSI(Master Ouput Slave Input): 主机(数据)输出/从设备(数据)输入引脚。 MISO(Master Input Slave Ouput): 主机(数据)输入/从设备(数据)输出引脚。 区别于 I2C 协议中只有一条 SDA 总线用于数据传输,可以看到 SPI 协议通过两 条信号线来传输数据,所以是全双工通讯,如下所示,
SPI协议介绍
起始信号、停止信号: 由前面提到的 NSS 片选线控制。 NSS 低有效。当从设 备上的 NSS 引脚被拉低表明主机选中该从设备,才能开始传输。 NSS 引脚被拉高表明传输结束。
时钟极性 CPOL/Clock Polarity
CPOL = 0:时钟空闲为低电平 0
CPOL = 1:时钟空闲为高电平1
时钟相位 CPHA /Clock Phase
CPHA = 0:在SCK的第一个跳变沿采样
CPHA = 1:在SCK的第二个跳变沿采样
SPI有四种时钟配置模式
主从双方需要工作在相同的模式下才能进行数据传输
数据传输中,SPI协议并无明确规定高位在先还是低位在先。但是主从双方要先 约定好才能进行数据传输。主机一般都采用MSB方式传输,先传输高位 LSB:least significant bit 表示二进制数据的最低位。 MSB : most significant bit 表示二进制数据的最高位。
SPI协议时序介绍 以下图为例
传输前主机先将从机的NSS拉低,作为起始信号。
时钟配置
CPOL = 0,所以时钟空闲时为低电平。
CPHA = 1,所以在SCK的第二个跳变沿,即下降沿开始采样。
在每个 SCK 周期的下降沿,通过 MOSI 和 MISO 传输一位数据。
数据一般是 8 位,采用 MSB 传输方式。在数据传输的过程中,每次接收到的数 据必须在下一次数据传输之前被采样,否则会导致 SPI 物理模块失效。因此, 在程序中传输完数据后,会去读取 SPI 设备里的数据, 即使这些数据在我们的程 序里是无用的(虽然发送后紧接着读取是无意义的,但仍然需要从寄存器中读出 来)。
区别于I2C,没有应答信号,传输结束后拉高NSS,作为停止信号。
设计一个 SPI 模块,要求如下所示,
支持全双工同步串行数据传输;
仅支持主设备工作模式;
支持 SPI 串行时钟频率的配置;
支持时钟极性和相位的配置;
仅支持 MSB 传输方式;
支持中断;
主要包括 5 个 8 位宽的寄存器:
SPI 控制寄存器
SPI 状态寄存器
SPI 发送/接收数据寄存器
SPI 外部寄存器
SPI 片选控制寄存器
SPI 有 6 个外部接口,分别是 SPI_CLK SPI_MISO SPI_MOSI SPI_CSN1 SPI_CSN2 SPI_CSN3 。这里我们只设计 SPI 主模式,所以对于 FPGA 来说, SPI_MISO 始终 是输入,其他接口始终是输出。
SPI 协议没有将读写严格区别开来,因为 SPI 的数据通道有两个,可以同时进行读 写,属于全双工通信。
主机读写数据的时序操作如下所示,
- 主机操作片选控制寄存器,将从机 CS 拉低。
- 主机操作发送数据寄存器,写入 8 bit 的数据,然后读出 8 bit 的数据。
主机操作片选控制寄存器,将从机 CS 拉高。 我们可以将 SPI 模块作为一个 APB 外设,挂在 APB 总线上。那么就需要设计一个 APB 接口来对寄存器进行读写操作。我们需要对每个 APB 接口分配一个地址,这样 才能通过译码电路区分开来不同的 APB 。在 config.h 文件中定了 9 路 APB 地址, 默认使用 APB0 作为 GPIO ,现在我们为 SPI 模块分配 APB6 ,对应地址为 0xbfe70000 ,如下所示,
最后实现的结构框图如下所示,
在顶层文件 godson_mcu_top.v 中例化我们设计的模块,如下所示,
在约束文件中,将例化好的 SPI 的输出引脚与原理图上的合适引脚进行连接即可,如下所示,.
2. 软件设计,基于 SPI 模块与 W25Q128 型号的 SPI FLASH 芯片通信
既然已经设计好了硬件电路,我们就可以进行软件程序的编写了。在硬件 SPI 模块设 计过程中我们为 APB 分配的地址是 0xbfe90000 ,并且 SPI 相关寄存器的偏移地址 是 0x00 0x01 0x02 0x03 0x04,所以软件上需要对应好。一个不错的方法是用结构体 指针来访问寄存器。由于这个结构体指针使用频率很高,所以通过宏定义进行重命名 ( SPI ),如下所示,
编写的函数声明如下所示
然后可以基于 SPI 相函数编写 SPI 访问 W25Q128 的函数,这里以读取芯片 ID 为 例,如下所示,
我们可以在 main.c 中调用相应函数验证 SPI 通信,如下所示