4. 第一个裸机程序:Hello World工程创建、串口打印验证、编译与下载流程、启动模式配置

说实话,学Zynq最让人兴奋的一刻,就是看到串口终端里跳出那行"Hello World"。这标志着你的PS端已经跑起来了,硬件链路通了,开发环境也配对了。今天我们就一步步把这个过程走通。

4.1 工程创建:从零搭建一个裸机项目

我个人习惯用Vivado + Vitis这套组合。先打开Vivado,创建一个新的Block Design,把Zynq Processing System IP拖进来。嗯,这里有个关键点——一定要先配置好PS端的时钟和DDR,否则后面跑不起来。

我的习惯:在Vivado里做完Block Design后,先Validate Design,确认没有红色报错再Generate Output Products。这一步能省掉后面很多排查时间。

导出硬件描述文件(.xsa)后,打开Vitis。新建一个Application Project,选择刚才导出的.xsa文件,平台选"standalone"——这就是裸机环境。模板里有个"Hello World",直接选它就行。

我曾经遇到过一个问题:新建工程后编译报错,说找不到"xparameters.h"。后来发现是.xsa文件路径有中文,Vitis解析不了。所以工程路径千万别带中文,空格也最好别用。

4.2 串口打印验证:Hello World是怎么出来的

模板生成的代码其实很简单,核心就几行:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"

int main()
{
    init_platform();
    print("Hello World\n\r");
    cleanup_platform();
    return 0;
}

这里有个细节——print()函数用的是xil_printf.h里的实现,而不是标准C的printf。为什么?因为裸机环境下没有操作系统帮你管理标准输出,Xilinx自己封装了一个轻量级的打印函数,直接操作UART寄存器。

说白了,print("Hello World\n\r")这行代码,最终会通过Zynq PS端的UART控制器,把字符一个个发出去。串口那边收到后,在终端上显示出来。

避坑指南:我曾经在调试时发现串口只打印了"Hello"就卡住了。排查了半天,发现是换行符的问题——\n在Windows终端里需要配合\r才能正确换行。所以记得用\n\r,而不是单独\n。

4.3 编译与下载流程:把程序烧进芯片

编译很简单,在Vitis里右键工程选"Build Project"。第一次编译会慢一些,因为要链接库文件。编译成功后,你会看到.elf文件生成。

下载方式有两种:

方式 适用场景 特点
JTAG下载 调试阶段 直接通过调试器加载,掉电丢失
QSPI Flash烧写 量产/脱机运行 掉电保留,上电自动加载

调试阶段我建议用JTAG。在Vitis里点"Debug"按钮,它会自动把.elf文件通过JTAG链加载到DDR里,然后PC指针跳到main函数入口。这时候打开串口终端(波特率115200,8N1),就能看到输出了。

注意:JTAG下载时,Zynq的PS端必须已经完成初始化——包括PLL锁定、DDR校准。如果下载后程序没跑起来,先检查PS端的配置状态寄存器。

4.4 启动模式配置:芯片上电后怎么知道该干啥

Zynq的启动模式由硬件引脚决定。说白了,芯片上电后,BootROM会去读这些引脚的电平,然后决定从哪个设备加载第一阶段启动代码(FSBL)。

常见的启动模式有这些:

模式 引脚配置(MIO[5:2]) 启动设备
JTAG 0000 调试器直接加载
QSPI 0010 板载Flash
SD卡 0110 SD0(通常是SD卡槽)
NAND 1000 NAND Flash

你想想看,如果板子上电后什么都没反应,很可能就是启动模式引脚没配置对。我遇到过一块板子,SD卡启动死活不行,最后发现是MIO[5:2]的焊接虚焊了,导致BootROM读到了错误的电平。

调试技巧:在Vivado里配置PS时,可以设置"Boot Mode"为"JTAG Only"。这样即使板子上的启动引脚没焊对,也能通过JTAG强制加载程序。适合前期调试。

下面这张图展示了从按下复位键到Hello World打印出来的完整流程:

Zynq裸机Hello World启动流程 上电复位 BootROM读取MIO[5:2] 确定启动设备 加载FSBL 初始化PS + DDR 加载用户程序(.elf) 到DDR指定地址 跳转到main() 执行print("Hello World") 串口输出Hello World 启动模式引脚 MIO[5:2] = 0010 → QSPI MIO[5:2] = 0110 → SD卡 MIO[5:2] = 0000 → JTAG 调试建议 前期用JTAG模式调试 确认无误后再切到Flash/SD

嗯,到这里,你的第一个裸机程序就跑通了。从工程创建到串口打印,再到启动模式配置,这一套流程是后面所有外设驱动开发的基础。你想想看,如果连Hello World都出不来,后面I2C、SPI、Ethernet就更别谈了。

总结一下关键点:
  • 工程路径不要有中文或空格
  • 串口打印用\n\r换行
  • JTAG调试时确认PS端初始化完成
  • 启动模式引脚配置要跟实际硬件一致