STM32内部参考电压+DMA精准采集电池电压
程序员文章站
2022-03-18 10:06:12
最近项目又遇到了电池电压采集,锂电池的电压范围是4.2到2.8一般,当锂电池低于3.3V时,单片机供电电压会小于3.3V,那么电池电压参考计算4096就不能对应3.3,所以必须采用内部参考电压。(我项目中用到的是RP104N331 LDO,实际上当电池电压在3.5V左右时,LDO输出就已经不是3.3V,严重影响精度)VREFINT_CAL = *(__IO uint16_t *)(0X1FF80078);首先需要从数据手册知道VREFINT_CAL 的地址信息,读出16的值,所以这里采用了 uint1...
最近项目又遇到了电池电压采集,锂电池的电压范围是4.2到2.8一般,当锂电池低于3.3V时,单片机供电电压会小于3.3V,那么电池电压参考计算4096就不能对应3.3,所以必须采用内部参考电压。(我项目中用到的是RP104N331 LDO,实际上当电池电压在3.5V左右时,LDO输出就已经不是3.3V,严重影响精度)
VREFINT_CAL = *(__IO uint16_t *)(0X1FF80078);
首先需要从数据手册知道VREFINT_CAL 的地址信息,读出16的值,所以这里采用了 uint16_t
同时stm32 开启两路ADC,一路是要采集的ADC,一路是内部参考电压
ADC_ChannelConfTypeDef sConfig = {0};
VREFINT_CAL = *(__IO uint16_t *)(0X1FF80078);
V_temp1 = 3.3 / 4096 * (float)ADC_buffer[0] / 0.6;
VDDA = 3 * VREFINT_CAL/(float)ADC_buffer[1];
V_temp2 = VDDA/4096 * (float)ADC_buffer[0] / 0.6;
printf("VDDA %f \r\n",VDDA);
printf("%f V %f V\r\n",V_temp1,V_temp2);
printf("%d %d\r\n",ADC_buffer[0],ADC_buffer[1]);
V1是常规的3.3V作为参考电压,VDDA可以通过VREFINT_CAL 计算得出,V_temp2是以VDDA得出,经过开关电源测试,V_temp2误差基本保持在0.01V
由于我们采集双通道,所以采用DMA传输
这里有几个参数需要注意,一个是ClockPrescaler,我这里采用ADC_CLOCK_ASYNC_DIV128,分频不同居然采集的电压不同,而且误差极大,这里一直找不到相关原因,另一个是LowPowerFrequencyMode,由于这里分频比较大,导致采用时钟很低,所以ENABLE,具体低于多少打开手册有要求,但是实测使能或者不使能差别并不是很大,最后在初始化完成之后需要增加校准函数HAL_ADCEx_Calibration_Start
下面是adc.c源码,这里的DMA是连续采集,只需在主函数开启一次HAL_ADC_Start_DMA(&hadc, (uint32_t *)ADC_buffer, 2)
/**
******************************************************************************
* File Name : ADC.c
* Description : This file provides code for the configuration
* of the ADC instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
/* USER CODE BEGIN 0 */
__IO uint32_t uwADCxConvertedValue1 = 0;
__IO uint32_t uwADCxConvertedValue2 = 0;
__IO uint16_t VREFINT_CAL ;
float V_temp1,V_temp2,VDDA;
extern uint32_t ADC_buffer[2];
/* USER CODE END 0 */
ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;
/* ADC init function */
void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV128;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = ENABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC GPIO Configuration
PA1 ------> ADC_IN1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC Init */
hdma_adc.Instance = DMA1_Channel1;
hdma_adc.Init.Request = DMA_REQUEST_0;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_adc) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc);
/* USER CODE BEGIN ADC1_MspInit 1 */
// if (HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED) != HAL_OK)
// {
// Error_Handler();
// }
/* USER CODE END ADC1_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */
/* USER CODE END ADC1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC GPIO Configuration
PA1 ------> ADC_IN1
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1);
/* ADC1 DMA DeInit */
HAL_DMA_DeInit(adcHandle->DMA_Handle);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
void Acquisition_voltage()
{
//float VDDA;
ADC_ChannelConfTypeDef sConfig = {0};
VREFINT_CAL = *(__IO uint16_t *)(0X1FF80078);
V_temp1 = 3.3 / 4096 * (float)ADC_buffer[0] / 0.6;
VDDA = 3 * VREFINT_CAL/(float)ADC_buffer[1];
V_temp2 = VDDA/4096 * (float)ADC_buffer[0] / 0.6;
printf("VDDA %f \r\n",VDDA);
printf("%f V %f V\r\n",V_temp1,V_temp2);
printf("%d %d\r\n",ADC_buffer[0],ADC_buffer[1]);
// //printf("VREFINT_CAL %d \r\n",VREFINT_CAL);
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
//
//
// if (HAL_ADC_Start(&hadc) != HAL_OK)
// {
// /* Start Conversation Error */
// Error_Handler();
// }
//
// HAL_ADC_PollForConversion(&hadc, 1000);
//
// /* Check if the continous conversion of regular channel is finished */
// if ((HAL_ADC_GetState(&hadc) & HAL_ADC_STATE_REG_EOC) == HAL_ADC_STATE_REG_EOC)
// {
// /*##-6- Get the converted value of regular channel ########################*/
// uwADCxConvertedValue1 = HAL_ADC_GetValue(&hadc);
//
// }
//
printf("uwADCxConvertedValue1 %d\r\n",uwADCxConvertedValue1);
// V_temp = 3.3 / 4096 * uwADCxConvertedValue1 / 0.6;
// printf("V_temp %f V\r\n",V_temp);
sConfig.Channel = ADC_CHANNEL_17;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
//
//
// if (HAL_ADC_Start(&hadc) != HAL_OK)
// {
// /* Start Conversation Error */
// Error_Handler();
// }
//
// HAL_ADC_PollForConversion(&hadc, 1000);
//
// /* Check if the continous conversion of regular channel is finished */
// if ((HAL_ADC_GetState(&hadc) & HAL_ADC_STATE_REG_EOC) == HAL_ADC_STATE_REG_EOC)
// {
// /*##-6- Get the converted value of regular channel ########################*/
// uwADCxConvertedValue2 = HAL_ADC_GetValue(&hadc);
// }
//
// printf("uwADCxConvertedValue2 %d\r\n",uwADCxConvertedValue2);
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
本文地址:https://blog.csdn.net/qq_36097683/article/details/108580900