零基础入门正点原子领航者ZYNQ Vitis开发教程
零基础入门正点原子领航者ZYNQ Vitis开发教程
前言
对于编程初学者来说,嵌入式开发往往是一个充满挑战但又极具吸引力的领域。Xilinx(现 AMD)的 ZYNQ 系列芯片作为异构可编程片上系统(SoC),将 ARM 架构的处理系统(PS)与 FPGA 的可编程逻辑(PL)完美融合,为嵌入式开发提供了强大的硬件平台。正点原子的领航者 ZYNQ 开发板则是这一平台的优秀载体,配套的 Vitis 开发指南为初学者提供了完整的学习路径。
本教程以编程初学者为目标受众,基于正点原子领航者 ZYNQ 开发板的 Vitis 开发内容,系统、全面地讲解 ZYNQ 嵌入式开发的核心概念、实现原理及实际应用。教程内容从基础的开发环境搭建开始,逐步深入到核心代码解析、功能实现、项目扩展等内容,每个章节都设置了知识要点和实践练习,帮助读者巩固所学内容,真正掌握 ZYNQ 开发的方法和技巧。
1. 项目概述
1.1 项目背景
在传统的嵌入式开发中,开发者往往需要分别学习 MCU 开发和 FPGA 开发,两者之间存在明显的技术鸿沟。Xilinx 推出的 ZYNQ 系列芯片打破了这一界限,将 ARM Cortex-A9 处理器(PS)与 FPGA 可编程逻辑(PL)集成在单颗芯片中,实现了软硬件协同开发的新模式。
正点原子的领航者 ZYNQ 开发板基于 Xilinx Zynq-7000 系列芯片(XC7Z010/XC7Z020),提供了丰富的外设接口和完整的开发资料,是初学者入门 ZYNQ 开发的理想选择。Vitis 作为 Xilinx 统一的软件平台,取代了传统的 SDK,为 ZYNQ 开发提供了统一的软件开发环境,支持裸机开发、Linux 开发和硬件加速开发等多种模式。
1.2 开发意义
学习 ZYNQ Vitis 开发对于编程初学者具有多方面的意义:
-
掌握异构开发思维:ZYNQ 的 PS+PL 架构让开发者同时接触到软件编程和硬件逻辑设计,培养异构开发的思维,拓宽技术视野。
-
提升嵌入式开发能力:通过 ZYNQ 开发,开发者可以掌握 ARM 裸机编程、Linux 系统移植、驱动开发、FPGA 逻辑设计等多方面的技能,提升综合嵌入式开发能力。
-
适应工业需求:ZYNQ 在工业控制、图像处理、人工智能、航空航天等领域有着广泛的应用,掌握 ZYNQ 开发技能可以更好地适应工业界的需求。
-
培养工程实践能力:通过实际项目的开发,开发者可以培养工程实践能力,学会解决实际开发中遇到的问题,提高问题解决能力。
1.3 应用场景
ZYNQ 系列芯片凭借其强大的处理能力和灵活的可编程逻辑,在多个领域有着广泛的应用:
-
工业控制:ZYNQ 可以实现高精度的运动控制、实时数据采集和处理,适用于工业机器人、数控机床、智能传感器等工业控制设备。
-
图像处理:ZYNQ 的 PL 部分可以实现图像的硬件加速处理,如边缘检测、图像滤波、图像压缩等,PS 部分可以实现图像的算法处理和人机交互,适用于智能摄像头、机器视觉系统、医疗图像处理设备等。
-
人工智能:ZYNQ 可以部署轻量化的 AI 模型,如 TensorFlow Lite、Pytorch Lite 等,实现边缘 AI 推理,适用于智能安防、智能交通、智能家居等领域。
-
航空航天:ZYNQ 的高可靠性和高性能使其适用于航空航天领域的嵌入式系统,如卫星通信设备、飞行控制系统等。
-
消费电子:ZYNQ 可以用于智能穿戴设备、智能家居设备、智能玩具等消费电子产品,实现复杂的功能和交互。
知识要点
-
ZYNQ 是异构可编程片上系统,集成了 ARM Cortex-A9 处理器和 FPGA 可编程逻辑。
-
Vitis 是 Xilinx 统一的软件平台,支持 ZYNQ 的多种开发模式。
-
正点原子领航者 ZYNQ 开发板是入门 ZYNQ 开发的理想载体。
实践练习
-
调研 ZYNQ 在你感兴趣的领域的应用案例,整理成一份简单的报告。
-
查找正点原子领航者 ZYNQ 开发板的硬件资源清单,了解其提供的外设接口。
2. 开发环境搭建
2.1 所需软件、工具及版本要求
要进行正点原子领航者 ZYNQ 的 Vitis 开发,需要安装以下软件和工具:
-
Vivado Design Suite:用于 ZYNQ 的硬件设计,包括 Block Design 创建、IP 核配置、比特流生成等。推荐使用 2020.2 或 2023.1 版本,与正点原子的开发资料版本保持一致。
-
Vitis Unified Software Platform:用于 ZYNQ 的软件开发,包括裸机程序开发、Linux 应用开发等。版本需要与 Vivado 保持一致。
-
串口调试工具:如 Tera Term、PuTTY 等,用于查看开发板的串口输出。
-
JTAG 下载器驱动:用于连接开发板和电脑,进行程序下载和调试。
-
CH340 串口驱动:用于开发板的 USB 转串口功能,第一次连接开发板时需要安装。
2.2 Vivado 安装步骤
2.2.1 下载 Vivado 安装包
-
访问 AMD Xilinx 官网(https://www.amd.com/zh-cn/developer/resources/vitis.html),下载对应版本的 Vivado Design Suite 安装包。
-
选择 “Full Product Installation” 进行完整安装,安装包大小约 40-50GB,建议使用断点续传工具进行下载。
2.2.2 安装 Vivado
-
解压下载的安装包,进入解压后的目录,执行
xsetup命令启动安装向导。 -
在安装向导中,选择 “Custom” 自定义安装,勾选以下必要组件:
-
Vivado Design Suite
-
Vitis Core Development Kit
-
Vitis Embedded Development(用于 ZYNQ 嵌入式开发)
-
取消勾选不需要的组件,如 Model Composer、Documentation Navigator 等,以节省安装空间。
-
-
选择安装路径,建议选择非中文、无空格的路径,如
D:\Xilinx\Vivado\2020.2。 -
等待安装完成,安装时间约 1-2 小时,具体时间取决于电脑配置。
2.2.3 配置环境变量
安装完成后,需要配置环境变量,以便在命令行中调用 Vivado 和 Vitis 工具:
-
右键 “此电脑”,选择 “属性”->“高级系统设置”->“环境变量”。
-
在 “系统变量” 中,点击 “新建”,添加以下环境变量:
-
变量名:
XILINX_VIVADO,变量值:D:\Xilinx\Vivado\2020.2(根据实际安装路径修改) -
变量名:
XILINX_VITIS,变量值:D:\Xilinx\Vitis\2020.2(根据实际安装路径修改)
-
-
在 “系统变量” 中找到
PATH变量,点击 “编辑”,添加以下路径:-
%XILINX_VIVADO%\bin -
%XILINX_VITIS%\bin
-
-
点击 “确定” 保存环境变量配置,重启电脑使配置生效。
2.3 Vitis 配置步骤
2.3.1 启动 Vitis
-
双击桌面的 Vitis 图标,或者在命令行中输入
vitis命令启动 Vitis IDE。 -
选择工作空间路径,建议选择非中文、无空格的路径,如
D:\ZYNQ_Workspace。
2.3.2 配置 License
Vitis 需要 License 才能使用完整功能,对于初学者可以申请免费的 WebPack License:
-
在 Vitis IDE 中,选择 “Help”->“Licensing”。
-
点击 “Get Free WebPack License”,登录 Xilinx 账户(需要提前注册)。
-
按照向导完成 License 的申请和导入,License 会自动生效。
2.4 串口驱动和 JTAG 驱动安装
2.4.1 CH340 串口驱动安装
-
打开正点原子开发板资料中的 “6_软件资料 / 1_软件 / CH340 驱动 (USB 串口驱动)” 文件夹。
-
双击 “SETUP.EXE” 文件,按照向导完成驱动安装。
-
安装完成后,将开发板的 USB_UART 接口通过 Mini USB 线连接到电脑,在设备管理器中查看是否识别到 USB-SERIAL CH340 设备,若识别到则驱动安装成功。
2.4.2 JTAG 下载器驱动安装
-
打开 Vivado 安装目录下的
data\xicom\cable_drivers\win64文件夹。 -
双击
install_drivers.bat文件,按照向导完成 JTAG 下载器驱动的安装。 -
将 JTAG 下载器连接到开发板的 JTAG 接口,并连接到电脑,在设备管理器中查看是否识别到 Xilinx Cable 设备,若识别到则驱动安装成功。
知识要点
-
Vivado 和 Vitis 的版本需要保持一致,避免出现兼容性问题。
-
安装路径和工作空间路径不能包含中文和空格,否则会导致工具运行异常。
-
环境变量的配置是为了在命令行中方便调用 Vivado 和 Vitis 工具。
实践练习
-
按照步骤安装 Vivado 和 Vitis,配置环境变量,验证环境变量是否配置成功(在命令行中输入
vivado -version和vitis -version,查看是否输出版本信息)。 -
安装 CH340 串口驱动和 JTAG 驱动,连接开发板,在设备管理器中查看驱动是否识别成功。
3. 项目结构解析
3.1 ZYNQ 开发项目的文件组织方式
一个完整的 ZYNQ Vitis 开发项目通常包含以下几个部分:
-
Vivado 硬件工程:用于创建 ZYNQ 的硬件设计,包括 Block Design、IP 核配置、约束文件、比特流文件等。
-
Vitis 软件工程:用于开发 ZYNQ 的软件程序,包括应用工程、板级支持包(BSP)、源文件、头文件等。
-
启动镜像文件:包括 BOOT.BIN 文件,用于开发板的启动,包含 FSBL、比特流文件和应用程序。
3.1.1 Vivado 硬件工程结构
Vivado 硬件工程的典型结构如下:
hello_world_vivado/
├── hello_world.xpr # Vivado工程文件
├── hello_world.sdk/ # 导出到Vitis的硬件定义文件目录
├── src/
│ ├── system.bd # Block Design设计文件
│ ├── system_wrapper.v # 顶层HDL封装文件
│ └── constraints/
│ └── system.xdc # 约束文件(引脚约束、时钟约束等)
├── runs/
│ ├── impl_1/ # 实现目录,包含比特流文件
│ │ ├── system_wrapper.bit
│ │ └── system_wrapper.sysdef
│ └── synth_1/ # 综合目录
└── ip/ # IP核目录,包含使用的IP核文件
3.1.2 Vitis 软件工程结构
Vitis 软件工程的典型结构如下:
hello_world_vitis/
├── hello_world_platform/ # 平台工程,包含硬件定义信息
│ ├── platform.spr
│ └── export/
│ └── hello_world_platform/
│ ├── hello_world_platform.xpfm
│ └── system.hdf
├── hello_world_app/ # 应用工程
│ ├── hello_world_app.spr
│ ├── src/
│ │ ├── main.c # 主程序文件
│ │ └── platform_config.h
│ ├── Debug/
│ │ ├── hello_world_app.elf # 编译生成的可执行文件
│ │ └── hello_world_app.map
│ └── hello_world_app_bsp/ # 板级支持包(BSP)
│ ├── bsp.spr
│ ├── src/
│ │ ├── ps7_init.c # PS初始化文件
│ │ ├── ps7_init.h
│ │ └── xilinx_bsp/
│ │ ├── drivers/ # 外设驱动文件
│ │ ├── libs/ # 库文件
│ │ └── include/ # 头文件
│ └── lib/
│ └── libxil.a
└── BOOT.BIN # 启动镜像文件
3.2 核心模块功能说明
3.2.1 Block Design(BD)模块
Block Design 是 Vivado 中用于图形化创建 ZYNQ 硬件设计的模块,通过拖拽 IP 核并连接来完成硬件系统的搭建。在 ZYNQ 开发中,Block Design 通常包含以下核心 IP 核:
-
ZYNQ7 Processing System:ZYNQ 的处理系统 IP 核,包含 ARM Cortex-A9 处理器、内存控制器、外设接口等,是 ZYNQ 的核心模块。
-
AXI GPIO:通用输入输出 IP 核,用于控制开发板的 LED、按键等外设。
-
AXI UARTLite:串口通信 IP 核,用于实现开发板与电脑的串口通信。
-
AXI Timer:定时器 IP 核,用于实现定时功能和中断功能。
-
AXI DMA:直接内存访问 IP 核,用于实现 PS 与 PL 之间的高速数据传输。
3.2.2 板级支持包(BSP)模块
板级支持包(BSP)是 Vitis 中为特定硬件平台提供的软件支持包,包含了硬件驱动、库文件、初始化代码等,为应用程序提供了访问硬件的接口。BSP 的核心功能包括:
-
PS 初始化:包含
ps7_init.c和ps7_init.h文件,用于初始化 ZYNQ 的 PS 部分,包括时钟、DDR、外设等。 -
外设驱动:包含各种外设的驱动文件,如 GPIO 驱动、UART 驱动、定时器驱动等,为应用程序提供了访问外设的 API。
-
库文件:包含 Xilinx 提供的库文件,如
libxil.a,提供了常用的函数和工具。 -
配置文件:包含平台配置文件,如
platform_config.h,定义了硬件平台的相关参数。
3.2.3 应用程序模块
应用程序模块是开发者编写的实现具体功能的代码,通常包含以下部分:
-
主程序文件:
main.c,是应用程序的入口,包含了程序的主要逻辑。 -
头文件:包含各种外设的头文件和自定义的头文件,用于声明函数和宏定义。
-
编译输出文件:编译生成的
.elf文件,是可执行文件,可以下载到开发板上运行。
知识要点
-
Vivado 硬件工程负责 ZYNQ 的硬件设计,Vitis 软件工程负责软件开发,两者通过硬件定义文件(.hdf 或.xsa)进行关联。
-
Block Design 是图形化搭建 ZYNQ 硬件系统的工具,通过 IP 核的拖拽和连接完成硬件设计。
-
BSP 为应用程序提供了访问硬件的接口,包含了硬件驱动和库文件。
实践练习
-
创建一个简单的 Vivado 硬件工程,创建 Block Design,添加 ZYNQ7 Processing System IP 核,配置基本参数,导出硬件定义文件到 Vitis。
-
在 Vitis 中创建应用工程,基于导出的硬件定义文件,查看 BSP 的结构,了解各个文件的作用。
4. 核心代码逐行解析
4.1 Hello World 实验代码解析
Hello World 实验是 ZYNQ 开发的入门实验,通过串口打印 “Hello World” 信息,验证开发环境是否正常。以下是 Hello World 实验的核心代码:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
int main()
{
init_platform(); // 初始化平台
print("Hello World from ZYNQ!\n"); // 打印Hello World信息
cleanup_platform(); // 清理平台
return 0;
}
4.1.1 头文件解析
-
#include <stdio.h>:标准输入输出头文件,提供了标准的输入输出函数,如printf。 -
#include "platform.h":平台头文件,包含了平台初始化和清理的函数声明。 -
#include "xil_printf.h":Xilinx 提供的打印函数头文件,xil_printf函数是 Xilinx 定义的用于打印字符串的函数,比标准的printf函数更适合嵌入式系统。
4.1.2 主函数解析
init_platform();:初始化平台函数,用于初始化硬件平台,使能缓存和初始化 UART 等外设。该函数的定义在platform.c文件中,代码如下:
void init_platform(void)
{
#ifdef __MICROBLAZE__
#ifdef XPAR_MICROBLAZE_USE_DCACHE
Xil_DCacheEnable();
#endif
#ifdef XPAR_MICROBLAZE_USE_ICACHE
Xil_ICacheEnable();
#endif
#endif
#ifdef STDOUT_IS_16550
XUartNs550_SetBaudRate(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ, 115200);
XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS);
#endif
}
该函数的作用是:
-
对于 MicroBlaze 处理器,使能数据缓存和指令缓存。
-
对于 UART 串口,设置波特率为 115200,数据位为 8 位。
print("Hello World from ZYNQ!\n");:打印函数,用于将字符串通过 UART 串口发送出去。print函数是xil_printf的宏定义,在xil_printf.h中定义:
#define print xil_printf
xil_printf函数的实现是基于 UART 串口的,将字符串逐字符发送到 UART 串口。
cleanup_platform();:清理平台函数,用于清理硬件平台,关闭缓存等。该函数的定义在platform.c文件中,代码如下:
void cleanup_platform(void)
{
#ifdef __MICROBLAZE__
#ifdef XPAR_MICROBLAZE_USE_DCACHE
Xil_DCacheDisable();
#endif
#ifdef XPAR_MICROBLAZE_USE_ICACHE
Xil_ICacheDisable();
#endif
#endif
}
该函数的作用是:
-
对于 MicroBlaze 处理器,关闭数据缓存和指令缓存。
-
对于 ZYNQ 的 ARM 处理器,该函数实际上没有实际作用,因为 ZYNQ 的 ARM 处理器的缓存管理由操作系统或启动代码负责。
return 0;:主函数返回值,表示程序正常结束。
4.2 GPIO 控制 LED 实验代码解析
GPIO 控制 LED 实验是通过 ZYNQ 的 PS 端 MIO 控制 LED 的亮灭,实现 LED 的闪烁效果。以下是 GPIO 控制 LED 实验的核心代码:
#include <stdio.h>
#include "platform.h"
#include "xgpiops.h"
#include "xil_printf.h"
#include "xil_types.h"
#include "xparameters.h"
#include "sleep.h"
// 定义GPIO设备ID和LED引脚
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define LED_PIN 7 // MIO7引脚,连接到PS_LED0
// GPIO驱动实例
XGpioPs Gpio;
int main()
{
int Status;
XGpioPs_Config *ConfigPtr;
init_platform();
// 初始化GPIO驱动
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置LED引脚为输出模式
XGpioPs_SetDirectionPin(&Gpio, LED_PIN, 1);
// 使能LED引脚的输出
XGpioPs_SetOutputEnablePin(&Gpio, LED_PIN, 1);
// 循环控制LED闪烁
while (1) {
// 点亮LED
XGpioPs_WritePin(&Gpio, LED_PIN, 1);
sleep(1); // 延时1秒
// 熄灭LED
XGpioPs_WritePin(&Gpio, LED_PIN, 0);
sleep(1); // 延时1秒
}
cleanup_platform();
return 0;
}
4.2.1 头文件解析
-
#include "xgpiops.h":ZYNQ PS 端 GPIO 驱动的头文件,提供了 PS 端 GPIO 的控制函数。 -
#include "xil_types.h":Xilinx 定义的类型头文件,定义了一些自定义的数据类型。 -
#include "xparameters.h":参数定义头文件,定义了硬件平台的参数,如 GPIO 设备 ID、基地址等。 -
#include "sleep.h":延时函数头文件,提供了sleep函数,用于实现延时。
4.2.2 宏定义解析
-
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID:定义 GPIO 设备 ID,XPAR_XGPIOPS_0_DEVICE_ID是在xparameters.h中定义的宏,表示 PS 端 GPIO 设备的 ID。 -
#define LED_PIN 7:定义 LED 引脚为 MIO7 引脚,该引脚连接到开发板的 PS_LED0。
4.2.3 主函数解析
-
XGpioPs Gpio;:定义 GPIO 驱动实例,用于操作 GPIO 外设。 -
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);:查找 GPIO 设备的配置信息,根据设备 ID 获取配置结构体。 -
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);:初始化 GPIO 驱动,根据配置信息初始化 GPIO 实例。 -
XGpioPs_SetDirectionPin(&Gpio, LED_PIN, 1);:设置 LED 引脚为输出模式,第二个参数为 1 表示输出,0 表示输入。 -
XGpioPs_SetOutputEnablePin(&Gpio, LED_PIN, 1);:使能 LED 引脚的输出,第二个参数为 1 表示使能输出,0 表示关闭输出。 -
循环控制 LED 闪烁:
-
XGpioPs_WritePin(&Gpio, LED_PIN, 1);:将 LED 引脚设置为高电平,点亮 LED。 -
sleep(1);:延时 1 秒。 -
XGpioPs_WritePin(&Gpio, LED_PIN, 0);:将 LED 引脚设置为低电平,熄灭 LED。 -
sleep(1);:延时 1 秒。
-
4.3 GPIO 中断实验代码解析
GPIO 中断实验是通过按键触发中断,在中断服务程序中控制 LED 的亮灭。以下是 GPIO 中断实验的核心代码:
#include <stdio.h>
#include "platform.h"
#include "xgpiops.h"
#include "xil_printf.h"
#include "xil_types.h"
#include "xparameters.h"
#include "xscugic.h"
#include "sleep.h"
// 定义GPIO设备ID、按键引脚和LED引脚
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define KEY_PIN 50 // MIO50引脚,连接到PS_KEY0
#define LED_PIN 7 // MIO7引脚,连接到PS_LED0
// GPIO驱动实例和中断控制器实例
XGpioPs Gpio;
XScuGic Intc;
// 中断服务程序
void KeyIntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
static u32 LedState = 0;
// 翻转LED状态
LedState = !LedState;
XGpioPs_WritePin(&Gpio, LED_PIN, LedState);
// 清除中断状态
XGpioPs_IntrClearPin(&Gpio, KEY_PIN);
}
int main()
{
int Status;
XGpioPs_Config *ConfigPtr;
XScuGic_Config *IntcConfigPtr;
XGpioPs_IntrType IntrType;
init_platform();
// 初始化GPIO驱动
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 初始化中断控制器
IntcConfigPtr = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
if (IntcConfigPtr == NULL) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc, IntcConfigPtr, IntcConfigPtr->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置按键引脚为输入模式
XGpioPs_SetDirectionPin(&Gpio, KEY_PIN, 0);
// 使能按键引脚的中断
XGpioPs_IntrEnablePin(&Gpio, KEY_PIN);
// 设置中断类型为下降沿触发
IntrType.Type = XGPIOPS_IRQ_TYPE_EDGE_FALLING;
XGpioPs_SetIntrTypePin(&Gpio, KEY_PIN, &IntrType);
// 设置中断回调函数
XGpioPs_SetCallbackHandler(&Gpio, &Gpio, KeyIntrHandler);
// 连接中断
Status = XScuGic_Connect(&Intc, XPAR_FABRIC_GPIOPS_0_INTR,
(Xil_ExceptionHandler)XGpioPs_IntrHandler, &Gpio);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 使能中断
XScuGic_Enable(&Intc, XPAR_FABRIC_GPIOPS_0_INTR);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
// 无限循环
while (1) {
sleep(1);
}
cleanup_platform();
return 0;
}
4.3.1 头文件解析
#include "xscugic.h":ZYNQ 中断控制器驱动的头文件,提供了中断控制器的控制函数。
4.3.2 宏定义解析
-
#define KEY_PIN 50:定义按键引脚为 MIO50 引脚,该引脚连接到开发板的 PS_KEY0。 -
#define XPAR_FABRIC_GPIOPS_0_INTR:GPIO 中断号,在xparameters.h中定义,表示 PS 端 GPIO 的中断号。
4.3.3 中断服务程序解析
KeyIntrHandler函数是按键中断的服务程序,当按键按下时触发中断,执行该函数:
-
static u32 LedState = 0;:定义静态变量LedState,用于保存 LED 的状态。 -
LedState = !LedState;:翻转 LED 状态,0 变为 1,1 变为 0。 -
XGpioPs_WritePin(&Gpio, LED_PIN, LedState);:根据LedState的值设置 LED 引脚的电平,控制 LED 的亮灭。 -
XGpioPs_IntrClearPin(&Gpio, KEY_PIN);:清除按键引脚的中断状态,否则中断会一直触发。
4.3.4 主函数解析
-
初始化 GPIO 驱动和中断控制器:与 GPIO 控制 LED 实验类似,初始化 GPIO 驱动和中断控制器。
-
设置按键引脚为输入模式:
XGpioPs_SetDirectionPin(&Gpio, KEY_PIN, 0);,0 表示输入模式。 -
使能按键引脚的中断:
XGpioPs_IntrEnablePin(&Gpio, KEY_PIN);。 -
设置中断类型为下降沿触发:
IntrType.Type = XGPIOPS_IRQ_TYPE_EDGE_FALLING;,表示当按键引脚的电平从高电平变为低电平时触发中断。 -
设置中断回调函数:
XGpioPs_SetCallbackHandler(&Gpio, &Gpio, KeyIntrHandler);,当中断触发时,调用KeyIntrHandler函数。 -
连接中断:
XScuGic_Connect(&Intc, XPAR_FABRIC_GPIOPS_0_INTR, (Xil_ExceptionHandler)XGpioPs_IntrHandler, &Gpio);,将 GPIO 中断连接到中断控制器。 -
使能中断:
XScuGic_Enable(&Intc, XPAR_FABRIC_GPIOPS_0_INTR);使能中断控制器中的 GPIO 中断,Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);使能处理器的中断。 -
无限循环:
while (1) { sleep(1); },程序进入无限循环,等待中断触发。
知识要点
-
xil_printf是 Xilinx 定义的打印函数,比标准的printf更适合嵌入式系统。 -
PS 端 GPIO 的控制需要使用
xgpiops.h中的函数,包括初始化 GPIO、设置引脚方向、读写引脚电平、设置中断等。 -
中断控制器的初始化和配置需要使用
xscugic.h中的函数,包括初始化中断控制器、连接中断、使能中断等。
实践练习
-
编写 Hello World 实验代码,下载到开发板上运行,查看串口输出。
-
编写 GPIO 控制 LED 实验代码,实现 LED 的闪烁效果,下载到开发板上验证。
-
编写 GPIO 中断实验代码,通过按键控制 LED 的亮灭,下载到开发板上验证。
5. 功能实现步骤
5.1 Hello World 实验实现步骤
5.1.1 硬件设计步骤
-
打开 Vivado,创建新工程,工程名为
hello_world,选择 RTL Project,不添加源文件和约束文件。 -
选择器件型号,根据开发板的 ZYNQ 芯片型号选择,XC7Z020 选择
xc7z020clg400-2,XC7Z010 选择xc7z010clg400-1。 -
创建 Block Design,命名为
system,点击 “+” 号添加ZYNQ7 Processing SystemIP 核。 -
双击
ZYNQ7 Processing SystemIP 核,进入配置界面:-
配置 UART:在
Peripheral I/O Pins页面,勾选 MIO14 和 MIO15 为 UART0。 -
配置 DDR:在
DDR Configuration页面,选择 DDR 器件型号,XC7Z020 选择MT41J256M16RE-125,XC7Z010 选择MT41J128M16HA-125。 -
移除 PL 相关接口:在
Clock Configuration页面,取消勾选FCLK_CLK0;在PS-PL Configuration页面,取消勾选FCLK_RESET0_N和M AXI GP0 interface。
-
-
点击 “OK” 完成配置,点击 “Run Block Automation” 自动连接 IP 核。
-
保存 Block Design,点击 “Validate Design” 验证设计是否正确。
-
生成 Output Products:右键
system.bd,选择 “Generate Output Products”,选择 Global Synthesis Options,点击 “Generate”。 -
创建 HDL Wrapper:右键
system.bd,选择 “Create HDL Wrapper”,选择 “Let Vivado manage wrapper and auto-update”,点击 “OK”。 -
导出硬件:选择 “File”->“Export”->“Export Hardware”,不勾选 “Include bitstream”,点击 “OK”,导出到
hello_world.sdk目录。 -
启动 Vitis:选择 “File”->“Launch Vitis”,设置工作空间为
hello_world_vitis,点击 “OK”。
5.1.2 软件开发步骤
-
在 Vitis 中,选择 “File”->“New”->“Application Project”,点击 “Next”。
-
选择平台工程,选择刚才导出的硬件平台,点击 “Next”。
-
输入应用工程名为
hello_world_app,点击 “Next”。 -
选择工程模板为 “Hello World”,点击 “Finish”。
-
打开
src/main.c文件,查看代码,修改打印信息为"Hello World from ZYNQ!\n"。 -
编译工程:右键
hello_world_app工程,选择 “Build Project”,编译生成hello_world_app.elf文件。 -
下载程序:连接开发板的 JTAG 接口和 USB_UART 接口,右键
hello_world_app工程,选择 “Run As”->“1 Launch on Hardware (System Debugger)”。 -
查看串口输出:打开串口调试工具,设置波特率为 115200,数据位为 8,停止位为 1,无校验位,查看串口输出的 “Hello World from ZYNQ!” 信息。
5.2 GPIO 控制 LED 实验实现步骤
5.2.1 硬件设计步骤
-
打开 Vivado,创建新工程,工程名为
gpio_led,选择 RTL Project,不添加源文件和约束文件。 -
选择器件型号,根据开发板的 ZYNQ 芯片型号选择。
-
创建 Block Design,命名为
system,添加ZYNQ7 Processing SystemIP 核,配置方法与 Hello World 实验相同。 -
导出硬件,启动 Vitis。
5.2.2 软件开发步骤
-
在 Vitis 中,创建应用工程,工程名为
gpio_led_app,选择 “Empty Application” 模板。 -
在
src目录下创建main.c文件,编写 GPIO 控制 LED 的代码,如 4.2 节所示。 -
编译工程,下载程序到开发板。
-
查看 LED 的闪烁效果,验证实验是否成功。
5.3 GPIO 中断实验实现步骤
5.3.1 硬件设计步骤
-
打开 Vivado,创建新工程,工程名为
gpio_intr,选择 RTL Project,不添加源文件和约束文件。 -
选择器件型号,根据开发板的 ZYNQ 芯片型号选择。
-
创建 Block Design,命名为
system,添加ZYNQ7 Processing SystemIP 核,配置方法与 Hello World 实验相同。 -
导出硬件,启动 Vitis。
5.3.2 软件开发步骤
-
在 Vitis 中,创建应用工程,工程名为
gpio_intr_app,选择 “Empty Application” 模板。 -
在
src目录下创建main.c文件,编写 GPIO 中断实验的代码,如 4.3 节所示。 -
编译工程,下载程序到开发板。
-
按下按键,查看 LED 的状态是否翻转,验证实验是否成功。
知识要点
-
ZYNQ 开发分为硬件设计和软件开发两个部分,硬件设计在 Vivado 中完成,软件开发在 Vitis 中完成。
-
Block Design 的配置需要根据开发板的硬件原理图进行,确保引脚配置正确。
-
应用工程的编译和下载需要连接开发板的 JTAG 接口和串口接口,通过 JTAG 下载程序,通过串口查看输出信息。
实践练习
-
按照步骤完成 Hello World 实验,验证开发环境是否正常。
-
按照步骤完成 GPIO 控制 LED 实验,实现 LED 的闪烁效果。
-
按照步骤完成 GPIO 中断实验,通过按键控制 LED 的亮灭。
6. 常见问题与解决方案
6.1 硬件设计常见问题
6.1.1 Block Design 中 IP 连接出错
问题现象:综合时报错 “Unconnected port”,提示 IP 核的端口未连接。
原因分析:IP 核之间的连接不完整,或者时钟、复位信号未连接。
解决方案:
-
检查所有 IP 核的必需端口是否连接,如时钟、复位、AXI 总线等。
-
使用 “Validate Design” 工具检查设计,查看报错信息,根据提示修复连接问题。
-
对于 ZYNQ7 Processing System IP 核,确保
Run Block Automation操作完成,自动连接必要的端口。
6.1.2 地址映射冲突
问题现象:访问 PL 寄存器时数据异常,或者编译时报地址映射冲突。
原因分析:多个 IP 核的地址范围重叠,导致地址映射冲突。
解决方案:
-
在 Address Editor 中查看各个 IP 核的地址范围,确保每个 IP 核有唯一的地址空间。
-
修改冲突的 IP 核的地址范围,避免重叠。
-
查看
xparameters.h文件中的地址定义,确保与 Address Editor 中的地址一致。
6.1.3 时钟频率配置错误
问题现象:系统运行不稳定,或者出现超时错误。
原因分析:时钟配置错误,导致 IP 核的时钟频率不符合要求。
解决方案:
-
检查 Clock Configuration 页面中的时钟配置,确保输入时钟频率与开发板的实际时钟频率一致。
-
检查各个 IP 核的时钟频率设置,确保 IP 核的时钟频率在其支持的范围内。
-
使用 Vivado 的时序分析工具,查看时序报告,修复时序违例问题。
6.2 软件开发常见问题
6.2.1 串口无输出
问题现象:下载程序后,串口调试工具没有输出信息。
原因分析:串口配置错误,或者硬件连接问题。
解决方案:
-
检查串口调试工具的配置,确保波特率、数据位、停止位、校验位与硬件设计中的配置一致(默认波特率为 115200)。
-
检查开发板的 USB_UART 接口连接是否正确,驱动是否安装成功。
-
查看
platform.c文件中的串口初始化代码,确保串口初始化正确。 -
在程序中添加
xil_printf打印语句,验证程序是否运行到打印部分。
6.2.2 程序无法启动
问题现象:下载程序后,程序无法运行,或者卡在启动阶段。
原因分析:硬件定义文件错误,或者 BSP 配置错误。
解决方案:
-
检查硬件定义文件(.hdf 或.xsa)是否正确导出,是否包含正确的硬件信息。
-
重新生成 BSP,确保 BSP 的配置与硬件设计一致。
-
检查
ps7_init.c文件中的初始化代码,确保 PS 初始化正确。 -
使用调试工具,设置断点,查看程序的执行流程,定位问题所在。
6.2.3 GPIO 操作无响应
问题现象:GPIO 控制 LED 实验中,LED 没有按照预期亮灭。
原因分析:GPIO 配置错误,或者引脚定义错误。
解决方案:
-
检查引脚定义是否正确,确保 LED 引脚和按键引脚与开发板的原理图一致。
-
检查 GPIO 的方向设置和输出使能设置是否正确,输出引脚需要设置为输出模式并使能输出。
-
查看
xparameters.h文件中的 GPIO 设备 ID 是否正确。
6.3 下载调试常见问题
6.3.1 JTAG 连接失败
问题现象:无法连接到开发板,下载程序时报错 “Failed to connect to the target”。
原因分析:JTAG 驱动未安装成功,或者硬件连接问题。
解决方案:
-
检查 JTAG 下载器的驱动是否安装成功,在设备管理器中查看是否识别到 Xilinx Cable 设备。
-
检查 JTAG 下载器与开发板的连接是否正确,确保 JTAG 接口的引脚连接正确。
-
重启 Vivado 和 Vitis,重新连接开发板。
-
尝试更换 JTAG 下载器或 USB 线,排除硬件故障。
6.3.2 程序下载失败
问题现象:下载程序时报错 “FLASH OPERATION FAILED”,或者程序无法烧写到 Flash 中。
原因分析:Flash 配置错误,或者硬件连接问题。
解决方案:
-
检查 Flash 的配置是否正确,确保 Flash 型号与开发板的 Flash 型号一致。
-
检查 Flash 的引脚约束是否正确,确保 Flash 的引脚与原理图一致。
-
尝试使用 XSCT 命令行工具下载程序,查看详细的报错信息。
-
检查开发板的电源是否稳定,确保供电正常。
知识要点
-
硬件设计中的问题通常与 IP 核连接、地址映射、时钟配置有关,需要仔细检查 Block Design 的配置。
-
软件开发中的问题通常与串口配置、BSP 配置、代码逻辑有关,需要逐步排查。
-
下载调试中的问题通常与 JTAG 连接、Flash 配置、电源供电有关,需要检查硬件连接和驱动安装。
实践练习
-
模拟一个地址映射冲突的问题,在 Block Design 中添加两个 IP 核,设置相同的地址范围,查看报错信息,然后修复问题。
-
模拟一个串口无输出的问题,修改串口的波特率配置,查看现象,然后修复问题。
7. 项目扩展与优化建议
7.1 初学者项目扩展思路
对于初学者来说,可以从以下几个方面对基础实验进行扩展,提升开发能力:
-
多 LED 控制:在 GPIO 控制 LED 实验的基础上,扩展控制多个 LED,实现流水灯效果,或者根据按键状态控制不同的 LED 组合。
-
按键消抖:在 GPIO 中断实验的基础上,添加按键消抖功能,避免按键抖动导致的多次触发中断。可以使用软件消抖(延时判断)或硬件消抖(RC 滤波)的方法。
-
串口通信:在 Hello World 实验的基础上,扩展串口通信功能,实现电脑与开发板之间的双向通信,如发送指令控制 LED,或者接收开发板发送的数据。
-
定时器应用:使用 AXI Timer IP 核,实现定时功能,如定时采集数据、定时控制 LED 闪烁等。
-
I2C 通信:使用 ZYNQ 的 I2C 外设,实现与 I2C 设备的通信,如读取 I2C 温湿度传感器的数据,通过串口打印出来。
-
SPI 通信:使用 ZYNQ 的 SPI 外设,实现与 SPI 设备的通信,如读取 SPI Flash 的数据,或者控制 SPI 显示屏。
-
简单的人机交互:结合按键和 LED,实现简单的人机交互界面,如通过按键选择不同的功能,LED 显示当前功能状态。
7.2 项目优化建议
-
代码优化:
-
使用模块化编程,将不同的功能封装成函数,提高代码的可读性和可维护性。
-
避免使用全局变量,尽量使用局部变量和函数参数传递数据。
-
优化代码的执行效率,避免不必要的循环和计算。
-
-
硬件优化:
-
合理使用 IP 核,避免不必要的资源浪费,对于简单的功能可以使用逻辑电路实现,而不是使用 IP 核。
-
优化时钟树设计,减少时钟延迟和抖动,提高系统的稳定性。
-
合理规划地址映射,提高数据传输的效率。
-
-
性能优化:
-
使用 DMA 进行数据传输,提高 PS 与 PL 之间的数据传输速度,尤其是在处理大量数据时。
-
合理使用缓存,提高数据访问的速度,对于频繁访问的数据可以缓存到 OCM(片上存储器)中。
-
优化中断处理,减少中断服务程序的执行时间,提高系统的实时性。
-
7.3 进阶项目建议
当掌握了基础实验后,可以尝试以下进阶项目,提升综合开发能力:
-
简易示波器:使用 ZYNQ 的 ADC 外设采集模拟信号,通过 PL 进行信号处理,PS 端将采集到的数据通过串口或以太网发送到电脑,在电脑上显示波形。
-
智能温湿度监测系统:使用 I2C 温湿度传感器采集温湿度数据,通过串口或以太网发送到电脑,实现数据的实时监测和存储。
-
简单的图像处理系统:使用 OV5640 摄像头采集图像数据,通过 PL 进行图像预处理(如边缘检测、图像滤波),PS 端处理后通过 HDMI 显示出来。
-
工业控制模拟系统:结合多个外设,实现简单的工业控制模拟系统,如电机控制、传感器数据采集、报警功能等。
知识要点
-
项目扩展可以从基础实验出发,逐步增加功能,提升开发能力。
-
代码优化和硬件优化可以提高系统的性能和稳定性,培养良好的开发习惯。
-
进阶项目可以综合运用所学的知识,提升综合开发能力。
实践练习
-
在 GPIO 控制 LED 实验的基础上,实现流水灯效果,使用定时器控制 LED 的切换速度。
-
在 GPIO 中断实验的基础上,添加按键消抖功能,使用软件消抖的方法,延时 10ms 判断按键状态。
-
实现一个简单的串口通信功能,电脑发送指令 “LED_ON” 点亮 LED,发送 “LED_OFF” 熄灭 LED。
8. 学习资源推荐
8.1 官方文档
-
Xilinx 官方文档:
-
《Zynq-7000 SoC Technical Reference Manual》(UG585):ZYNQ-7000 系列芯片的技术参考手册,详细介绍了 ZYNQ 的架构、外设、寄存器等信息。
-
《Vitis Unified Software Platform Documentation》:Vitis 软件平台的官方文档,详细介绍了 Vitis 的使用方法和开发流程。
-
《Vivado Design Suite User Guide》:Vivado 设计套件的用户指南,详细介绍了 Vivado 的使用方法和硬件设计流程。
-
-
正点原子官方资料:
-
《领航者 ZYNQ 之嵌入式 Vitis 开发指南》:正点原子官方的开发指南,详细介绍了领航者 ZYNQ 开发板的硬件资源和 Vitis 开发流程,包含多个实验案例。
-
正点原子官方论坛(http://www.openedv.com/):提供了丰富的 ZYNQ 开发资料和技术交流平台。
-
8.2 视频教程
-
B 站视频:
-
正点原子《手把手教你学 ZYNQ》:详细介绍了 ZYNQ 的架构、开发环境搭建、基础实验等内容,适合初学者入门。
-
《ZYNQ 入门到实战》:详细介绍了 ZYNQ 的软硬件协同开发,包含多个实战项目。
-
-
达尔闻在线:
- 《ZYNQ 开发实战教程》:提供了系统的 ZYNQ 开发教程,从基础到进阶,适合不同层次的开发者。
8.3 书籍推荐
-
《Zynq-7000 SoC 嵌入式系统设计与实现》(何宾著):详细介绍了 Zynq-7000 SoC 的嵌入式系统设计方法,包括硬件设计、软件开发、Linux 系统移植等内容,适合有一定基础的开发者。
-
《FPGA 异构计算》(陆佳华著):介绍了 FPGA 异构计算的概念和方法,包含 ZYNQ 的软硬件协同开发内容,适合进阶学习。
-
《Xilinx Zynq-7000 All Programmable SoC 设计指南》:详细介绍了 Zynq-7000 SoC 的设计流程和方法,包含多个设计案例,适合工程实践。
8.4 开源项目
-
GitHub 开源项目:
-
Xilinx 官方开源项目:https://github.com/Xilinx,提供了多个 ZYNQ 开发的开源项目和示例代码。
-
ZYNQ 开源项目:搜索 “Zynq” 相关的开源项目,如 Zynq 图像处理项目、Zynq 工业控制项目等,学习开源项目的代码和设计思路。
-
-
Gitee 开源项目:
- 搜索 “ZYNQ” 相关的开源项目,国内的开源项目更适合中文开发者学习。
8.5 社区和论坛
-
Xilinx 官方论坛:https://forums.xilinx.com/,提供了 ZYNQ 开发的技术交流平台,可以在论坛上提问和交流。
-
电子发烧友论坛:https://www.elecfans.com/,提供了丰富的嵌入式开发资料和技术交流平台。
-
知乎:搜索 “ZYNQ” 相关的话题,了解开发者的经验和分享。
知识要点
-
官方文档是最权威的学习资料,包含了详细的技术信息和开发指南。
-
视频教程适合初学者入门,直观地展示开发流程和操作方法。
-
开源项目和社区论坛可以帮助开发者学习实际项目的开发经验,解决开发中遇到的问题。
实践练习
-
阅读《领航者 ZYNQ 之嵌入式 Vitis 开发指南》的部分章节,了解更多的实验案例。
-
在 GitHub 上搜索一个 ZYNQ 开源项目,查看项目的代码结构和设计思路,尝试复现项目的功能。
-
在 Xilinx 官方论坛上注册账号,浏览相关的技术帖子,尝试回答一个简单的问题。
总结
本教程以正点原子领航者 ZYNQ 开发板为载体,以编程初学者为目标受众,系统、全面地介绍了 ZYNQ Vitis 开发的核心概念、实现原理及实际应用。教程从开发环境搭建开始,逐步深入到核心代码解析、功能实现、项目扩展等内容,每个章节都设置了知识要点和实践练习,帮助读者巩固所学内容。
通过本教程的学习,读者可以掌握 ZYNQ 的硬件设计和软件开发方法,学会开发简单的嵌入式项目,为进一步学习 ZYNQ 的高级开发(如 Linux 系统移植、硬件加速开发等)打下基础。希望读者能够通过实际的项目开发,不断提升自己的嵌入式开发能力,在嵌入式领域取得更大的进步。
更多推荐

所有评论(0)