欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32L031 ADC管脚电压采样

程序员文章站 2022-06-30 17:12:54
...

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通道采样。
STM32L031 ADC管脚电压采样
本例程采用非DMA和中断的方式,配置参数如下
STM32L031 ADC管脚电压采样
保存后系统生成的代码如下:

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-