前言
最近在学习小壁虎EFM32芯片的使用,需要用ADC采集电压,学习之中,遇到一些小问题和需要注意的地方,现将其记录下来,避免以后自己再次踩坑和往后可以学而时习之;好了,进入正题。
代码
主函数:
int main (void)
{
CHIP_Init();
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
/*
* 开启LE时钟,只要使用到低频外设,都需要开启该时钟
*/
CMU_ClockEnable(cmuClock_CORELE, true);
CMU_ClockEnable(cmuClock_GPIO, true);
adcInit(); /* ADC初始化 */
// EMU_EnterEM1(); /* 进入EM1等待ADC转换结束 */
while(1)
{
Voltage_Value = GetulVoltage_Value(); //获取电池电压
}
}
ADC初始化函数:
void adcInit (void)
{
CMU_ClockEnable(cmuClock_ADC0, true); /* 使能ADC模块时钟 */
ADC_Init_TypeDef tAdcInit = {
.ovsRateSel = adcOvsRateSel2, /* ADC过采样配置 */
.lpfMode = adcLPFilterBypass, /* 旁路输入滤波RC电路 */
.warmUpMode = adcWarmupNormal, /* 正常预热模式 */
.timebase = ADC_TimebaseCalc(0), /* 基时间配置 */
.prescale = ADC_PrescaleCalc(7000000, 0), /* ADC时钟分频配置 */
.tailgate = false /* 不使能Tailgate */
};
ADC_InitSingle_TypeDef tSingleInit = {
.prsSel = adcPRSSELCh0, /* 选择PRS通道0 */
.acqTime = adcAcqTime16, /* 配置采集时间为16周期 */
.reference = adcRef2V5, /* 使用VDD参考电压 */
.resolution = adcRes12Bit, /* 使用12位分辨率 */
.input = adcSingleInpCh5, /* 选择ADC通道 */
.diff = false, /* 不采用差分采集模式 */
.prsEnable = false, /* 禁能PRS输入 */
.leftAdjust = false, /* 使用右对准 */
.rep = false /* 不使用连续转换 */
};
ADC_Init(ADC0, &tAdcInit);
ADC_InitSingle(ADC0, &tSingleInit); /* 初始化ADC单次转换 */
/*
* 使能单次采集完成中断
*/
ADC_IntEnable(ADC0, ADC_IF_SINGLE);
NVIC_EnableIRQ(ADC0_IRQn);
}
需要注意地方和修改的地方:
1、参考基准电压:
.reference = adcRef2V5, / 使用VDD参考电压 /
需要更改参考电压,如果你使用蓄电池供电,一般不采用VDD作为参考电压,因为随着电池的使用,电压会降低,导致采集到的电压数据不准确;采用内部的电压作为基准电压最为稳妥,我选用了内部电压2.5V,EFM32也提供了几种参考电压供你选择,如下:
typedef enum
{ /** Internal 1.25V reference. */
adcRef1V25 = _ADC_SINGLECTRL_REF_1V25,
/** Internal 2.5V reference. */
adcRef2V5 = _ADC_SINGLECTRL_REF_2V5,
/** Buffered VDD. */
adcRefVDD = _ADC_SINGLECTRL_REF_VDD,
/** Internal differential 5V reference. */
adcRef5VDIFF = _ADC_SINGLECTRL_REF_5VDIFF,
/** Single ended ext. ref. from pin 6. */
adcRefExtSingle = _ADC_SINGLECTRL_REF_EXTSINGLE,
/** Differential ext. ref. from pin 6 and 7. */
adcRef2xExtDiff = _ADC_SINGLECTRL_REF_2XEXTDIFF,
/** Unbuffered 2xVDD. */
adcRef2xVDD = _ADC_SINGLECTRL_REF_2XVDD
} ADC_Ref_TypeDef;
2、ADC通道
.input = adcSingleInpCh5, / 选择ADC通道 /
根据你所选用GPIO口,确定通道,可参考芯片规格书PIN脚部分说明。
typedef enum
{
/* Differential mode disabled */
adcSingleInpCh0 = _ADC_SINGLECTRL_INPUTSEL_CH0, /**< Channel 0. */
adcSingleInpCh1 = _ADC_SINGLECTRL_INPUTSEL_CH1, /**< Channel 1. */
adcSingleInpCh2 = _ADC_SINGLECTRL_INPUTSEL_CH2, /**< Channel 2. */
adcSingleInpCh3 = _ADC_SINGLECTRL_INPUTSEL_CH3, /**< Channel 3. */
adcSingleInpCh4 = _ADC_SINGLECTRL_INPUTSEL_CH4, /**< Channel 4. */
adcSingleInpCh5 = _ADC_SINGLECTRL_INPUTSEL_CH5, /**< Channel 5. */
adcSingleInpCh6 = _ADC_SINGLECTRL_INPUTSEL_CH6, /**< Channel 6. */
adcSingleInpCh7 = _ADC_SINGLECTRL_INPUTSEL_CH7, /**< Channel 7. */
adcSingleInpTemp = _ADC_SINGLECTRL_INPUTSEL_TEMP, /**< Temperature reference. */
adcSingleInpVDDDiv3 = _ADC_SINGLECTRL_INPUTSEL_VDDDIV3, /**< VDD divided by 3. */
adcSingleInpVDD = _ADC_SINGLECTRL_INPUTSEL_VDD, /**< VDD. */
adcSingleInpVSS = _ADC_SINGLECTRL_INPUTSEL_VSS, /**< VSS. */
adcSingleInpVrefDiv2 = _ADC_SINGLECTRL_INPUTSEL_VREFDIV2, /**< Vref divided by 2. */
adcSingleInpDACOut0 = _ADC_SINGLECTRL_INPUTSEL_DAC0OUT0, /**< DAC output 0. */
adcSingleInpDACOut1 = _ADC_SINGLECTRL_INPUTSEL_DAC0OUT1, /**< DAC output 1. */
/* TBD: Use define when available */
adcSingleInpATEST = 15, /**< ATEST. */
/* Differential mode enabled */
adcSingleInpCh0Ch1 = _ADC_SINGLECTRL_INPUTSEL_CH0CH1, /**< Positive Ch0, negative Ch1. */
adcSingleInpCh2Ch3 = _ADC_SINGLECTRL_INPUTSEL_CH2CH3, /**< Positive Ch2, negative Ch3. */
adcSingleInpCh4Ch5 = _ADC_SINGLECTRL_INPUTSEL_CH4CH5, /**< Positive Ch4, negative Ch5. */
adcSingleInpCh6Ch7 = _ADC_SINGLECTRL_INPUTSEL_CH6CH7, /**< Positive Ch6, negative Ch7. */
/* TBD: Use define when available */
adcSingleInpDiff0 = 4 /**< Differential 0. */
} ADC_SingleInput_TypeDef;
3、ADC采集电压
uint16_t GetulVoltage_Value(void)
{
ADC_Start(ADC0, adcStartSingle); /* 启动单次转换 */
EMU_EnterEM1(); /* 进入EM1等待ADC转换结束 */
ulVoltage = ADC_DataSingleGet(ADC0) * 22300 / 4095; /* 计算采集到的电压值 */
return (ulVoltage);
}
需要注意的地方:
1、ulVoltage = ADC_DataSingleGet(ADC0) * 22300 / 4095;
“22300”怎样得到的?
采集电压图
电路图使用了串联分压,电压计算公式:R2/(R1+R2)*参考电压
R2:3.3M
R1:390K
参考电压:2.5V
完成以上的操作就可以正常的读取到电池电压了。
总结
本次记录和分享到此结束,其实ADC采集电压是平时最常用的一种模拟量转换,没有难度,初学者注意细节便能熟练灵活的使用到自己代码和电路之中。
完整函数:
#include "efm32.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_adc.h"
#include "em_lcd.h"
#include "em_chip.h"
#include "em_gpio.h"
#include "system.h"
#include "segmentlcd.h"
extern void adcInit(void);
uint16_t ulVoltage;
uint16_t Voltage_Value = 0;
uint16_t GetulVoltage_Value(void)
{
ADC_Start(ADC0, adcStartSingle); /* 启动单次转换 */
EMU_EnterEM1(); /* 进入EM1等待ADC转换结束 */
ulVoltage = ADC_DataSingleGet(ADC0) * 22300 / 4095; /* 计算采集到的电压值 */
return (ulVoltage);
}
int main (void)
{
CHIP_Init();
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
/*
* 开启LE时钟,只要使用到低频外设,都需要开启该时钟
*/
CMU_ClockEnable(cmuClock_CORELE, true);
CMU_ClockEnable(cmuClock_GPIO, true);
adcInit(); /* ADC初始化 */
// EMU_EnterEM1(); /* 进入EM1等待ADC转换结束 */
while(1)
{
Voltage_Value = GetulVoltage_Value(); //获取电池电压
}
}
void adcInit (void)
{
CMU_ClockEnable(cmuClock_ADC0, true); /* 使能ADC模块时钟 */
ADC_Init_TypeDef tAdcInit = {
.ovsRateSel = adcOvsRateSel2, /* ADC过采样配置 */
.lpfMode = adcLPFilterBypass, /* 旁路输入滤波RC电路 */
.warmUpMode = adcWarmupNormal, /* 正常预热模式 */
.timebase = ADC_TimebaseCalc(0), /* 基时间配置 */
.prescale = ADC_PrescaleCalc(7000000, 0), /* ADC时钟分频配置 */
.tailgate = false /* 不使能Tailgate */
};
ADC_InitSingle_TypeDef tSingleInit = {
.prsSel = adcPRSSELCh0, /* 选择PRS通道0 */
.acqTime = adcAcqTime16, /* 配置采集时间为16周期 */
.reference = adcRef2V5, /* 使用VDD参考电压 */
.resolution = adcRes12Bit, /* 使用12位分辨率 */
.input = adcSingleInpCh5, /* 选择ADC通道 */
.diff = false, /* 不采用差分采集模式 */
.prsEnable = false, /* 禁能PRS输入 */
.leftAdjust = false, /* 使用右对准 */
.rep = false /* 不使用连续转换 */
};
ADC_Init(ADC0, &tAdcInit);
ADC_InitSingle(ADC0, &tSingleInit); /* 初始化ADC单次转换 */
/*
* 使能单次采集完成中断
*/
ADC_IntEnable(ADC0, ADC_IF_SINGLE);
NVIC_EnableIRQ(ADC0_IRQn);
}
void ADC0_IRQHandler (void)
{
ADC_IntClear(ADC0, ADC_IF_SINGLE); /* 清ADC中断标志位 */
}