你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

stm32g070rbt6 ADC1 多通道循环自动采样DMA传输到ram,采样时间要设置得很长采样值才能相对准确

[复制链接]
易~ 提问时间:2026-2-2 15:01 / 未解决
stm32g070rbt6 ADC1 多通道循环自动采样DMA传输到ram,采样值不准,而且开启校准的话值会更不准


/* USER CODE BEGIN Header */
/**

---

* @file           : main.c
* @brief          : Main program body

---

* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*

---

*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint16_t adc_buffer[3]={0};
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//                /************************* ADC?? ???? *************************/
//    //
//    if (LL_ADC_IsEnabled(ADC1) == 1) {
//        LL_ADC_Disable(ADC1);
//    }
//    //
//    LL_ADC_StartCalibration(ADC1);
//    //
//    Timeout = ADC_CALIBRATION_TIMEOUT_MS;
//    while (LL_ADC_IsCalibrationOnGoing(ADC1) == 1) {
//        if (LL_SYSTICK_IsActiveCounterFlag()) {
//            if (Timeout-- == 0) {
//                Error_Handler();
//            }
//        }
//    }
//    /********************************************************************/
/* USER CODE END 0 */

/**

* @brief  The application entry point.
* @retval int
  */
  int main(void)
  {

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, 3);

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

/**

* @brief System Clock Configuration
* @retval None
  */
  void SystemClock_Config(void)
  {
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
  while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
  {
  }

/* HSI configuration and activation */
LL_RCC_HSI_Enable();
while(LL_RCC_HSI_IsReady() != 1)
{
}

/* Main PLL configuration and activation */
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_1, 8, LL_RCC_PLLR_DIV_2);
LL_RCC_PLL_Enable();
LL_RCC_PLL_EnableDomain_SYS();
while(LL_RCC_PLL_IsReady() != 1)
{
}

/* Set AHB prescaler*/
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);

/* Sysclk activation on the main PLL */
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}

/* Set APB1 prescaler*/
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_Init1msTick(64000000);
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
LL_SetSystemCoreClock(64000000);
}

/**

* @brief ADC1 Initialization Function
* @param None
* @retval None
  */
  static void MX_ADC1_Init(void)
  {

/* USER CODE BEGIN ADC1_Init 0 */
#define USE_TIMEOUT 1
#define ADC_CALIBRATION_TIMEOUT_MS 100U
/* USER CODE END ADC1_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_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
/**ADC1 GPIO Configuration
PA2   ------> ADC1_IN2
PA3   ------> ADC1_IN3
PA6   ------> ADC1_IN6
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* ADC1 DMA Init */

/* ADC1 Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_ADC1);

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 ADC1_Init 1 */
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&ADC1->DR);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t) adc_buffer);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, (uint32_t) 3);
/* USER CODE END ADC1_Init 1 */

/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/

#define ADC_CHANNEL_CONF_RDY_TIMEOUT_MS ( 1U)
#if (USE_TIMEOUT == 1)
uint32_t Timeout ; /* Variable used for Timeout management */
#endif /* USE_TIMEOUT */

ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2;
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);
LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);

/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_3RANKS;
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED;
ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN;
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_39CYCLES_5);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_39CYCLES_5);
LL_ADC_DisableIT_EOC(ADC1);
LL_ADC_DisableIT_EOS(ADC1);

/* Enable ADC internal voltage regulator */
LL_ADC_EnableInternalRegulator(ADC1);
/* Delay for ADC internal voltage regulator stabilization. */
/* Compute number of CPU cycles to wait for, from delay in us. */
/* Note: Variable divided by 2 to compensate partially */
/* CPU processing cycles (depends on compilation optimization). */
/* Note: If system core clock frequency is below 200kHz, wait time */
/* is only a few CPU processing cycles. */
__IO uint32_t wait_loop_index;
wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
while(wait_loop_index != 0)
{
wait_loop_index--;
}

        /************************* ADC?? ???? *************************/
//
//    if (LL_ADC_IsEnabled(ADC1) == 1) {
//        LL_ADC_Disable(ADC1);
//    }
//    //
//    LL_ADC_StartCalibration(ADC1);
//    //
//    Timeout = ADC_CALIBRATION_TIMEOUT_MS;
//    while (LL_ADC_IsCalibrationOnGoing(ADC1) == 1) {
//        if (LL_SYSTICK_IsActiveCounterFlag()) {
//            if (Timeout-- == 0) {
//                Error_Handler();
//            }
//        }
//    }
/********************************************************************/

/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_2);

/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_COMMON_1);

/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_3);

/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_COMMON_1);

/** Configure Regular Channel
*/
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_6);

/* Poll for ADC channel configuration ready */
#if (USE_TIMEOUT == 1)
Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
#endif /* USE_TIMEOUT */
while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0)
{
#if (USE_TIMEOUT == 1)
/* Check Systick counter flag to decrement the time-out value */
if (LL_SYSTICK_IsActiveCounterFlag())
{
if(Timeout-- == 0)
{
Error_Handler();
}
}
#endif /* USE_TIMEOUT */
}
/* Clear flag ADC channel configuration ready */
LL_ADC_ClearFlag_CCRDY(ADC1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_6, LL_ADC_SAMPLINGTIME_COMMON_1);
/* USER CODE BEGIN ADC1_Init 2 */
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); //
LL_ADC_Enable(ADC1);
//
while(LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0);
LL_ADC_ClearFlag_ADRDY(ADC1);
LL_ADC_REG_StartConversion(ADC1);
/* USER CODE END ADC1_Init 2 */

}

/**

* Enable DMA controller clock
  */
  static void MX_DMA_Init(void)
  {

/* Init with LL driver */
/* DMA controller clock enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
//NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
//NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Ch4_7_DMAMUX1_OVR_IRQn interrupt configuration */
//NVIC_SetPriority(DMA1_Ch4_7_DMAMUX1_OVR_IRQn, 0);
//NVIC_EnableIRQ(DMA1_Ch4_7_DMAMUX1_OVR_IRQn);

}

/**

* @brief GPIO Initialization Function
* @param None
* @retval None
  */
  static void MX_GPIO_Init(void)
  {
  /* USER CODE BEGIN MX_GPIO_Init_1 */
  /* USER CODE END MX_GPIO_Init_1 */

/* GPIO Ports Clock Enable */
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);

/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**

* @brief  This function is executed in case of error occurrence.
* @retval None
  */
  void Error_Handler(void)
  {
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
  }

#ifdef  USE_FULL_ASSERT
/**

* @brief  Reports the name of the source file and the source line number
* where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
    */
    void assert_failed(uint8_t *file, uint32_t line)
    {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */
收藏 评论17 发布时间:2026-2-2 15:01

举报

17个回答
butterflyspring 回答时间:2026-2-4 14:05:38
这个电阻值的分压电路,决定了它对ADC就是个高内阻电路,


不过它对电池电压检测,没有必要连续检测。定时间隔检测一下就好了。

这样它的采样时间长点也没问题。
xmshao 回答时间:2026-2-4 10:01:34

易~ 发表于 2026-2-3 17:10
硬件检查过了,比较稳定的产品,之前是单次多通道转换,现在需求变更只能改成连续多通道DMA的方式,
之前 ...

[md]采样电路的输入阻抗太大了,R27 R28同时缩小10倍吧。

butterflyspring 回答时间:2026-2-3 17:26:36
易~ 发表于 2026-2-3 17:10
[md]硬件检查过了,比较稳定的产品,之前是单次多通道转换,现在需求变更只能改成连续多通道DMA的方式,
之前 ...

这个电路的确是对ADC采样有要求,需要更长的采样时间。


两个电阻比较大,那么它上面的电流就非常小,对ADC的采样电容充电时间必然会长一些。

对于ADC来说,就是一个高内阻。它的电压采样是有难度的。不考虑成本的话,加个跟随器是最好的。

你可以将这个分压电阻等比例减小,估计采样速度就可以提高些。

xmshao 回答时间:2026-2-3 16:55:09

易~ 发表于 2026-2-3 14:22
结果异常,采样时间要设置得很长采样值才能和实际值差距小,可能和时钟有关,单次采样的时候采样时间很短也可 ...

看你的补充描述,咋看起来有点逆天的味道。

问题很可能根本不是ADC本身的问题,先把芯片本身坏损原因除外,我怀疑你硬件系统有问题。建议你重点检查下你的硬件这块,尤其供电、接地这些。

时钟配置这块尽量使用CubeMx配置,至于基于HAL库还是LL库创建工程倒无所谓。如果单纯手动

操作寄存器,万一配置了不合适的时钟频率恐难以一时发现。

ADC本身配合DMA是完全可以正常工作的,这点放心。我随便选择了3个通道做了测试,2个通道接固定电压信号,另一通道悬空。ADC工作时钟16MHz, 设置12.5CLK采样时间。结果是正常的。

image.png

butterflyspring 回答时间:2026-2-3 15:51:27
易~ 发表于 2026-2-3 14:22
结果异常,采样时间要设置得很长采样值才能和实际值差距小,可能和时钟有关,单次采样的时候采样时间很短也可 ...

可以增加一个通道采集内部参考电压 VREFINT。


这个电压是固定电压,试试采样时间是否也需要在多通道扫描下增加才能采集的准。

通过这个方式判断ADC的采样时间需拉长是硬件问题,还是采样时刻问题。


butterflyspring 回答时间:2026-2-2 17:58:52
建议楼主先从Cube MX 开始,先用HAL 库生成代码。这样方便更多的小伙伴交流


然后根据需要再改回LL库。 好处是有了参照。 避免以往一些细节

有些代码需要前后关联,或者顺序。有了成功的HAL库生成的代码,按照它的顺序,不容易出错。

还有一种方式混合编程,也可以节省一定代码空间,又能完成高效正确的初始化配置。
易~ 回答时间:2026-2-3 09:39:26

ADC单次和自动连续转换的时钟差异

butterflyspring 发表于 2026-2-2 17:58
建议楼主先从Cube MX 开始,先用HAL 库生成代码。这样方便更多的小伙伴交流。

和hal版本对比过了,hal版本的采样时钟也要设置得很慢才能采样到一定精度的值,而且采样时间长了精度会和时间短了一样不高.
之前的程序可以,之前的程序使用的是当次转换非DMA模式,有两路ADC采样,用于RFID检卡和检测电池电压,因为需求变更,需要再增加一路ADC用于检测直流有刷电机的电枢电流,由于电机ADC要在中断里面采样(10ms的采样周期)用于电机堵转控制,所以三路ADC的实现方式用自动连续转换DMA传输的方式,之前在同一个的硬件上使用下面的配置如下,ADC转换时间很短,原帖上面是需求变更后的程序,
当前的程序要达到一定的精度的话要把时钟配置成下面这样,时钟配置和之前的差异很大,不清楚这是什么原因

:
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_SYSCLK);

LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV16);
LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_160CYCLES_5);

之前的单次转换程序:

/* Timeout values for ADC operations. */
/* (calibration, enable settling time, disable settling time, ...)          */
/* Values defined to be higher than worst cases: low clock frequency,       */
/* maximum prescalers.                                                      */
/* Unit: ms                                                                 */
#define ADC_CHANNEL_CONF_RDY_TIMEOUT_MS  (   1U)
#define ADC_CALIBRATION_TIMEOUT_MS       (   1U)
#define ADC_ENABLE_TIMEOUT_MS            (   1U)
#define ADC_DISABLE_TIMEOUT_MS           (   1U)
#define ADC_STOP_CONVERSION_TIMEOUT_MS   (   1U)
#define ADC_CONVERSION_TIMEOUT_MS        (4000U)

/**

* @brief ADC1 Initialization Function
* @param None
* @retval None
  */
  static int MX_ADC1_Init(void)
  {
  unsigned int Timeout ; /* Variable used for Timeout management */

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_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_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);

  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  /**ADC1 GPIO Configuration
  PA2   ------> ADC1_IN2
  PA3   ------> ADC1_IN3
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  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);
  LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE);

  /* Poll for ADC channel configuration ready */

  Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;

  while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0){
  /* Check Systick counter flag to decrement the time-out value */
  if (LL_SYSTICK_IsActiveCounterFlag()){
  if(Timeout-- == 0)
  {
  print_err("LL ADC REG SetSequencerConfigurable error...\n");
  return MID_ERROR_ERROR;
  }
  }
  }
  /* Clear flag ADC channel configuration ready */
  /// 经过测试,要想用非dma和中断模式只有这样配置可以正确进行多通道转换:扫描模式+单次转换模式+间断转换模式(每个间断组一个通道)
  LL_ADC_ClearFlag_CCRDY(ADC1);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS;
  //ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  //ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_1RANK;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_CONTINUOUS;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);

  LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV1);
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_1CYCLE_5);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_1CYCLE_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);

  /* Enable ADC internal voltage regulator */
  LL_ADC_EnableInternalRegulator(ADC1);

  //LL_ADC_REG_SetSequencerChAdd(ADC1, LL_ADC_CHANNEL_3 | LL_ADC_CHANNEL_8);
  /* Delay for ADC internal voltage regulator stabilization. */
  /* Compute number of CPU cycles to wait for, from delay in us. */
  /* Note: Variable divided by 2 to compensate partially */
  /* CPU processing cycles (depends on compilation optimization). */
  /* Note: If system core clock frequency is below 200kHz, wait time */
  /* is only a few CPU processing cycles. */
  unsigned int wait_loop_index;
  wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
  while(wait_loop_index != 0)
  {
  wait_loop_index--;
  }

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_2);
  /* Poll for ADC channel configuration ready */
  Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
  while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0){
  /* Check Systick counter flag to decrement the time-out value */
  if (LL_SYSTICK_IsActiveCounterFlag()){
  if(Timeout-- == 0)
  {
  print_err("LL ADC REG SetSequencerRanks error...\n");
  return MID_ERROR_ERROR;
  }
  }

  }
  /* Clear flag ADC channel configuration ready */
  LL_ADC_ClearFlag_CCRDY(ADC1);
  LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_COMMON_1);

  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_3);
  /* Poll for ADC channel configuration ready */
  Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
  while (LL_ADC_IsActiveFlag_CCRDY(ADC1) == 0){
  /* Check Systick counter flag to decrement the time-out value */
  if (LL_SYSTICK_IsActiveCounterFlag()){
  if(Timeout-- == 0)
  {
  print_err("LL ADC REG SetSequencerRanks error...\n");
  return MID_ERROR_ERROR;
  }
  }
  }
  /* Clear flag ADC channel configuration ready */
  LL_ADC_ClearFlag_CCRDY(ADC1);
  LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_COMMON_1);

  /* USER CODE BEGIN ADC1_Init 2 */

  Timeout = ADC_CHANNEL_CONF_RDY_TIMEOUT_MS;
  LL_ADC_Enable(ADC1);//开启ADC
  while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0)
  {
  if (LL_SYSTICK_IsActiveCounterFlag()){
  if(Timeout-- == 0)
  {
  print_err("LL ADC Enable error...\n");
  return MID_ERROR_ERROR;
  }
  }
  }//等待ADC准备好

  return MID_ERROR_OK;
  }

/***************************************************************************************
** 函数名称: ConversionStartPoll_ADC_GrpRegular
** 功能描述: 开始ADC规则转换
** 参    数: ADCx                        ADC instance
** 返 回 值: None
****************************************************************************************/
static int ConversionStartPoll_ADC_GrpRegular(ADC_TypeDef *ADCx)
{
unsigned int Timeout = ADC_CONVERSION_TIMEOUT_MS;

if ((LL_ADC_IsEnabled(ADCx) == 1) && \
  (LL_ADC_IsDisableOngoing(ADCx) == 0) && \
  (LL_ADC_REG_IsConversionOngoing(ADCx) == 0))
{
        LL_ADC_REG_StartConversion(ADCx);//开始ADC转换
}
else
{
        //*错误:无法执行ADC转换启动*/
        print_err("adc enable failed...\n");
        return MID_ERROR_ERROR;
}

while (LL_ADC_IsActiveFlag_EOC(ADCx) == 0)
{
        if (LL_SYSTICK_IsActiveCounterFlag()){
                if(Timeout-- == 0)
                {
                        print_err("adc Conversion timeout...\n");
                        LL_ADC_ClearFlag_EOC(ADCx);//清除转换完成标志
                        return MID_ERROR_ERROR;
                }
        }
}//等待转换完成

LL_ADC_ClearFlag_EOC(ADCx);//清除转换完成标志

return MID_ERROR_OK;
}

/***************************************************************************************************************/

void adc_init_service(void)
{
MX_ADC1_Init();
}

void adc_reinit_service()
{
#if 1
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC);

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
/**ADC1 GPIO Configuration
PA3   ------> ADC1_IN3
PB0   ------> ADC1_IN8
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
#endif
}

void adc_deinit_service(void)
{
LL_ADC_REG_StopConversion(ADC1);
LL_ADC_ClearFlag_EOC(ADC1);//清除转换完成标志
LL_ADC_ClearFlag_ADRDY(ADC1);//清除ADRDY标志
LL_ADC_DisableInternalRegulator(ADC1);
LL_ADC_Disable(ADC1);//关闭ADC
LL_ADC_DeInit(ADC1);
}

//--------------------------------------------------------------------------------------------------------------//
static int get_adc_value(unsigned short *value)
{
int idx = 0;
unsigned int Timeout = ADC_CONVERSION_TIMEOUT_MS;

if(!LL_ADC_IsEnabled(ADC1))
{
        LL_ADC_Enable(ADC1);
        while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0){
                 if (LL_SYSTICK_IsActiveCounterFlag()){
                        if(Timeout-- == 0)
                        {
                                return MID_ERROR_ERROR;
                        }
                }
        }//等待ADC准备好
}


for(idx=0; idx<2; idx++)
{
        /* Check if the continous conversion is finished */
        if(ConversionStartPoll_ADC_GrpRegular(ADC1) == MID_ERROR_OK)
        {
                /* Get the converted value */
                value[idx] = LL_ADC_REG_ReadConversionData12(ADC1);
        }
}

//LL_ADC_Disable(ADC1);

return MID_ERROR_OK;
}

unsigned short get_battery_value_service(void)
{

unsigned short AD_Value[2] = {0};        //AD_Value[0] --> PA2 channel 3(battery adc value)
                                                                        //AD_Value[1] --> PA3 channel 8(mifare adc value)
get_adc_value(AD_Value);

return AD_Value[1];
}

unsigned short get_mifare_value_service(void)
{
unsigned short AD_Value[2] = {0};        //AD_Value[0] --> PA2 channel 3(battery adc value)
//AD_Value[1] --> PA3 channel 8(mifare adc value)

get_adc_value(AD_Value);

return AD_Value[0];
}
xmshao 回答时间:2026-2-3 11:28:34

呵呵

至少要先把问题现象、疑惑点清晰的描述出来,之后跟上必要代码就行了。

ADC1 多通道循环自动采样DMA传输到ram,标题倒是很清晰了。具体问题是什么呢?

实现不了?结果异常?

易~ 回答时间:2026-2-3 14:22:38
xmshao 发表于 2026-2-3 11:28
[md]呵呵

至少要先把问题现象、疑惑点清晰的描述出来,之后跟上必要代码就行了。

结果异常,采样时间要设置得很长采样值才能和实际值差距小,可能和时钟有关,单次采样的时候采样时间很短也可以采样到相对准的值,但是ADC多通道循环采样DMA传送的时候就要采样时间很长而且分频值要很大才行,但是目前不清楚原因,
易~ 回答时间:2026-2-3 16:52:13
butterflyspring 发表于 2026-2-3 15:51
可以增加一个通道采集内部参考电压 VREFINT。

ADC使用内部时钟,配置如下
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HSI);
LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV1);
下面是两种sampletime的采样值,

//1.5cycle下,VREFINT采样值:0x0653,通道3采样的ADC:0x088A
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_1CYCLE_5);
//160.5cycle下,VREFINT采样值:0x063E,通道3采样的ADC:0x0C1F
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5);
可以看出两种不同sampletime的情况下,VERFINT的采样值差不多,但是通道3的采样值差异很大,通道3是通过分压电阻采样电池电压用的,VCC三分之一采样
VCC->1M->488K->GND,VCC是7.7V,
                |
                V
            通道3


Snipaste_2026-02-03_16-48-34.png
易~ 回答时间:2026-2-3 17:10:58
xmshao 发表于 2026-2-3 16:55
看你的补充描述,咋看起来有点逆天的味道。

问题很可能根本不是ADC本身的问题,先把芯片本身坏损原 ...

[md]硬件检查过了,比较稳定的产品,之前是单次多通道转换,现在需求变更只能改成连续多通道DMA的方式,
之前的ADC使用内部时钟,配置如下,
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HSI);
LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV1);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_1CYCLE_5);
现在配成这样不行,但是采样VERFINT的话,不管sampletime是在1.5cycle还是160.5cycle情况下,采样值比较准差距不大,其他通道在不同的sampletime下采样值差距很大
易~ 回答时间:2026-2-3 17:35:39
butterflyspring 发表于 2026-2-3 17:26
这个电路的确是对ADC采样有要求,需要更长的采样时间。
使用单次多通道,1.5cycle采样可以的,改成多通道连续转换后160.5cycle都不行(同样的硬件),比较好奇这个是什么原因
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HSI);
LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV1);
LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_1CYCLE_5);
易~ 回答时间:2026-2-4 11:24:12

xmshao 发表于 2026-2-4 10:01
采样电路的输入阻抗太大了,R27 R28同时缩小10倍吧。

这么做是为了尽量降低功耗

易~ 回答时间:2026-2-4 17:12:07
butterflyspring 发表于 2026-2-4 14:05
这个电阻值的分压电路,决定了它对ADC就是个高内阻电路,

电池的话只是举个例子,还有另外两个通道包括检卡ADC和电机电枢电流采样,电机电枢电流采样间隔很短,而且要在10ms定时中断里面处理,但是在中断里面开启ADC转换并等待转换结束是不妥当的,电池和检卡ADC又要在main函数里面处理,g070只有一个ADC,这些需求冲突了,所以才想到连续转换DMA传输的方法,,这样adc资源使用不会冲突(不会产生多线程问题),但是要adc转换比较快,能自动转换
两种转换的ADC的时钟源都是HSI,然后同样的采样效果,采样时间却大不相同,而且不一定准,令人费解
,其中单次多通道转换改变时钟的话得出的结果差异不大,但是多通道连续转换改变时钟配置的话差异比较大

单次多通道间断转换:

   /* Clear flag ADC channel configuration ready */
   LL_ADC_ClearFlag_CCRDY(ADC1);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_1RANK;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE;
  ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_CLOCK_ASYNC_DIV1);
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_1CYCLE_5);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_1CYCLE_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);

多通道连续转换(开不开启DMA不影响转换):

   /* Clear flag ADC channel configuration ready */
   LL_ADC_ClearFlag_CCRDY(ADC1);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_3RANKS;
  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_OVERWRITTEN;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE);
  LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5);
  LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_2, LL_ADC_SAMPLINGTIME_160CYCLES_5);
  LL_ADC_DisableIT_EOC(ADC1);
  LL_ADC_DisableIT_EOS(ADC1);




12下一页

所属标签

相似问题

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版