这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » NUCLEO-U083RC学习历程30-ADC同时采集多个通道数据

共2条 1/1 1 跳转至

NUCLEO-U083RC学习历程30-ADC同时采集多个通道数据

助工
2025-02-16 16:06:03     打赏

我们在使用STM32 的ADC采集外部的电压时,难免会遇到同时采集多路数据,今天就和大家分享一下,如何使用STM32cube MX同时采集多路ADC的数据。

一:方案设计目标:使用 ADC 外设转换多个通道。ADC 转换在扫描序列中连续执行。

为了方便调试,这里我们同时采集一路外部输入电压,两路内部ADC的电压;即ADC 配置为单转换模式,来自 SW 触发器。ADC 组常规(所有 STM32 器件的 ADC 上可用的默认组)的定序器配置为转换 3 个通道:1 个来自 GPIO 的通道,2 个内部通道:内部电压基准 VrefInt 和温度传感器。

为了不占用CPU资源,本节我们使用DMA的方式来采集ADC的数据;DMA 配置为以循环模式在 RAM 内存中以大小为 3 的元素数组(每个通道的转换数据一个数组地址)传输转换数据。

二:STM32cube MX 软件配置如下所示:

0216-6.png

如上图所示:这里我们使用的通道分别是 ADC1的通道8、内部电压值和内部温度值;

这里为了方便使用其他的方法,采集ADC值,这里我们使用DMA的方式采集;DMA配置如下所示:

0216-7.png

ADC配置代码如下所示:

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.LowPowerAutoPowerOff = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 3;
  hadc1.Init.DiscontinuousConvMode = ENABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_79CYCLES_5;
  hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_160CYCLES_5;
  hadc1.Init.OversamplingMode = DISABLE;
  hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_VREFINT;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  sConfig.Rank = ADC_REGULAR_RANK_3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

这里我们需要配置一下DMA的软件代码:

  if(hadc->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */

  /** Initializes the peripherals clocks
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_SYSCLK;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* Peripheral clock enable */
    __HAL_RCC_ADC_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration
    PA4     ------> ADC1_IN8
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Request = DMA_REQUEST_ADC;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC_COMP1_2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC_COMP1_2_IRQn);

主程序函数采集如下所示:

    if(ubDmaTransferStatus == 1)
    {
      uhADCxConvertedData_VrefAnalog_mVolt = VDDA_APPLI;

      /* Computation of ADC conversions raw data to physical values           */
      /* using LL ADC driver helper macro.                                    */
      uhADCxConvertedData_VoltageGPIO_mVolt        = __LL_ADC_CALC_DATA_TO_VOLTAGE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[0], LL_ADC_RESOLUTION_12B);
      uhADCxConvertedData_VrefInt_mVolt            = __LL_ADC_CALC_DATA_TO_VOLTAGE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[1], LL_ADC_RESOLUTION_12B);
      hADCxConvertedData_Temperature_DegreeCelsius = __LL_ADC_CALC_TEMPERATURE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[2], LL_ADC_RESOLUTION_12B);
			temperate=(float)hADCxConvertedData_Temperature_DegreeCelsius*(3.3/4096);				//电压值 
			temperate=(1.43-temperate)/0.0043+25;			//转换为温度值 	 
      /* Update status variable of DMA transfer */
      ubDmaTransferStatus = 0;
      /* Toggle LED 4 times */
      tmp_index = 4*2;
      while(tmp_index != 0)
      {
        BSP_LED_Toggle(LED4);
        HAL_Delay(LED_BLINK_FAST);
        tmp_index--;
      }
      HAL_Delay(500); /* Delay to highlight toggle sequence */
    }

ADC中断处理函数:当ADC数据转换完成,触发完成中断,在主程序内调用

/**
  * @brief  DMA transfer complete callback
  * @note   This function is executed when the transfer complete interrupt
  *         is generated
  * @retval None
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
  /* Update status variable of DMA transfer */
  ubDmaTransferStatus = 1;
}

整个项目执行过程解释如下:

ADC 每秒在每个转换开始时依次在扫描序列的 3 个通道之间执行一个通道转换(启用不连续模式)。

注意:如果禁用了不连续模式,则整个扫描序列将从一个转换开始转换为突发。

每个序列秩的 ADC 转换数据由 DMA 传输到数组 “uhADCxConvertedData” 中:

uhADCxConvertedData[0]:ADC 通道设置在序列秩 1 上(GPIO 作为模拟输入)

uhADCxConvertedData[1]:ADC 通道设置在序列秩 2 (VrefInt) 上

uhADCxConvertedData[2]:ADC 通道设置在序列秩 3(温度传感器)上

当 sequence 完成时,它从头开始: sequence 中的第一个通道,第一个数组地址中的数据传输(回滚)。

使用 LL ADC 驱动程序帮助程序宏将 ADC 转换原始数据计算为物理值:

连接到模拟电压电源 Vdda 的模拟参考电压值 (Vref+)(单位:mV)

GPIO 引脚上的电压值(其上映射为 ADC 通道,cf 引脚下方)(单位:mV)

内部电压基准 VrefInt 的值(单位:mV)

温度值(单位:摄氏度)

注: 模拟基准电压 (Vref+) 是根据内部电压基准 VrefInt 的 ADC 转换计算的,并用于计算其他转换数据。此电压应对应于文字 “VDDA_APPLI” 的值。当应用程序中的电压 Vref+ 值未知时,可以执行此过程。(在本例中,情况并非如此,因为目标板由 LDO 稳压器提供已知的恒定电压值“VDDA_APPLI”)。在 Vref+ 连接到 Vdd 的典型情况下,它允许推导出 Vdd 值。

0216-8.png





关键词: NUCLEO-U083RC     连续模式     多路同时采集    

专家
2025-02-16 19:59:08     打赏
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]