【自学嵌入式:stm32单片机】AD单通道
AD单通道
接线图

PA0到PB1这10个引脚是ADC的10个通道
代码实现
标准库实现
已开源到:https://gitee.com/qin-ruiqian/jiangkeda-stm32
AD.h
#ifndef __AD_H
#define __AD_H
void AD_Init(void);
uint16_t AD_GetValue(void);
#endif
AD.c
#include "stm32f10x.h"                  // Device header
//初始化AD
void AD_Init(void)
{
	//基本步骤
	//开启RCC时钟,包括ADC和GPIO的时钟
	//ADCCLK的分频器也需要配置一下
	//配置GPIO,把需要用的GPIO配置成模拟输入模式
	//配置多路开关,把左边的通道接入到右边的规则列表里
	//配置ADC转换器
	//需要看门狗就配置,需要中断就在中断输出控制里用ITConfig函数开启对应的中断输出
	//然后再在NVIC里,配置一下优先级,这样就能触发中断
	//开关控制,调用ADC_Cmd函数,开启ADC
	RCC_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; //PA0
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//选择规则组的输入通道 
	//只有一个通道,使用非扫描模式,指定通道就放在第一个序列1的位置
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //最后一个参数是采样周期,目前没要求,随便配置,采样时间就是55.5个ADCCLK的周期
	
	//结构体初始化ADC
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式,不是双ADC
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发源选择,不使用外部触发,也就是内部软件触发的意思
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换
	ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数目,目前是一个通道,所以给1
	ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描
	ADC_Init(ADC1, &ADC_InitStructure);
	
	//开启ADC电源
	ADC_Cmd(ADC1, ENABLE);
	
	//校准ADC
	ADC_ResetCalibration(ADC1); //复位
	while(ADC_GetResetCalibrationStatus(ADC1) == SET); //返回复位校准的状态,等待复位完成,还需要加一个while循环
	//ADC_GetCal...获取的是CR2寄存器的RSTCAL标志位,该位由软件设置并由硬件清除
	//在校准寄存器被初始化后,该位将被清除,该位被软件置1,开始复位校准,完成后,该位变0
	ADC_StartCalibration(ADC1); //开始校准
	while(ADC_GetCalibrationStatus(ADC1) == SET); //获取校准状态
}
//返回AD转换的值
uint16_t AD_GetValue(void)
{
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件ADC转换,如果是设置连续转换,这个就移动到初始化位置就OK,只需软件启动一次
	//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.6us
	return 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 ADValue; //模数转换值
float Voltage; //实际电压值,通过线性变换得到
int main(void)
{
	MYOLED_Init();
	AD_Init();
	MYOLED_ShowString(0,0,"ADValue:");
	MYOLED_ShowString(0,1,"Voltage:0.00V");
	while(1)
	{
		ADValue = AD_GetValue();
		//4095是因为ADC的结构没有办法达到最大量程
		Voltage = (float)ADValue / 4095 * 3.3; //因为电压范围是0-3.3V
		MYOLED_ShowNum(8,0,ADValue,4);
		MYOLED_ShowNum(8,1,Voltage,1); //先显示整数部分
		MYOLED_ShowNum(10,1,(uint16_t)(Voltage * 100) % 100,2); //取两位小数
		Delay_ms(100); //让它刷新慢一些
	}
}
HAL库实现
已开源到:https://gitee.com/qin-ruiqian/jiangkeda-stm32-hal
ADC配置如下图,其他配置参考开源代码的ioc文件


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);
#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)
{
	HAL_ADC_Start(&ad->hadc); //软件开启ADC
	HAL_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"
#include "MYOLED.h"
#include "AD.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 ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
/* USER CODE BEGIN PV */
AD ad; //ADC结构体
uint16_t ADValue; //模数转换值
float Voltage; //实际电压值,通过线性变换得到
/* 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_Init(&ad, hadc1);
  MYOLED_ShowString(0,0,"ADValue:");
  MYOLED_ShowString(0,1,"Voltage:0.00V");
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  	ADValue = AD_GetValue(&ad);
		//4095是因为ADC的结构没有办法达到最大量程
		Voltage = (float)ADValue / 4095 * 3.3; //因为电压范围是0-3.3V
		MYOLED_ShowNum(8,0,ADValue,4);
		MYOLED_ShowNum(8,1,Voltage,1); //先显示整数部分
		MYOLED_ShowNum(10,1,(uint16_t)(Voltage * 100) % 100,2); //取两位小数
		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 */
实现效果

 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号