FPGA|CPLD|ASIC论坛
直播中

卢新硕

5年用户 39经验值
擅长:可编程逻辑 接口/总线/驱动 EDA/IC设计
私信 关注
[经验]

从C语言角度看ZYNQ7000的XADC配置与运行

  1. 主函数/*
  2. * main.c
  3. *
  4. *  Created on: 2016年6月25日
  5. *      Author: Administrator
  6. */
  7. #include
  8. //#include "platform.h"
  9. #include "xadcps.h"
  10. #include "xil_types.h"
  11. #define XPAR_AXI_XADC_0_DEVICE_ID 0

  12. //void print(char *str);

  13. static XAdcPs XADCMonInst;
  14. /*
  15. Raw Temp 39966 Real Temp 34.191864

  16. Raw VccInt 22202 Real VccInt 1.016327

  17. Raw VccAux 39171 Real VccAux 1.793106

  18. Raw VccBram 22171 Real VccBram 1.014908

  19. Raw VccPInt 22166 Real VccPInt 1.014679

  20. Raw VccPAux 39224 Real VccPAux 1.795532

  21. Raw VccDDR 32738 Real VccDDR 1.498627

  22. *
  23. * */
  24. int main()
  25. {

  26.   XAdcPs_Config *ConfigPtr;
  27.   XAdcPs *XADCInstPtr = &XADCMonInst;

  28.   //status of initialisation
  29.   int Status_ADC;


  30.   //temperature readings
  31.   u32 TempRawData;
  32.   float TempData;

  33.   //Vcc Int readings
  34.   u32 VccIntRawData;
  35.   float VccIntData;

  36.   //Vcc Aux readings
  37.   u32 VccAuxRawData;
  38.   float VccAuxData;

  39.   //Vbram readings
  40.   u32 VBramRawData;
  41.   float VBramData;

  42.   //VccPInt readings
  43.   u32 VccPIntRawData;
  44.   float VccPIntData;

  45.   //VccPAux readings
  46.   u32 VccPAuxRawData;
  47.   float VccPAuxData;

  48.   //Vddr readings
  49.   u32 VDDRRawData;
  50.   float VDDRData;

  51. // init_platform();

  52.     //printf("Adam Edition MicroZed Using Vivado How To Printf nr");
  53.     printf("新年快乐~~!!!2015-12-31 nr");


  54.     //XADC initilization

  55.      ConfigPtr = XAdcPs_LookupConfig(XPAR_AXI_XADC_0_DEVICE_ID);
  56.        if (ConfigPtr == NULL) {
  57.            return XST_FAILURE;
  58.          }

  59.        Status_ADC = XAdcPs_CfgInitialize(XADCInstPtr,ConfigPtr,ConfigPtr->BaseAddress);
  60.        if(XST_SUCCESS != Status_ADC){
  61.            print("ADC INIT FAILEDnr");
  62.            return XST_FAILURE;
  63.         }

  64.        //self test
  65.        Status_ADC = XAdcPs_SelfTest(XADCInstPtr);
  66.      if (Status_ADC != XST_SUCCESS) {
  67.        return XST_FAILURE;
  68.      }

  69.      
  70.                 //stop sequencer
  71.                 XAdcPs_SetSequencerMode(XADCInstPtr,XADCPS_SEQ_MODE_SINGCHAN);

  72.                 //disable alarms
  73.                 XAdcPs_SetAlarmEnables(XADCInstPtr, 0x0);

  74.                 //configure sequencer to just sample internal on chip parameters
  75.                 XAdcPs_SetSeqInputMode(XADCInstPtr, XADCPS_SEQ_MODE_SAFE);


  76.                 //configure the channel enables we want to monitor

  77.                 XAdcPs_SetSeqChEnables(XADCInstPtr,XADCPS_CH_TEMP|XADCPS_CH_VCCINT|XADCPS_CH_VCCAUX|XADCPS_CH_VBRAM|XADCPS_CH_VCCPINT| XADCPS_CH_VCCPAUX|XADCPS_CH_VCCPDRO);

  78.                 while(1)
  79.                 {
  80.                         TempRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_TEMP);
  81.                         TempData = XAdcPs_RawToTemperature(TempRawData);
  82.                         printf("Raw Temp %lu Real Temp %f nr", TempRawData, TempData);

  83.                         VccIntRawData= XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCINT);
  84.                         VccIntData = XAdcPs_RawToVoltage(VccIntRawData);
  85.                         printf("Raw VccInt %lu Real VccInt %f nr", VccIntRawData,VccIntData);

  86.                         VccAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCAUX);
  87.                         VccAuxData = XAdcPs_RawToVoltage(VccAuxRawData);
  88.                         printf("Raw VccAux %lu Real VccAux %f nr", VccAuxRawData,VccAuxData);


  89.                         VBramRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VBRAM);
  90.                         VBramData = XAdcPs_RawToVoltage(VBramRawData);
  91.                         printf("Raw VccBram %lu Real VccBram %f nr", VBramRawData, VBramData);

  92.                         VccPIntRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPINT);
  93.                         VccPIntData = XAdcPs_RawToVoltage(VccPIntRawData);
  94.                         printf("Raw VccPInt %lu Real VccPInt %f nr", VccPIntRawData, VccPIntData);

  95.                         VccPAuxRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPAUX);
  96.                         VccPAuxData = XAdcPs_RawToVoltage(VccPAuxRawData);
  97.                         printf("Raw VccPAux %lu Real VccPAux %f nr", VccPAuxRawData, VccPAuxData);

  98.                         VDDRRawData = XAdcPs_GetAdcData(XADCInstPtr, XADCPS_CH_VCCPDRO);
  99.                         VDDRData = XAdcPs_RawToVoltage(VDDRRawData);
  100.                         printf("Raw VccDDR %lu Real VccDDR %f nr", VDDRRawData, VDDRData);
  101.                  }

  102.                 return 0;
  103. }


代码分析:

#include
包含基本输入输出操作指令
#defineXPAR_AXI_XADC_0_DEVICE_ID 0
//宏定义XADC的设备IDentity为0

typedef struct {
    u16  DeviceId;      /**< Unique ID of device */
    u32  BaseAddress;  /**
} XAdcPs_Config;
//定义结构体XAdcPs_Config,结构体包含两个内容,一个是设备id,另一个是设备的基本地址
typedef struct {
    XAdcPs_Config Config;  /**< XAdcPs_Config of current device */
    u32  IsReady;       /**< Device is initialized andready  */

} 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 FAILEDnr");
           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 setFIFO 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 commuincationchannel between XADC and PS,If set,the PS-XADC will remain in reset until a 0is written to this bit */

    XAdcPs_WriteReg((InstancePtr)->Config.BaseAddress,
                        XADCPS_MCTL_OFFSET,0x00);

    /*
     * Indicate the instance is now ready to useand
     * 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 FAILEDnr");
           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 andrelease 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 itsdefault state
     */
    XAdcPs_Reset(InstancePtr);

    /*
* Write a value into the Alarm Thresholdregisters, 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 theConfiguration 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));



回帖(1)

郑啦啦

2019-9-23 16:37:22
分析得很透彻
举报

更多回帖

×
20
完善资料,
赚取积分