上篇中我们简单了解了XADC和其基本使用方法,包括对片内温度和各种电源电压的测量。本文我们将学习XADC的报警功能和中断的使用方法。程序中我们设置温度和VCCPAUX的上、下报警阈值。当超出这个范围时,便进入中断进行报警提示。
由于要使用中断系统,我们翻出两个“老伙计”,第14篇中的sys_intr.h和sys_intr.c。将GIC初始化和串口中断初始化分开,这样当设计中有多个中断源时,编写代码会更方便。
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设备的支持驱动程序
#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℃后,温度报警也触发。
在初始化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处浮动。我们据此设置阈值,来观察效果。
初始化部分使用XScuGic_Connect和XScuGic_Enable函数设置UART中断,和前面的PL中断和定时器中断类似,不再详述。XADC中断也是一种SPI,查看第7篇中的表格。
其中断号为39,在头文件中宏定义。
这部分包括两步:使能对应的中断(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。
中断处理函数的大致流程如下:
为防止超出设定的阈值后就无限进入中断,检测到一次报警便使用XAdcPs_IntrDisable函数禁用掉该中断。
但我发现当禁用了VCCPAUX中断后,虽然不会无限进入了,但是触发了温度中断后,还会再次打印出VCCPAUX的值。这说明应该是VCCPAUX超出阈值后,Alarm5仍会置位对应的中断标志位,只不过不会进入中断状态了。博主一般不会太仔细研究底层的寄存器功能结构,如果你知道背后的工作机制,一定要留言告诉我。
本文介绍了XADC的报警功能和中断使用示例。本文以两种报警中断为例,使用其它XADC中断时方法类似,很容易修改。