学会Zynq(23)XADC报警功能与中断使用示例

上篇中我们简单了解了XADC和其基本使用方法,包括对片内温度和各种电源电压的测量。本文我们将学习XADC的报警功能和中断的使用方法。程序中我们设置温度和VCCPAUX的上、下报警阈值。当超出这个范围时,便进入中断进行报警提示。

SDK程序设计

由于要使用中断系统,我们翻出两个“老伙计”,第14篇中的sys_intr.h和sys_intr.c。将GIC初始化和串口中断初始化分开,这样当设计中有多个中断源时,编写代码会更方便。

user_xadc.h文件的代码如下:

#ifndef SRC_USER_XADC_H_
#define SRC_USER_XADC_H_

#include <stdio.h>
#include "sleep.h"
#include "xil_printf.h"
#include "xadcps.h"       //XADC设备的支持驱动程序
#include "xscugic.h"

#define XADCINTR_ID	  XPAR_XADCPS_INT_ID  //中断ID

int XADC_Init(XAdcPs* XADCMonInst);
int XADC_Inter(XScuGic *Intc, XAdcPs *XAdcPtr, u16 IntrId);
void XADC_Alarm_Init(XAdcPs *XAdcInstPtr);
void XAdcInterruptHandler(void *CallBackRef);

#endif /* SRC_USER_XADC_H_ */

user_xadc.c文件的代码如下,设计XADC的中断初始化函数、中断处理函数和报警设置函数:

#include "user_xadc.h"

//---------------------------------------------------------
//                   XADC初始化函数
//---------------------------------------------------------
int 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 XST_FAILURE;
	}
	Status = XAdcPs_CfgInitialize(XADCMonInst,ConfigPtr,ConfigPtr->BaseAddress);
	if (Status != XST_SUCCESS) {
		xil_printf("XADC Init FAILED!\r\n");
		return XST_FAILURE;
	}

	//XADC自检
	Status = XAdcPs_SelfTest(XADCMonInst);
	if (Status != XST_SUCCESS) {
		xil_printf("XADC selftest FAILED!\r\n");
		return XST_FAILURE;
	}

	XAdcPs_SetSequencerMode(XADCMonInst,XADCPS_SEQ_MODE_SAFE); //默认模式,报警值不启动
	XAdcPs_SetAlarmEnables(XADCMonInst, 0x0);   //禁用所有报警
	XAdcPs_SetSeqInputMode(XADCMonInst, XADCPS_SEQ_MODE_SAFE);  //设置模拟输入模式

	//设置片内温度的报警阈值 <20 | >45
	XAdcPs_SetAlarmThreshold(XADCMonInst, XADCPS_ATR_TEMP_UPPER,XAdcPs_TemperatureToRaw(45));
	XAdcPs_SetAlarmThreshold(XADCMonInst, XADCPS_ATR_TEMP_LOWER,XAdcPs_TemperatureToRaw(20));
	//设置VCCPAUX的报警阈值 <1.55 | >1.79
	XAdcPs_SetAlarmThreshold(XADCMonInst, XADCPS_ATR_VCCPAUX_UPPER,XAdcPs_VoltageToRaw(1.79));
	XAdcPs_SetAlarmThreshold(XADCMonInst, XADCPS_ATR_VCCPAUX_LOWER,XAdcPs_VoltageToRaw(1.55));

	xil_printf("XADC Init SUCCESS!\r\n");
	return XST_SUCCESS;
}

//---------------------------------------------------------
//                   XADC中断初始化函数
//---------------------------------------------------------
int XADC_Inter(XScuGic *Intc, XAdcPs *XAdcPtr, u16 IntrId)
{
	int Status;

	Status = XScuGic_Connect(Intc, IntrId,
					(Xil_InterruptHandler)XAdcInterruptHandler,
					(void *)XAdcPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}
	XScuGic_Enable(Intc, IntrId);

	return XST_SUCCESS;
}

//---------------------------------------------------------
//                   XADC报警功能设置
//---------------------------------------------------------
void XADC_Alarm_Init(XAdcPs *XAdcInstPtr)
{
	u32 IntrStatus;

	//清除中断状态寄存器中的所有位。
	IntrStatus = XAdcPs_IntrGetStatus(XAdcInstPtr);
	XAdcPs_IntrClear(XAdcInstPtr, IntrStatus);

	//使能片内温度的Alarm 0中断和VCCPAUX的Alarm 5中断
	XAdcPs_IntrEnable(XAdcInstPtr,
		(XADCPS_INTX_ALM5_MASK | XADCPS_INTX_ALM0_MASK));

	//使能对应的报警功能
	XAdcPs_SetAlarmEnables(XAdcInstPtr, (XADCPS_CFR1_ALM_VCCPAUX_MASK
								| XADCPS_CFR1_ALM_TEMP_MASK));
	XAdcPs_SetSequencerMode(XAdcInstPtr, XADCPS_SEQ_MODE_INDEPENDENT);
}

//---------------------------------------------------------
//                   XADC中断处理函数
//---------------------------------------------------------
void XAdcInterruptHandler(void *CallBackRef)
{
	u32 IntrStatusValue;
	XAdcPs *XAdcPtr = (XAdcPs *)CallBackRef;
	u32 TempRawData, VccPAuxRawData;
	float TempData, VccPAuxData;

	// 获取中断状态标志
	IntrStatusValue = XAdcPs_IntrGetStatus(XAdcPtr);

	// Alarm 0中断
	if (IntrStatusValue & XADCPS_INTX_ALM0_MASK) {
		xil_printf("Temp Warning!\r\n");
		TempRawData = XAdcPs_GetAdcData(XAdcPtr, XADCPS_CH_TEMP);
		TempData = XAdcPs_RawToTemperature(TempRawData);
		printf("Raw Temp %lu Real Temp %f \r\n", TempRawData, TempData);
		XAdcPs_IntrDisable(XAdcPtr,XADCPS_INTX_ALM0_MASK); //禁用中断
	}
	// Alarm 5中断
	if (IntrStatusValue & XADCPS_INTX_ALM5_MASK) {
		xil_printf("VccPAUX Warning!\r\n");
		VccPAuxRawData = XAdcPs_GetAdcData(XAdcPtr, XADCPS_CH_VCCPAUX);
		VccPAuxData = XAdcPs_RawToVoltage(VccPAuxRawData);
		printf("Raw VccPAux %lu Real VccPAux %f \r\n", VccPAuxRawData, VccPAuxData);
		XAdcPs_IntrDisable(XAdcPtr,XADCPS_INTX_ALM5_MASK); //禁用中断
	}

	// 清除中断状态标志
	XAdcPs_IntrClear(XAdcPtr, IntrStatusValue);
}

main.c文件的代码如下

//---------------------------------------------------------------
//            Writen by CUIT 刘奇 2019.3.28
//            此程序为XADC报警功能和中断示例
//---------------------------------------------------------------

#include "user_xadc.h"
#include "sys_intr.h"

static XScuGic Intc;  //GIC
static XAdcPs XADCInst;  //XADC

void System_Init(void)
{
	XADC_Init(&XADCInst);
	Init_Intr_System(&Intc);
	Setup_Intr_Exception(&Intc);
	XADC_Inter(&Intc, &XADCInst, XADCINTR_ID);
	XADC_Alarm_Init(&XADCInst);
}

int main()
{
	System_Init();
	while(1);
}

测试结果如下,VCCPAUX报警很快触发。耐心等待一会,当Zynq片内温度超过45℃后,温度报警也触发。

“”

相关API函数

1.报警阈值设置

在初始化XADC时,使用XAdcPs_SetAlarmThreshold函数设置报警阈值。

void XAdcPs_SetAlarmThreshold(XAdcPs *InstancePtr,u8 AlarmThrReg,u16 Value)

第二个参数是要设置的报警阈值寄存器;第三个参数时要写入的16位阈值。各种报警阈值寄存器的宏定义在xadcps.h文件中。XADC的8个报警中断共有16个报警寄存器,各有一个上阈值和下阈值。

“”

我这里只设置了温度和VCCPAUX的上、下阈值。设置阈值时借助XAdcPs_VoltageToRaw和XAdcPs_TemperatureToRaw两个“宏定义函数”将普通意义上的电压和温度值转换为寄存器中存储的值。

轻负载情况下,博主测试使用的Zynq7010在刚上电时在40℃以下,工作一段时间后会到达50~60℃之间。二VCCPAUX正常会在1.79V处浮动。我们据此设置阈值,来观察效果。

2.XADC中断初始化

初始化部分使用XScuGic_Connect和XScuGic_Enable函数设置UART中断,和前面的PL中断和定时器中断类似,不再详述。XADC中断也是一种SPI,查看第7篇中的表格。

“”

其中断号为39,在头文件中宏定义。

3.报警功能设置

这部分包括两步:使能对应的中断(XAdcPs_IntrEnable函数)和设置对应的报警功能(XAdcPs_SetAlarmEnables函数)。XADC共支持8种不同的中断Alarm0~6和过温中断(过温用XAdcPs_SetOverTemp函数设置,而不是XAdcPs_SetAlarmThreshold函数)。

下表给出了各种中断宏定义(XAdcPs_IntrEnable使用的参数)和报警宏定义(XAdcPs_SetAlarmEnables使用的参数)之间的对应关系。

中断宏定义 报警宏定义 中断
XADCPS_CFR1_ALM_TEMP_MASK XADCPS_INTX_ALM0_MASK Alarm0-温度
XADCPS_CFR1_ALM_VCCINT_MASK XADCPS_INTX_ALM1_MASK Alarm1-VCCINT
XADCPS_CFR1_ALM_VCCAUX_MASK XADCPS_INTX_ALM2_MASK Alarm2-VCCAUX
XADCPS_CFR1_ALM_VBRAM_MASK XADCPS_INTX_ALM3_MASK Alarm3-VBRAM
XADCPS_CFR1_ALM_VCCPINT_MASK XADCPS_INTX_ALM4_MASK Alarm4-VCCPINT
XADCPS_CFR1_ALM_VCCPAUX_MASK XADCPS_INTX_ALM5_MASK Alarm5-VCCPAUX
XADCPS_CFR1_ALM_VCCPDRO_MASK XADCPS_INTX_ALM6_MASK Alarm6-VCCPDRO
XADCPS_CFR1_OT_MASK XADCPS_INTX_OT_MASK 过温

我们这里只使用自定义温度和VCCPAUX,因此只设置Alarm0和Alarm5。

4.中断处理函数

中断处理函数的大致流程如下:

获取中断状态标志,检查触发的是哪种中断;
根据不同的中断类型,执行不同的操作,Alarm0中断则温度报警,Alarm5则VCCPAUX报警;
清除中断标志状态;
为防止超出设定的阈值后就无限进入中断,检测到一次报警便使用XAdcPs_IntrDisable函数禁用掉该中断。

但我发现当禁用了VCCPAUX中断后,虽然不会无限进入了,但是触发了温度中断后,还会再次打印出VCCPAUX的值。这说明应该是VCCPAUX超出阈值后,Alarm5仍会置位对应的中断标志位,只不过不会进入中断状态了。博主一般不会太仔细研究底层的寄存器功能结构,如果你知道背后的工作机制,一定要留言告诉我。

总结

本文介绍了XADC的报警功能和中断使用示例。本文以两种报警中断为例,使用其它XADC中断时方法类似,很容易修改。

---------------------


文章来源:FPGADesigner的博客
*本文由作者授权转发,如需转载请联系作者本人

最新文章

最新文章