XADC实验
1、XADC概述
Xilinx7系列内部自带一个双通道12位分辨率的高速(1MSPS 1M sample per second)采样速率的模拟混合信号处理模块,双通道的ADC支持单极和差分工作模式,最多支持17路外部模拟输入通道。称为XADC(Xilinx Analog signal Module),有JTAG和DRP(Dynamic Reconfiguration Port)接口,用于访问状态寄存器和控制寄存器(DRP),其中控制寄存器可以通过DRP进行读写从而实现XADC的初始化配置,状态寄存器只可进行读取操作,ADC将采样转换后的值保存在对应的状态寄存器,通过DRP即可将其读出。
单极工作模式和差分工作模式:
单极工作模式是指AD的模拟电压输入口检测到的电压相当于对地电压。单端输入只有一个输入引脚ADCIN,使用公共端作为电路的返回端,这种方式的优点是简单,缺点是如果Vin受到干扰,由于GND电位始终是0,所以最终ADC的采样值也会随着干扰而变化。
差分工作模式是指检测两个输入口之间的电压,比如一个是3.0V另一个是2.5V,如果两者进入差分工作模式,检测到的电压等于其差值,即0.5V。通常这两根差分线会布线在一起,所以他们收到的干扰是相近的,输入共模干扰,在输入ADC时会被减掉,从而降低了干扰,缺点是接线复杂
2、源码分析
源码main.c文件
/*
* main.c
*
* Created on: 2016年6月25日
* Author: Administrator
*/
#include <stdio.h>
//#include "platform.h"
#include "xadcps.h"
#include "xil_types.h"
#define XPAR_AXI_XADC_0_DEVICE_ID 0
//void print(char *str);
static XAdcPs XADCMonInst;
/*
Raw Temp 39966 Real Temp 34.191864
Raw VccInt 22202 Real VccInt 1.016327
Raw VccAux 39171 Real VccAux 1.793106
Raw VccBram 22171 Real VccBram 1.014908
Raw VccPInt 22166 Real VccPInt 1.014679
Raw VccPAux 39224 Real VccPAux 1.795532
Raw VccDDR 32738 Real VccDDR 1.498627
*
* */
int main()
{
XAdcPs_Config *ConfigPtr;
XAdcPs *XADCInstPtr = &XADCMonInst;
//status of initialisation
int Status_ADC;
//temperature readings
u32 TempRawData;
float TempData;
//Vcc Int readings
u32 VccIntRawData;
float VccIntData;
//Vcc Aux readings
u32 VccAuxRawData;
float VccAuxData;
//Vbram readings
u32 VBramRawData;
float VBramData;
//VccPInt readings
u32 VccPIntRawData;
float VccPIntData;
//VccPAux readings
u32 VccPAuxRawData;
float VccPAuxData;
//Vddr readings
u32 VDDRRawData;
float VDDRData;
// init_platform();
//printf("Adam Edition MicroZed Using Vivado How To Printf ");
printf("新年快乐~~!!!2015-12-31 ");
//XADC initilization
ConfigPtr = XAdcPs_LookupConfig(XPAR_AXI_XADC_0_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Status_ADC = XAdcPs_CfgInitialize(XADCInstPtr,ConfigPtr,ConfigPtr->BaseAddress);
if(XST_SUCCESS != Status_ADC){
print("ADC INIT FAILED ");
return XST_FAILURE;
}
//self test
Status_ADC = XAdcPs_SelfTest(XADCInstPtr);
if (Status_ADC != XST_SUCCESS) {
return XST_FAILURE;
}
//stop sequencer
XAdcPs_SetSequencerMode(XADCInstPtr,XADCPS_SEQ_MODE_SINGCHAN);
//disable alarms
XAdcPs_SetAlarmEnables(XADCInstPtr, 0x0);
//configure sequencer to just sample internal on chip parameters
XAdcPs_SetSeqInputMode(XADCInstPtr, XADCPS_SEQ_MODE_SAFE);
//configure the channel enables we want to monitor
XAdcPs_SetSeqChEnables(XADCInstPtr,XADCPS_CH_TEMP|XADCPS_CH_VCCINT|XADCPS_CH_VCCAUX|XADCPS_CH_VBRAM|XADCPS_CH_VCCPINT| XADCPS_CH_VCCPAUX|XADCPS_CH_VCCPDRO);
while(1)
{
TempRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_TEMP);
TempData = XAdcPs_RawToTemperature(TempRawData);
printf("Raw Temp %lu Real Temp %f ", TempRawData, TempData);
VccIntRawData= XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCINT);
VccIntData = XAdcPs_RawToVoltage(VccIntRawData);
printf("Raw VccInt %lu Real VccInt %f ", VccIntRawData,VccIntData);
VccAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCAUX);
VccAuxData = XAdcPs_RawToVoltage(VccAuxRawData);
printf("Raw VccAux %lu Real VccAux %f ", VccAuxRawData,VccAuxData);
VBramRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VBRAM);
VBramData = XAdcPs_RawToVoltage(VBramRawData);
printf("Raw VccBram %lu Real VccBram %f ", VBramRawData, VBramData);
VccPIntRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPINT);
VccPIntData = XAdcPs_RawToVoltage(VccPIntRawData);
printf("Raw VccPInt %lu Real VccPInt %f ", VccPIntRawData, VccPIntData);
VccPAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPAUX);
VccPAuxData = XAdcPs_RawToVoltage(VccPAuxRawData);
printf("Raw VccPAux %lu Real VccPAux %f ", VccPAuxRawData, VccPAuxData);
VDDRRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPDRO);
VDDRData = XAdcPs_RawToVoltage(VDDRRawData);
printf("Raw VccDDR %lu Real VccDDR %f ", VDDRRawData, VDDRData);
}
return 0;
}
分析
#include<stdio.h>
包含基本输入输出操作指令
#define XPAR_AXI_XADC_0_DEVICE_ID 0
//宏定义XADC的设备IDentity为0
typedef struct {
u16 DeviceId; /**< Unique ID of device */
u32 BaseAddress; /**< Device base address */
} XAdcPs_Config;
//定义结构体XAdcPs_Config,结构体包含两个内容,一个是设备id,另一个是设备的基本地址
typedef struct {
XAdcPs_Config Config; /**< XAdcPs_Config of current device */
u32 IsReady; /**< Device is initialized and ready */
} XAdcPs;
//该结构体定义了设备的实例数据,第一部分包含两个内容(id、basement),第二部分为设备的成功初始化完毕标志。
程序语句1: XAdcPs_Config *ConfigPtr;
定义了一个结构体指针,结构体使用指针时对于其中内容的操作需要使用->操作符而不可使用包含操作符 . ,该结构体指针所指向的地址存储单元寄存了设备的id与基础地址
程序语句2:XAdcPs *XADCInstPtr = &XADCMonInst;
本语句与上类似,但是多一个ready信号,将在XAdcPs_CfgInitalize()语句中进行综合。此外,与数组不同的是,结构名并不是结构的地址,因此在对结构指针进行初始化时需要在结构名前加取址符&
程序语句3:
ConfigPtr = XAdcPs_LookupConfig(XPAR_AXI_XADC_0_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
该语句用于XADC的初始化,首先查看XAdcPs_LookupConfig函数的定义:
XAdcPs_Config *XAdcPs_LookupConfig(u16 DeviceId)
{
XAdcPs_Config *CfgPtr = NULL;
u32 Index;
for (Index=0; Index < 1; Index++) {
if (XAdcPs_ConfigTable[Index].DeviceId == DeviceId) {
CfgPtr = &XAdcPs_ConfigTable[Index];
break;
}
}
return CfgPtr;
}
传入参数是16bit的设备ID,返回的是一个XAdcPs_Config结构体格式的指针,返回值将指针地址赋值给配置结构指针ConfigPtr,该子函数的定义中,将传入的ID参数与Xilinx头文件中已定义的XADC的设备ID进行相等判断,if true,则将库中设备配置文件表XAdcPs_ConfigTable结构的地址的赋给开头定义的结构指针并返回。如果不等,则定义的指针仍然为NULL,这会在接下来的判断语句中用到。
目的:以上代码就是从系统中查看是否有该设备的定义,如果有,可以看到关于该设备描述的结构以及该设备在ZYNQ地址空间中的地址。就是一个查询语句。
程序语句4:
Status_ADC=XAdcPs_CfgInitialize(XADCInstPtr,ConfigPtr,ConfigPtr->BaseAddress);
if(XST_SUCCESS != Status_ADC){
print("ADC INIT FAILED ");
return XST_FAILURE;
}
该语句的作用是对设备进行初始化配置,首先声明几个调用的子函数:
(1)、断言函数—Xil_AssertNonvoid(Expression)
#define Xil_AssertNonvoid(Expression)
{
if (Expression) {
Xil_AssertStatus = XIL_ASSERT_NONE;
} else {
Xil_Assert(__FILE__, __LINE__);
Xil_AssertStatus = XIL_ASSERT_OCCURRED;
return 0;
}
}
该函数作用是如果传入参数为true则程序正常执行,否则条件返回错误,终止程序运行。
(2)、地址数据读取函数
#define XAdcPs_ReadReg(BaseAddress, RegOffset)
(Xil_In32((BaseAddress) + (RegOffset)))
通过宏定义的形式定义函数,在宏定义时若隔行操作需要加反斜杠 ,其中的Xil_In32函数为一内联(inline)函数,内联函数可以减少一次函数调用的时间。为什么这么设计?查阅到的答案是:在头文件中加内联声明inline,外不要使用不会内联,要使用的话必须加static,文本域为全局。
static INLINE u32 Xil_In32(UINTPTR Addr)
{
return *(volatile u32 *) Addr;
此处还需要理解*(volatile *)的作用:第一是强制转换为指针类型 volatile *将一个普通的32位值转换为地址。第二是再加一个*的作用是对地址进行取值。
(3)、地址数据写入
#define XAdcPs_WriteReg(BaseAddress, RegOffset, Data)
(Xil_Out32((BaseAddress) + (RegOffset), (Data)))
原函数定义
int XAdcPs_CfgInitialize(XAdcPs *InstancePtr, XAdcPs_Config *ConfigPtr,
u32 EffectiveAddr)
{
u32 RegValue;
/*
*对输入的两个结构体指针进行断言处理,断言处理完成向下进行
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
/*
* 将设备ID和设备地址赋值给实例结构,此处C语法是结构指针只能用->不可用.
*/
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.BaseAddress = EffectiveAddr;
/* 首先是将设备进行解锁,unlock,这个寄存器的基本地址XPAR_XDCFG_0_BASEADDRESSs=0xF8007000u 偏移地址UNLOCK_OFFSET=0x034,解锁值XAdcPs_WriteReg是0x757bdf0d,根据数据手册,该寄存器的作用是保护设备寄存器面授ROM代码损坏*/
XAdcPs_WriteReg(XPAR_XDCFG_0_BASEADDR, XADCPS_UNLK_OFFSET,XADCPS_UNLK_VALUE);
/*本语句是Xilinx的套路用法,首先将控制寄存器的值读出来,然后再根据要设置的项目进行或处理,即置一设置,
Enable the PS access of xadc and set FIFO thresholds */
RegValue = XAdcPs_ReadReg((InstancePtr)->Config.BaseAddress,
XADCPS_CFG_OFFSET);
RegValue = RegValue | XADCPS_CFG_ENABLE_MASK |
XADCPS_CFG_CFIFOTH_MASK | XADCPS_CFG_DFIFOTH_MASK;
XAdcPs_WriteReg((InstancePtr)->Config.BaseAddress,
XADCPS_CFG_OFFSET, RegValue);
/* Release xadc from reset 本语句的作用是将XADC从复位中释放,XADCPS_MCTL_OFFSET=0x18,即偏移地址为0x18,该寄存器的第四位【4】平时为1,则PS-XADC的通道不通,所以需要从复位中释放,也就是写入0,其实只给bit【4】写0就可以,但是还得重新写函数,麻烦了,所以就一起写0吧,因为其他位都没有用,bit4的说明是:This bit will reset the commuincation channel between XADC and PS,If set,the PS-XADC will remain in reset until a 0 is written to this bit */
XAdcPs_WriteReg((InstancePtr)->Config.BaseAddress,
XADCPS_MCTL_OFFSET, 0x00);
/*
* Indicate the instance is now ready to use and
* initialized without error.以上语句执行完毕之后就可以将Isready位置一了,表示初始化配置完成
*/
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
return XST_SUCCESS; /*执行完毕,返回0 */
}
/*程序返回之后,将返回值XST_SUCCESS给Status进行判断,不相等即初始化失败,其实也可以判断IsReady*/
if(XST_SUCCESS != Status_ADC){
print("ADC INIT FAILED ");
return XST_FAILURE;
}
程序语句5:Status_ADC = XAdcPs_SelfTest(XADCInstPtr);
(1)、XAdcPs_Reset(InstancePtr);
void XAdcPs_Reset(XAdcPs *InstancePtr)
{
/*
* Assert the arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Generate the reset by Control* register and release from reset
*再次将XADC从复位中释放,
*/
XAdcPs_WriteReg((InstancePtr)->Config.BaseAddress,
XADCPS_MCTL_OFFSET, 0x10);
XAdcPs_WriteReg((InstancePtr)->Config.BaseAddress,
XADCPS_MCTL_OFFSET, 0x00);
}
函数定义
int XAdcPs_SelfTest(XAdcPs *InstancePtr)
{
int Status;
u32 RegValue;
/*
* 断言语句判断输入的结构指针是否为空,初始化成功是否为真
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Reset the device to get it back to its default state
*/
XAdcPs_Reset(InstancePtr);
/*
* Write a value into the Alarm Threshold registers, read it back, and
* do the comparison将一个值写入警报阈值寄存器并读取,进行比较判断是否设置成功
*/
XAdcPs_SetAlarmThreshold(InstancePtr, XADCPS_ATR_VCCINT_UPPER,
XADCPS_ATR_TEST_VALUE);
RegValue = XAdcPs_GetAlarmThreshold(InstancePtr, XADCPS_ATR_VCCINT_UPPER);
if (RegValue == XADCPS_ATR_TEST_VALUE) {
Status = XST_SUCCESS;
} else {
Status = XST_FAILURE;
}
/*
* Reset the device again to its default state.
*/
XAdcPs_Reset(InstancePtr);
/*
* Return the test result.
*/
return Status;
}
Status_ADC = XAdcPs_SelfTest(XADCInstPtr);
if (Status_ADC != XST_SUCCESS) {
return XST_FAILURE;
}
程序语句6:XAdcPs_SetSequencerMode(XADCInstPtr,XADCPS_SEQ_MODE_SINGCHAN);
void XAdcPs_SetSequencerMode(XAdcPs *InstancePtr, u8 SequencerMode)
{
u32 RegValue;
/*
* Assert the arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid((SequencerMode <= XADCPS_SEQ_MODE_SIMUL_SAMPLING) ||
(SequencerMode == XADCPS_SEQ_MODE_INDEPENDENT));
/*
* Set the specified sequencer mode in the Configuration Register 1.在配置寄存器1中读取寄存器的值,然后设置这个寄存器,使XADC停止顺序模式
*/
RegValue = XAdcPs_ReadInternalReg(InstancePtr,
XADCPS_CFR1_OFFSET);
RegValue &= (~ XADCPS_CFR1_SEQ_VALID_MASK);
RegValue |= ((SequencerMode << XADCPS_CFR1_SEQ_SHIFT) &
XADCPS_CFR1_SEQ_VALID_MASK);
XAdcPs_WriteInternalReg(InstancePtr, XADCPS_CFR1_OFFSET,
RegValue);
}
程序语句7:
XAdcPs_SetAlarmEnables(XADCInstPtr, 0x0);
//同上,先读取控制寄存器1,然后通过或的形式取消alarm
//配置XADC仅进行内部电压转换
XAdcPs_SetSeqInputMode(XADCInstPtr, XADCPS_SEQ_MODE_SAFE);
//设置检测通道
XAdcPs_SetSeqChEnables(XADCInstPtr,XADCPS_CH_TEMP|XADCPS_CH_VCCINT|XADCPS_CH_VCCAUX|XADCPS_CH_VBRAM|XADCPS_CH_VCCPINT| XADCPS_CH_VCCPAUX|XADCPS_CH_VCCPDRO);
程序语句8:数据读取
TempRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_TEMP);
//
RegData = XAdcPs_ReadInternalReg(InstancePtr,
(XADCPS_TEMP_OFFSET +
Channel));