前面我们基于Zynq实现了最基本的串口打印hello world功能,这一篇文章我们会基于hello world的基础上实现PS端SPI Flash的功能。
完成功能:Zynq单板配置SPI Flash并且进行SPI Flash验证。
实验平台:智芯融的BM7030开发板,其他开发板操作类似。
核心芯片:Xilinx Xc7z030-2ffg676I
实验步骤:Zynq配置SPI的Flash 芯片,并进行初始化。
SPI协议概述
串行外设接口(SPI)是微控制器和外围IC(如传感器、ADC、DAC、移位寄存器、SRAM等)之间使用最广泛的接口之一。SPI是一种同步、全双工、主从式接口。来自主机或从机的数据在时钟上升沿或下降沿同步。主机和从机可以同时传输数据。SPI接口可以是3线式或4线式。本文重点介绍常用的4线SPI接口。
接口
图1. 含主机和从机的SPI配置。
4线SPI器件有四个信号:
1) SCLK:串行时钟,用来同步数据传输,由主机输出;
2) MOSI:主机输出从机输入(Master Output Slaver Input)数据线;
3) MISO:主机输入从机输出数据线;
4) SS:片选线,低电平有效,由主机输出。
在SPI总线上,某一时刻可以出现多个从机,但只能存在一个主机,主机通过片选线来确定要通信的从机。这就要求从机的MISO口具有三态特性,使得该口线在器件未被选通时表现为高阻抗。
产生时钟信号的器件称为主机。主机和从机之间传输的数据与主机产生的时钟同步。同I2C接口相比,SPI器件支持更高的时钟频率。用户应查阅产品数据手册以了解SPI接口的时钟频率规格。
SPI接口只能有一个主机,但可以有一个或多个从机。图1显示了主机和从机之间的SPI连接。
来自主机的片选信号用于选择从机。这通常是一个低电平有效信号,拉高时从机与SPI总线断开连接。当使用多个从机时,主机需要为每个从机提供单独的片选信号。本文中的片选信号始终是低电平有效信号。
MOSI和MISO是数据线。MOSI将数据从主机发送到从机,MISO将数据从从机发送到主机。
SPI传输的优缺点
SPI接口具有如下优点:
1) 支持全双工操作;
2) 操作简单;
3) 数据传输速率较高。
同时,它也具有如下缺点:
1) 需要占用主机较多的口线(每个从机都需要一根片选线);
2) 只支持单个主机。
硬件环境搭建
Vivado中配置Zynq IP核,SPI控制器可以路由到MIO或EMIO。选择路由到MIO时SS[1]和SS[2]是可选的。配置DDR、UART等其它选项。
查看BM7030硬件原理图中PS端MIO口的配置如下:
SPI Flash的硬件原理图设计如下:
在Vivado的Block Diagram中点击Zynq IP核,点击MIO Configuration:
选中Quad SPI Flash,Single SS 4bit IO,选择LVCMOS3.3V,对应MIO1-6
可以核对和原理图的IO管脚是否一致。
在Block Design空白处点击右键,点击Validate Design,点击确定
右键点击ARM_SOCCreate HDL Wrapper形成新的arm_soc_wrapper文件,wrapper文件就是将Processing_System7_0封装为一个Verilog接口的文件
选择Let Vivado manage Wrapper and auto-update,生成新的arm_soc_wrapper.v 文件
生成新的arm_soc_wrapper.v文件后,因为FPGA外部接口未改变,arm_soc_top.v最后形成的顶层文件不用更改;点击保存,模块会自动分层,source文件层结构如下图:
点击左侧Generate Bitstream,会生成bit文件
当Bitstream 生成后操作如下:FileExportHardwareExport Hardware选择Include bitstream OK
FileLaunch SDK 会生成SDK界面,从Vivado到SDK界面,是硬件环境到软件环境的转换
SDK start界面如下图所示:
其中硬件描述文件system.hdf 列出了每个PS7-cortexta9_[0-1]中每个模块的地址映射,如ps7_uart_0的起始地址位为0xe0000000,最高地址位为0xe0000fff. 新增加的PS_qspi_0的起始地址位0xe000d000,最高地址位为0xe000dfff。
建立应用程序项目:FilenewApplication Project,命名为SPI_Flash_test,
选择空白模板empty Application:
点击src点击右键,分别增加head文件qspips_header.h和xqspips_selftest_example.c以及主函数文件main.c文件。
头文件qspips_header.h定义了函数定义和xilinx的qspi需要的头文件类型:
#ifndef QSPIPS_HEADER_H /* prevent circular inclusions */
#define QSPIPS_HEADER_H /* by using protection macros */
#include “xil_types.h”
#include “xil_assert.h”
#include “xstatus.h”
#include “xparameters.h”
int QspiPsSelfTestExample(u16 DeviceId);
#endif
测试SPI Flash的自测程序如下:
#include “xparameters.h”
#include “xqspips.h”
#include “xil_printf.h”
XQspiPs Qspi;
int QspiPsSelfTestExample(u16 DeviceId)
{
int Status;
XQspiPs_Config *QspiConfig;
/*
* Initialize the QSPI device.
*/
QspiConfig = XQspiPs_LookupConfig(DeviceId);
if (NULL == QspiConfig) {
return XST_FAILURE;
}
Status = XQspiPs_CfgInitialize(&Qspi, QspiConfig, QspiConfig->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Perform a self-test to check hardware build.
*/
Status = XQspiPs_SelfTest(&Qspi);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
主函数main.c是打印测试串口信息:
#include <stdio.h>
#include “xparameters.h”
#include “xqspips.h”
#include “qspips_header.h”
int main ()
{
int Status;
print(“\r\n Running SPI Flash test \r\n”);
Status = QspiPsSelfTestExample(XPAR_PS7_QSPI_0_DEVICE_ID);
if (Status == 0) {
print(“SPI Flash test PASSED\r\n”);
}
else {
print(“SPI Flash test FAILED\r\n”);
}
return 0;
}
该程序是SPI设备初始化的程序,使用XQspiPs_SelfTest函数对SPI设备进行自检。这个函数会执行一次所有SPI控制器寄存器的读取和写入,主要是对延时参数,相当于复位SPI设备。自检成功返回XST_SUCCESS;读取或写入某个寄存器失败时返回XST_REGISTER_ERROR。
如果SPI Flash 配置成功,串口则会打印如下信息:
本文的项目主要是进行SPI Flash的硬件初始化,后续会有更深入的应用,比如会应用Flash做启动文件作为Zynq的启动程序和其他应用程序等等。