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-

猜你喜欢

转载自blog.csdn.net/hwytree/article/details/103333769
今日推荐