学会Zynq(22)XADC测量片内温度与电源电压

本文将介绍如何在PS中调用Zynq内部的XADC模块进行片内温度和电源电压测量。先了解XADC的相关知识,再通过实例体会XADC的用法,学习XADC API函数的使用。


XADC介绍

XADC中文全称应该是“Xilinx模拟混合信号模块”,是FPGA中的一个硬核。在7系列FPGA中,XADC提供了DRPJTAG接口,用于访问XADC的状态和控制寄存器。Zynq中添加了第三个接口,称作PS-XADC接口,PS通过此接口来控制XADC。使用XADC可以满足一定的模拟数据采集和设备监控需求。XADC有组成部分有:

  • 两个12位、1MSPS的ADC,每个ADC有独立的跟踪和保持放大器;支持单极、双极、查分等多种模拟信号输入类型;1ms采样率,最高支持500kHz信号带宽。
  • 一个模拟多路复用器,最高支持17个外部模拟输入通道;在不需要额外封装引脚的情况下增加支持的外部通道的数量
  • 片上热传感器和电压传感器

XADC可以采用片内参考电路,监测片内温度和电源电压时可以无需外部有源器件。如果要实现ADC的全12位性能,还是需要外部加一个1.25V的参考电压IC。

XADC把最近的测量结果、最大值、最小值分别存储在专用寄存器中。我们可以自定义报警阈值,当温度或电源电压超过这个值时便会发出信号。据此,我们可以在程序中控制系统断电来保护芯片。

XADC的系统模块框图如下:
学会Zynq(22)XADC测量片内温度与电源电压_第1张图片


Zynq设计

Vivado中建立工程,配置好Zynq的MIO电压、DDR、串口。XADC模块位于PL部分,需要使用PL中的时钟。因此配置Zynq时钟时要将FCLK_CLK0视作XADC的工作时钟,我这里设置为100MHz。
学会Zynq(22)XADC测量片内温度与电源电压_第2张图片
添加XADC Wizard IP核,点击上方出现的run connection automation,Vivado会自动添加其它辅助模块,并完成连线。设计整体框图如下:
学会Zynq(22)XADC测量片内温度与电源电压_第3张图片
自动添加了两个子模块,Processor System Reset主要负责为整个处理器系统提供复位功能,包括处理器、互联网络和外设。AXI Interconnect主要完成AXI接口间的位宽、时钟或协议的转换。一般这两个模块都是根据系统自动添加的,只要大致了解其功能即可。


SDK程序设计

配置完成后导入到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”。
学会Zynq(22)XADC测量片内温度与电源电压_第4张图片


相关API函数

1.XADC初始化

对XADC设备初始化操作和前面GPIO设备、中断设备、定时器设备的初始化过程一样,不再赘述。

接着使用XAdcPs_SelfTest函数对XADC设备进行自检,该函数先将XADC复位,向报警门限寄存器写一个值再读取进行比较,然后再次复位XADC。如果比较结果相同则返回XST_SUCCESS,否则返回XST_FAILURE,程序中可以根据自检函数的返回值判断XADC工作是否正常。

	int XAdcPs_SelfTest(XAdcPs *InstancePtr)  //函数原型

2.XADC配置

之后使用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)

3.数据采集与转换

使用XAdcPs_GetAdcData函数获取指定通道的ADC转换数据。设置的通道应该是0-6和13-31。通道7-9用于校准设备,无法获得与这三个通道相关的数据。

	u16 XAdcPs_GetAdcData(XAdcPs *InstancePtr, u8 Channel)

使用两个宏定义XAdcPs_RawToVoltageXAdcPs_RawToTemperature可以将XADC采集到的原始数据转换为电压(V)和温度(℃)。其等效的C语言函数接口为:

	float XAdcPs_RawToTemperature(u32 AdcData);
	float XAdcPs_RawToVoltage(u32 AdcData);

总结

XADC的使用算是比较复杂,本文只是简单的使用其测量了Zynq内部的温度和电源电压。XADC也是SPI中的一个中断源,还可以采集外部信号。熟练掌握XADC的使用,不仅需要编程能力,还要对其结构和工作原理有一定了解。

你可能感兴趣的:(FPGA,Zynq)