STM32内部参考电压+DMA精准采集电池电压
最近项目又遇到了电池电压采集,锂电池的电压范围是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****/
STM32内部参考电压+DMA精准采集电池电压相关推荐
- STM32使用内部参考电压提高ADC采集准确度
我们在使用ADC采集外部电压时,一般默认参考电压为MCU的供电电压,例如单片机供电电压为3.3V时,我们计算采集电压的公式为: 假设12位ADC 采集电压=(AD值/4096)*3.3: 但是如果因为 ...
- STM32内部参考电压的使用
一.STM32的内部参照电压VREFINT和ADCx_IN17相连接,它的作用是相当于一个标准电压测量点(和MSP430不一样..),内部参照电压VREFINT只能出现在主ADC1中使用. 内部参照电 ...
- 带内部参考电压(VREFINT)校正的STM32 DMA 内置温度采集
笔者今天来介绍一下STM32ADC内置温度的采集,重点是通过内置参考电压来避免ADC参考电压VDDA对温度ADC采集的影响. 1.STM32ADC简介 stm32F4系列ADC,逐次趋近型AD.1 ...
- 基于STM32HAL库ADC+DMA模式,高精度采集电池电量与芯片内部温度方法 (48脚 使用内部参考电压方案)
目录 概述 1.原理图 2.在这先普及一下概念 3.通过查看STM32L0中文数据手册中301页,第14.10 小节 ,DataSheet 4.ADC通道转换模式的理解 5.STM32CubeMx工具 ...
- STM32 ADC采样使用内部参考电压
整理也能进步!写得清楚才能理解更深. [问题背景] 在使用ADC时,通常的用法是Vref+接电源VDD3.3V,然后计算时直接用3.3V做参考电压,但是这种方法忽略了一些情况如供电电压有可能随外部一些 ...
- STM32/APM32 用DMA采集ADC1多通道--标准库
本文使用的是APM32E103作为示例的, STM32F/E以及APM32F等系列同样适用. 一.ADC及其通道 ADC1:最多16个外部通道,2个内部通道.内部通道分别是温度传感器和参考电压 ①:温 ...
- stm32 精确电压测量法(内部参考电压)
芯片型号:stm32l051c8(其它型号请参考datasheet,仅供参考) 使用ADC采集电压时若使用外部参考电压,如果外部电压变化,且低于正常LDO工作电压时,输出的电压将发生改变,导致基准电压 ...
- STM8L051之通过ADC1与DMA读取内部参考电压,求取VDD电源电压---库函数版
stm8L051芯片内部的参考电压与电源电压有一定的关系, 这在芯片供电电压变化的情况下,测量外部ADC电压输入 提供一个确定的参考电压.这里提前厘清下:该内部参考 电压VREFINT 并非ADC 的 ...
- STM32使用ADC+DMA进行多通道模拟量采集 (踩坑及傻瓜式解析)
STM32使用ADC+DMA进行多通道模拟量采集 (踩坑及通俗解析) 利用STM32的片上外设可采集多个模拟量(如传感器数值),并在嵌入式程序中使用.如果只使用了一个通道,用时令ADC转换而后读取 ...
最新文章
- FFT对信噪比的增益计算
- 你多久没换过壁纸了?新年了,换一换吧!
- JAVA WEB知识总结之一--responserequest
- 笔记-中项案例题-2021年上-范围管理
- java gui 单选_java GUI编程(swing)之三swing单选框复选框组件
- java 蓝桥杯训练 数的统计
- 计算机设备安装属于劳务吗,​安装服务费属于劳务费吗
- java对象 引用 原理,java对象引用和对象值得行为
- using的一种用法
- python设置excel边框_用Python操作Excel电子表格?单元格边框如何设置?样式有哪些?...
- 你必须会的微服务之Hystrix熔断器
- 微软亚洲研究院2017年笔试编程题
- 百练2801解题报告---填词
- 关于ARM的22个常用概念--的确经典
- 天龙八部科举答题问题和答案(全6/8)
- 巴特勒船长-百年一遇的男人
- 2022-2028全球与中国锂电池隔膜市场现状及未来发展趋势
- 2022年卡塔尔世界杯的“科技与狠活”
- does not have write access to 'C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET File
- 瑞星2008正式版升级包 官方安装包