STM32--ADC

一、ADC简介

测量电压值:在STM32F103系列里面大部分场景里面都是0到3.3V

ADC的值:为什么是从0到4095?STM32 常用 ADC 是 12 位分辨率,对应的数字范围就是 0 到 2¹²-1(即 4095)

扩展借助 "传感器" 将亮度、湿度等非电物理量转换为 ADC 可采集的模拟电压(0~VREF+,通常 0~3.3V),再通过 ADC 采集该电压并换算为对应的物理量值

二、ADC的外设关键知识点

多通道的时候需要配合DMA数据转移,否则后来的数据会被覆盖

三、ADC程序实现

adc.c

复制代码
/**
 ****************************************************************************************************
 * @file        adc.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2020-04-23
 * @brief       ADC 驱动代码
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 MiniSTM32 V4开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * V1.0 20200423
 * 第一次发布
 *
 ****************************************************************************************************
 */

#include "./BSP/ADC/adc.h"
#include "./SYSTEM/delay/delay.h"
#include "stm32f1xx_hal_adc.h"
#include "stm32f1xx_hal_rcc.h"
#include "stm32f1xx_hal_def.h"
ADC_HandleTypeDef g_adc_handle;   /* ADC句柄 */

/**
 * @brief       ADC初始化函数
 *   @note      本函数支持ADC1/ADC2任意通道, 但是不支持ADC3
 *              我们使用12位精度, ADC采样时钟=12M, 转换时间为: 采样周期 + 12.5个ADC周期
 *              设置最大采样周期: 239.5, 则转换时间 = 252 个ADC周期 = 21us
 * @param       无
 * @retval      无
 */
void adc_init(void)    //初始化函数
{
//	  RCC_PeriphCLKInitTypeDef adc_clk_config;
	  __HAL_RCC_ADC1_CLK_ENABLE();
	  __HAL_RCC_GPIOA_CLK_ENABLE();
//	  adc_clk_config.PeriphClockSelection = RCC_PERIPHCLK_ADC;    
//	  adc_clk_config.AdcClockSelection = RCC_ADCPCLK2_DIV6 ;
	  RCC->CFGR &= ~RCC_CFGR_ADCPRE;  // 清除ADC预分频位
    RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6;  // 设置预分频系数为6
	
	
	  GPIO_InitTypeDef gpio_init_struct;
	
	  gpio_init_struct.Pin = GPIO_PIN_3;            
    gpio_init_struct.Mode = GPIO_MODE_ANALOG  ;        
    HAL_GPIO_Init(GPIOA, &gpio_init_struct);       /* 初始化LED0引脚 */

	  
	
    g_adc_handle.Instance = ADC1;                        /* 选择哪个ADC */
    g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;       /* 数据对齐方式:右对齐 */
    g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;       /* 非扫描模式,仅用到一个通道 */
    g_adc_handle.Init.ContinuousConvMode = DISABLE;          /* 关闭连续转换模式 */
    g_adc_handle.Init.NbrOfConversion = 1;                   /* 赋值范围是1~16,本实验用到1个规则通道序列 */
    g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; /* 触发转换方式:软件触发 */
		
    HAL_ADC_Init(&g_adc_handle);                             /* 初始化 */

    HAL_ADCEx_Calibration_Start(&g_adc_handle);              /* 校准ADC */
		
    // 定义ADC通道配置结构体(用于配置ADC的采集通道、转换优先级、采样时间等参数)
    ADC_ChannelConfTypeDef adc_ch_config;

    // 配置要采集的ADC通道为通道3(对应GPIOA_PIN_3,与硬件接线的模拟输入引脚对应)
    adc_ch_config.Channel = ADC_CHANNEL_3; 

    // 配置该通道在规则转换序列中的优先级(排名为1,即第一个进行转换,单通道采集时固定设为1)
    adc_ch_config.Rank = 1;	

    // 配置该通道的采样时间为239.5个ADC时钟周期
    // 采样时间越长,抗干扰能力越强,采集精度越高,但转换速度越慢(此为F1系列最大采样时间,适合高精度采集场景)
    adc_ch_config.SamplingTime = ADC_SAMPLETIME_239CYCLES_5 ;

     // 调用HAL库通道配置函数,将上述配置应用到ADC1(g_adc_handle为ADC1的句柄)
     // 执行后,ADC1将按照配置的通道、优先级和采样时间进行模拟信号采集
     HAL_ADC_ConfigChannel(&g_adc_handle,&adc_ch_config);
		
}


/**
  * @brief       ADC数据读取函数(获取12位ADC转换结果)
  * @param       无
  * @retval      uint16_t:返回ADC转换后的原始数值(范围0~4095,对应电压0~3.3V)
  */
uint16_t adc_get_date(void)       //数据读取
{
	  // 启动ADC规则通道的转换(软件触发,开始采集模拟信号)
	  HAL_ADC_Start(&g_adc_handle);
	  
	  // 等待ADC转换完成,超时时间设置为10ms
	  // 若在10ms内转换完成,函数返回HAL_OK;超时则返回HAL_TIMEOUT,此处简化未做超时判断
	  HAL_ADC_PollForConversion(&g_adc_handle,10);
	  
	  // 获取ADC转换后的原始数据,并强制转换为16位无符号整数返回
	  // 转换结果为12位精度,存储在ADC数据寄存器中,取值范围0~4095
	  return  (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}

(1)RCC_PeriphCLKInitTypeDef 这个结构体属于F4系列,F1系列没有,所以使用

RCC->CFGR &= ~RCC_CFGR_ADCPRE; // 清除ADC预分频位

RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; // 设置预分频系数为6 代替

(2)uint16_t adc_get_date(void)这个函数需要在adc.h中定义(在非main.c文件中自定义的函数或变量都需要在该函数或变量所处xx.c(源文件)对应的xx.h(头文件)中定义,如下例

复制代码
// adc.c(源文件,放函数的实现)
#include "adc.h"  // 包含对应的头文件

// 定义函数(实现ADC采集逻辑)
uint16_t adc_get_date(void)
{
    HAL_ADC_Start(&g_adc_handle);
    HAL_ADC_PollForConversion(&g_adc_handle, 100);
    return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);
}



// adc.h(头文件,放函数的声明)
#ifndef __ADC_H  // 防止头文件重复包含
#define __ADC_H

// 声明函数(告诉编译器:这个函数在其他文件中定义,可被外部调用)
uint16_t adc_get_date(void);

// 若有其他函数/变量,也在这里声明(如adc_init)
void adc_init(void);

#endif



// main.c(其他文件,调用函数)
#include "adc.h"  // 包含头文件,即可使用adc_get_date

int main(void)
{
    uint16_t val = adc_get_date();  // 直接调用,无警告/错误
    // ...
}

举一反三

复制代码
// adc.c中定义变量
ADC_HandleTypeDef g_adc_handle;  // 定义全局变量

// adc.h中声明变量(用extern)
extern ADC_HandleTypeDef g_adc_handle;  // 声明:该变量在其他文件中定义

main.c

复制代码
/**
 ******************************************************************************
 * @file     main.c
 * @author   正点原子团队(ALIENTEK)
 * @version  V1.0
 * @date     2020-08-20
 * @brief    新建工程实验-HAL库版本 实验
 * @license  Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ******************************************************************************
 * @attention
 * 
 * 实验平台:正点原子 STM32F103 开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 ******************************************************************************
 */

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "led.h"
#include "./BSP/ADC/adc.h"

//void led_init(void);  
                     /* LED初始化函数声明 */

int main(void)
{
	  uint16_t adc_date;
	  float adc_vol;
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
	  usart_init(115200);
    led_init();                             /* LED初始化 */
	  adc_init();
	 
    while(1)
    { 
      adc_date = adc_get_date();
			printf("ADC原始数据: %d\r\n",adc_date);
			
			adc_vol = (adc_date * 3.3)/4095;
			printf("ADC电压值: %.2f\r\n",adc_vol);
			
			delay_ms(100);

    }
}

实验结果PA3不接

当前 PA3 未连接任何信号时,ADC 输出固定为 4095(对应电压 3.3V),是STM32F103 ADC 模拟输入引脚悬空的正常现象,原因及验证方法如下:

一、原因:模拟输入引脚悬空时,内部高阻抗导致采集到电源电压

STM32 的 ADC 模拟输入引脚是高阻抗输入,当引脚悬空时,没有外部信号拉低 / 拉偏电压,引脚会通过 PCB 的寄生电容 / 电阻耦合到电源电压(3.3V),因此 ADC 采集到的原始值会接近满量程(12 位 ADC 的满量程是 4095,对应 3.3V)。

二、验证方法:给 PA3 接不同信号,观察输出变化

  1. 接 GND(地) :将 PA3 引脚直接连接到开发板的 GND 引脚,此时 ADC 原始数据应接近0,电压值接近0V

  2. 接电位器(可变电阻) :用一个 10K 电位器,一端接 3.3V,一端接 GND,中间抽头接 PA3,旋转电位器时,ADC 原始数据会在0~4095之间变化,电压值对应0~3.3V

  3. 接传感器(如光敏电阻):若连接光敏电阻等模拟传感器,ADC 值会随外部环境(光线强度)变化。

三、总结

PA3 悬空时输出 4095 是正常的硬件特性,并非代码问题。只需给 PA3 连接实际的模拟输入信号(如 GND、电位器、传感器),即可看到 ADC 值随外部信号变化

PA3接GND

相关推荐
一路往蓝-Anbo1 天前
STM32单线串口通讯实战(五):RTOS架构 —— 线程安全与零拷贝设计
c语言·开发语言·stm32·单片机·嵌入式硬件·观察者模式·链表
向阳逐梦1 天前
电路结构分析之半桥驱动、自举电路
单片机·嵌入式硬件
不染尘.1 天前
操作系统发展史和常见习题汇总
arm开发·嵌入式硬件·draw.io
清风6666661 天前
基于单片机的智能感应式汽车雨刮器控制系统设计
单片机·嵌入式硬件·汽车·毕业设计·课程设计·期末大作业
普中科技1 天前
【普中51单片机开发攻略--基于普中-2&普中-3&普中-4】-- 第 15 章 IO 扩展(串转并)-74HC595
单片机·嵌入式硬件·51单片机·开发板·74hc595·普中科技
RaLi和夕1 天前
硬件电路设计学习笔记3.比较器
笔记·嵌入式硬件·学习
Source.Liu1 天前
【ESP32】 Arduino 全面介绍
单片机·物联网
manduic1 天前
雅特力科技AT32WB415实现高性能MCU设计
科技·单片机·嵌入式硬件·雅特力
一路往蓝-Anbo1 天前
STM32单线串口通讯实战(一):物理层拓扑与STM32G0硬件配置
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网