本文将介绍如何在PS中调用Zynq内部的XADC模块进行片内温度和电源电压测量。先了解XADC的相关知识,再通过实例体会XADC的用法,学习XADC API函数的使用。
XADC中文全称应该是“Xilinx模拟混合信号模块”,是FPGA中的一个硬核。在7系列FPGA中,XADC提供了DRP和JTAG接口,用于访问XADC的状态和控制寄存器。Zynq中添加了第三个接口,称作PS-XADC接口,PS通过此接口来控制XADC。使用XADC可以满足一定的模拟数据采集和设备监控需求。XADC有组成部分有:
XADC可以采用片内参考电路,监测片内温度和电源电压时可以无需外部有源器件。如果要实现ADC的全12位性能,还是需要外部加一个1.25V的参考电压IC。
XADC把最近的测量结果、最大值、最小值分别存储在专用寄存器中。我们可以自定义报警阈值,当温度或电源电压超过这个值时便会发出信号。据此,我们可以在程序中控制系统断电来保护芯片。
Vivado中建立工程,配置好Zynq的MIO电压、DDR、串口。XADC模块位于PL部分,需要使用PL中的时钟。因此配置Zynq时钟时要将FCLK_CLK0视作XADC的工作时钟,我这里设置为100MHz。
添加XADC Wizard IP核,点击上方出现的run connection automation,Vivado会自动添加其它辅助模块,并完成连线。设计整体框图如下:
自动添加了两个子模块,Processor System Reset主要负责为整个处理器系统提供复位功能,包括处理器、互联网络和外设。AXI Interconnect主要完成AXI接口间的位宽、时钟或协议的转换。一般这两个模块都是根据系统自动添加的,只要大致了解其功能即可。
配置完成后导入到SDK中。使用XADC监测Zynq内部温度和电源电压,以串口方式输出。user_xadc.h文件代码如下:
#ifndef SRC_USER_XADC_H_
#define SRC_USER_XADC_H_
#include
#include "sleep.h"
#include "xil_printf.h"
#include "xadcps.h" //XADC设备的支持驱动程序
void XADC_Init(XAdcPs* XADCMonInst);
void XADC_Printf(XAdcPs* XADCInstPtr);
#endif /* SRC_USER_XADC_H_ */
user_xadc.c文件的代码如下:
#include "user_xadc.h"
void XADC_Init(XAdcPs* XADCMonInst)
{
int Status;
/* 初始化XADC */
XAdcPs_Config *ConfigPtr;
ConfigPtr = XAdcPs_LookupConfig(XPAR_XADC_WIZ_0_DEVICE_ID);
if (ConfigPtr == NULL) {
xil_printf("Can't find XADC device.\r\n");
return;
}
Status = XAdcPs_CfgInitialize(XADCMonInst,ConfigPtr,ConfigPtr->BaseAddress);
if (Status != XST_SUCCESS) {
xil_printf("XADC Init FAILED!\r\n");
return;
}
//XADC自检
Status = XAdcPs_SelfTest(XADCMonInst);
if (Status != XST_SUCCESS) {
xil_printf("XADC selftest FAILED!\r\n");
return;
}
XAdcPs_SetSequencerMode(XADCMonInst,XADCPS_SEQ_MODE_SINGCHAN); //设置排序器模式
XAdcPs_SetAlarmEnables(XADCMonInst, 0x0); //设置是否启用报警
XAdcPs_SetSeqInputMode(XADCMonInst, XADCPS_SEQ_MODE_SAFE); //设置模拟输入模式
/* 使能特定通道 */
XAdcPs_SetSeqChEnables(XADCMonInst,XADCPS_CH_TEMP|XADCPS_CH_VCCINT|XADCPS_CH_VCCAUX|XADCPS_CH_VBRAM|XADCPS_CH_VCCPINT|XADCPS_CH_VCCPAUX|XADCPS_CH_VCCPDRO);
xil_printf("XADC Init SUCCESS!\r\n");
}
void XADC_Printf(XAdcPs* XADCInstPtr)
{
u32 TempRawData,VccIntRawData,VccAuxRawData,VBramRawData,VccPIntRawData,VccPAuxRawData,VDDRRawData;
float TempData,VccIntData,VccAuxData,VBramData,VccPIntData,VccPAuxData,VDDRData;
TempRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_TEMP);
TempData = XAdcPs_RawToTemperature(TempRawData);
printf("Raw Temp %lu Real Temp %f \n\r", TempRawData, TempData);
VccIntRawData= XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCINT);
VccIntData = XAdcPs_RawToVoltage(VccIntRawData);
printf("Raw VccInt %lu Real VccInt %f \n\r", VccIntRawData,VccIntData);
VccAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCAUX);
VccAuxData = XAdcPs_RawToVoltage(VccAuxRawData);
printf("Raw VccAux %lu Real VccAux %f \n\r", VccAuxRawData,VccAuxData);
VBramRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VBRAM);
VBramData = XAdcPs_RawToVoltage(VBramRawData);
printf("Raw VccBram %lu Real VccBram %f \n\r", VBramRawData, VBramData);
VccPIntRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPINT);
VccPIntData = XAdcPs_RawToVoltage(VccPIntRawData);
printf("Raw VccPInt %lu Real VccPInt %f \n\r", VccPIntRawData, VccPIntData);
VccPAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPAUX);
VccPAuxData = XAdcPs_RawToVoltage(VccPAuxRawData);
printf("Raw VccPAux %lu Real VccPAux %f \n\r", VccPAuxRawData, VccPAuxData);
VDDRRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPDRO);
VDDRData = XAdcPs_RawToVoltage(VDDRRawData);
printf("Raw VccDDR %lu Real VccDDR %f \n\r", VDDRRawData, VDDRData);
}
main.c文件的代码如下:
#include "user_xadc.h"
static XAdcPs XADCInst; //XADC
int main()
{
XADC_Init(&XADCInst);
while(1)
{
xil_printf("-------------XADC result-------------\r\n");
XADC_Printf(&XADCInst);
sleep(5);
}
}
SDK Terminal中添加串口,运行程序,将看到每隔5s便打印一次Zynq内部温度和各种电压信息,包括原始数据“raw”和转换后的实际数据“real”。
对XADC设备初始化操作和前面GPIO设备、中断设备、定时器设备的初始化过程一样,不再赘述。
接着使用XAdcPs_SelfTest函数对XADC设备进行自检,该函数先将XADC复位,向报警门限寄存器写一个值再读取进行比较,然后再次复位XADC。如果比较结果相同则返回XST_SUCCESS,否则返回XST_FAILURE,程序中可以根据自检函数的返回值判断XADC工作是否正常。
int XAdcPs_SelfTest(XAdcPs *InstancePtr) //函数原型
之后使用4个函数依次设置XADC的工作模式。首先用XAdcPs_SetSequencerMode函数,设置通道序列器模式,一次只能选择一个模式。各模式的宏定义在xadcps.h文件中,这里设置为“单通道”模式。
void XAdcPs_SetSequencerMode(XAdcPs *InstancePtr, u8 SequencerMode)
使用XAdcPs_SetAlarmEnables函数设置是否使能报警输出,第二个参数为1表示启用,为0表示禁用。
void XAdcPs_SetAlarmEnables(XAdcPs *InstancePtr, u16 AlmEnableMask)
使用XAdcPs_SetSeqInputMode函数设置模拟输入模式,设置成功返回XST_SUCCESS,否则返回XST_FAILURE。
int XAdcPs_SetSeqInputMode(XAdcPs *InstancePtr, u32 InputModeChMask)
使用XAdcPs_ SetSeqChEnables函数设置启用哪些通道,设置成功返回XST_SUCCESS,否则返回XST_FAILURE。各通道的宏定义在xadcps.h文件中。
int XAdcPs_SetSeqChEnables(XAdcPs *InstancePtr, u32 ChEnableMask)
使用XAdcPs_GetAdcData函数获取指定通道的ADC转换数据。设置的通道应该是0-6和13-31。通道7-9用于校准设备,无法获得与这三个通道相关的数据。
u16 XAdcPs_GetAdcData(XAdcPs *InstancePtr, u8 Channel)
使用两个宏定义XAdcPs_RawToVoltage和XAdcPs_RawToTemperature可以将XADC采集到的原始数据转换为电压(V)和温度(℃)。其等效的C语言函数接口为:
float XAdcPs_RawToTemperature(u32 AdcData);
float XAdcPs_RawToVoltage(u32 AdcData);
XADC的使用算是比较复杂,本文只是简单的使用其测量了Zynq内部的温度和电源电压。XADC也是SPI中的一个中断源,还可以采集外部信号。熟练掌握XADC的使用,不仅需要编程能力,还要对其结构和工作原理有一定了解。