解决STM32 LL库下ADC DMA多通道连续扫描采集通道错乱问题

         cubemx配置ADC+DMA转换后,代码在 adc.c 中将ADC_REG_InitStruct.DMATransfer 属性设置为:

        LL_ADC_REG_DMA_TRANSFER_UNLIMITED 或者

        LL_ADC_REG_DMA_TRANSFER_LIMITED(在MX中配置时只有这两选项)

,都会在初始化ADC时同时使能DMA。

/* ADC init function */
void MX_ADC_Init(void)
{

  /* USER CODE BEGIN ADC_Init 0 */

  /* USER CODE END ADC_Init 0 */

  LL_ADC_InitTypeDef ADC_InitStruct = {0};
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_ADC1);

  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
  /**ADC GPIO Configuration
  PA0   ------> ADC_IN0
  PA5   ------> ADC_IN5
  PA6   ------> ADC_IN6
  PA7   ------> ADC_IN7
  PB0   ------> ADC_IN8
  PB1   ------> ADC_IN9
  */
  GPIO_InitStruct.Pin = OTP_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(OTP_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = VSENSE_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(VSENSE_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = OVP1_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(OVP1_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = OVP2_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(OVP2_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SIGNAL1_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(SIGNAL1_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SIGNAL2_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(SIGNAL2_GPIO_Port, &GPIO_InitStruct);

  /* ADC DMA Init */

  /* ADC Init */
  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD);

  /* USER CODE BEGIN ADC_Init 1 */

  /* USER CODE END ADC_Init 1 */

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_0);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_5);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_6);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_7);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_8);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_9);

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  ADC_InitStruct.Clock = LL_ADC_CLOCK_ASYNC;
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  LL_ADC_REG_SetSequencerScanDirection(ADC1, LL_ADC_REG_SEQ_SCAN_DIR_FORWARD);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_239CYCLES_5);
  /* USER CODE BEGIN ADC_Init 2 */

  /* USER CODE END ADC_Init 2 */

}

开启通道转换前,如果调用了 LL_ADC_StartCalibration 开启ADC校准,ADC的DMA转换通道顺序会错乱(比如原本通道0的数据会跑掉其他通道去),在该函数的表述中就有 

        In case of usage of ADC with DMA transfer:
  *         On this STM32 serie, ADC DMA transfer request should be disabled
  *         during calibration:  * @note   In case of usage of ADC with DMA transfer:
  *         On this STM32 serie, ADC DMA transfer request should be disabled
  *         during calibration:
  *         Calibration factor is available in data register
  *         and also transfered by DMA.
  *         To not insert ADC calibration factor among ADC conversion data
  *         in array variable, DMA transfer must be disabled during
  *         calibration.
  *         (DMA transfer setting backup and disable before calibration,
  *         DMA transfer setting restore after calibration.
  *         Refer to functions @ref LL_ADC_REG_GetDMATransfer(),
  *         @ref LL_ADC_REG_SetDMATransfer() ).

/**
  * @brief  Start ADC calibration in the mode single-ended
  *         or differential (for devices with differential mode available).
  * @note   On this STM32 serie, a minimum number of ADC clock cycles
  *         are required between ADC end of calibration and ADC enable.
  *         Refer to literal @ref LL_ADC_DELAY_CALIB_ENABLE_ADC_CYCLES.
  * @note   In case of usage of ADC with DMA transfer:
  *         On this STM32 serie, ADC DMA transfer request should be disabled
  *         during calibration:
  *         Calibration factor is available in data register
  *         and also transfered by DMA.
  *         To not insert ADC calibration factor among ADC conversion data
  *         in array variable, DMA transfer must be disabled during
  *         calibration.
  *         (DMA transfer setting backup and disable before calibration,
  *         DMA transfer setting restore after calibration.
  *         Refer to functions @ref LL_ADC_REG_GetDMATransfer(),
  *         @ref LL_ADC_REG_SetDMATransfer() ).
  * @note   On this STM32 serie, setting of this feature is conditioned to
  *         ADC state:
  *         ADC must be ADC disabled.
  * @rmtoll CR       ADCAL          LL_ADC_StartCalibration
  * @param  ADCx ADC instance
  * @retval None
  */
__STATIC_INLINE void LL_ADC_StartCalibration(ADC_TypeDef *ADCx)
{
  /* Note: Write register with some additional bits forced to state reset     */
  /*       instead of modifying only the selected bit for this function,      */
  /*       to not interfere with bits with HW property "rs".                  */
  MODIFY_REG(ADCx->CR,
             ADC_CR_BITS_PROPERTY_RS,
             ADC_CR_ADCAL);
}

 由于在初始化时开启了DMA使能,会导致ADC在校准时产生的数据也被DMA搬运;应在 LL_ADC_StartCalibration 前 ADC_REG_InitStruct.DMATransfer 设置为 LL_ADC_REG_DMA_TRANSFER_NONE

LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);

关闭ADCDMA转换使能;

物联沃分享整理
物联沃-IOTWORD物联网 » 解决STM32 LL库下ADC DMA多通道连续扫描采集通道错乱问题

发表评论