本上介绍了Zynq中的SPI控制器。本文再系统总结下对SPI协议的理解,加强对其认识。最后再说明Zynq中如果配置和使用SPI控制器。
SPI是串行外设接口(Serial Peripheral Interface)的缩写。标准四根线只使用4根信号线进行通信:MISO(主输入-从输出)、MOSI(主输出-从输入)、时钟SCLK、从机选择信号SS(有时也称为片选信号CS)。 SPI协议有如下特点:
主机设备在访问从机设备前,必须将SS信号拉低,使能从机设备;当主机要结束本次通信时,再把SS信号拉高即可。
程序中我们都是通过寄存器操纵Tx/Rx FIFO,数据串行化工作由SPI硬件完成(分为手动和自动两种设置)。由于SPI协议“时钟驱动”和“数据交换”的特点,当主机向TxFIFO写数据,进行数据传输时,同时也会把接收到的数据存储到RxFIFO中。下一次传输之前我们应该将RxFIFO中的全部数据都读取出来。
SPI的读、写是同时进行的。如果我们只希望进行写操作,则忽略接收到的字节即可;如果要读取从机中的数据,由于从机无法主动发送数据,因此需要主机向从机发送空字节,来引导从机的传输。
SPI的最大优点是速度快(是UART的几十倍),这也是EEPROM、Flash等芯片采用SPI接口的原因。比如我们将比较大的FPGA bin文件烧写到Flash中,用的便是SPI协议。其支持连续传输模式,无需不断检测帧的起始与结束,同步传输的效率比异步传输也要高。
SPI的缺点主要是抗干扰能力比RS422/485要差,不适合远距离传输。另外没有与IIC协议类似的应答机制,以告诉用户是否收到了数据。编程实现上比UART要困难一些,因为我们平时使用的EEPROM、Flash等都提供了现成的命令格式,按照时序图设计即可。当我们自定义主机和从机间的通信时,还要根据设计目的定义一些命令。
Vivado中配置Zynq IP核,SPI控制器可以路由到MIO或EMIO。选择路由到MIO时SS[1]和SS[2]是可选的。配置DDR、UART等其它选项。
由于我使用的MZ7X和Redpitaya开发板在MIO部分没有留出SPI使用的管脚,因此选择将SPI 0路由到EMIO接口(不使用SPI 1)。直接将SPI_0管脚“右键->Make External”引出到PL管脚。
对引出的SPI管脚做约束,选择开发板上的扩展接口:
其中spi_0_io1_io为SPI的MISO信号,spi_0_io0_io为MOSI信号。将硬件环境导出到SDK中。
本文设计一个简单的SPI设备的自检程序,学会Zynq中SPI控制器的初始化。user_spi.h文件的代码如下:
#ifndef SRC_USER_SPI_H_
#define SRC_USER_SPI_H_
#include "xparameters.h"
#include "xspips.h"
#include "xil_printf.h"
#define SPI_DEVICE_ID XPAR_XSPIPS_0_DEVICE_ID
int SpiPs_Init(XSpiPs *Spi, u16 DeviceId);
#endif /* SRC_USER_SPI_H_ */
user_spi.c文件的代码如下:
#include "user_spi.h"
//--------------------------------------------------------------------
// SPI设备初始化函数
//--------------------------------------------------------------------
int SpiPs_Init(XSpiPs* Spi, u16 DeviceId)
{
int Status;
XSpiPs_Config *SpiConfig;
// 初始化SPI设备
SpiConfig = XSpiPs_LookupConfig(DeviceId);
if (NULL == SpiConfig) {
return XST_FAILURE;
}
Status = XSpiPs_CfgInitialize(Spi, SpiConfig, SpiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 进行硬件自检
Status = XSpiPs_SelfTest(Spi);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
main.c文件的代码如下:
#include "user_spi.h"
XSpiPs Spi; //SPI设备
int main(void)
{
int Status;
xil_printf("SPI Selftest Example \r\n");
/* SPI初始化 */
Status = SpiPs_Init(&Spi, SPI_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("SPI Selftest Example Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran SPI Selftest Example\r\n");
return XST_SUCCESS;
}
SPI设备初始化的程序相信大家都很熟悉了,与本系列前面那些定时器设备、中断设备等等的初始化过程都相同。最后使用XSpiPs_SelfTest函数对SPI设备进行自检。这个函数会执行一次所有SPI控制器寄存器的读取和写入,相当于复位SPI设备。自检成功返回XST_SUCCESS;读取或写入某个寄存器失败时返回XST_REGISTER_ERROR。
运行结果如上,表明SPI设备自检成功,可以正常使用。
本文总结了一些对SPI协议的理解,与第28篇结合希望我们可以对Zynq中SPI的具体情况有所认识。最后简单介绍了下Zynq中SPI的环境搭建以及自检程序设计。下一篇将着重讲述如何实现两块Zynq间SPI的主、从机通信,将不会再介绍这些基础内容。