STM32L031 ADC管脚电压采样
STM32L031 ADC管脚电压采样
为了更精确的体现ADC对管脚采样的电压值,需要对当前的供电电压的变化也进行参考计算,涉及到STM32 Internal voltage reference (VREFINT) 即内部电压参考的应用。
VREFINT内部连接到ADC_IN17输入通道,VREFINT实际上是一个内部稳压低电压值,也就是芯片供电在一定范围(譬如1.65V~3.6V)应用时,这个电压不变。但是芯片ADC供电(有的芯片是单独的ADC供电接口,有的芯片ADC供电直接连接到芯片供电)变化时,对这个不变的VREFINT的采样值会产生变化,ST在出厂时,基于特定的芯片供电电压,将对VREFINT的采样值,存放在系统存储区域,可以读取出来使用。
本例程基于STM32CUBEIDE对PA0进行电压采样设计。
ADC配置
在STM32CUBEIDE的配置界面,选择ADC并使能IN0和Vrefint通道采样。
本例程采用非DMA和中断的方式,配置参数如下
保存后系统生成的代码如下:
ADC_HandleTypeDef hadc;
static void MX_ADC_Init(void);
/**
* @brief ADC Initialization Function
* @param None
* @retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** 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_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_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 = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
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();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
/**
* @brief ADC MSP Initialization
* This function configures the hardware resources used in this example
* @param hadc: ADC handle pointer
* @retval None
*/
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hadc->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */
/* USER CODE END ADC1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC GPIO Configuration
PA0-CK_IN ------> ADC_IN0
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
/**
* @brief ADC MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hadc: ADC handle pointer
* @retval None
*/
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
{
if(hadc->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
PA0-CK_IN ------> ADC_IN0
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0);
/* USER CODE BEGIN ADC1_MspDeInit 1 */
/* USER CODE END ADC1_MspDeInit 1 */
}
}
ADC采样函数
设计的思路是,每次采样管脚前,基于对Vrefint的采样读取,与ST出厂时配置的Vrefint值对比,获得当前的芯片ADC供电校正电压(对STM32L031,与芯片供电电压相同),从而对管脚电压采样时,用此实时校正的电压做为参考,得到对应的管脚电压值。
编写单次采样函数代码如下:
uint32_t GET_ADC(uint32_t Channel) {
uint32_t adc_conv_var;
extern ADC_HandleTypeDef hadc;
ADC_ChannelConfTypeDef adcConf;
__HAL_RCC_ADC1_CLK_ENABLE();
// Await the the Vrefint used by adc is set
while (__HAL_PWR_GET_FLAG(PWR_FLAG_VREFINTRDY) == RESET) {
};
// Deselect all channels
adcConf.Channel = ADC_CHANNEL_MASK;
adcConf.Rank = ADC_RANK_NONE;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
// Configure adc channel
adcConf.Channel = Channel;
adcConf.Rank = ADC_RANK_CHANNEL_NUMBER;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
// Calibrate ADC
HAL_ADC_Stop(&hadc);
HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
// Start converison
HAL_ADC_Start(&hadc);
// Waiting for the end of conversion
HAL_ADC_PollForConversion(&hadc, 20); // overtime 20ms
// Read result
adc_conv_var = HAL_ADC_GetValue(&hadc);
// Stop
HAL_ADC_Stop(&hadc);
// Deselect all channels
adcConf.Channel = ADC_CHANNEL_MASK;
adcConf.Rank = ADC_RANK_NONE;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
__HAL_RCC_ADC1_CLK_DISABLE();
return adc_conv_var;
}
编写多次采样平均值的函数如下:
uint32_t GET_ADC_AVG(uint32_t Channel, uint32_t times) {
uint32_t adc_conv_var;
uint64_t adc_acc = 0;
extern ADC_HandleTypeDef hadc;
ADC_ChannelConfTypeDef adcConf;
HAL_StatusTypeDef adc_polling_status;
uint32_t i;
__HAL_RCC_ADC1_CLK_ENABLE();
// Await Vrefint used by adc is set
while (__HAL_PWR_GET_FLAG(PWR_FLAG_VREFINTRDY) == RESET) {
};
// Deselect all channels
adcConf.Channel = ADC_CHANNEL_MASK;
adcConf.Rank = ADC_RANK_NONE;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
// Configure adc channel
adcConf.Channel = Channel;
adcConf.Rank = ADC_RANK_CHANNEL_NUMBER;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
// Calibrate ADC
HAL_ADC_Stop(&hadc);
HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
// Start conversion
HAL_ADC_Start(&hadc);
for (i = 0; i < times;) {
// Waiting for conversion end
adc_polling_status = HAL_ADC_PollForConversion(&hadc, 20); // overtime 20ms
if (adc_polling_status == HAL_OK) {
// read data
adc_acc += (uint64_t) HAL_ADC_GetValue(&hadc);
i++;
}
}
// Stop
HAL_ADC_Stop(&hadc);
// Deselect all channels
adcConf.Channel = ADC_CHANNEL_MASK;
adcConf.Rank = ADC_RANK_NONE;
HAL_ADC_ConfigChannel(&hadc, &adcConf);
__HAL_RCC_ADC1_CLK_DISABLE();
adc_conv_var = adc_acc / times;
return adc_conv_var;
}
执行采样和计算电压
在需要进行ADC采样和计算电压时,STM32L031d的Vrefint是在3V ADC供电电压下测试并写入系统存储区域, 如果当前应用的ADC供电电压为3.3V, 按照如下方式写代码:
#define Samle_Times 1000; //Sampling times for average
uint16_t i_VDD_CALI;
uint32_t i_VDD_VALUE;
uint32_t BATTER_VALUE;
double BATTERY_VOL;
i_VDD_CALI = (*((uint16_t *)(0x1FF80078)))*3/3.3;
i_VDD_VALUE = GET_ADC(ADC_CHANNEL_17); //Or i_VDD_VALUE = GET_ADC(ADC_CHANNEL_VREFINT);
BATTER_VALUE = GET_ADC_AVG(ADC_CHANNEL_0, Samle_Times);
BATTER_VALUE =(uint32_t)(BATTER_VALUE*(((double)i_VDD_CALI)/i_VDD_VALUE));
BATTERY_VOL = (((double) BATTER_VALUE) / 4096 * 3.3) ;
BATTERY_VOL就是得到的PA0管脚电压值。
对ADC_CHANNEL_VREFINT的采样,可以根据情况,也采用GET_ADC_AVG()函数使用采样平均值。
如采用中断或者DMA的方式,对管脚电压的采样值校正计算方式,可参考上述代码进行修改即可。
-End-
推荐阅读