【ZYNQ】ZYNQ7000 XADC 及其驱动示例

XADC 简介

ZYNQ SoC 的 XADC 模块包括两个 12 位的模数转换器,转换速率可以达到 1MSPS(每秒一百万次采样)。它带有片上温度和电压传感器,可以测量芯片工作时的温度和供电电压。

在 7 系列的 FPGA 中,XADC 提供了 JTAG 和 DRP(dynamic reconfiguration port)接口,用于访问其内部的状态和控制寄存器。在 ZYNQ-7000 SoC 器件中,XADC 增加了一个 PS-XADC 接口,用于 PS 中的软件控制 XADC 模块。

XADC 模块的系统框图如下所示:

【ZYNQ】ZYNQ7000 XADC 及其驱动示例_第1张图片

在图中 PL-JTAG 接口可以用于开发工具(Vivado)控制 XADC 模块,包括读取芯片温度信息等。而运行在 PS 中的软件则可以通过两种方式与 XADC 进行通信:

  • 1、 通过 PS-XADC 接口,此时不需要对 PL 进行编程配置;
  • 2、 通过 PS 到 PL 的 AXI Master 接口(M_AXI_GP),此时需要在 PL 中调用 AXI XADC IP 核。

在使用 PS 控制 XADC 时,如果对应用的性能要求较高,则推荐第二种方式,它使用的是一个并行的数据路径(DRP 接口)。而 PS-XADC 接口同 PL-JTAG 接口一样,使用的是串行的数据路径,相对较慢。不过使用 PS-XADC 接口不占用 PL 的资源,也不需要对 PL 进行编程,只需要对 PL 部分供电即可。

需要注意的是,PL-JTAG 接口与 PS-XADC 接口不能同时使用。而 XADC 可以在 PL-JTAG 接口(或PS-XADC 接口)与 DRP 接口之间进行仲裁。

硬件设计

XADC 为硬核模块,不需要对 PL 进行配置。PS 端只需完成 UART、DDR 等基础配置就可以。

【ZYNQ】ZYNQ7000 XADC 及其驱动示例_第2张图片

软件设计

本实验通过 PS-XADC 接口实现,创建基础 SDK 工程即可,添加源代码:

  • xadc.h
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __XADC_H__
#define __XADC_H__

#define USING_XADC

#ifdef USING_XADC
#include "xparameters.h"
#include "xadcps.h"

#define XADC_DEVICE_ID	XPAR_XADCPS_0_DEVICE_ID

struct Xadc_Data
{
    float temp;     // Temperature
    float vccint;   // PL kernel voltage
    float vccaux;   // PL auxiliary voltage
    float vccbram;  // PL BRAM voltage
    float vccpint;  // PS kernel voltage
    float vccpaux;  // PS auxiliary voltage
    float vccpdro;  // PS DDR voltage

    uint16_t raw_temp;
    uint16_t raw_vccint;
    uint16_t raw_vccaux;
    uint16_t raw_vccbram;
    uint16_t raw_vccpint;
    uint16_t raw_vccpaux;
    uint16_t raw_vccpdro;
};

typedef struct Xadc_Data XadcData_t;

int XadcPs_Init(XAdcPs *XadcInstancePtr, uint16_t DeviceId);
void XadcPs_GetData(XAdcPs *XadcInstancePtr, XadcData_t *XadcData);

#endif
#endif
  • xadc.c
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "xadc.h"

#ifdef USING_XADC
int XadcPs_Init(XAdcPs *XadcInstancePtr, uint16_t DeviceId)
{
    int Status;
    XAdcPs_Config *XadcConfigPtr;

    XadcConfigPtr = XAdcPs_LookupConfig(DeviceId);
    if (NULL == XadcConfigPtr) {
        return XST_FAILURE;
    }

    Status = XAdcPs_CfgInitialize(XadcInstancePtr, XadcConfigPtr, 
                        XadcConfigPtr->BaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    XAdcPs_SetSequencerMode(XadcInstancePtr, XADCPS_SEQ_MODE_SAFE);

    return Status;
}

void XadcPs_GetData(XAdcPs *XadcInstancePtr, XadcData_t *XadcData)
{
    XadcData->raw_temp = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_TEMP);
    XadcData->temp = XAdcPs_RawToTemperature(XadcData->raw_temp);

    XadcData->raw_vccint = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCINT);
    XadcData->vccint = XAdcPs_RawToVoltage(XadcData->raw_vccint);

    XadcData->raw_vccaux = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCAUX);
    XadcData->vccaux = XAdcPs_RawToVoltage(XadcData->raw_vccaux);

    XadcData->raw_vccbram = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VBRAM);
    XadcData->vccbram = XAdcPs_RawToVoltage(XadcData->raw_vccbram);

    XadcData->raw_vccpint = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPINT);
    XadcData->vccpint = XAdcPs_RawToVoltage(XadcData->raw_vccpint);

    XadcData->raw_vccpaux = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPAUX);
    XadcData->vccpaux = XAdcPs_RawToVoltage(XadcData->raw_vccpaux);

    XadcData->raw_vccpdro = XAdcPs_GetAdcData(XadcInstancePtr, XADCPS_CH_VCCPDRO);
    XadcData->vccpdro = XAdcPs_RawToVoltage(XadcData->raw_vccpdro);
}
#endif
  • app_xadc.c
/**
 * Copyright (c) 2022-2023,HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "xadc.h"

#ifdef USING_XADC
#include "stdio.h"
#define kprintf printf

/**
 * xil_printf does not support printing floating point numbers
*/

static XAdcPs Xadc;
static XadcData_t xadc_data;

int xadc_read_data(void)
{
    XadcPs_GetData(&Xadc, &xadc_data);

    kprintf("On Chip Temperature:  %f C    \r\n", xadc_data.temp);
    kprintf("PL Kernel Voltage:     %f V    \r\n", xadc_data.vccint);
    kprintf("PL Auxiliary Voltage:  %f V    \r\n", xadc_data.vccaux);
    kprintf("PL BRAM Voltage:       %f V    \r\n", xadc_data.vccbram);
    kprintf("PS Kernel Voltage:     %f V    \r\n", xadc_data.vccpint);
    kprintf("PS Auxiliary Voltage:  %f V    \r\n", xadc_data.vccpaux);
    kprintf("PS DDR Voltage:        %f V    \r\n", xadc_data.vccpdro);

    return 0;
}

int app_xadc_init(void)
{
    int Status = XST_SUCCESS;
    Status = XadcPs_Init(&Xadc, XADC_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    return Status;
}
#endif

更多 ZYNQ 相关源码也可以在这里 Github ZYNQ-SPACE 找到。

主函数种首先调用 app_xadc_init() 对 XADC 初始化,需要读取时调用 xadc_read_data() 即可。

验证

  • 测试平台:黑金ZYNQ7035
  • 开发工具:Vivado2017.4
  • 实验现象:

【ZYNQ】ZYNQ7000 XADC 及其驱动示例_第3张图片

参考资料

  • UG585
  • 正点原子 ZYNQ 领航者
  • 黑金 ZYNQ7035

更多内容

  • CSDN博客:@Hello阿尔法
  • 哔哩哔哩:@Hello阿尔法
  • 知乎:@Hello阿尔法

你可能感兴趣的:(ZYNQ7000,ZYNQ)