简介:本资源是面向FPGA嵌入式开发工程师与高校研究生的实战型工程包,聚焦Xilinx Zynq UltraScale+ MPSoC平台(XCZU2CG/XCZU2EG/XCZU4EV)在VITIS环境下实现PL端SYSMON模拟电压测量的核心技术方案。资源提供从硬件配置、驱动开发到数据读取的完整闭环实现,适用于电源监控、系统健康诊断及嵌入式测控类项目开发。压缩包共1825个文件,含390个C源码、714个头文件(.h)、56个Makefile构建脚本、81个依赖描述(.d)及27个说明文档(.txt),并包含libxil.a、libc.a等关键静态库与底层驱动支持文件,整体大小为31.59MB。已有117人下载学习,内容结构清晰,覆盖VITIS工程创建、SYSMON通道配置、ARM处理器侧C驱动编写、编译部署及硬件验证全流程,可直接复用于同类MPSoC电压监测场景。
1. 项目概述:在XCZU4EV上打通PL SYSMON的模拟采样链路
最近在调试一块基于Xilinx Zynq UltraScale+ MPSoC XCZU4EV芯片的板卡,遇到了一个挺实际的需求:需要通过FPGA可编程逻辑(PL)部分的SYSMON模块,去测量几路外部输入的模拟电压。这个需求在工业控制、电源监控或者传感器接口的场合非常常见。虽然Xilinx官方文档对SYSMON(系统监视器)有详尽的描述,但真要把从Vivado工程创建、IP核配置、到Vitis中编写应用代码、最后正确读取电压值的全链路跑通,里面还是有不少细节和“坑”需要特别注意。这个项目就是记录下我用Vitis 2022.1环境,在XCZU4EV-EVB开发板上实现PL SYSMON电压采集的完整过程与核心要点。
简单来说,SYSMON是Xilinx FPGA和MPSoC芯片内部集成的硬核模块,相当于一个高精度、多通道的ADC(模数转换器)。在Zynq UltraScale+器件中,它不仅能监控芯片内部的温度、电压,更能通过专用的模拟输入引脚(如VP/VN)测量外部电压。我们的目标就是配置并使用PL侧的SYSMON,周期性地读取连接到这些引脚的外部模拟电压值,并将其换算成我们熟悉的电压单位(如伏特)。整个过程涉及硬件描述(Vivado)、嵌入式软件开发(Vitis)以及两者之间的数据交互,非常适合用来理解MPSoC软硬件协同开发的基本流程。
2. 核心硬件架构与SYSMON IP核配置解析
2.1 XCZU4EV的SYSMON资源与PL/PS分工
XCZU4EV作为Zynq UltraScale+ MPSoC家族的一员,其SYSMON模块在架构上有些特殊之处,理解清楚是后续配置不犯错的前提。这颗芯片内部实际上包含两个主要的SYSMON实例:一个位于处理系统(PS)内部,主要用于监控PS核心电压、温度等;另一个则位于可编程逻辑(PL)部分,也就是我们本项目要使用的。
PL SYSMON是一个独立的硬核,它拥有自己专用的模拟输入通道。在XCZU4EV上,PL SYSMON通常通过芯片的VP_0/VN_0等差分引脚对来接入外部模拟信号。这里有一个关键点:PL SYSMON的访问和控制,必须通过PL侧的AXI接口来完成。这意味着,即使我们最终是在PS上运行C语言应用程序来读取数据,也需要在Vivado中为PL SYSMON配置一个AXI接口(通常是AXI4-Lite),并将其挂载到PS的AXI互联矩阵上,从而让PS端的处理器能够通过内存映射IO的方式对其进行读写操作。这种设计使得SYSMON的采样触发、通道选择、数据读取等操作,完全由软件灵活控制。
2.2 Vivado中SYSMON IP核的详细配置步骤
在Vivado中创建Block Design时,添加并配置SYSMON IP核是整个硬件设计的核心。我以Vitis 2022.1配套的Vivado 2022.1为例,操作步骤如下:
- 创建工程与Block Design:新建一个RTL工程,选择对应的XCZU4EV器件型号。在Flow Navigator中点击“Create Block Design”。
- 添加并配置Zynq UltraScale+ MPSoC IP:这是PS的配置核。双击添加后,在配置界面中,根据板卡原理图,使能所需的PS外设接口(如UART用于打印、GPIO等)。最重要的是,在“PS-PL Configuration” -> “PS-PL Interfaces” -> “Master Interface”下,确保至少使能了一个
M_AXI_HPM0_FPD或类似的AXI主接口,用于PS访问PL外设。 - 添加并配置SYSMON IP核:在Diagram空白处右键,选择“Add IP”,搜索并添加“SYSMON”。双击该IP核进行配置。
- Basic标签页:
Enable VUSER0/1/2/3通常保持默认(未使能),除非你有特殊的多路复用需求。ADC Conversion Rate(ADC转换速率)需要根据你的信号频率和精度要求来设置。更高的速率意味着更快的采样,但可能牺牲一些精度。对于慢变化的直流或低频信号,选择较低的速率(如10 SPS)可以获得更好的噪声性能。 - Channel Setup标签页:这是配置的重中之重。你需要在此处使能(
Enable)你计划使用的模拟输入通道。例如,如果你使用差分输入对VP_0和VN_0,就找到对应的VAUXP[0]和VAUXN[0]并勾选。务必注意:对于单端输入,你需要将负端(N)连接到指定的固定电压(如内部参考地)。同时,为每个使能的通道设置合适的Averaging(平均)次数。平均可以有效抑制随机噪声,提高测量稳定性,但会增加采样时间。对于一般应用,选择16次或64次平均是个不错的起点。 - Interface Options标签页:确认
Enable DRP是勾选的,这是通过AXI接口访问SYSMON寄存器所必需的。Enable JTAG也可以勾选,方便后期通过Vivado Hardware Manager进行在线调试。
- Basic标签页:
- 连接与地址分配:使用“Run Connection Automation”功能,Vivado会自动将SYSMON的
s_axi接口连接到Zynq MPSoC IP的AXI主接口上,并分配地址空间。务必检查连接是否正确建立。 - 生成顶层HDL与输出产品:在Block Design上右键,依次选择“Generate Output Products”和“Create HDL Wrapper”。最后,在“Generate Bitstream”之前,建议先执行“Validate Design”检查有无错误。
注意:SYSMON的模拟输入引脚电压范围是有限制的,通常是0V到1V(相对于内部VREF)。如果你的外部信号电压超出此范围,必须使用外部电阻分压网络或运算放大器进行调理,否则会损坏芯片。这是硬件设计上首要的安全考量。
2.3 引脚约束与硬件平台导出
配置好IP核并生成Bitstream后,还需要正确约束SYSMON的模拟输入引脚。这需要参考你的板卡原理图。在Vivado中打开“I/O Planning”或直接编辑XDC约束文件,添加类似如下的约束:
# 示例:将芯片的AM2引脚(对应VP_0)和AM3引脚(对应VN_0)约束为SYSMON的差分输入对 set_property PACKAGE_PIN AM2 [get_ports VP_0] set_property PACKAGE_PIN AM3 [get_ports VN_0] set_property IOSTANDARD LVCMOS18 [get_ports {VP_0 VN_0}] # 对于SYSMON模拟引脚,通常还需要设置特定的IOSTANDARD,如ANALOG,请以器件手册为准完成约束后,重新生成Bitstream。最后,在Vivado的“File” -> “Export” -> “Export Hardware”中,选择“Include bitstream”,导出.xsa文件。这个文件包含了硬件比特流和系统描述信息,是后续在Vitis中创建平台工程和应用工程的基础。
3. Vitis嵌入式应用开发与驱动API详解
3.1 创建Vitis平台工程与应用工程
Vitis环境负责嵌入式软件部分的开发。启动Vitis 2022.1,工作空间建议选择一个干净的目录。
- 创建平台工程:
File->New->Platform Project。输入工程名(如zcu4ev_sysmon_platform),点击Next。在“Hardware Specification”页面,选择“Create from hardware specification (XSA)”,然后浏览并导入上一步从Vivado导出的.xsa文件。Vitis会自动解析硬件信息。在“Software Specification”页面,至少需要勾选“standalone”作为操作系统(因为我们运行的是裸机程序),以及“xilffs”等你可能需要的库。点击Finish完成创建。之后,右键点击平台工程,选择“Build Project”来生成平台。 - 创建应用工程:
File->New->Application Project。输入应用工程名(如sysmon_polling)。在“Platform”页面,选择刚才创建并构建好的平台工程。点击Next,选择“Empty Application (C)”作为模板。点击Finish。
现在,你得到了一个空的应用工程。我们需要在其中编写主程序,并正确配置板级支持包(BSP)设置。
3.2 配置BSP与理解XSysMon库
在Vitis的“Explorer”视图中,找到你的应用工程,其下会有一个[app_name]_bsp的工程。右键点击它,选择“Board Support Package Settings”。
在出现的配置窗口中,找到“Overview” ->standalone->sysmon。这里有几个关键设置:
sysmon_polled_example:这是一个示例代码开关,可以先打开,它会生成一个示例文件供参考,但后期我们最好自己写。sysmon_wait_temp:监控温度的中断设置,本项目不关注可以不管。- 更重要的是,你需要确保
xsysmon驱动库被包含进来。通常,当你选择了包含SYSMON的硬件平台时,Vitis会自动将其添加到BSP中。
xsysmon是Xilinx提供的标准驱动库,它封装了所有操作SYSMON硬件的底层函数。我们的应用程序将主要调用这个库的API。理解几个核心API至关重要:
XSysMon_CfgInitialize(&SysMonInst, &ConfigPtr, ConfigPtr.BaseAddr):初始化SYSMON驱动实例。XSysMon_SetSeqAvgMode(&SysMonInst, XSM_AVG_16_SAMPLES):设置所有通道的平均模式。XSysMon_SetSeqChEnables(&SysMonInst, ChannelMask):使能特定的采样通道序列。XSysMon_SetSeqAcqTime(&SysMonInst, XSM_SEQ_CH0, 0x1F):设置通道的采集时间。XSysMon_GetAdcData(&SysMonInst, XSM_CH_AUX_MIN+ChannelNum, &AdcData):获取指定通道的原始ADC码值。XSysMon_RawToVoltage(AdcData):将原始ADC码值转换为电压值(伏特)。
3.3 编写数据采集应用程序
在应用工程的src文件夹下新建一个main.c文件。一个典型的轮询式采集程序结构如下:
#include <stdio.h> #include “xparameters.h” // 包含硬件地址定义 #include “xsysmon.h” #include “xil_printf.h” // 假设我们在Vivado中只使能了AUX通道0 #define SYSMON_DEVICE_ID XPAR_SYSMON_0_DEVICE_ID #define CHANNEL_NUM 0 // 对应VAUX[0] int main() { XSysMon SysMonInst; XSysMon_Config *ConfigPtr; u32 AdcData; float Voltage; // 1. 查找并初始化SYSMON驱动 ConfigPtr = XSysMon_LookupConfig(SYSMON_DEVICE_ID); if (ConfigPtr == NULL) { xil_printf(“SYSMON Config lookup failed\r\n”); return XST_FAILURE; } XSysMon_CfgInitialize(&SysMonInst, ConfigPtr, ConfigPtr->BaseAddr); // 2. 进行自校准(推荐) XSysMon_SetSequencerMode(&SysMonInst, XSM_SEQ_MODE_SINGCHAN); XSysMon_GetCalibCoefficients(&SysMonInst); // 等待校准完成 while (XSysMon_IsCalibDone(&SysMonInst) != XST_SUCCESS); // 3. 配置通道参数 // 设置平均模式为16次 XSysMon_SetSeqAvgMode(&SysMonInst, XSM_AVG_16_SAMPLES); // 设置通道采集时间(根据信号源阻抗调整) XSysMon_SetSeqAcqTime(&SysMonInst, XSM_SEQ_CH_AUX00, 0x10); // 使能我们关心的通道序列 XSysMon_SetSeqChEnables(&SysMonInst, XSM_SEQ_CH_AUX00_MASK); // 4. 启动序列发生器为连续模式 XSysMon_SetSequencerMode(&SysMonInst, XSM_SEQ_MODE_CONTINPASS); xil_printf(“SYSMON Polling Started…\r\n”); while (1) { // 5. 读取指定通道的ADC原始数据 if (XSysMon_GetSeqAdcData(&SysMonInst, XSM_CH_AUX00, &AdcData) == XST_SUCCESS) { // 6. 将原始数据转换为电压 Voltage = XSysMon_RawToVoltage(AdcData); xil_printf(“CH%d Raw: 0x%08X, Voltage: %.6f V\r\n”, CHANNEL_NUM, AdcData, Voltage); } else { xil_printf(“Read data failed.\r\n”); } // 简单延时,控制采样频率 for (int i=0; i<1000000; i++); // 注意:这不是精确延时,仅示例 } return 0; }这段代码完成了从初始化、校准、配置到循环读取和打印电压的全过程。关键点在于XSysMon_GetSeqAdcData函数,它从序列发生器结果寄存器中读取数据,这种方式在连续采样模式下效率较高。
4. 系统集成、调试与数据验证
4.1 编译、链接与硬件部署
代码编写完成后,在Vitis中右键点击应用工程,选择“Build Project”。Vitis会调用交叉编译工具链,将你的C代码、BSP库以及启动文件链接成可在PS上运行的ELF(可执行与可链接格式)文件。
接下来是硬件部署:
- 用JTAG/USB线缆连接开发板与电脑,并给开发板上电。
- 在Vitis中,打开“Window” -> “Show View” -> “Targets”。在Targets视图里,右键点击“Xilinx” -> “Add Target”。
- 选择“Hardware Server”和对应的JTAG电缆(如
Digilent USB Cable),连接后应该能看到检测到的XCZU4EV器件。 - 在“Targets”视图中,右键点击该器件,选择“Program Device”。在弹出的对话框中,首先需要“Program FPGA”,这里要选择之前Vivado生成的比特流文件(
.bit,通常包含在.xsa或平台工程中)。这一步将硬件设计配置到PL中。 - FPGA编程成功后,再右键点击器件下的“psu_cortexa53_0”(即A53应用处理器核心),选择“Run As” -> “Launch on Hardware (Single Application Debug)”。Vitis会自动将编译好的ELF文件下载到DDR内存中,并开始执行。
如果一切顺利,你将在Vitis自带的串口终端(Terminal)中看到程序打印出的电压信息。
4.2 数据校准与精度提升实践
直接从XSysMon_RawToVoltage()函数读出的电压值是一个基于内部参考的换算值。为了获得最高精度,特别是绝对精度,需要进行系统校准。SYSMON模块本身提供了校准系数寄存器。上述代码中的XSysMon_GetCalibCoefficients函数就是用来获取这些工厂预校准的增益和偏移系数,并在内部计算中应用它们。
然而,对于要求极高的应用,工厂校准可能仍不足以消除板级误差。这时需要进行两点校准:
- 零点校准:将模拟输入引脚短接到已知的0V参考地(通常是板卡上的模拟地AGND),读取此时的ADC输出码值,记为
OffsetCode。 - 满量程校准:输入一个精确的、接近满量程(如0.95V)的已知参考电压
V_ref,读取ADC输出码值,记为FullScaleCode。 - 计算实际电压:对于任何新的测量码值
MeasuredCode,使用以下公式计算实际电压V_actual:V_actual = V_ref * (MeasuredCode - OffsetCode) / (FullScaleCode - OffsetCode)
你可以在初始化阶段完成一次校准,并将OffsetCode和FullScaleCode存储起来,用于后续所有测量值的换算。
4.3 常见问题排查与调试技巧
在实际操作中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
Vitis编译报错,找不到xsysmon.h等头文件 | BSP中未正确包含SYSMON驱动库。 | 1. 检查应用工程对应的BSP设置,确认sysmon驱动已启用。2. 在 main.c中检查#include “xsysmon.h”路径是否正确,通常就是如上写法,编译器会在BSP目录中查找。 |
| 程序运行后,读取的ADC数据始终为0或固定值 | 1. 硬件连接错误,信号未接入。 2. SYSMON IP通道未使能或配置错误。 3. 序列发生器未启动或模式错误。 4. 读取函数地址或通道号错误。 | 1. 用万用表测量VP/VN引脚,确认外部电压已正确施加。2. 回顾Vivado中SYSMON IP的 Channel Setup,确认目标通道已勾选Enable。3. 在代码中,确保调用了 XSysMon_SetSequencerMode启动序列发生器(例如设为XSM_SEQ_MODE_CONTINPASS)。4. 核对 XSysMon_GetSeqAdcData函数调用中的通道宏(如XSM_CH_AUX00)是否与IP配置的通道号一致。 |
| 读取的电压值跳动很大(噪声大) | 1. 外部信号本身噪声大。 2. 模拟走线受到数字信号干扰。 3. 平均次数设置过低。 4. 采集时间不足,ADC采样电容未充分充电。 | 1. 检查信号源质量,必要时在输入端增加RC低通滤波。 2. 检查PCB布局,确保模拟走线远离高速数字线,并做好电源滤波。 3. 在Vivado IP配置或代码中,增加 Averaging次数(如64或256)。4. 增加 XSysMon_SetSeqAcqTime中设置的采集时间参数,特别是当信号源阻抗较高时。 |
| 转换得到的电压值与万用表测量值存在固定偏差 | 1. 未进行校准。 2. 外部分压电阻精度不够或温漂大。 3. SYSMON内部参考电压有微小偏差。 | 1. 实施上文所述的两点校准法。 2. 使用高精度、低温漂的电阻进行分压。 3. 尝试读取SYSMON的内部VREF测量值,看是否偏离典型值。 |
| 使用JTAG可以运行,但程序无法固化启动 | 1. 启动模式(Boot Mode)设置不正确。 2. 生成的BOOT.BIN文件不完整或格式错误。 | 1. 确认板卡启动模式跳线设置为从Flash(如QSPI)启动。 2. 在Vitis中,使用“Create Boot Image”工具,正确添加FSBL(First Stage Bootloader)、比特流文件和应用程序ELF,生成最终的BOOT.BIN,并烧写到启动Flash中。 |
调试心得:善用Vivado Hardware Manager中的“Debug Probes”功能。你可以在Vivado中为SYSMON的AXI接口信号或状态信号添加调试核(ILA),将其与比特流一起编译。下载比特流后,在Hardware Manager中触发采集,可以直观地看到PS是否发出了正确的读写时序、SYSMON是否返回了数据,这对于排查复杂的通信问题非常有效。
5. 进阶应用:中断模式与多通道扫描
5.1 从轮询到中断的效率提升
前面的示例使用的是轮询方式,即CPU不断主动去读取数据。这在低采样率或单任务系统中可行,但会浪费CPU资源。更高效的方式是使用中断。SYSMON在序列转换完成(EOS - End of Sequence)或某个通道数据超过报警阈值时,可以产生中断。
配置中断的主要步骤:
- 初始化中断控制器:在BSP中使能
GIC(通用中断控制器),并在代码中初始化XScuGic驱动。 - 连接中断处理函数:编写一个中断服务程序(ISR),在其中读取ADC数据并清除中断标志。使用
XScuGic_Connect将该ISR与SYSMON的中断ID(在xparameters.h中定义,如XPAR_FABRIC_SYSMON_0_VEC_ID)连接起来。 - 配置SYSMON中断:使用
XSysMon_SetIntrEnable函数使能特定的中断源(如XSM_IPIF_INTR_END_OF_SEQ_MASK)。 - 启动序列发生器:和轮询模式一样,启动连续或单次序列。
- 全局使能中断:在GIC和CPU层面使能中断。
这样,每次转换序列完成,硬件会自动触发中断,CPU再跳转到ISR中处理数据,大大提高了系统效率,使得CPU可以在采样间隔期间处理其他任务。
5.2 实现多通道自动扫描与数据缓存
在实际项目中,往往需要监测多路电压。SYSMON支持通过序列发生器(Sequencer)自动按顺序扫描多个已使能的通道。
配置多通道扫描的关键:
- 在IP核中使能多个通道:在Vivado的SYSMON IP配置中,勾选所有需要使用的VAUX通道。
- 在代码中设置通道掩码:使用
XSysMon_SetSeqChEnables函数,传入一个位掩码,其中每一位对应一个通道。例如,要使能通道0、1、2,可以设置ChannelMask = XSM_SEQ_CH_AUX00_MASK | XSM_SEQ_CH_AUX01_MASK | XSM_SEQ_CH_AUX02_MASK。 - 选择序列模式:设置为
XSM_SEQ_MODE_CONTINPASS(连续通道扫描模式)。 - 读取数据:在中断ISR中或轮询时,你需要知道当前是哪个通道的数据转换完成了。可以通过读取状态寄存器
XSysMon_GetStatus,或者更简单地在ISR中遍历所有使能通道并读取其数据寄存器。对于连续模式,每次EOS中断意味着一个完整的扫描循环结束,此时可以读取所有通道的数据。
为了不丢失数据,特别是高速采样时,建议在ISR中仅将ADC原始码值存入一个环形缓冲区(Ring Buffer),而将耗时的码值转换、滤波、打印等操作放在主循环或低优先级任务中。这需要设计一个简单的生产者-消费者模型,确保线程安全。
5.3 性能优化与资源考量
当通道数多、采样率高时,需要注意以下几点:
- 吞吐量瓶颈:SYSMON的转换速率、AXI总线的带宽、CPU处理数据的速度都可能成为瓶颈。需要计算理论最大采样率(转换时间+数据读取时间),确保满足应用需求。
- 内存使用:为环形缓冲区分配足够的内存,并考虑使用DMA(如果SYSMON IP和系统支持)来将数据直接搬移到内存,进一步减轻CPU负担。
- 电源噪声影响:高速采样对电源质量更敏感。确保为芯片的模拟电源引脚(如
VCCADC)提供了干净、稳定的供电,并遵循数据手册的推荐布局进行去耦。
通过这个项目,你不仅能掌握在XCZU4EV MPSoC上使用PL SYSMON进行电压采集的基本技能,更能深入理解软硬件协同设计中IP核配置、驱动调用、数据流处理以及性能调试的全套方法。这些经验对于开发其他基于AXI总线的PL外设,具有直接的参考价值。
本文还有配套的精品资源,点击获取