这个问题困扰我很久了,网上关于stm32l4的资料也不多,本来打算用 cubeMX配置一下ADC再测一把的,但cubeMX上的ADC1 没有IN0,也就没法计算Vref。
如果哪位大神调测过这部分代码,帮忙看看,或者直接贴已调过的代码也行,多谢!!!!
**根据手册给出的计算公式,求芯片的供电电压Vbat的电压需要先计算Vdda的电压,再计算vbat的电压,如下图所示:
**
其中的相关参数见下图:
问题是:用这个计算方法,算出来的电压值误差0.15V左右:
调试的数据如下:
代码中使用的计算公式为:
power.vbatVal = 9 * VREFINT_CAL * vbatData / vrefData / 4095;
其中:
#define VREFINT_CAL (*(u16 *)0x1FFF75AA) // 如图,值为1652
vbatData为Vbat/3 的采样值,所以公式中是*9(3*3)
vbatData 和 vrefData 均为10次采样数据的平均值
如果以上的计算公式没有问题,那就要看配置了,上代码了啊:
// 内部供电电压 ADC1_IN0
// PA0 ADC1_IN5 配置为模拟输入
// 内部温度传感器 ADC1_IN17
void freqAdcInit(void)
{
// DMA 设置
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_ADC_CLK_ENABLE();
RCC->APB2RSTR|=1<<8; //ADCs 复位
RCC->APB2RSTR&=~(1<<8); //复位结束
// ADC设置
MODIFY_REG(ADC1->CR, ADC_CR_DEEPPWD_Msk, (0UL << ADC_CR_DEEPPWD_Pos)); // DEEPPWD: Deep-power-down enable
MODIFY_REG(ADC1->CR, ADC_CR_DEEPPWD_Msk, (0UL << ADC_CR_DEEPPWD_Pos)); // DEEPPWD: Deep-power-down enable
MODIFY_REG(ADC1->CR, ADC_CR_ADVREGEN_Msk, (1UL << ADC_CR_ADVREGEN_Pos)); // ADVREGEN: ADC voltage regulator enable
MODIFY_REG(ADC1->CFGR, ADC_CFGR_DISCEN_Msk, (0UL << ADC_CFGR_DISCEN_Pos)); // 失能不连续模式
MODIFY_REG(ADC1->CFGR, ADC_CFGR_CONT_Msk, (1UL << ADC_CFGR_CONT_Pos)); // 使能连续模式
MODIFY_REG(ADC1->CFGR, ADC_CFGR_OVRMOD_Msk, (1UL << ADC_CFGR_OVRMOD_Pos)); // 过采样
CLEAR_BIT(ADC1->CFGR, ADC_CFGR_ALIGN); // 0 为右对齐
MODIFY_REG(ADC1->CFGR, ADC_CFGR_RES, ADC_RESOLUTION_12B); // 12位
SET_BIT(ADC1->CFGR, ADC_CFGR_DMACFG); // DMA循环内存访问
SET_BIT(ADC1->CFGR, ADC_CFGR_DMAEN); // 使能DMA
MODIFY_REG(ADC1->SMPR1, ADC_SMPR1_SMP0, (0x7UL << ADC_SMPR1_SMP0_Pos)); // 采样时间47.5T
MODIFY_REG(ADC1->SMPR1, ADC_SMPR1_SMP5, (0x7UL << ADC_SMPR1_SMP5_Pos)); // 采样时间47.5T
MODIFY_REG(ADC1->SMPR2, ADC_SMPR2_SMP17, (0x7UL << ADC_SMPR2_SMP17_Pos)); // 采样时间47.5T
MODIFY_REG(ADC1->CFGR, ADC_CFGR_DISCNUM, (0x0UL << ADC_CFGR_DISCNUM_Pos)); //
MODIFY_REG(ADC1->SQR1, ADC_SQR1_L, ((ADC_CHANNELS - 1) << ADC_SQR1_L_Pos)); // 规则采样个数3 要-1
// MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ1, (0x0UL << ADC_SQR1_SQ1_Pos)); // 规则采样 1 ---- IN0
MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ1, (0x5UL << ADC_SQR1_SQ1_Pos)); // 规则采样 2 ---- IN5
// MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ3, (0x11UL << ADC_SQR1_SQ3_Pos)); // 规则采样 3 ---- IN17 温度
//MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_PRESC_Msk, (0x7UL << ADC_CCR_PRESC_Pos)); // 16分频
MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_CKMODE_Msk, (0x1UL << ADC_CCR_CKMODE_Pos)); // HCLK/1 时钟
}
void freqADC_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_PL_Msk, (0x3UL << DMA_CCR_PL_Pos)); // Very high
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_MSIZE_Msk, (0x2UL << DMA_CCR_MSIZE_Pos)); // 00: 8-bits 01: 16-bits 10: 32-bits 11: Reserved
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_PSIZE_Msk, (0x2UL << DMA_CCR_PSIZE_Pos)); // 00: 8-bits 01: 16-bits 10: 32-bits 11: Reserved
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_MINC_Msk, (0x1UL << DMA_CCR_MINC_Pos)); // Memory increment mode
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_PINC_Msk, (0x0UL << DMA_CCR_PINC_Pos)); // Peripheral increment mode
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_CIRC_Msk, (0x1UL << DMA_CCR_CIRC_Pos)); // Circular mode
MODIFY_REG(DMA1_Channel1->CCR, DMA_CCR_DIR_Msk, (0x0UL << DMA_CCR_DIR_Pos)); // DIR: Data transfer direction 0: Read from peripheral
MODIFY_REG(DMA1_Channel1->CNDTR, DMA_CNDTR_NDT_Msk, ((DMA_DATALEN * ADC_CHANNELS) << DMA_CNDTR_NDT_Pos)); // the remaining bytes to be transmitted
DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR); // 外设地址
DMA1_Channel1->CMAR = (uint32_t)(fftData.adcDMAData); // 内存地址
}
/* 检查电池余量 */
void batteryCheckTask(void)
{
float vrefData, vbatData, persent;
// 非电池供电不检查
if (power.powerFlag != Power_Barttery) {
return;
}
// step1 采集 vref
ADC_DMA_Stop();
MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_VREFEN_Msk, ADC_CCR_VREFEN); // 使能参考电压
MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ1, (0UL << ADC_SQR1_SQ1_Pos)); // 规则采样 1 ---- IN0
MODIFY_REG(DMA1_Channel1->CNDTR, DMA_CNDTR_NDT_Msk, ((POWER_DMA_DATA_LEN) << DMA_CNDTR_NDT_Pos)); // the remaining bytes to be transmitted
DMA1_Channel1->CMAR = (uint32_t)(power.vrefData); // 内存地址
ADC_DMA_Start();
delay_ms(4);
// step2 采集 vbat
ADC_DMA_Stop();
MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_VBATEN_Msk, ADC_CCR_VBATEN); // 使能BAT电压
MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ1, (18UL << ADC_SQR1_SQ1_Pos)); // 规则采样 1 ---- IN18
MODIFY_REG(DMA1_Channel1->CNDTR, DMA_CNDTR_NDT_Msk, ((POWER_DMA_DATA_LEN) << DMA_CNDTR_NDT_Pos)); // the remaining bytes to be transmitted
DMA1_Channel1->CMAR = (uint32_t)(power.vbatData); // 内存地址
ADC_DMA_Start();
delay_ms(4);
// step3 复位adc dma
ADC_DMA_Stop();
MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_VBATEN_Msk, 0); // 失能BAT电压
MODIFY_REG(ADC1_COMMON->CCR, ADC_CCR_VREFEN_Msk, 0); // 失能参考电压
MODIFY_REG(ADC1->SQR1, ADC_SQR1_SQ1, (5UL << ADC_SQR1_SQ1_Pos)); // 规则采样 1 ---- IN5
MODIFY_REG(DMA1_Channel1->CNDTR, DMA_CNDTR_NDT_Msk, ((DMA_DATALEN * ADC_CHANNELS) << DMA_CNDTR_NDT_Pos)); // the remaining bytes to be transmitted
DMA1_Channel1->CMAR = (uint32_t)(fftData.adcDMAData); // 内存地址
ADC_DMA_Start();
// step4 计算Vbat电压
vrefData = arrayAve(power.vrefData, POWER_DMA_DATA_LEN);
vbatData = arrayAve(power.vbatData, POWER_DMA_DATA_LEN);
power.vbatVal = 9 * VREFINT_CAL * vbatData / vrefData / 4095;
persent = 100.0f * (power.vbatVal - VBAT_VALTEAGE_MIN) / (VBAT_VALTEAGE_MAX - VBAT_VALTEAGE_MIN);
persent = persent > 100 ? 100 : persent;
persent = persent < 0 ? 0 : persent;
if (persent != power.batteryRemainPersent) {
power.batteryRemainPersent = persent;
power.powerFlag_old = Power_NULL;
}
}