ZYNQ SoC 的 XADC 模块包括两个 12 位的模数转换器,转换速率可以达到 1MSPS(每秒一百万次采样)。它带有片上温度和电压传感器,可以测量芯片工作时的温度和供电电压。
在 7 系列的 FPGA 中,XADC 提供了 JTAG 和 DRP(dynamic reconfiguration port)接口,用于访问其内部的状态和控制寄存器。在 ZYNQ-7000 SoC 器件中,XADC 增加了一个 PS-XADC 接口,用于 PS 中的软件控制 XADC 模块。
XADC 模块的系统框图如下所示:
在图中 PL-JTAG 接口可以用于开发工具(Vivado)控制 XADC 模块,包括读取芯片温度信息等。而运行在 PS 中的软件则可以通过两种方式与 XADC 进行通信:
在使用 PS 控制 XADC 时,如果对应用的性能要求较高,则推荐第二种方式,它使用的是一个并行的数据路径(DRP 接口)。而 PS-XADC 接口同 PL-JTAG 接口一样,使用的是串行的数据路径,相对较慢。不过使用 PS-XADC 接口不占用 PL 的资源,也不需要对 PL 进行编程,只需要对 PL 部分供电即可。
需要注意的是,PL-JTAG 接口与 PS-XADC 接口不能同时使用。而 XADC 可以在 PL-JTAG 接口(或PS-XADC 接口)与 DRP 接口之间进行仲裁。
XADC 为硬核模块,不需要对 PL 进行配置。PS 端只需完成 UART、DDR 等基础配置就可以。
本实验通过 PS-XADC 接口实现,创建基础 SDK 工程即可,添加源代码:
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#ifndef __XADC_H__
#define __XADC_H__
#define USING_XADC
#ifdef USING_XADC
#include "xparameters.h"
#include "xadcps.h"
#define XADC_DEVICE_ID XPAR_XADCPS_0_DEVICE_ID
struct Xadc_Data
{
float temp; // Temperature
float vccint; // PL kernel voltage
float vccaux; // PL auxiliary voltage
float vccbram; // PL BRAM voltage
float vccpint; // PS kernel voltage
float vccpaux; // PS auxiliary voltage
float vccpdro; // PS DDR voltage
uint16_t raw_temp;
uint16_t raw_vccint;
uint16_t raw_vccaux;
uint16_t raw_vccbram;
uint16_t raw_vccpint;
uint16_t raw_vccpaux;
uint16_t raw_vccpdro;
};
typedef struct Xadc_Data XadcData_t;
int XadcPs_Init(XAdcPs *XadcInstancePtr, uint16_t DeviceId);
void XadcPs_GetData(XAdcPs *XadcInstancePtr, XadcData_t *XadcData);
#endif
#endif
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#include "xadc.h"
#ifdef USING_XADC
int XadcPs_Init(XAdcPs *XadcInstancePtr, uint16_t DeviceId)
{
int Status;
XAdcPs_Config *XadcConfigPtr;
XadcConfigPtr = XAdcPs_LookupConfig(DeviceId);
if (NULL == XadcConfigPtr) {
return XST_FAILURE;
}
Status = XAdcPs_CfgInitialize(XadcInstancePtr, XadcConfigPtr,
XadcConfigPtr->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XAdcPs_SetSequencerMode(XadcInstancePtr, XADCPS_SEQ_MODE_SAFE);
return Status;
}
void XadcPs_GetData(XAdcPs *XadcInstancePtr, XadcData_t *XadcData)
{
XadcData->raw_temp = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_TEMP);
XadcData->temp = XAdcPs_RawToTemperature(XadcData->raw_temp);
XadcData->raw_vccint = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCINT);
XadcData->vccint = XAdcPs_RawToVoltage(XadcData->raw_vccint);
XadcData->raw_vccaux = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCAUX);
XadcData->vccaux = XAdcPs_RawToVoltage(XadcData->raw_vccaux);
XadcData->raw_vccbram = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VBRAM);
XadcData->vccbram = XAdcPs_RawToVoltage(XadcData->raw_vccbram);
XadcData->raw_vccpint = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPINT);
XadcData->vccpint = XAdcPs_RawToVoltage(XadcData->raw_vccpint);
XadcData->raw_vccpaux = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPAUX);
XadcData->vccpaux = XAdcPs_RawToVoltage(XadcData->raw_vccpaux);
XadcData->raw_vccpdro = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPDRO);
XadcData->vccpdro = XAdcPs_RawToVoltage(XadcData->raw_vccpdro);
}
#endif
/**
* Copyright (c) 2022-2023,HelloAlpha
*
* Change Logs:
* Date Author Notes
*/
#include "xadc.h"
#ifdef USING_XADC
#include "stdio.h"
#define kprintf printf
/**
* xil_printf does not support printing floating point numbers
*/
static XAdcPs Xadc;
static XadcData_t xadc_data;
int xadc_read_data(void)
{
XadcPs_GetData(&Xadc, &xadc_data);
kprintf("On Chip Temperature: %f C \r\n", xadc_data.temp);
kprintf("PL Kernel Voltage: %f V \r\n", xadc_data.vccint);
kprintf("PL Auxiliary Voltage: %f V \r\n", xadc_data.vccaux);
kprintf("PL BRAM Voltage: %f V \r\n", xadc_data.vccbram);
kprintf("PS Kernel Voltage: %f V \r\n", xadc_data.vccpint);
kprintf("PS Auxiliary Voltage: %f V \r\n", xadc_data.vccpaux);
kprintf("PS DDR Voltage: %f V \r\n", xadc_data.vccpdro);
return 0;
}
int app_xadc_init(void)
{
int Status = XST_SUCCESS;
Status = XadcPs_Init(&Xadc, XADC_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return Status;
}
#endif
更多 ZYNQ 相关源码也可以在这里 Github ZYNQ-SPACE 找到。
主函数种首先调用 app_xadc_init()
对 XADC 初始化,需要读取时调用 xadc_read_data()
即可。