文章目录

  • 前言
  • 一、ADC简介
  • 二、ADC基本原理
    • 1.ADC0809结构原理
    • 2.STM32结构原理
    • 3.规则序列和注入序列
      • 3.1规则序列
      • 3.2注入序列
  • 三、STM32软件配置
    • 1、对应寄存器配置讲解
    • 2、HAL库实现
  • 总结


前言

  本文从ADC的原理出发,讲解了逐次逼近型的物理结构、逐次逼近型的实现算法;STM32F1系列的内部ADC的实现原理,以及主要寄存器的配置;从寄存器的角度出发,讲解如何用代码实现对寄存器的配置;也讲解了ADC的各种模式。希望能够帮助大家对ADC底层理解有帮助!


一、ADC简介

  简单一句话,ADC是将模拟信号变成计算机可处理的离散信号,学过通信原理和数字信号处理的都知道(学过但不知道的,????),学的采样定理,这种采样出来的信号不是离散信号,那只是时间上离散,时间离散之后然后再对幅度进行量化后,才是离散信号。因此ADC不只是简简单单的采样,他还包括量化编码。

二、ADC基本原理

  大家谈论ADC时肯定避免不开,“你这个ADC是多少位的?”这个多少位指的是什么,有什么用。这个是跟他的分辨率相关的,比如,这个ADC是12位的,那数值范围是0~2^12-1, 我采集3.3V的信号,那我每一个数值表示的电压就是3.3/(2^12-1)V,这样是不是位数越高,可分辨的电压就越精细。

1.ADC0809结构原理

  我们首先先用一个ADC芯片了解整体结构,再去看STM32内部自带的ADC结构。STM32与ADC0809采用的都是逐次比较型,用下面这个部分将逐次比较型也一次讲清楚。

  这是从芯片ADC0809数据手册里截取出来的原理图,主要有这几个部分:电压比较器(紫色:comparator)、DAC(红色框)、SAR(蓝色S.A.R)、时钟控制(橙色:control & timing)。工作原理是:首先SAR先给个一半的值(1000 0000),去往DAC,输出电压值,再与通道采集的电压进行比较看大了还是小了,大了就是1,小了就是0,每次都取一半的值,反复直到8位都被成功赋值,与二分查找的算法一模一样。
  直接举例子,为了简单起见,举例子就用4位的,通道采到的电压是3.3V,参考的最高电压是4V。最终输出的是1101,具体流程看下面表格。

步骤 SAR输出值 比较 最终值
1 1000 (2V) 小于 1
2 1100(3V) 小于 1
3 1110 (3.5V) 大于 0
4 1101(3.25V) 小于 1

  1101转换出来应该是3.46V,中间这个差值就是量化误差导致的,这也从侧面说明位数越高,最终采到的电压就越准。

2.STM32结构原理

在这里插入图片描述

  红色框是采样的通道,从0-15,总共16个通道;绿色的是中断触发的信号;蓝色的是关键,是将模拟信号转换成数字信号的关键部分,类似于上文ADC0809的作用;这里我们可以看到主要的通道分为注入通道和规则通道,这两个通道是ADC的关键:
  注入通道:一次最多可接入4个通道进行模拟信号到数字信号的转换,他具有高优先级,可以打断任何规则转换,后端有4个字节的数据寄存器进行存储(JDR1、JDR2、JDR3、JDR4),不会对规则通道的数据有任何影响。他就像“VIP插队通道”。
  规则通道:一次可以接入16个通道进行模拟信号到数字信号的转换,但是,这16个通道不是一次就可以转换成功的,每次只能转换一个,而且后端的数据寄存器DR,只有16位,也就是说一次只能存储一个值。他就像“排队登机(经济舱队伍)”。

特性 规则通道 (单通道) 注入通道 (多通道)
接入通道 16个 4个
数据寄存器 1个 (DR) 4个独立寄存器 (JDR1~JDR4)
单通道风险 连续模式会覆盖 永不覆盖
多通道风险 必须用DMA,否则严重覆盖 无风险(各通道独立寄存器)
读取时机 需在下次转换前读取 可随时读取
典型读取方式 轮询DR/DMA/中断 直接访问JDRx

  我们现在思考,规则通道最多接入16个通道进来,那这个顺序是怎么样的,先转换谁后转换谁,这种模式行吗?俗话说无规矩不成方圆,是不是得规定个规则让STM32按照我们的想法去工作,注入通道也是同理,所以这就引出来下面要说的规则序列、注入序列

3.规则序列和注入序列

3.1规则序列

  在实际应用中,经常需要按特定的顺序轮流采集多个模拟通道(比如温度传感器、光照传感器、电位器等连接在不同的ADC引脚上)。规则序列转换就是为了满足这种多通道采集需求而设计的机制。
  主要的思想就是,将多个模拟输入通道预先定义为一个转换序列(列表)。当一次转换启动(触发)发生时,ADC会自动按照这个预定义的顺序,依次对这个序列中的所有通道执行一次完整的SAR转换。下面是ADC寄存器的映射,在“HAL库底层代码分析”这篇文章我已经讲过这个是怎么映射的。

  typedef struct
	{
	  __IO uint32_t SR;
	  __IO uint32_t CR1;
	  __IO uint32_t CR2;
	  __IO uint32_t SMPR1;
	  __IO uint32_t SMPR2;
	  __IO uint32_t JOFR1;
	  __IO uint32_t JOFR2;
	  __IO uint32_t JOFR3;
	  __IO uint32_t JOFR4;
	  __IO uint32_t HTR;
	  __IO uint32_t LTR;
	  __IO uint32_t SQR1;
	  __IO uint32_t SQR2;
	  __IO uint32_t SQR3;
	  __IO uint32_t JSQR;
	  __IO uint32_t JDR1;
	  __IO uint32_t JDR2;
	  __IO uint32_t JDR3;
	  __IO uint32_t JDR4;
	  __IO uint32_t DR;
	} ADC_TypeDef;

  首先,我们得告诉STM32我们这个规则序列里有几个通道,STM32F1最多支持16个通道(包括外部通道和内部通道如温度传感器、Vrefint)在一个规则序列中。入下图红色框所示,通过寄存器 ADC_SQR1 的 L[3:0] 位设置(0表示1个通道,15表示16个通道)。
在这里插入图片描述

(1) 通道顺序(Sequence): 你可以自由指定序列中每个位置对应哪个具体的ADC通道。这是通过一组寄存器实现的:
  ADC_SQR1: 存放序列中第13位(SQ13)到第16位(SQ16)的通道号。
  ADC_SQR2: 存放序列中第7位(SQ7)到第12位(SQ12)的通道号。
  ADC_SQR3: 存放序列中第1位(SQ1)到第6位(SQ6)的通道号。
  注:ADC_SQR2、ADC_SQR3没有列出来,大家可自行查看参考手册。
  例如,你想按顺序采集通道3、通道8、通道2、通道15。那么首先你需要设置序列长度 L[3:0] = 3(表示有4个通道,因为0=1, 1=2, …, 3=4),再设置具体通道:
  SQ1 = 3
  SQ2 = 8
  SQ3 = 2
  SQ4 = 15
(2) 已经知道通道是怎么设置的,但是按照什么样的规则去采集,这个是接下来说的,STM32F1有四种转换模式,如下表所示:

组合模式 SCAN位 CONT位 行为描述
单通道单次转换 DISABLE DISABLE 一次触发仅转换一个通道,需手动重启。
多通道单次转换 ENABLE DISABLE 一次触发扫描所有通道后停止,需下次触发重新扫描。
单通道连续转换 DISABLE ENABLE 自动重复转换同一通道,无需触发
多通道连续转换 ENABLE ENABLE 循环扫描所有通道,永不停止

STM32F1的ADC模式由两个关键寄存器控制位组合决定:
  扫描模式(SCAN,在ADC_CR1中):决定是否自动扫描多个通道。
  连续模式(CONT,在ADC_CR2中):决定是否自动重启转换。
(3) 举例子说明这四个通道:
  假设场景: 我们要用ADC测量3个模拟信号:
    通道0 (PA0): 测量电位器电压 (0-3.3V)
    通道1 (PA1): 测量光照传感器电压 (0-3.3V)
    通道2 (PA2): 测量NTC热敏电阻分压电压 (反应温度)

模式 1:单通道单次转换
行为: 一次启动信号(触发)只转换一个预先选定的通道,转换一次后就停止。需要再次启动才能转换同一个或另一个通道。

配置要点
  扫描模式(SCAN) = 禁用
  连续模式(CONT) = 禁用
  规则序列长度(SQR1.L) = 1 (因为只转换一个通道)
  规则序列1(SQR3.SQ1) = 你要转换的通道号 (如 0)

操作流程示例 (测量通道0)
  配置ADC:SCAN=0, CONT=0, SQR1.L=0 (表示1个通道), SQR3.SQ1=0 (通道0)。
  启动转换: 软件设置SWSTART位或等待硬件触发。
  ADC动作:对通道0执行一次完整的SAR转换。
  转换完成:EOC标志置位(可能产生中断)。
  读取数据: 从ADC_DR寄存器读取通道0的转换结果。
  ADC停止: 等待下一次启动信号。

但我想测量通道1怎么办?
  重新配置: 修改规则序列1(SQR3.SQ1) = 1 (通道1)。
  启动转换: 再次触发。
  ADC动作:对通道1执行一次SAR转换。
  读取数据:从ADC_DR读取通道1结果。

模式 2:多通道单次扫描
行为: 一次启动信号(触发)后,ADC自动按预先设定的顺序扫描转换规则序列中指定的所有通道(通道0->1->2),全部转换完成后停止。需要下一次启动信号才会重新开始扫描整个序列。

配置要点
  扫描模式(SCAN) = 启用
  连续模式(CONT) = 禁用
  规则序列长度(SQR1.L) = 2 (表示3个通道,0=1, 1=2, 2=3, 3=4…)

规则序列:
  SQR3.SQ1 = 0 (第一个转换:通道0)
  SQR3.SQ2 = 1 (第二个转换:通道1)
  SQR3.SQ3 = 2 (第三个转换:通道2)
这种模式必须使用DMA或中断读取每个通道结果!因为前文说了规则通道只有一个16位的寄存器去存储数据,一次只能存一个,那你连着采集三个通道,会造成数据覆盖。

操作流程示例 (测量所有3个通道):
  配置DMA:源地址=ADC_DR,目标地址=一个数组(如adcResults[3]),数据长度=3,每次ADC_DR有数据就搬运。
  启动转换: 触发一次。
  ADC动作:
    转换SQ1 (通道0) -> 结果存入ADC_DR -> DMA搬运adcResults[0]
    自动转换SQ2 (通道1) -> 结果存入ADC_DR (覆盖通道0结果) -> DMA搬运adcResults[1]
    自动转换SQ3 (通道2) -> 结果存入ADC_DR (覆盖通道1结果) -> DMA搬运adcResults[2]
    所有通道转换完成:EOC标志置位(可能产生中断)。
    ADC停止: 等待下一次触发。
  此时数组adcResults[0], [1], [2] 分别保存了通道0, 1, 2的最新数据。

模式 3:单通道连续转换
行为: 只需要启动一次,ADC就会不停地、连续地转换同一个通道。永不停止,除非你关闭连续模式或禁用ADC。

配置要点
  扫描模式(SCAN) = 禁用
  连续模式(CONT) = 启用
  规则序列长度(SQR1.L) = 1
  规则序列1(SQR3.SQ1) = 目标通道号 (如 0)。

操作流程示例 (连续测量通道0):

  配置ADC:SCAN=0, CONT=1, SQR1.L=0, SQR3.SQ1=0。
  启动转换: 触发一次 (软件SWSTART或硬件触发)。
  ADC动作
    转换通道0 -> 结果存入ADC_DR -> EOC置位。
    立即自动开始下一次转换通道0 -> 结果存入ADC_DR (覆盖前一次) -> EOC置位。
    循环往复,永不停止…
  读取数据:你需要在每次EOC后(或使用DMA)尽快读取ADC_DR,否则结果会被下一次转换覆盖。通常用循环缓冲+DMA或定时读取。

模式 4:多通道连续扫描
行为: 只需要启动一次,ADC就会不停地、循环地按顺序扫描转换规则序列中的所有通道 (通道0->1->2->0->1->2->…)。永不停止。

配置要点:

  扫描模式(SCAN) = 启用
  连续模式(CONT) = 启用
  规则序列长度(SQR1.L) = 2 (3个通道)
  规则序列:SQR3.SQ1=0, SQ2=1, SQ3=2。
  必须使用DMA! (数据流太快,中断来不及处理)。

操作流程示例 (循环测量3个通道):

  **配置DMA:**源地址=ADC_DR,目标地址=一个循环缓冲区 (如adcResults[3][N],N组数据),数据长度=3 (或DMA循环模式),每次ADC_DR有数据就搬运。

  启动转换: 触发一次。

  ADC动作

    转换SQ1 (通道0) -> ADC_DR -> DMA搬运到 adcResults[0][0]

    转换SQ2 (通道1) -> ADC_DR -> DMA搬运到 adcResults[1][0]

    转换SQ3 (通道2) -> ADC_DR -> DMA搬运到 adcResults[2][0]

    自动回到SQ1 (通道0) -> ADC_DR -> DMA搬运到 adcResults[0][1]

    转换SQ2 (通道1) -> ADC_DR -> DMA搬运到 adcResults[1][1]

    … 无限循环…

总结一下这四种模式:

模式 SCAN CONT 触发次数 通道数 动作 数据读取关键 典型应用场景
单通道单次转换 0 0 每通道1次 1 转1次停 读ADC_DR 偶尔测一个传感器
多通道单次扫描 1 0 1次/轮询 扫完所有停 DMA/中断 (防覆盖) 周期性读多个传感器
单通道连续转换 0 1 1次启动 1 连续转同一通道 DMA/频繁读 (防覆盖) 高速采集单信号 (如音频)
多通道连续扫描 1 1 1次启动 循环扫所有通道 必须DMA (循环缓冲) 实时监控多路信号 (如电机控制)

(3) 规则序列的转换需要由一个事件来启动。可以是:
  软件触发: 通过设置寄存器位(如 ADC_CR2 的 SWSTART 或 JSWSTART 用于注入组)来立即启动转换。
  硬件触发: 由外部信号(如定时器捕获/比较事件、EXTI线)触发转换。触发源通过 ADC_CR2 的 EXTSEL[2:0] 位选择。

(4) 在使用规则组时有个最关键的限制,它只有一个数据寄存器 ADC_DR 来存放结果。这意味着:当ADC自动转换序列中的后续通道时,前一个通道的结果会被覆盖掉。如果你想保留序列中所有通道的转换结果,必须在每次单个通道转换完成后(或者至少在下一个通道转换开始前)将 ADC_DR 的值读走并保存起来,因此这就是为什么adc要配合DMA或者中断来进行的原因,读取数据大多数情况下用的时DMA,中断效率太低了,一般不采用。

(5) 转换时间(总序列时间):
  整个规则序列完成所需的总时间取决于有多少个通道、每个通道的采样时间、每个通道逐次逼近的转换时间。
  每个通道的采样时间:通过 ADC_SMPRx 寄存器为每个通道配置采样保持电容的充电时间(越长越准确,抗噪能力越强,但转换也越慢)。采样时间占用若干个ADC时钟周期。
  每个通道的逐次逼近转换时间:固定为12.5或13.5个ADC时钟周期(取决于 ADC_CR1 的 RES[1:0] 分辨率设置,12位模式通常是12.5)。
  ADC时钟频率(ADCCLK):由 RCC_CFGR 的 ADCPRE 分频器设置,最高不超过14MHz(STM32F1)。
  总转换时间 ≈ 序列长度 * (采样时间 + 12.5 或 13.5) 个 ADCCLK 周期。

3.2注入序列

注入序列的模式主要分为两种单通道注入和多通道注入两种。
  单通道注入:序列中只配置一个通道。
  多通道注入:序列配置多个通道(最多4个),按顺序转换
  如下图所示,这是注入通道配置的顺序,JL[1:0]这一位是说明需要转换几个通道,JSQ4~1是用来确定转换的是哪个通道,注意看紫色框,告诉了他是什么顺序,如果配置了4个通道,那顺序就是JSQ1>>JSQ2>>JSQ3>>JSQ4;如果配置的是3个通道,那顺序是JSQ2>>JSQ3>>JSQ4;如果配置的是2个通道,那顺序是JSQ3>>JSQ4;如果配置的是1个通道,那顺序是JSQ4。
在这里插入图片描述

三、STM32软件配置

1、对应寄存器配置讲解

  需要明确ADC在开始工作前我们需要进行校准,在STM32的ADC采样前进行校准主要是解决硬件固有误差。下面是对寄存器进行配置的代码,有助于提高对底层逻辑的理解。主要是对CR1和CR2寄存器进行配置,大家看这个得对着手册去看,这里就不粘贴出手册了,直接说寄存器。
  step1:配置工作模式,这个是配置协同工作的,就比如有两三个ADC,那就需要配置这个寄存器,但我们只是一个ADC在工作,所以就配成独立工作模式;配置非扫描模式。
  step2:配置成单次转换,SWSTART触发(软件触发),这里要注意当你用外部触发时,你得使能他,这个使能寄存器就是EXTTRIG寄存器,软件触发也不例外;再配置成右对齐模式。
  step3:配置通道,L[3:0]=000(转换一个通道),看到这肯定有疑问,怎么没有去配置哪个通道,只有转换的个数,别急,这个是在adc_get_result函数里配置的,直接配置直接读。
  step4:开启,然后校准。
  剩下的两个函数是配置采样速率和读取数据的函数,大家自己看吧,对着参考手册看也很简单,提前声明:此代码不能直接用,没有配置时钟,主要时让大家学习。

/**
 * @brief       ADC初始化函数
 */
void adc_init(void)
{
    ADC_ADCX->CR1 &= ~(0XF << 16);  /* 工作模式清零 */
    ADC_ADCX->CR1 |= 0 << 16;       /* 独立工作模式 */
    ADC_ADCX->CR1 &= ~(1 << 8);     /* 非扫描模式 */

    ADC_ADCX->CR2 &= ~(1 << 1);     /* 单次转换模式 */
    ADC_ADCX->CR2 &= ~(7 << 17);
    ADC_ADCX->CR2 |= 7 << 17;       /* 软件控制转换 */
    ADC_ADCX->CR2 |= 1 << 20;       /* 使用用外部触发(SWSTART)!!! 必须使用一个事件来触发 */
    ADC_ADCX->CR2 &= ~(1 << 11);    /* 右对齐 */
    
    ADC_ADCX->SQR1 &= ~(0XF << 20); /* L[3:0]清零 */
    ADC_ADCX->SQR1 |= 0 << 20;      /* 1个转换在规则序列中 也就是只转换规则序列1 */

    ADC_ADCX->CR2 |= 1 << 0;        /* 开启AD转换器 */

    ADC_ADCX->CR2 |= 1 << 3;        /* 使能复位校准 */

    while (ADC_ADCX->CR2 & 1 << 3); /* 等待校准结束 */

    /* 该位由软件设置并由硬件清除  在校准寄存器被初始化后该位将被清除 */
    ADC_ADCX->CR2 |= 1 << 2;        /* 开启AD校准 */

    while (ADC_ADCX->CR2 & 1 << 2); /* 等待校准结束 */
}

/**
 * @brief       设置ADC通道采样时间
 * @param       adcx : adc结构体指针, ADC1 / ADC2
 * @param       ch   : 通道号, 0~17
 * @param       stime: 采样时间  0~7, 对应关系为:
 */
void chanal_set (ADC_TypeDef *adcx, uint8_t ch, uint8_t stime)
{
    if (ch < 10)    /* 通道0~9,使用SMPR2配置 */
    {
        adcx->SMPR2 &= ~(7 << (3 * ch));        /* 通道ch 采样时间清空 */
        adcx->SMPR2 |= 7 << (3 * ch);           /* 通道ch 采样周期设置,周期越高精度越高 */
    }
    else    /* 通道10~17,使用SMPR1配置 */
    {
        adcx->SMPR1 &= ~(7 << (3 * (ch - 10))); /* 通道ch 采样时间清空 */
        adcx->SMPR1 |= 7 << (3 * (ch - 10));    /* 通道ch 采样周期设置,周期越高精度越高 */
    }
}

/**
 * @brief       获得ADC转换后的结果
 * @param       ch: 通道号, 0~17
 * @retval      无
 */
uint32_t get_value (uint8_t ch)
{
    adc_channel_set(ADC_ADCX, ch, 7);   /* 设置ADCX对应通道采样时间为239.5个时钟周期 */

    ADC_ADCX->SQR3 &= ~(0X1F << 5 * 0); /* 规则序列1通道清零 */
    ADC_ADCX->SQR3 |= ch << (5 * 0);    /* 规则序列1 通道 = ch */
    ADC_ADCX->CR2 |= 1 << 22;           /* 启动规则转换通道 */

    while (!(ADC_ADCX->SR & 1 << 1));   /* 等待转换结束 */

    return ADC_ADCX->DR;                /* 返回adc值 */
}

2、HAL库实现

  这是HAL库的配置方式,实际上当你把寄存器弄懂之后,你看HAL的配置其实很简单,只要对着他的说明去正常配置就可以,主要分为两个结构体,一个是基本配置(ADC_InitTypeDef),另一个通道配置(ADC_ChannelConfTypeDef),我给大家把这个配置结构体的注释写在代码里,以供大家参考,初始化函数就是根据自己所需去配置这些结构体就行。

typedef struct//基本配置
{
  /* ADC时钟配置 ---------------------------------------------------------- */
  uint32_t ClockPrescaler;        /*!< ADC时钟源选择及分频系数:
                                       - 选择同步时钟(源自APB时钟)或异步时钟(源自系统时钟/PLL)
                                       - 参考手册查看可用时钟列表
                                       - 注意:所有ADC实例共享此时钟配置
                                       - 重要限制:同步时钟模式需系统时钟50%占空比
                                       - 异步时钟需在RCC层预先使能
                                       - 只能在所有ADC禁用时修改此参数 */

  /* 转换精度配置 -------------------------------------------------------- */
  uint32_t Resolution;            /*!< ADC转换精度设置:
                                       - 可选值:6位/8位/10位/12位
                                       - 精度越高,转换时间越长 */

  /* 数据对齐方式 -------------------------------------------------------- */
  uint32_t DataAlign;             /*!< 转换结果在数据寄存器中的对齐方式:
                                       - 右对齐:正常模式,数据在DR寄存器的低12位
                                       - 左对齐:方便8位系统读取高位数据 */

  /* 扫描模式配置 -------------------------------------------------------- */
  uint32_t ScanConvMode;          /*!< 规则组扫描序列模式:
                                       - 完全可配置模式:自由设置序列长度和各位置通道
                                       - 非完全可配置模式:通道号固定对应序列位置
                                       - 多通道时自动启用序列器
                                       - 单通道时工作在单次模式
                                       - 可配合间断模式使用 */

  /* 转换结束标志选择 ---------------------------------------------------- */
  uint32_t EOCSelection;          /*!< 转换结束标志选择:
                                       - 单次转换结束(EOC) 或 序列转换结束(EOS)
                                       - 影响轮询和中断行为 */

  /* 低功耗自动等待 ------------------------------------------------------ */
  FunctionalState LowPowerAutoWait; /*!< 低功耗自动等待模式:
                                       - 使能:新转换在上次结果被读取后才开始
                                       - 自动调节转换频率,防止数据溢出
                                       - 注意:与中断/DMA模式不兼容
                                       - 推荐用于轮询模式节能 */

  /* 自动断电模式 -------------------------------------------------------- */
  FunctionalState LowPowerAutoPowerOff; /*!< 自动断电模式:
                                       - 使能:转换后自动断电,触发时重新上电
                                       - 可配合自动等待模式使用
                                       - 唤醒时有采样启动时间开销 */

  /* 连续转换模式 -------------------------------------------------------- */
  FunctionalState ContinuousConvMode; /*!< 连续转换模式开关:
                                       - 禁用:单次转换模式(触发一次转换一次)
                                       - 使能:连续转换模式(自动循环转换) */

  /* 规则通道数量 -------------------------------------------------------- */
  uint32_t NbrOfConversion;       /*!< 规则组转换通道数量:
                                       - 完全可配置模式:设置序列长度(1-8)
                                       - 非完全可配置模式:此参数被忽略
                                       - 只能在无转换进行时修改 */

  /* 间断模式 ------------------------------------------------------------ */
  FunctionalState DiscontinuousConvMode; /*!< 间断模式开关:
                                       - 使能:将序列分成多个子组
                                       - 要求:扫描模式使能且连续模式禁用
                                       - STM32F1仅支持单通道间断模式 */

  /* 外部触发源 ---------------------------------------------------------- */
  uint32_t ExternalTrigConv;      /*!< 规则组外部触发源选择:
                                       - 选择定时器/EXTI等硬件触发源
                                       - ADC_SOFTWARE_START:使用软件触发
                                       - 注意:触发源为所有ADC共享 */

  /* 外部触发边沿 -------------------------------------------------------- */
  uint32_t ExternalTrigConvEdge;  /*!< 外部触发信号边沿选择:
                                       - 上升沿/下降沿/双边沿触发
                                       - 软件触发时此参数无效 */

  /* DMA连续请求 --------------------------------------------------------- */
  FunctionalState DMAContinuousRequests; /*!< DMA请求模式:
                                       - 禁用:按转换次数请求(单次模式)
                                       - 使能:连续请求(需DMA配置为循环模式)
                                       - 防止DMA缓冲区溢出 */

  /* 数据溢出处理 -------------------------------------------------------- */
  uint32_t Overrun;               /*!< 数据溢出处理策略:
                                       - 覆盖旧数据 或 保留旧数据
                                       - 保留模式需在中断回调中及时保存数据
                                       - DMA模式下始终报告溢出错误 */

  /* 公共采样时间1 ------------------------------------------------------ */
  uint32_t SamplingTimeCommon1;   /*!< 第一组通道公共采样时间:
                                       - 单位:ADC时钟周期
                                       - 总转换时间 = 采样时间 + 固定处理时间
                                       - 内部通道(VrefInt/Temp)需满足最小采样时间
                                       - 参考数据手册计算最小采样时间 */

  /* 公共采样时间2 ------------------------------------------------------ */
  uint32_t SamplingTimeCommon2;   /*!< 第二组通道公共采样时间:
                                       - 特性同SamplingTimeCommon1
                                       - STM32F1支持两组独立采样时间配置
                                       - 允许不同通道组使用不同采样时间 */

  /* 过采样模式 ---------------------------------------------------------- */
  FunctionalState OversamplingMode;       /*!< 过采样功能开关:
                                       - 使能:提升有效分辨率
                                       - 只能在无转换进行时修改 */

  /* 过采样参数 ---------------------------------------------------------- */
  ADC_OversamplingTypeDef Oversampling;   /*!< 过采样详细参数:
                                       - 包含采样倍数、位移、触发模式
                                       - 覆盖先前过采样配置 */

  /* 触发频率模式 -------------------------------------------------------- */
  uint32_t TriggerFrequencyMode;  /*!< 触发频率模式选择:
                                       - 高频模式:默认模式
                                       - 低频模式:满足tIdle时间要求时使用
                                       - 低频模式增加2个ADC时钟周期延迟 */

} ADC_InitTypeDef;

//通道配置
typedef struct
{
  /* ADC通道选择 ---------------------------------------------------------- */
  uint32_t Channel;                /*!< 指定要配置的ADC通道:
                                       - 物理通道号(如ADC_CHANNEL_0对应PA0)
                                       - 内部通道:温度传感器(ADC_CHANNEL_TEMPSENSOR)、
                                         Vrefint(ADC_CHANNEL_VREFINT)等
                                       - 注意:部分通道在特定封装不可用
                                       - 需参考数据手册确认引脚可用性 */

  /* 规则组序列位置配置 ---------------------------------------------------- */
  uint32_t Rank;                   /*!< 设置通道在规则组序列中的位置:
                                       - 完全可配置模式:自由指定通道顺序(Rank1-16)
                                         [通道0可设为Rank3,通道1可设为Rank1]
                                       - 非完全可配置模式:通道号固定对应序列位置
                                         (通道0→Rank1,通道1→Rank2...)
                                       - 特殊值:ADC_RANK_NONE可移除该通道
                                       - 关键作用:决定多通道扫描顺序 */

  /* 采样时间配置 --------------------------------------------------------- */
  uint32_t SamplingTime;           /*!< 指定通道的采样时间:
                                       - 单位:ADC时钟周期
                                       - 总转换时间 = 采样时间 + 固定处理时间
                                         (12位:+12.5周期,10位:+10.5周期等)
                                       - STM32F1支持两组公共采样时间配置
                                         (通过ADC_InitTypeDef中的SamplingTimeCommon1/2设置)
                                       - 内部通道需满足最小采样时间要求
                                         温度传感器:≥10μs,Vrefint:≥3μs */
} ADC_ChannelConfTypeDef;

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 */
	HAL_Delay(1000);

  /* USER CODE END Init */

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

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  	MX_ADC1_Init();	
  /* USER CODE BEGIN 2 */
	HAL_ADCEx_Calibration_Start(&hadc1);//ADC校准
	HAL_ADC_Start(&hadc1);
	ADCbuffer[0] = HAL_ADC_GetValue(&hadc1);
}
/**
  * @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 */

  /** 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.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.LowPowerAutoPowerOff = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_1CYCLE_5;
  hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5;
  hadc1.Init.OversamplingMode = DISABLE;
  hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
  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_SAMPLINGTIME_COMMON_1;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

总结

  本文介绍了逐次逼近型的转换原理,从基础结构出发,讲述STM32内部ADC的运行过程,还有内部寄存器的配置,旨在了解ADC的内部工作原理,没有从HAL库最上层往底层介绍,这是本文的遗憾,写下这篇文章用于日后自己复习,如有错误,欢迎大家评论留言,一起进步!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐