零基础入门正点原子领航者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 开发对于编程初学者具有多方面的意义:

  1. 掌握异构开发思维:ZYNQ 的 PS+PL 架构让开发者同时接触到软件编程和硬件逻辑设计,培养异构开发的思维,拓宽技术视野。

  2. 提升嵌入式开发能力:通过 ZYNQ 开发,开发者可以掌握 ARM 裸机编程、Linux 系统移植、驱动开发、FPGA 逻辑设计等多方面的技能,提升综合嵌入式开发能力。

  3. 适应工业需求:ZYNQ 在工业控制、图像处理、人工智能、航空航天等领域有着广泛的应用,掌握 ZYNQ 开发技能可以更好地适应工业界的需求。

  4. 培养工程实践能力:通过实际项目的开发,开发者可以培养工程实践能力,学会解决实际开发中遇到的问题,提高问题解决能力。

1.3 应用场景

ZYNQ 系列芯片凭借其强大的处理能力和灵活的可编程逻辑,在多个领域有着广泛的应用:

  1. 工业控制:ZYNQ 可以实现高精度的运动控制、实时数据采集和处理,适用于工业机器人、数控机床、智能传感器等工业控制设备。

  2. 图像处理:ZYNQ 的 PL 部分可以实现图像的硬件加速处理,如边缘检测、图像滤波、图像压缩等,PS 部分可以实现图像的算法处理和人机交互,适用于智能摄像头、机器视觉系统、医疗图像处理设备等。

  3. 人工智能:ZYNQ 可以部署轻量化的 AI 模型,如 TensorFlow Lite、Pytorch Lite 等,实现边缘 AI 推理,适用于智能安防、智能交通、智能家居等领域。

  4. 航空航天:ZYNQ 的高可靠性和高性能使其适用于航空航天领域的嵌入式系统,如卫星通信设备、飞行控制系统等。

  5. 消费电子:ZYNQ 可以用于智能穿戴设备、智能家居设备、智能玩具等消费电子产品,实现复杂的功能和交互。

知识要点
  • ZYNQ 是异构可编程片上系统,集成了 ARM Cortex-A9 处理器和 FPGA 可编程逻辑。

  • Vitis 是 Xilinx 统一的软件平台,支持 ZYNQ 的多种开发模式。

  • 正点原子领航者 ZYNQ 开发板是入门 ZYNQ 开发的理想载体。

实践练习
  1. 调研 ZYNQ 在你感兴趣的领域的应用案例,整理成一份简单的报告。

  2. 查找正点原子领航者 ZYNQ 开发板的硬件资源清单,了解其提供的外设接口。


2. 开发环境搭建

2.1 所需软件、工具及版本要求

要进行正点原子领航者 ZYNQ 的 Vitis 开发,需要安装以下软件和工具:

  1. Vivado Design Suite:用于 ZYNQ 的硬件设计,包括 Block Design 创建、IP 核配置、比特流生成等。推荐使用 2020.2 或 2023.1 版本,与正点原子的开发资料版本保持一致。

  2. Vitis Unified Software Platform:用于 ZYNQ 的软件开发,包括裸机程序开发、Linux 应用开发等。版本需要与 Vivado 保持一致。

  3. 串口调试工具:如 Tera Term、PuTTY 等,用于查看开发板的串口输出。

  4. JTAG 下载器驱动:用于连接开发板和电脑,进行程序下载和调试。

  5. CH340 串口驱动:用于开发板的 USB 转串口功能,第一次连接开发板时需要安装。

2.2 Vivado 安装步骤

2.2.1 下载 Vivado 安装包
  1. 访问 AMD Xilinx 官网(https://www.amd.com/zh-cn/developer/resources/vitis.html),下载对应版本的 Vivado Design Suite 安装包。

  2. 选择 “Full Product Installation” 进行完整安装,安装包大小约 40-50GB,建议使用断点续传工具进行下载。

2.2.2 安装 Vivado
  1. 解压下载的安装包,进入解压后的目录,执行xsetup命令启动安装向导。

  2. 在安装向导中,选择 “Custom” 自定义安装,勾选以下必要组件:

    • Vivado Design Suite

    • Vitis Core Development Kit

    • Vitis Embedded Development(用于 ZYNQ 嵌入式开发)

    • 取消勾选不需要的组件,如 Model Composer、Documentation Navigator 等,以节省安装空间。

  3. 选择安装路径,建议选择非中文、无空格的路径,如D:\Xilinx\Vivado\2020.2

  4. 等待安装完成,安装时间约 1-2 小时,具体时间取决于电脑配置。

2.2.3 配置环境变量

安装完成后,需要配置环境变量,以便在命令行中调用 Vivado 和 Vitis 工具:

  1. 右键 “此电脑”,选择 “属性”->“高级系统设置”->“环境变量”。

  2. 在 “系统变量” 中,点击 “新建”,添加以下环境变量:

    • 变量名:XILINX_VIVADO,变量值:D:\Xilinx\Vivado\2020.2(根据实际安装路径修改)

    • 变量名:XILINX_VITIS,变量值:D:\Xilinx\Vitis\2020.2(根据实际安装路径修改)

  3. 在 “系统变量” 中找到PATH变量,点击 “编辑”,添加以下路径:

    • %XILINX_VIVADO%\bin

    • %XILINX_VITIS%\bin

  4. 点击 “确定” 保存环境变量配置,重启电脑使配置生效。

2.3 Vitis 配置步骤

2.3.1 启动 Vitis
  1. 双击桌面的 Vitis 图标,或者在命令行中输入vitis命令启动 Vitis IDE。

  2. 选择工作空间路径,建议选择非中文、无空格的路径,如D:\ZYNQ_Workspace

2.3.2 配置 License

Vitis 需要 License 才能使用完整功能,对于初学者可以申请免费的 WebPack License:

  1. 在 Vitis IDE 中,选择 “Help”->“Licensing”。

  2. 点击 “Get Free WebPack License”,登录 Xilinx 账户(需要提前注册)。

  3. 按照向导完成 License 的申请和导入,License 会自动生效。

2.4 串口驱动和 JTAG 驱动安装

2.4.1 CH340 串口驱动安装
  1. 打开正点原子开发板资料中的 “6_软件资料 / 1_软件 / CH340 驱动 (USB 串口驱动)” 文件夹。

  2. 双击 “SETUP.EXE” 文件,按照向导完成驱动安装。

  3. 安装完成后,将开发板的 USB_UART 接口通过 Mini USB 线连接到电脑,在设备管理器中查看是否识别到 USB-SERIAL CH340 设备,若识别到则驱动安装成功。

2.4.2 JTAG 下载器驱动安装
  1. 打开 Vivado 安装目录下的data\xicom\cable_drivers\win64文件夹。

  2. 双击install_drivers.bat文件,按照向导完成 JTAG 下载器驱动的安装。

  3. 将 JTAG 下载器连接到开发板的 JTAG 接口,并连接到电脑,在设备管理器中查看是否识别到 Xilinx Cable 设备,若识别到则驱动安装成功。

知识要点
  • Vivado 和 Vitis 的版本需要保持一致,避免出现兼容性问题。

  • 安装路径和工作空间路径不能包含中文和空格,否则会导致工具运行异常。

  • 环境变量的配置是为了在命令行中方便调用 Vivado 和 Vitis 工具。

实践练习
  1. 按照步骤安装 Vivado 和 Vitis,配置环境变量,验证环境变量是否配置成功(在命令行中输入vivado -versionvitis -version,查看是否输出版本信息)。

  2. 安装 CH340 串口驱动和 JTAG 驱动,连接开发板,在设备管理器中查看驱动是否识别成功。


3. 项目结构解析

3.1 ZYNQ 开发项目的文件组织方式

一个完整的 ZYNQ Vitis 开发项目通常包含以下几个部分:

  1. Vivado 硬件工程:用于创建 ZYNQ 的硬件设计,包括 Block Design、IP 核配置、约束文件、比特流文件等。

  2. Vitis 软件工程:用于开发 ZYNQ 的软件程序,包括应用工程、板级支持包(BSP)、源文件、头文件等。

  3. 启动镜像文件:包括 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 核:

  1. ZYNQ7 Processing System:ZYNQ 的处理系统 IP 核,包含 ARM Cortex-A9 处理器、内存控制器、外设接口等,是 ZYNQ 的核心模块。

  2. AXI GPIO:通用输入输出 IP 核,用于控制开发板的 LED、按键等外设。

  3. AXI UARTLite:串口通信 IP 核,用于实现开发板与电脑的串口通信。

  4. AXI Timer:定时器 IP 核,用于实现定时功能和中断功能。

  5. AXI DMA:直接内存访问 IP 核,用于实现 PS 与 PL 之间的高速数据传输。

3.2.2 板级支持包(BSP)模块

板级支持包(BSP)是 Vitis 中为特定硬件平台提供的软件支持包,包含了硬件驱动、库文件、初始化代码等,为应用程序提供了访问硬件的接口。BSP 的核心功能包括:

  1. PS 初始化:包含ps7_init.cps7_init.h文件,用于初始化 ZYNQ 的 PS 部分,包括时钟、DDR、外设等。

  2. 外设驱动:包含各种外设的驱动文件,如 GPIO 驱动、UART 驱动、定时器驱动等,为应用程序提供了访问外设的 API。

  3. 库文件:包含 Xilinx 提供的库文件,如libxil.a,提供了常用的函数和工具。

  4. 配置文件:包含平台配置文件,如platform_config.h,定义了硬件平台的相关参数。

3.2.3 应用程序模块

应用程序模块是开发者编写的实现具体功能的代码,通常包含以下部分:

  1. 主程序文件main.c,是应用程序的入口,包含了程序的主要逻辑。

  2. 头文件:包含各种外设的头文件和自定义的头文件,用于声明函数和宏定义。

  3. 编译输出文件:编译生成的.elf文件,是可执行文件,可以下载到开发板上运行。

知识要点
  • Vivado 硬件工程负责 ZYNQ 的硬件设计,Vitis 软件工程负责软件开发,两者通过硬件定义文件(.hdf 或.xsa)进行关联。

  • Block Design 是图形化搭建 ZYNQ 硬件系统的工具,通过 IP 核的拖拽和连接完成硬件设计。

  • BSP 为应用程序提供了访问硬件的接口,包含了硬件驱动和库文件。

实践练习
  1. 创建一个简单的 Vivado 硬件工程,创建 Block Design,添加 ZYNQ7 Processing System IP 核,配置基本参数,导出硬件定义文件到 Vitis。

  2. 在 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 主函数解析
  1. 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 位。

  1. print("Hello World from ZYNQ!\n");:打印函数,用于将字符串通过 UART 串口发送出去。print函数是xil_printf的宏定义,在xil_printf.h中定义:

#define print xil_printf

xil_printf函数的实现是基于 UART 串口的,将字符串逐字符发送到 UART 串口。

  1. 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 处理器的缓存管理由操作系统或启动代码负责。

  1. 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 主函数解析
  1. XGpioPs Gpio;:定义 GPIO 驱动实例,用于操作 GPIO 外设。

  2. ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);:查找 GPIO 设备的配置信息,根据设备 ID 获取配置结构体。

  3. Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);:初始化 GPIO 驱动,根据配置信息初始化 GPIO 实例。

  4. XGpioPs_SetDirectionPin(&Gpio, LED_PIN, 1);:设置 LED 引脚为输出模式,第二个参数为 1 表示输出,0 表示输入。

  5. XGpioPs_SetOutputEnablePin(&Gpio, LED_PIN, 1);:使能 LED 引脚的输出,第二个参数为 1 表示使能输出,0 表示关闭输出。

  6. 循环控制 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函数是按键中断的服务程序,当按键按下时触发中断,执行该函数:

  1. static u32 LedState = 0;:定义静态变量LedState,用于保存 LED 的状态。

  2. LedState = !LedState;:翻转 LED 状态,0 变为 1,1 变为 0。

  3. XGpioPs_WritePin(&Gpio, LED_PIN, LedState);:根据LedState的值设置 LED 引脚的电平,控制 LED 的亮灭。

  4. XGpioPs_IntrClearPin(&Gpio, KEY_PIN);:清除按键引脚的中断状态,否则中断会一直触发。

4.3.4 主函数解析
  1. 初始化 GPIO 驱动和中断控制器:与 GPIO 控制 LED 实验类似,初始化 GPIO 驱动和中断控制器。

  2. 设置按键引脚为输入模式:XGpioPs_SetDirectionPin(&Gpio, KEY_PIN, 0);,0 表示输入模式。

  3. 使能按键引脚的中断:XGpioPs_IntrEnablePin(&Gpio, KEY_PIN);

  4. 设置中断类型为下降沿触发:IntrType.Type = XGPIOPS_IRQ_TYPE_EDGE_FALLING;,表示当按键引脚的电平从高电平变为低电平时触发中断。

  5. 设置中断回调函数:XGpioPs_SetCallbackHandler(&Gpio, &Gpio, KeyIntrHandler);,当中断触发时,调用KeyIntrHandler函数。

  6. 连接中断:XScuGic_Connect(&Intc, XPAR_FABRIC_GPIOPS_0_INTR, (Xil_ExceptionHandler)XGpioPs_IntrHandler, &Gpio);,将 GPIO 中断连接到中断控制器。

  7. 使能中断:XScuGic_Enable(&Intc, XPAR_FABRIC_GPIOPS_0_INTR);使能中断控制器中的 GPIO 中断,Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);使能处理器的中断。

  8. 无限循环:while (1) { sleep(1); },程序进入无限循环,等待中断触发。

知识要点
  • xil_printf是 Xilinx 定义的打印函数,比标准的printf更适合嵌入式系统。

  • PS 端 GPIO 的控制需要使用xgpiops.h中的函数,包括初始化 GPIO、设置引脚方向、读写引脚电平、设置中断等。

  • 中断控制器的初始化和配置需要使用xscugic.h中的函数,包括初始化中断控制器、连接中断、使能中断等。

实践练习
  1. 编写 Hello World 实验代码,下载到开发板上运行,查看串口输出。

  2. 编写 GPIO 控制 LED 实验代码,实现 LED 的闪烁效果,下载到开发板上验证。

  3. 编写 GPIO 中断实验代码,通过按键控制 LED 的亮灭,下载到开发板上验证。


5. 功能实现步骤

5.1 Hello World 实验实现步骤

5.1.1 硬件设计步骤
  1. 打开 Vivado,创建新工程,工程名为hello_world,选择 RTL Project,不添加源文件和约束文件。

  2. 选择器件型号,根据开发板的 ZYNQ 芯片型号选择,XC7Z020 选择xc7z020clg400-2,XC7Z010 选择xc7z010clg400-1

  3. 创建 Block Design,命名为system,点击 “+” 号添加ZYNQ7 Processing System IP 核。

  4. 双击ZYNQ7 Processing System IP 核,进入配置界面:

    • 配置 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_NM AXI GP0 interface

  5. 点击 “OK” 完成配置,点击 “Run Block Automation” 自动连接 IP 核。

  6. 保存 Block Design,点击 “Validate Design” 验证设计是否正确。

  7. 生成 Output Products:右键system.bd,选择 “Generate Output Products”,选择 Global Synthesis Options,点击 “Generate”。

  8. 创建 HDL Wrapper:右键system.bd,选择 “Create HDL Wrapper”,选择 “Let Vivado manage wrapper and auto-update”,点击 “OK”。

  9. 导出硬件:选择 “File”->“Export”->“Export Hardware”,不勾选 “Include bitstream”,点击 “OK”,导出到hello_world.sdk目录。

  10. 启动 Vitis:选择 “File”->“Launch Vitis”,设置工作空间为hello_world_vitis,点击 “OK”。

5.1.2 软件开发步骤
  1. 在 Vitis 中,选择 “File”->“New”->“Application Project”,点击 “Next”。

  2. 选择平台工程,选择刚才导出的硬件平台,点击 “Next”。

  3. 输入应用工程名为hello_world_app,点击 “Next”。

  4. 选择工程模板为 “Hello World”,点击 “Finish”。

  5. 打开src/main.c文件,查看代码,修改打印信息为"Hello World from ZYNQ!\n"

  6. 编译工程:右键hello_world_app工程,选择 “Build Project”,编译生成hello_world_app.elf文件。

  7. 下载程序:连接开发板的 JTAG 接口和 USB_UART 接口,右键hello_world_app工程,选择 “Run As”->“1 Launch on Hardware (System Debugger)”。

  8. 查看串口输出:打开串口调试工具,设置波特率为 115200,数据位为 8,停止位为 1,无校验位,查看串口输出的 “Hello World from ZYNQ!” 信息。

5.2 GPIO 控制 LED 实验实现步骤

5.2.1 硬件设计步骤
  1. 打开 Vivado,创建新工程,工程名为gpio_led,选择 RTL Project,不添加源文件和约束文件。

  2. 选择器件型号,根据开发板的 ZYNQ 芯片型号选择。

  3. 创建 Block Design,命名为system,添加ZYNQ7 Processing System IP 核,配置方法与 Hello World 实验相同。

  4. 导出硬件,启动 Vitis。

5.2.2 软件开发步骤
  1. 在 Vitis 中,创建应用工程,工程名为gpio_led_app,选择 “Empty Application” 模板。

  2. src目录下创建main.c文件,编写 GPIO 控制 LED 的代码,如 4.2 节所示。

  3. 编译工程,下载程序到开发板。

  4. 查看 LED 的闪烁效果,验证实验是否成功。

5.3 GPIO 中断实验实现步骤

5.3.1 硬件设计步骤
  1. 打开 Vivado,创建新工程,工程名为gpio_intr,选择 RTL Project,不添加源文件和约束文件。

  2. 选择器件型号,根据开发板的 ZYNQ 芯片型号选择。

  3. 创建 Block Design,命名为system,添加ZYNQ7 Processing System IP 核,配置方法与 Hello World 实验相同。

  4. 导出硬件,启动 Vitis。

5.3.2 软件开发步骤
  1. 在 Vitis 中,创建应用工程,工程名为gpio_intr_app,选择 “Empty Application” 模板。

  2. src目录下创建main.c文件,编写 GPIO 中断实验的代码,如 4.3 节所示。

  3. 编译工程,下载程序到开发板。

  4. 按下按键,查看 LED 的状态是否翻转,验证实验是否成功。

知识要点
  • ZYNQ 开发分为硬件设计和软件开发两个部分,硬件设计在 Vivado 中完成,软件开发在 Vitis 中完成。

  • Block Design 的配置需要根据开发板的硬件原理图进行,确保引脚配置正确。

  • 应用工程的编译和下载需要连接开发板的 JTAG 接口和串口接口,通过 JTAG 下载程序,通过串口查看输出信息。

实践练习
  1. 按照步骤完成 Hello World 实验,验证开发环境是否正常。

  2. 按照步骤完成 GPIO 控制 LED 实验,实现 LED 的闪烁效果。

  3. 按照步骤完成 GPIO 中断实验,通过按键控制 LED 的亮灭。


6. 常见问题与解决方案

6.1 硬件设计常见问题

6.1.1 Block Design 中 IP 连接出错

问题现象:综合时报错 “Unconnected port”,提示 IP 核的端口未连接。
原因分析:IP 核之间的连接不完整,或者时钟、复位信号未连接。
解决方案

  1. 检查所有 IP 核的必需端口是否连接,如时钟、复位、AXI 总线等。

  2. 使用 “Validate Design” 工具检查设计,查看报错信息,根据提示修复连接问题。

  3. 对于 ZYNQ7 Processing System IP 核,确保Run Block Automation操作完成,自动连接必要的端口。

6.1.2 地址映射冲突

问题现象:访问 PL 寄存器时数据异常,或者编译时报地址映射冲突。
原因分析:多个 IP 核的地址范围重叠,导致地址映射冲突。
解决方案

  1. 在 Address Editor 中查看各个 IP 核的地址范围,确保每个 IP 核有唯一的地址空间。

  2. 修改冲突的 IP 核的地址范围,避免重叠。

  3. 查看xparameters.h文件中的地址定义,确保与 Address Editor 中的地址一致。

6.1.3 时钟频率配置错误

问题现象:系统运行不稳定,或者出现超时错误。
原因分析:时钟配置错误,导致 IP 核的时钟频率不符合要求。
解决方案

  1. 检查 Clock Configuration 页面中的时钟配置,确保输入时钟频率与开发板的实际时钟频率一致。

  2. 检查各个 IP 核的时钟频率设置,确保 IP 核的时钟频率在其支持的范围内。

  3. 使用 Vivado 的时序分析工具,查看时序报告,修复时序违例问题。

6.2 软件开发常见问题

6.2.1 串口无输出

问题现象:下载程序后,串口调试工具没有输出信息。
原因分析:串口配置错误,或者硬件连接问题。
解决方案

  1. 检查串口调试工具的配置,确保波特率、数据位、停止位、校验位与硬件设计中的配置一致(默认波特率为 115200)。

  2. 检查开发板的 USB_UART 接口连接是否正确,驱动是否安装成功。

  3. 查看platform.c文件中的串口初始化代码,确保串口初始化正确。

  4. 在程序中添加xil_printf打印语句,验证程序是否运行到打印部分。

6.2.2 程序无法启动

问题现象:下载程序后,程序无法运行,或者卡在启动阶段。
原因分析:硬件定义文件错误,或者 BSP 配置错误。
解决方案

  1. 检查硬件定义文件(.hdf 或.xsa)是否正确导出,是否包含正确的硬件信息。

  2. 重新生成 BSP,确保 BSP 的配置与硬件设计一致。

  3. 检查ps7_init.c文件中的初始化代码,确保 PS 初始化正确。

  4. 使用调试工具,设置断点,查看程序的执行流程,定位问题所在。

6.2.3 GPIO 操作无响应

问题现象:GPIO 控制 LED 实验中,LED 没有按照预期亮灭。
原因分析:GPIO 配置错误,或者引脚定义错误。
解决方案

  1. 检查引脚定义是否正确,确保 LED 引脚和按键引脚与开发板的原理图一致。

  2. 检查 GPIO 的方向设置和输出使能设置是否正确,输出引脚需要设置为输出模式并使能输出。

  3. 查看xparameters.h文件中的 GPIO 设备 ID 是否正确。

6.3 下载调试常见问题

6.3.1 JTAG 连接失败

问题现象:无法连接到开发板,下载程序时报错 “Failed to connect to the target”。
原因分析:JTAG 驱动未安装成功,或者硬件连接问题。
解决方案

  1. 检查 JTAG 下载器的驱动是否安装成功,在设备管理器中查看是否识别到 Xilinx Cable 设备。

  2. 检查 JTAG 下载器与开发板的连接是否正确,确保 JTAG 接口的引脚连接正确。

  3. 重启 Vivado 和 Vitis,重新连接开发板。

  4. 尝试更换 JTAG 下载器或 USB 线,排除硬件故障。

6.3.2 程序下载失败

问题现象:下载程序时报错 “FLASH OPERATION FAILED”,或者程序无法烧写到 Flash 中。
原因分析:Flash 配置错误,或者硬件连接问题。
解决方案

  1. 检查 Flash 的配置是否正确,确保 Flash 型号与开发板的 Flash 型号一致。

  2. 检查 Flash 的引脚约束是否正确,确保 Flash 的引脚与原理图一致。

  3. 尝试使用 XSCT 命令行工具下载程序,查看详细的报错信息。

  4. 检查开发板的电源是否稳定,确保供电正常。

知识要点
  • 硬件设计中的问题通常与 IP 核连接、地址映射、时钟配置有关,需要仔细检查 Block Design 的配置。

  • 软件开发中的问题通常与串口配置、BSP 配置、代码逻辑有关,需要逐步排查。

  • 下载调试中的问题通常与 JTAG 连接、Flash 配置、电源供电有关,需要检查硬件连接和驱动安装。

实践练习
  1. 模拟一个地址映射冲突的问题,在 Block Design 中添加两个 IP 核,设置相同的地址范围,查看报错信息,然后修复问题。

  2. 模拟一个串口无输出的问题,修改串口的波特率配置,查看现象,然后修复问题。


7. 项目扩展与优化建议

7.1 初学者项目扩展思路

对于初学者来说,可以从以下几个方面对基础实验进行扩展,提升开发能力:

  1. 多 LED 控制:在 GPIO 控制 LED 实验的基础上,扩展控制多个 LED,实现流水灯效果,或者根据按键状态控制不同的 LED 组合。

  2. 按键消抖:在 GPIO 中断实验的基础上,添加按键消抖功能,避免按键抖动导致的多次触发中断。可以使用软件消抖(延时判断)或硬件消抖(RC 滤波)的方法。

  3. 串口通信:在 Hello World 实验的基础上,扩展串口通信功能,实现电脑与开发板之间的双向通信,如发送指令控制 LED,或者接收开发板发送的数据。

  4. 定时器应用:使用 AXI Timer IP 核,实现定时功能,如定时采集数据、定时控制 LED 闪烁等。

  5. I2C 通信:使用 ZYNQ 的 I2C 外设,实现与 I2C 设备的通信,如读取 I2C 温湿度传感器的数据,通过串口打印出来。

  6. SPI 通信:使用 ZYNQ 的 SPI 外设,实现与 SPI 设备的通信,如读取 SPI Flash 的数据,或者控制 SPI 显示屏。

  7. 简单的人机交互:结合按键和 LED,实现简单的人机交互界面,如通过按键选择不同的功能,LED 显示当前功能状态。

7.2 项目优化建议

  1. 代码优化

    • 使用模块化编程,将不同的功能封装成函数,提高代码的可读性和可维护性。

    • 避免使用全局变量,尽量使用局部变量和函数参数传递数据。

    • 优化代码的执行效率,避免不必要的循环和计算。

  2. 硬件优化

    • 合理使用 IP 核,避免不必要的资源浪费,对于简单的功能可以使用逻辑电路实现,而不是使用 IP 核。

    • 优化时钟树设计,减少时钟延迟和抖动,提高系统的稳定性。

    • 合理规划地址映射,提高数据传输的效率。

  3. 性能优化

    • 使用 DMA 进行数据传输,提高 PS 与 PL 之间的数据传输速度,尤其是在处理大量数据时。

    • 合理使用缓存,提高数据访问的速度,对于频繁访问的数据可以缓存到 OCM(片上存储器)中。

    • 优化中断处理,减少中断服务程序的执行时间,提高系统的实时性。

7.3 进阶项目建议

当掌握了基础实验后,可以尝试以下进阶项目,提升综合开发能力:

  1. 简易示波器:使用 ZYNQ 的 ADC 外设采集模拟信号,通过 PL 进行信号处理,PS 端将采集到的数据通过串口或以太网发送到电脑,在电脑上显示波形。

  2. 智能温湿度监测系统:使用 I2C 温湿度传感器采集温湿度数据,通过串口或以太网发送到电脑,实现数据的实时监测和存储。

  3. 简单的图像处理系统:使用 OV5640 摄像头采集图像数据,通过 PL 进行图像预处理(如边缘检测、图像滤波),PS 端处理后通过 HDMI 显示出来。

  4. 工业控制模拟系统:结合多个外设,实现简单的工业控制模拟系统,如电机控制、传感器数据采集、报警功能等。

知识要点
  • 项目扩展可以从基础实验出发,逐步增加功能,提升开发能力。

  • 代码优化和硬件优化可以提高系统的性能和稳定性,培养良好的开发习惯。

  • 进阶项目可以综合运用所学的知识,提升综合开发能力。

实践练习
  1. 在 GPIO 控制 LED 实验的基础上,实现流水灯效果,使用定时器控制 LED 的切换速度。

  2. 在 GPIO 中断实验的基础上,添加按键消抖功能,使用软件消抖的方法,延时 10ms 判断按键状态。

  3. 实现一个简单的串口通信功能,电脑发送指令 “LED_ON” 点亮 LED,发送 “LED_OFF” 熄灭 LED。


8. 学习资源推荐

8.1 官方文档

  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 的使用方法和硬件设计流程。

  2. 正点原子官方资料

    • 《领航者 ZYNQ 之嵌入式 Vitis 开发指南》:正点原子官方的开发指南,详细介绍了领航者 ZYNQ 开发板的硬件资源和 Vitis 开发流程,包含多个实验案例。

    • 正点原子官方论坛(http://www.openedv.com/):提供了丰富的 ZYNQ 开发资料和技术交流平台。

8.2 视频教程

  1. B 站视频

    • 正点原子《手把手教你学 ZYNQ》:详细介绍了 ZYNQ 的架构、开发环境搭建、基础实验等内容,适合初学者入门。

    • 《ZYNQ 入门到实战》:详细介绍了 ZYNQ 的软硬件协同开发,包含多个实战项目。

  2. 达尔闻在线

    • 《ZYNQ 开发实战教程》:提供了系统的 ZYNQ 开发教程,从基础到进阶,适合不同层次的开发者。

8.3 书籍推荐

  1. 《Zynq-7000 SoC 嵌入式系统设计与实现》(何宾著):详细介绍了 Zynq-7000 SoC 的嵌入式系统设计方法,包括硬件设计、软件开发、Linux 系统移植等内容,适合有一定基础的开发者。

  2. 《FPGA 异构计算》(陆佳华著):介绍了 FPGA 异构计算的概念和方法,包含 ZYNQ 的软硬件协同开发内容,适合进阶学习。

  3. 《Xilinx Zynq-7000 All Programmable SoC 设计指南》:详细介绍了 Zynq-7000 SoC 的设计流程和方法,包含多个设计案例,适合工程实践。

8.4 开源项目

  1. GitHub 开源项目

    • Xilinx 官方开源项目:https://github.com/Xilinx,提供了多个 ZYNQ 开发的开源项目和示例代码。

    • ZYNQ 开源项目:搜索 “Zynq” 相关的开源项目,如 Zynq 图像处理项目、Zynq 工业控制项目等,学习开源项目的代码和设计思路。

  2. Gitee 开源项目

    • 搜索 “ZYNQ” 相关的开源项目,国内的开源项目更适合中文开发者学习。

8.5 社区和论坛

  1. Xilinx 官方论坛https://forums.xilinx.com/,提供了 ZYNQ 开发的技术交流平台,可以在论坛上提问和交流。

  2. 电子发烧友论坛https://www.elecfans.com/,提供了丰富的嵌入式开发资料和技术交流平台。

  3. 知乎:搜索 “ZYNQ” 相关的话题,了解开发者的经验和分享。

知识要点
  • 官方文档是最权威的学习资料,包含了详细的技术信息和开发指南。

  • 视频教程适合初学者入门,直观地展示开发流程和操作方法。

  • 开源项目和社区论坛可以帮助开发者学习实际项目的开发经验,解决开发中遇到的问题。

实践练习
  1. 阅读《领航者 ZYNQ 之嵌入式 Vitis 开发指南》的部分章节,了解更多的实验案例。

  2. 在 GitHub 上搜索一个 ZYNQ 开源项目,查看项目的代码结构和设计思路,尝试复现项目的功能。

  3. 在 Xilinx 官方论坛上注册账号,浏览相关的技术帖子,尝试回答一个简单的问题。


总结

本教程以正点原子领航者 ZYNQ 开发板为载体,以编程初学者为目标受众,系统、全面地介绍了 ZYNQ Vitis 开发的核心概念、实现原理及实际应用。教程从开发环境搭建开始,逐步深入到核心代码解析、功能实现、项目扩展等内容,每个章节都设置了知识要点和实践练习,帮助读者巩固所学内容。

通过本教程的学习,读者可以掌握 ZYNQ 的硬件设计和软件开发方法,学会开发简单的嵌入式项目,为进一步学习 ZYNQ 的高级开发(如 Linux 系统移植、硬件加速开发等)打下基础。希望读者能够通过实际的项目开发,不断提升自己的嵌入式开发能力,在嵌入式领域取得更大的进步。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐