/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file adc.c * @brief This file provides code for the configuration * of the ADC instances. ****************************************************************************** * @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 "adc.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ ADC_HandleTypeDef hadc1; /* ADC1 init function */ void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* 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) */ hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = ENABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 9; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_0; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = 2; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_2; sConfig.Rank = 3; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_3; sConfig.Rank = 4; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_4; sConfig.Rank = 5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_5; sConfig.Rank = 6; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_6; sConfig.Rank = 7; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_7; sConfig.Rank = 8; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_14; sConfig.Rank = 9; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* STM32F4 ADC 上电**自校准**:RM0090 §13.3.6 / AN4976 要求的必需步骤,CubeMX 不生成, * 且本仓库的 HAL 是裁剪子集、**没有** HAL_ADCEx_Calibration_Start()(已核实 * stm32f4xx_hal_adc_ex.c/.h 均无该函数)→ 用本文件 USER CODE BEGIN 1 里的等价实现。 * 必须在首次转换之前完成,否则 ADC 存在失调误差(未校准的偏移可达数十 LSB)。 * 放 USER CODE 区 = L2,重生成不丢;由 cubemx-survival 审计项 adc1-calibration 兜底。 */ if (adc1_calibrate() != 0) { Error_Handler(); } /* USER CODE END ADC1_Init 2 */ } void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /* ADC1 clock enable */ __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); /**ADC1 GPIO Configuration PA0-WKUP ------> ADC1_IN0 PA1 ------> ADC1_IN1 PA2 ------> ADC1_IN2 PA3 ------> ADC1_IN3 PA4 ------> ADC1_IN4 PA5 ------> ADC1_IN5 PA6 ------> ADC1_IN6 PA7 ------> ADC1_IN7 PC4 ------> ADC1_IN14 */ GPIO_InitStruct.Pin = ADC9_RES_Pin|ADC10_IC_Pin|ADC11_IN_Pin|ADC12_UC_Pin |ADC13_RES_Pin|ADC14_TEMP_Pin|ADC15_IB_Pin|ADC16_RES_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = ADC17_UB_Pin; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(ADC17_UB_GPIO_Port, &GPIO_InitStruct); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspDeInit 0 */ /* USER CODE END ADC1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_ADC1_CLK_DISABLE(); /**ADC1 GPIO Configuration PA0-WKUP ------> ADC1_IN0 PA1 ------> ADC1_IN1 PA2 ------> ADC1_IN2 PA3 ------> ADC1_IN3 PA4 ------> ADC1_IN4 PA5 ------> ADC1_IN5 PA6 ------> ADC1_IN6 PA7 ------> ADC1_IN7 PC4 ------> ADC1_IN14 */ HAL_GPIO_DeInit(GPIOA, ADC9_RES_Pin|ADC10_IC_Pin|ADC11_IN_Pin|ADC12_UC_Pin |ADC13_RES_Pin|ADC14_TEMP_Pin|ADC15_IB_Pin|ADC16_RES_Pin); HAL_GPIO_DeInit(ADC17_UB_GPIO_Port, ADC17_UB_Pin); /* USER CODE BEGIN ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* CR2.CAL(bit 3)。⚠ 本仓库的 CMSIS 设备头 stm32f407xx.h 是裁剪版,**未定义** * ADC_CR2_CAL(已核实:CR2 只有 ADON/CONT/DMA/DDS/EOCS/ALIGN/JEXTSEL/JEXTEN/ * JSWSTART/EXTSEL/EXTEN/SWSTART,没有 CAL) * → 按 RM0090 在此补齐,命名避开 CMSIS 前缀以免将来重定义冲突。 */ #define ADC1_CAL_BIT (0x1UL << 3U) /* 断电/校准等待上限(ms)。CAL 实测在数十 µs 内清零,10 ms 是 100 倍以上余量 */ #define ADC1_CAL_TIMEOUT_MS (10U) /* ADON 0→1 后的稳定等待(µs),同 HAL 的 ADC_STAB_DELAY_US 口径 */ #define ADC1_STAB_DELAY_US (20U) /* * 函数功能:ADC1 上电自校准(等价于 HAL_ADCEx_Calibration_Start,本仓库 HAL 无此函数) * 入口参数:无(操作 ADC1 寄存器) * 返 回 值:0 - 校准完成;-1 - 等待超时(ADON 未清零 / CAL 未清零) * 限定条件:MX_ADC1_Init() 之后、**首次转换之前**;内核启动前可调用 * (只用 HAL_GetTick 与寄存器,不触碰任何 CMSIS/FreeRTOS 对象) * 函数说明:RM0090 §13.3.6 时序——① ADC 必须处于关闭态(清 ADON 并等其生效); * ② 置 ADON 上电,等 Tstab;③ 置 CAL 启动自校准;④ 完成后**硬件自动**清 CAL, * 等其清零即完成。校准值存在 ADC 内部偏置寄存器,此后所有转换都带上校正。 * 缺这一步的后果不是"读不到"而是"读得有偏"——正是 §7.2 那类 * adc1_err/异常值的候选根因之一,见 docs/硬件平台/ADC通道映射说明.md。 */ int adc1_calibrate(void) { uint32_t tickstart; /* 1) 前置条件:ADC 必须关闭 */ CLEAR_BIT(ADC1->CR2, ADC_CR2_ADON); tickstart = HAL_GetTick(); while ((ADC1->CR2 & ADC_CR2_ADON) != 0U) { if ((HAL_GetTick() - tickstart) > ADC1_CAL_TIMEOUT_MS) { return -1; } } /* 2) 上电(ADON 0→1),等待稳定后再启动校准 */ SET_BIT(ADC1->CR2, ADC_CR2_ADON); { __IO uint32_t wait_loop_index = (ADC1_STAB_DELAY_US * (SystemCoreClock / 1000000U)); while (wait_loop_index != 0U) { wait_loop_index--; } } /* 3) 启动自校准,等硬件把 CAL 清零 */ SET_BIT(ADC1->CR2, ADC1_CAL_BIT); tickstart = HAL_GetTick(); while ((ADC1->CR2 & ADC1_CAL_BIT) != 0U) { if ((HAL_GetTick() - tickstart) > ADC1_CAL_TIMEOUT_MS) { return -1; } } return 0; } /* USER CODE END 1 */