目录
- AD多通道
- 接线图
- 代码实现
- 标准库实现
- AD.h
- AD.c
- main.c
- HAL库实现
- AD.h
- AD.c
- main.c
- 标准库实现
- 实现效果
AD多通道
接线图

在这里我们使用了4个AD通道,多次转换,连续扫描,会出现数据覆盖问题,需要用到下一节DMA的知识,所以这里用单次转换非扫描模式,只需要在触发转换之前,手动更改一下列表的第一个通道就行了
代码实现
标准库实现
已开源到:https://gitee.com/qin-ruiqian/jiangkeda-stm32
AD.h
#ifndef __AD_H
#define __AD_Hvoid AD_Init(void);
uint16_t AD_GetValue(uint8_t ADC_Channel);#endif
AD.c
#include "stm32f10x.h" // Device header//初始化AD
void AD_Init(void)
{//基本步骤//开启RCC时钟,包括ADC和GPIO的时钟//ADCCLK的分频器也需要配置一下//配置GPIO,把需要用的GPIO配置成模拟输入模式//配置多路开关,把左边的通道接入到右边的规则列表里//配置ADC转换器//需要看门狗就配置,需要中断就在中断输出控制里用ITConfig函数开启对应的中断输出//然后再在NVIC里,配置一下优先级,这样就能触发中断//开关控制,调用ADC_Cmd函数,开启ADCRCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟,ADC都是APB2上的设备RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟,准备开启PA0口RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择6分频,上一篇文章说,只能开启6和8分频,最大14MHz//配置GPIO,让PA0口变为模拟输入的引脚GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //PA0,1,2,3GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//结构体初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式,不是双ADCADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发源选择,不使用外部触发,也就是内部软件触发的意思ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数目,目前是一个通道,所以给1ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描ADC_Init(ADC1, &ADC_InitStructure);//开启ADC电源ADC_Cmd(ADC1, ENABLE);//校准ADCADC_ResetCalibration(ADC1); //复位while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准的状态,等待复位完成,还需要加一个while循环//ADC_GetCal...获取的是CR2寄存器的RSTCAL标志位,该位由软件设置并由硬件清除//在校准寄存器被初始化后,该位将被清除,该位被软件置1,开始复位校准,完成后,该位变0ADC_StartCalibration(ADC1); //开始校准while(ADC_GetCalibrationStatus(ADC1) == SET); //获取校准状态
}//返回AD转换的值,指定对应通道
uint16_t AD_GetValue(uint8_t ADC_Channel)
{//选择规则组的输入通道 //只有一个通道,使用非扫描模式,指定通道就放在第一个序列1的位置ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); //最后一个参数是采样周期,目前没要求,随便配置,采样时间就是55.5个ADCCLK的周期ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件ADC转换//ADC_FLAG_EOC规则组转换完成标志位//状态寄存器里,有EOC转换结束标志位,该位由硬件在(规则或注入)通道组转换结束时设置//由软件清除或由读取ADC_DR(数据寄存器)时清除//0:转换未完成//1:转换完成while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //转换未完成一直循环等待直到转换完成//具体等待时间是采样周期55.5,转换周期是固定的12.5,加在一起就是68个周期//前面配置的ADCCLK是72MHz的6分频,也就是12MHz,进行68个周期才能完成//最终时间是1/12M * 68 ,结果大概是5.6usreturn ADC_GetConversionValue(ADC1); //获取转换结果,它直接读取ADC的DR数据寄存器//因为读取DR寄存器会自动清楚EOC标志位,所以这之后我们就不需要再手动清除标志位了}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "MYOLED.h"
#include "AD.h"uint16_t AD0, AD1, AD2, AD3;int main(void)
{MYOLED_Init();AD_Init();MYOLED_ShowString(0,0,"AD0:");MYOLED_ShowString(0,1,"AD1:");MYOLED_ShowString(0,2,"AD2:");MYOLED_ShowString(0,3,"AD3:");while(1){//单次转换非扫描模式AD0 = AD_GetValue(ADC_Channel_0);AD1 = AD_GetValue(ADC_Channel_1);AD2 = AD_GetValue(ADC_Channel_2);AD3 = AD_GetValue(ADC_Channel_3);MYOLED_ShowNum(4,0,AD0,4);MYOLED_ShowNum(4,1,AD1,4);MYOLED_ShowNum(4,2,AD2,4);MYOLED_ShowNum(4,3,AD3,4);Delay_ms(100);}
}
HAL库实现
已开源到:https://gitee.com/qin-ruiqian/jiangkeda-stm32-hal
AD.h
/** AD.h** Created on: Aug 14, 2025* Author: Administrator*/#ifndef HARDWARE_AD_H_
#define HARDWARE_AD_H_typedef struct AD
{ADC_HandleTypeDef hadc; //当前用的是哪个ADC转换器
}AD;void AD_Init(AD* ad, ADC_HandleTypeDef hadc);
uint16_t AD_GetValue(AD* ad, uint32_t ADC_Channel);#endif /* HARDWARE_AD_H_ */
AD.c
/** AD.c** Created on: Aug 14, 2025* Author: Administrator*/
#include "stm32f1xx_hal.h"
#include "AD.h"//初始化AD
void AD_Init(AD* ad, ADC_HandleTypeDef hadc)
{ad->hadc = hadc;//在 HAL 库中,ADC 的校准操作被封装成了一个专门的函数,//无需像标准库那样分步调用复位校准、等待复位完成、//开始校准、等待校准完成等多个步骤。if (HAL_ADCEx_Calibration_Start(&(ad->hadc)) != HAL_OK){// 校准失败处理,没有处理,不写}
}//获取AD的值
uint16_t AD_GetValue(AD* ad, uint32_t ADC_Channel)
{ADC_ChannelConfTypeDef sConfig = {0};// 配置当前需要转换的通道sConfig.Channel = ADC_Channel; // 指定通道(如ADC_CHANNEL_0~3)sConfig.Rank = ADC_REGULAR_RANK_1; // 规则组第1位sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5; // 采样时间55.5周期HAL_ADC_ConfigChannel(&(ad->hadc), &sConfig); //设置通道等HAL_ADC_Start(&ad->hadc); //软件开启ADCHAL_ADC_PollForConversion(&(ad->hadc), HAL_MAX_DELAY); //等待转换uint16_t value = (uint16_t)HAL_ADC_GetValue(&(ad->hadc)); // 获取转换结果(自动清除EOC标志位)return value;
}
main.c
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 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 */
#include "MYOLED.h"
#include "AD.h"
/* 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 ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;/* USER CODE BEGIN PV */
uint16_t AD0, AD1, AD2, AD3;
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* 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. */HAL_Init();/* 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_ADC1_Init();/* USER CODE BEGIN 2 */MYOLED_Init();AD ad;AD_Init(&ad, hadc1);MYOLED_ShowString(0,0,"AD0:");MYOLED_ShowString(0,1,"AD1:");MYOLED_ShowString(0,2,"AD2:");MYOLED_ShowString(0,3,"AD3:");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){//单次转换非扫描模式AD0 = AD_GetValue(&ad, ADC_CHANNEL_0);AD1 = AD_GetValue(&ad, ADC_CHANNEL_1);AD2 = AD_GetValue(&ad, ADC_CHANNEL_2);AD3 = AD_GetValue(&ad, ADC_CHANNEL_3);MYOLED_ShowNum(4,0,AD0,4);MYOLED_ShowNum(4,1,AD1,4);MYOLED_ShowNum(4,2,AD2,4);MYOLED_ShowNum(4,3,AD3,4);HAL_Delay(100);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}
}/*** @brief ADC1 Initialization Function* @param None* @retval None*/
static 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 *//** Common config*/hadc1.Instance = ADC1;hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;hadc1.Init.ContinuousConvMode = DISABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion = 1;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}/** Configure Regular Channel*/sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN ADC1_Init 2 *//* USER CODE END ADC1_Init 2 */}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* USER CODE BEGIN MX_GPIO_Init_1 *//* USER CODE END MX_GPIO_Init_1 *//* GPIO Ports Clock Enable */__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_SET);/*Configure GPIO pins : PB8 PB9 */GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* 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 */
实现效果

