FPGA 之 MicroBlaze XADC 实验
Vivado IP 核提供了 XADC 软核,XADC 包含两个模数转换器(ADC),一个模拟多路复用器,片上温度和片上电压传感器等。我们可以利用这个模块监测芯片温度和供电电压,也可以用来测量外部的模拟电压信号。
7 系列的 XADC IP 核包括两个 12 位的模数转换器,转换速率可以达到 1MSPS(每秒一百万次采样)。它带有片上温度和电压传感器,可以测量芯片工作时的温度和供电电压。用户可以设置报警阈值,用来检测温度过高或者供电电压异常等事件。除此之外,通过 XADC IP 核内部的模拟多路复用器,它支持最多 17路外部模拟输入信号的测量,且支持单极、双极和差分等信号类型。
XADC内部结构
左边红框圈 1 的,共十七组差分信号,这些端口也可接收单端信号,其中一对 VP/N_0 为专用的模拟差分输入,其他 16 对为复用的,不使用时可作为普通的管脚。最上边红框 2,表明 XADC 模块也可采集片上传感器测量到的芯片温度、供电电压等信息;右边红框圈 3,是两个 12 位的模数转换器。XADC 采集转换后的数据存储在状态寄存器的专用寄存器内,可由 FPGA 内部动态配置端口(DRP----Dynamic Reconfiguration Port)的 16 位同步读/写端口访问;ADC 的转换数据也可以由 JTAG 访问,当使用这种方式时,并不需要直接去例化 XADC 模块,因为这是一个已经存在与 FPGA JTAG 结构的专用接口,此时因为没有在设计中直接例化 XADC 模块,故 XADC IP 核工作在一种预先定义好的模式即缺省模式,此模式下 XADC IP 核专用于监视芯片上的供电电压和芯片温度。以上是对 XADC IP 核的简介。
看了一圈XADC 没太看懂 但是 不重要 因为我是复制粘贴的 只要了解 大概怎么用的就好了
实验任务 : 本章的实验任务是通过 AXI 接口读取 XADC 测量的芯片温度、供电电压等信息,并通过串口打印出来。
我们研究一下这里的系统框图
CPU 通过 AXI 接口直接读取 XADC 模块采集的温度和电压数据,然后通过串口打印出来。
下面展示Block Design
下面 展示 完整的在 vitis 下的 C语言代码
#include "xsysmon.h"
#include "xparameters.h"
#include "xstatus.h"
#include "stdio.h"
#include "sleep.h"
//XADC ID
#define SYSMON_DEVICE_ID XPAR_SYSMON_0_DEVICE_ID
static XSysMon SysMonInst; //XADC 驱动实例
int SysMonFractionToInt(float FloatNum);
int main(void){
XSysMon_Config *ConfigPtr;
u32 TempRawData; //温度 原始数据
u32 VccAuxRawData; //辅助电压 原始数据
u32 VccIntRawData; //内核电压 原始数据
u32 VccBRAMdata; //BRAM 电压 原始数据
float TempData; //温度
float VccAuxData; //辅助电压
float VccIntData; //内核电压
float VBRAM; //BRAM 电压
float MaxData; //最大值
float MinData; //最小值
//初始化 XADC 器件
ConfigPtr = XSysMon_LookupConfig(SYSMON_DEVICE_ID);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
XSysMon_CfgInitialize(&SysMonInst, ConfigPtr, ConfigPtr->BaseAddress);
//默认安全模式
XSysMon_SetSequencerMode(&SysMonInst, XSM_SEQ_MODE_SAFE);
//使能的相应的通道
XSysMon_SetSeqChEnables(&SysMonInst,XSM_SEQ_CH_TEMP| //温度
XSM_SEQ_CH_VCCINT | //内核电压
XSM_SEQ_CH_VCCAUX| //辅助电压
XSM_SEQ_CH_VBRAM //BRAM 电压
);
//设置为循环模式
XSysMon_SetSequencerMode(&SysMonInst,XSM_SEQ_MODE_CONTINPASS);
while(1){
//读取温度
TempRawData = XSysMon_GetAdcData(&SysMonInst, XSM_CH_TEMP);
TempData = XSysMon_RawToTemperature(TempRawData);
xil_printf("\r\nThe Current Temperature is %0d.%03d Centigrades.\r\n",
(int)(TempData), SysMonFractionToInt(TempData));
//读取最大温度
TempRawData = XSysMon_GetMinMaxMeasurement(&SysMonInst, XSM_MAX_TEMP);
MaxData = XSysMon_RawToTemperature(TempRawData);
xil_printf("The Maximum Temperature is %0d.%03d Centigrades. \r\n",
(int)(MaxData), SysMonFractionToInt(MaxData));
//读取最小温度
TempRawData = XSysMon_GetMinMaxMeasurement(&SysMonInst, XSM_MIN_TEMP);
MinData = XSysMon_RawToTemperature(TempRawData);
xil_printf("The Minimum Temperature is %0d.%03d Centigrades. \r\n",
(int)(MinData), SysMonFractionToInt(MinData));
//读取核心电压
VccIntRawData = XSysMon_GetAdcData(&SysMonInst, XSM_CH_VCCINT);
VccIntData = XSysMon_RawToVoltage(VccIntRawData);
xil_printf("The Current VCCINT is %0d.%03d Volts. \r\n",
(int)(VccIntData), SysMonFractionToInt(VccIntData));
//读取辅助器电
VccAuxRawData = XSysMon_GetAdcData(&SysMonInst,XSM_CH_VCCAUX );
VccAuxData = XSysMon_RawToVoltage(VccAuxRawData);
xil_printf("The Current VCCAUX is %0d.%03d Volts. \r\n",
(int)(VccAuxData), SysMonFractionToInt(VccAuxData));
//读取 BRAM 电压
VccBRAMdata = XSysMon_GetAdcData(&SysMonInst,XSM_CH_VCCAUX );
VBRAM = XSysMon_RawToVoltage(VccBRAMdata);
xil_printf("The Current VBRAM is %0d.%03d Volts. \r\n",
(int)(VBRAM), SysMonFractionToInt(VBRAM));
sleep(1);
}
return 0;
}
int SysMonFractionToInt(float FloatNum)
{
float Temp;
Temp = FloatNum;
if (FloatNum < 0) {
Temp = -(FloatNum);
}
//将浮点数线束部分扩大 1000 倍,以便打印
return( ((int)((Temp -(float)((int)Temp)) * (1000.0f))));
}
下面对C语言代码进行细致的分析
我从完整的c语言开始分析 希望能有所感悟
就当是我们自己在做这个东西
按照惯例 写下我们的头文件
#include "xparameters.h"
#include "stdio.h"
这是给我们印象最深的两项了
对此我们先搁置 一下
处理下面的事项
下一步按照惯例设定 XDC的ID 序号 来自 xparameters
然后我们想我们使用xdc 添加一个xdc驱动实例
它前缀设置了static 静态变量的形式
XSysMon 来自 一个新的.h 文件 头文件内记得加入
这个结构体的意思是
typedef struct {
XSysMon_Config Config; /< XSysMon_Config of current device */u32 IsReady; /< Device is initialized and ready */
u32 Mask; /**< Store the previously written value
in CONVST register */
} XSysMon;
驱动程序的实例数据。用户需要分配一个变量
*用于系统中的每个系统监视器/ADC设备。指向的指针
*然后将这种类型的变量传递给驱动程序API函数。
*/
下面一句话是声明函数 我们不看 进入主函数
我们会发现 其实 这个 XSysMon_Config 是来自于 XSysMon.h的 是 上面显示的结构体的一部分
typedef struct {
u16 DeviceId; /< Unique ID of device */UINTPTR BaseAddress; /< Device base address */
int IncludeInterrupt; /< Supports Interrupt driven mode */u8 IpType; /< 1 - System Management */
/**< 0 - XADC/System Monoitor */
} XSysMon_Config;
接下来进行初始化
其实真正的初始化是 第35行的这句话
但是我们看看30行这句话
XSysMon_LookupConfig 这个函数 后面带有ID 就是先查找这个XDC ID是否存在 然后它下面马上跟了一句 不存在return 什么 就是这样
OK 我们接下来看35行的函数
XSysMon_CfgInitialize 从名字上我们就可以看出是初始化XDC的意思 具体分析下 用到了什么参数
我们索引进去会得到这样的原函数
int XSysMon_CfgInitialize(XSysMon *InstancePtr, XSysMon_Config *ConfigPtr,UINTPTR EffectiveAddr)
InstancePtr is a pointer to the XSysMon instance. 第一句是指向实例的指针
就是我们在上面定义的 驱动示例
下一句是ConfigPtr指向XSysMon设备配置结构。 指向了我们在主函数定义的结构体
第三句指的是 设备的基地址 正好来自于我们的上一个结构体中的内容
初始化完毕之后 我们设定进入安全模式
大概懂了