版本:Vivado2020.2(Vitis)
任务:通过 PS XADC 接口读取XADC测量的芯片温度、供电电压,并通过串口打印出来
目录
一、介绍
二、硬件设计
三、软件设计
四、效果
一、介绍
XADC(Xilinx Analog-to-Digital Converter)是 ZYNQ SoC 内部集成的 12 位精度、1 MSPS(每秒百万采样) 的模数转换模块,可直接测量芯片内/外部的模拟信号,无需外接 ADC 芯片, 是 Zynq SoC 中 PL 端已有的硬核模块,可以被 PS端直接访问和控制。在 7 系的 FPGA 中,XADC 提供了 JTAG 和 DRP(dynamic reconfiguration port)接口,用于访问其 内部的状态和控制寄存器。在 ZYNQ-7000 SoC 器件中,XADC 增加了一个 PS-XADC 接口,用于 PS 中的软件控制 XADC 模块。
XADC核心功能
功能 | 说明 |
---|---|
片上传感器监测 | 实时测量 ZYNQ 的 芯片温度、供电电压(如 VCCINT、VCCAUX) |
外部模拟信号采集 | 通过专用模拟输入引脚(VP/VN)连接外部传感器(如温度、光敏、压力传感器...) |
报警机制 | 可设置阈值触发中断(如温度超限报警) |
自动化校准 | 出厂预校准,支持动态校准偏移误差 |
如图所示为XADC模块的系统框图(右侧为内外部模拟输入):
PL-JTAG 接口可以用于开发工具(Vivado)控制 XADC 模块,包括读取芯片温度信息、供电电压。 而运行在 PS 中的软件则可以通过两种方式与 XADC 进行通信:
1、 通过 PS-XADC 接口,此时不需要对 PL 进行编程配置;
2、 通过 PS 到 PL 的 AXI Master 接口(M_AXI_GP),需在 PL 中调用 AXI XADC IP 核。
在使用 PS 控制 XADC 时,如果对应用的性能要求较高,则推荐第二种方式,它使用的是一个并行的 数据路径(DRP 接口)。而 PS-XADC 接口同 PL-JTAG 接口一样,使用的是串行的数据路径,相对较慢。 不过使用 PS-XADC 接口不占用 PL 的资源,也不需要对 PL 进行编程,只需要对 PL 部分供电即可。 需要注意的是,PL-JTAG 接口与 PS-XADC 接口不能同时使用,而 XADC 可以在 PL-JTAG 接口(或 PS-XADC 接口)与 DRP 接口之间进行仲裁。
注意:XDAC模拟输入电压峰峰值最大值需查看用户手册,我用到的zedboardf开发板最大1V,因为模拟电压信号不好产生,所以就跟着网上例程直接用 XADC 测芯片温度、供电电压。
二、硬件设计
(1)系统框图如图所示,系统搭建用到了UART(用于Debug)、DDR(存储器):
注意:XADC 是 Zynq SoC 中 PL 端已有的硬核模块,属于 Zynq 芯片的硬件资源,不占用可编程逻辑(LUT/FF)资源,可以被 PS端直接访问和控制,所以不需要对 PL 端进行配置。
(3)最后整体 bd 设计部分如图所示:设计检查、Generate Output Products、 Create HDL Wrapper、(管脚约束、Gnerate Bitstream、(无PL端设计这两部忽略))、Export Hardware(不用包含比特流文件)、启动Vitis
三、软件设计
可以打开官方提供的私有定时器的示例工程,这里可以选XADC轮询的示例工程,方便在设计时进行对照参考。同时对比中断,简单介绍一下轮询(简单来说就是主函数中主动调用函数读数据,所以相比于中断会持续cpu):
轮询 对比 中断
轮询(Polling) | 中断(Interrupt) | |
---|---|---|
触发方式 | CPU 主动循环查询 | 外设主动通知 CPU |
实时性 | 延迟高(依赖查询频率) | 延迟低(立即响应) |
CPU 占用 | 高(持续占用) | 低(事件触发才处理) |
适用场景 | 简单任务、低实时性需求 | 高实时性、多任务系统 |
#include "xil_printf.h"
#include "stdio.h"
#include "xparameters.h"
#include "xadcps.h"
#include "sleep.h"
//===================用户自定义宏===================//
#define XADC_DEVICE_ID XPAR_XADCPS_0_DEVICE_ID //宏定义XADC器件ID
//======================实例化======================//
XAdcPs XAdc; //定义XADC实例
//=====================函数声明=====================//
static void XADC_Init(); //声明XADC初始化函数
static void XADC_Print(); //声明XADC读取并打印内部数据函数
//======================主函数======================//
int main()
{
xil_printf("PS XADC Test\r\n");
//XADC初始化
XADC_Init();
while(1)
{
//延时3秒,每3秒读一次数据
sleep(3);
//XADC读取并打印内部数据
XADC_Print();
}
return 0;
}
//==================XADC初始化函数==================//
void XADC_Init()
{
//定义器件ID(指针类型)
XAdcPs_Config *ConfigPtr;
//根据器件ID,查找器件配置信息
ConfigPtr = XAdcPs_LookupConfig(XADC_DEVICE_ID);
//初始化XADC
XAdcPs_CfgInitialize(&XAdc, ConfigPtr,ConfigPtr->BaseAddress);
//(可选)XADC自检
int Status = XAdcPs_SelfTest(&XAdc);
if (Status != XST_SUCCESS) { xil_printf("XADC Self Test Error! \r\n"); }
//设置 XADC 操作模式为“默认安全模式”
XAdcPs_SetSequencerMode(&XAdc, XADCPS_SEQ_MODE_SAFE);
}
//============XADC读取并打印内部数据函数============//
void XADC_Print()
{
//定义原始数据
u32 temp_rawdata; //温度原始数据
u32 vcc_pint_rawdata; //PS 内核电压 原始数据
u32 vcc_int_rawdata; //PL 内核电压 原始数据
//定义转换后数据
float temp; //温度
float vcc_pint; //PS 内核电压
float vcc_int; //PL 内核电压
//获取原始温度传感器数据,并转换成温度信息 (调用自带的数据转换函数,参数为XADC实例指针、数据通道号)
temp_rawdata = XAdcPs_GetAdcData(&XAdc, XADCPS_CH_TEMP);
temp = XAdcPs_RawToTemperature(temp_rawdata);
//获取 VCCPINT 传感器数据,并转换成电压信息
vcc_pint_rawdata = XAdcPs_GetAdcData(&XAdc, XADCPS_CH_VCCPINT);
vcc_pint = XAdcPs_RawToVoltage(vcc_pint_rawdata);
//获取 VCCINT 传感器数据,并转换成电压信息
vcc_int_rawdata = XAdcPs_GetAdcData(&XAdc, XADCPS_CH_VCCINT);
vcc_int = XAdcPs_RawToVoltage(vcc_int_rawdata);
//打印温度、电压信息(用printf函数来打印,头文件stdio.h)
printf("Temp %f C \n", temp_rawdata, temp);
printf("VccPint %f V \n", vcc_pint_rawdata, vcc_pint);
printf("VccInt %f V \r\n", vcc_int_rawdata, vcc_int);
}
实际上可读取的电压数据还有很多,这里示意两个。
四、效果
上板串口打印Debug信息,此后每隔3s打印读取到的温度和电压数据