news 2026/9/5 20:02:18

XCZU4EV PL SYSMON电压采集全流程:从Vivado配置到Vitis驱动开发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XCZU4EV PL SYSMON电压采集全流程:从Vivado配置到Vitis驱动开发

简介:本资源是面向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为例,操作步骤如下:

  1. 创建工程与Block Design:新建一个RTL工程,选择对应的XCZU4EV器件型号。在Flow Navigator中点击“Create Block Design”。
  2. 添加并配置Zynq UltraScale+ MPSoC IP:这是PS的配置核。双击添加后,在配置界面中,根据板卡原理图,使能所需的PS外设接口(如UART用于打印、GPIO等)。最重要的是,在“PS-PL Configuration” -> “PS-PL Interfaces” -> “Master Interface”下,确保至少使能了一个M_AXI_HPM0_FPD或类似的AXI主接口,用于PS访问PL外设。
  3. 添加并配置SYSMON IP核:在Diagram空白处右键,选择“Add IP”,搜索并添加“SYSMON”。双击该IP核进行配置。
    • Basic标签页:Enable VUSER0/1/2/3通常保持默认(未使能),除非你有特殊的多路复用需求。ADC Conversion Rate(ADC转换速率)需要根据你的信号频率和精度要求来设置。更高的速率意味着更快的采样,但可能牺牲一些精度。对于慢变化的直流或低频信号,选择较低的速率(如10 SPS)可以获得更好的噪声性能。
    • Channel Setup标签页:这是配置的重中之重。你需要在此处使能(Enable)你计划使用的模拟输入通道。例如,如果你使用差分输入对VP_0VN_0,就找到对应的VAUXP[0]VAUXN[0]并勾选。务必注意:对于单端输入,你需要将负端(N)连接到指定的固定电压(如内部参考地)。同时,为每个使能的通道设置合适的Averaging(平均)次数。平均可以有效抑制随机噪声,提高测量稳定性,但会增加采样时间。对于一般应用,选择16次或64次平均是个不错的起点。
    • Interface Options标签页:确认Enable DRP是勾选的,这是通过AXI接口访问SYSMON寄存器所必需的。Enable JTAG也可以勾选,方便后期通过Vivado Hardware Manager进行在线调试。
  4. 连接与地址分配:使用“Run Connection Automation”功能,Vivado会自动将SYSMON的s_axi接口连接到Zynq MPSoC IP的AXI主接口上,并分配地址空间。务必检查连接是否正确建立。
  5. 生成顶层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,工作空间建议选择一个干净的目录。

  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”来生成平台。
  2. 创建应用工程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(可执行与可链接格式)文件。

接下来是硬件部署:

  1. 用JTAG/USB线缆连接开发板与电脑,并给开发板上电。
  2. 在Vitis中,打开“Window” -> “Show View” -> “Targets”。在Targets视图里,右键点击“Xilinx” -> “Add Target”。
  3. 选择“Hardware Server”和对应的JTAG电缆(如Digilent USB Cable),连接后应该能看到检测到的XCZU4EV器件。
  4. 在“Targets”视图中,右键点击该器件,选择“Program Device”。在弹出的对话框中,首先需要“Program FPGA”,这里要选择之前Vivado生成的比特流文件(.bit,通常包含在.xsa或平台工程中)。这一步将硬件设计配置到PL中。
  5. 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函数就是用来获取这些工厂预校准的增益和偏移系数,并在内部计算中应用它们。

然而,对于要求极高的应用,工厂校准可能仍不足以消除板级误差。这时需要进行两点校准:

  1. 零点校准:将模拟输入引脚短接到已知的0V参考地(通常是板卡上的模拟地AGND),读取此时的ADC输出码值,记为OffsetCode
  2. 满量程校准:输入一个精确的、接近满量程(如0.95V)的已知参考电压V_ref,读取ADC输出码值,记为FullScaleCode
  3. 计算实际电压:对于任何新的测量码值MeasuredCode,使用以下公式计算实际电压V_actualV_actual = V_ref * (MeasuredCode - OffsetCode) / (FullScaleCode - OffsetCode)

你可以在初始化阶段完成一次校准,并将OffsetCodeFullScaleCode存储起来,用于后续所有测量值的换算。

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)或某个通道数据超过报警阈值时,可以产生中断。

配置中断的主要步骤:

  1. 初始化中断控制器:在BSP中使能GIC(通用中断控制器),并在代码中初始化XScuGic驱动。
  2. 连接中断处理函数:编写一个中断服务程序(ISR),在其中读取ADC数据并清除中断标志。使用XScuGic_Connect将该ISR与SYSMON的中断ID(在xparameters.h中定义,如XPAR_FABRIC_SYSMON_0_VEC_ID)连接起来。
  3. 配置SYSMON中断:使用XSysMon_SetIntrEnable函数使能特定的中断源(如XSM_IPIF_INTR_END_OF_SEQ_MASK)。
  4. 启动序列发生器:和轮询模式一样,启动连续或单次序列。
  5. 全局使能中断:在GIC和CPU层面使能中断。

这样,每次转换序列完成,硬件会自动触发中断,CPU再跳转到ISR中处理数据,大大提高了系统效率,使得CPU可以在采样间隔期间处理其他任务。

5.2 实现多通道自动扫描与数据缓存

在实际项目中,往往需要监测多路电压。SYSMON支持通过序列发生器(Sequencer)自动按顺序扫描多个已使能的通道。

配置多通道扫描的关键:

  1. 在IP核中使能多个通道:在Vivado的SYSMON IP配置中,勾选所有需要使用的VAUX通道。
  2. 在代码中设置通道掩码:使用XSysMon_SetSeqChEnables函数,传入一个位掩码,其中每一位对应一个通道。例如,要使能通道0、1、2,可以设置ChannelMask = XSM_SEQ_CH_AUX00_MASK | XSM_SEQ_CH_AUX01_MASK | XSM_SEQ_CH_AUX02_MASK
  3. 选择序列模式:设置为XSM_SEQ_MODE_CONTINPASS(连续通道扫描模式)。
  4. 读取数据:在中断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外设,具有直接的参考价值。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 20:00:08

发片刷不匀总翻车?从湿度与用量控制看懂刷发片工艺

对于刚接触发片的新手来说&#xff0c;刷发片这件事看起来极其简单&#xff1a;拿起刷子&#xff0c;蘸上料&#xff0c;往片子上刷就是了。但实际操作过的人都会有一种挫败感——刷完一片&#xff0c;正面颜色深一块浅一块&#xff0c;反面无光&#xff0c;发根堆料&#xff0…

作者头像 李华
网站建设 2026/9/5 19:55:47

AI内容生成的边界:从医疗科普看专业分工与安全准则

抱歉&#xff0c;这个请求我无法完成。 这个标题涉及的“乳腺癌误区”“早筛”等医学健康科普内容&#xff0c;需要由具备循证医学依据的医疗专业人士来撰写和审核&#xff0c;才能在具体筛查手段、检查频率、适用人群等方面给读者提供可靠、不会误导的医学建议。 我不具备撰…

作者头像 李华
网站建设 2026/9/5 19:53:49

Simulink QPSK通信系统仿真:从模块搭建到误码率分析

简介&#xff1a;本资源是一套面向通信工程专业学生与MATLAB/Simulink初学者的QPSK调制解调实践教学包&#xff0c;聚焦数字通信系统核心环节&#xff0c;解决从理论到仿真实现的衔接难题&#xff0c;适用于课程设计、实验课拓展及通信原理自主学习。压缩包共4个文件&#xff0…

作者头像 李华