> 技术文档 > 【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

一、ADC简介

  • 1.1、什么是ADC?
  • 1.2、常见的ADC类型
  • 1.3、并联比较型工作示意图
  • 1.4、逐次逼近型工作示意图
  • 1.5、ADC的特性参数
  • 1.6、STM32各系列ADC的主要特性

二、ADC工作原理

  • 2.1、ADC框图简介(F1/ F4 /F7/H7)
  • 2.2、参考电压/模拟部分电压
  • 2.3、输入通道
  • 2.4、转换序列
  • 2.5、触发源
  • 2.6、转换时间
  • 2.7、数据寄存器
  • 2.8、中断
  • 2.9、单次转换模式和连续转换模式
  • 2.10、扫描模式

三、单通道ADC采集实验

  • 3.1、实验简要
  • 3.2、ADC寄存器介绍
  • 3.3、单通道ADC采集实验配置步骤
  • 3.4、编程实战:单通道ADC采集实验

四、单通道ADC采集(DMA读取)

  • 4.1、实验简要
  • 4.2、单通道ADC采集(DMA读取)实验配置步骤
  • 4.3、编程实战:单通道ADC采集(DMA读取)实验

五、多通道ADC采集(DMA读取)

  • 5.1、实验简要
  • 5.2、编程实战:多通道ADC采集(DMA读取)实验

六、单通道ADC过采样实验

  • 6.1、怎么用过采样和求均值的方式提高ADC的分辨率?
  • 6.2、实验简要
  • 6.3、编程实战:单通道ADC过采样(16位分辨率)实验

七、内部温度传感器实验

  • 7.1、STM32内部温度传感器简介
  • 7.2、温度计算方法
  • 7.3、实验简要
  • 7.4、编程实战:内部温度传感器实验

八、光敏传感器实验

  • 8.1、光敏二极管简介
  • 8.2、实验原理
  • 8.3、实验简要
  • 8.4、编程实战:光敏传感器实验

九、总结

一、ADC简介

1.1、什么是ADC?

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
ADC(模数转换器) 是一种电子设备,用于将连续的模拟信号转换为相应的离散数字数据。这在许多应用中非常重要,特别是在嵌入式系统和数据采集系统中。

具体来说,ADC将来自外部环境的模拟信号(例如来自传感器的温度、压力、光强等)转换为数字形式,以便微处理器或其他数字电路可以处理和分析这些数据。ADC的输出通常是二进制数字,代表了模拟信号的离散值。

在嵌入式系统中,ADC常常用于采集传感器数据,比如监控温度、湿度、光照等环境参数。这些数据随后可以被处理器用来做出决策、控制系统或者在显示器上显示。

总的来说,ADC是将模拟世界的数据转换为数字世界的重要桥梁,使得我们能够对现实世界的各种信号进行量化和处理。

1.2、常见的ADC类型

常见ADC类型:https://blog.csdn.net/whereismatrix/article/details/81814431
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
常见的ADC类型包括并行比较型和逐次逼近型。它们各有优点和缺点:

  1. 并行比较型 ADC

    • 优点:
      • 转换速度非常快,适用于需要高速转换的应用。
      • 可以实现高速率的采样。
    • 缺点:
      • 成本较高,因为需要大量的比较器和电路来并行处理输入信号。
      • 功耗较高,因为需要同时工作的电路较多。
      • 分辨率通常较低,受到布局和电路复杂性的限制。
  2. 逐次逼近型 ADC

    • 优点:
      • 结构相对简单,成本较低。
      • 功耗较低,因为只有一个比较器在工作。
    • 缺点:
      • 转换速度较慢,每次转换需要多个时钟周期。
      • 适用于较低速率的采样,不适合需要高速率采样的应用。

在选择 ADC 类型时,需要根据应用的具体要求权衡各种因素,包括转换速度、成本、功耗和分辨率等。

1.3、并联比较型工作示意图

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

1.4、逐次逼近型工作示意图

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
逐次逼近型 ADC (Successive Approximation ADC)通常由以下几个主要部分组成:

  1. 控制电路:控制电路负责管理 ADC 的整个转换过程。它通常包括时钟控制、状态机、控制逻辑等,用于确保 ADC 能够按照预定的顺序和时序完成模拟信号到数字信号的转换。

  2. 数码寄存器:数码寄存器是 ADC 中用于存储转换结果的部分。它的位数决定了 ADC 的分辨率。在逐次逼近型 ADC 中,数码寄存器通常是一个 N 位的寄存器,其中 N 表示 ADC 的分辨率。

  3. D/A 转换器:D/A 转换器(Digital-to-Analog Converter)用于产生 ADC 内部的参考电压或比较电压。在逐次逼近型 ADC 中,D/A 转换器通常用于生成逼近比较电压,以便与输入模拟信号进行比较。

  4. 电压比较器:电压比较器用于比较输入模拟信号和由 D/A 转换器产生的比较电压。比较器的输出结果将用于 ADC 控制电路中的决策逻辑,驱动 ADC 进行逐步逼近型转换过程。

逐次逼近型 ADC 通过多次逼近比较的过程来逐渐逼近输入模拟信号的真实值,并将结果存储在数码寄存器中。这种类型的 ADC 结构相对简单,适用于许多低速应用场景。

1.5、ADC的特性参数

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
ADC 特性参数包括:

  1. 分辨率:ADC 的分辨率表示其能够辨别的最小模拟量的能力。通常以位数来表示,例如 8 位、10 位、12 位、16 位等。更高的分辨率意味着 ADC 能够更精细地将模拟信号转换为数字信号。

  2. 转换时间:转换时间是完成一次 A/D 转换所需的时间。它决定了 ADC 的采样率,即每秒钟能够完成多少次转换。转换时间越短,ADC 的采样率就越高,能够更快地对模拟信号进行采样和转换。

  3. 精度:ADC 的精度是指其输出数字量与输入模拟量之间的误差。它受到多种因素的影响,包括 ADC 自身的性能、环境条件(如温度、气压等)以及电路设计的质量等。精度通常以百分比或者 LSB(最小刻度基本单位)来表示。

  4. 量化误差:量化误差是指将模拟信号转换为数字信号时,由于量化过程的精度限制而引入的误差。这个误差是由数字量化的离散性质所导致的,通常使用四舍五入等近似方法,因此会产生一定的误差。量化误差的大小取决于 ADC 的分辨率,分辨率越高,量化误差通常越小。

1.6、STM32各系列ADC的主要特性

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
根据您提供的信息,以下是 STM32 各系列 ADC 的主要特性:

  1. F1 系列

    • ADC 类型:逐次逼近型
    • 分辨率:12 位
    • ADC 时钟频率:最大 14MHz
    • 采样时间:采样时间越长,转换结果相对越准确,但转换速度越慢
    • 转换时间:与 ADC 时钟频率、分辨率和采样时间等有关
    • 供电电压:VSSA: 0V,VDDA: 2.4V~3.6V(全速运行)
    • 参考电压:VREF–: 0V,VREF+: 一般为 3.3V
    • 输入电压:VREF–≤VIN≤VREF+
  2. F4 系列

    • ADC 类型:逐次逼近型
    • 分辨率:6/8/10/12 位
    • ADC 时钟频率:最大 36MHz
    • 其他特性与 F1 类似,但有更高的分辨率和 ADC 时钟频率。
  3. F7 系列

    • ADC 类型:逐次逼近型
    • 分辨率:6/8/10/12 位
    • ADC 时钟频率:最大 36MHz
    • 其他特性与 F4 类似,但有更高的分辨率和可能更高的 ADC 时钟频率。
  4. H7 系列

    • ADC 类型:逐次逼近型
    • 分辨率:8/10/12/14/16 位
    • ADC 时钟频率:最大 36MHz
    • 其他特性与 F4/F7 类似,但有更高的分辨率。

以上是基于您提供的信息对各系列 STM32 的 ADC 特性的概述。请注意,具体的特性可能因芯片型号和具体配置而略有差异,建议查阅相关的数据手册以获取更详细的信息。

二、ADC工作原理

2.1、ADC框图简介(F1/ F4 /F7/H7)

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
ADC(模拟-数字转换器)的框图结构通常包括以下部分:

  1. 参考电压/模拟部分电压

    • 这部分提供了 ADC 输入信号的参考电压,用于将模拟信号转换为数字信号。
  2. 输入通道

    • ADC 可以从多个输入通道中选择进行转换。这些输入通道可以连接到外部传感器、电路或其他模拟源。
  3. 转换序列

    • 转换序列定义了 ADC 转换的顺序,即每次转换涉及的输入通道的顺序。
  4. 触发源

    • 触发源确定 ADC 何时启动转换。它可以是软件触发(通过代码触发)或外部触发(来自外部信号)。
  5. 转换时间

    • 转换时间是完成一次模拟到数字转换所需的时间。它取决于 ADC 的时钟频率、分辨率和采样时间等因素。
  6. 数据寄存器

    • 转换后的数字输出存储在数据寄存器中,供微处理器或其他设备使用。
  7. 中断

    • ADC 可以在转换完成时生成中断请求,以通知微处理器有新的数据可用。

这些部分共同构成了 ADC 的框图结构,使其能够将模拟信号转换为数字信号,并与微处理器进行通信。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
在STM32H7系列中,ADC的框图结构通常包括以下部分:

  1. VREF+电压

    • 这是ADC的参考电压,用于确定模拟输入信号的范围。
  2. ADC双时钟域架构

    • STM32H7的ADC采用了双时钟域架构,以实现更高的性能和精度。
  3. 输入通道

    • ADC可以从多个输入通道中选择进行转换。这些输入通道可以连接到外部传感器、电路或其他模拟源。
  4. 转换序列

    • 转换序列定义了ADC转换的顺序,即每次转换涉及的输入通道的顺序。
  5. 触发源

    • 触发源确定ADC何时启动转换。它可以是软件触发(通过代码触发)或外部触发(来自外部信号)。
  6. 转换时间

    • 转换时间是完成一次模拟到数字转换所需的时间。它取决于ADC的时钟频率、分辨率和采样时间等因素。
  7. 参考电压

    • 参考电压提供了ADC转换所需的参考基准。
  8. ADC核心

    • ADC核心执行模拟到数字转换的实际过程。
  9. 数据寄存器

    • 转换后的数字输出存储在数据寄存器中,以便供微处理器或其他设备使用。
  10. 中断

  • ADC可以在转换完成时生成中断请求,以通知微处理器有新的数据可用。
  1. 通道预选控制信号
  • 这些信号用于预选ADC转换中涉及的通道,以便更高效地进行转换。

这些部分共同构成了STM32H7系列ADC的框图结构,使其能够有效地将模拟信号转换为数字信号,并与微处理器进行通信。

2.2、参考电压/模拟部分电压

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
在STM32中,ADC的参考电压(VREF+)和模拟部分电压有以下特点和限制:

  1. ADC供电电源

    • ADC模块的供电电源是由VSSA(模拟地)和VDDA(模拟电源)提供的。
    • VDDA的电压范围通常为2.4V至3.6V。
  2. ADC输入电压范围

    • ADC输入的模拟电压(VIN)的范围取决于参考电压的设定。
    • 参考电压(VREF+)和参考地(VREF-)之间的电压范围定义了ADC输入的有效范围。
    • 在STM32中,常见的参考电压为3.3V,因此ADC的输入电压范围通常为0V至3.3V。

因此,当使用STM32的ADC时,需要确保模拟输入信号的电压在VREF-和VREF+之间,并且ADC的供电电源在正常工作范围内。

2.3、输入通道

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

2.4、转换序列

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

规则组和注入组

在STM32中,ADC的转换序列被组织为规则组和注入组,具体特点如下:

  1. 规则转换组

    • 规则转换组用于处理常规的A/D转换需求。
    • 最多可以有16个转换通道,每个通道可以配置为一个独立的转换序列。
    • 这些转换按照预先定义的顺序进行,可以由软件触发或由外部事件触发。
  2. 注入转换组

    • 注入转换组用于处理一些具有优先级的特殊转换需求,通常用于处理紧急或重要的转换。
    • 最多可以有4个转换通道,每个通道可以配置为一个独立的转换序列。
    • 注入转换可以在规则转换之前或之后触发,具体取决于其优先级设置。

通过规则组和注入组的灵活组合,STM32的ADC可以满足各种不同的转换需求,从而更好地适应不同的应用场景。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
规则组和注入组的执行优先级如下所示:

  1. 规则组执行优先级

    • 规则组的转换优先级相对较低。
    • 当规则组和注入组同时触发转换时,规则组通常会等待注入组完成转换后再执行。
  2. 注入组执行优先级

    • 注入组具有较高的转换优先级。
    • 如果注入组启动了转换,并且规则组也需要进行转换,注入组的转换会优先执行。
    • 注入组可以在规则组转换之前或之后触发转换,具体取决于其优先级设置。

总的来说,注入组的转换具有较高的优先级,可以在规则组转换之前或之后执行,并且可以在一些特殊情况下优先执行。规则组的转换相对较为常规,优先级较低,会等待注入组转换完成后才执行。

规则序列和注入序列

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
规则序列和注入序列是STM32中ADC转换序列的两种类型,它们的设置可以通过相关的寄存器进行配置。

  1. 规则序列

    • 规则序列用于执行常规的ADC转换任务,可以包含多个转换通道,并按照设置的顺序依次执行。
    • 通过配置规则序列,可以定义需要转换的通道及其顺序。
    • 规则序列最多可以包含16个转换通道。
  2. 注入序列

    • 注入序列用于执行具有高优先级的ADC转换任务,通常用于实时性要求较高的应用场景。
    • 注入序列也可以包含多个转换通道,但其优先级高于规则序列,且在某些情况下可以中断规则序列的转换来执行自己的转换。
    • 注入序列最多可以包含4个转换通道。

配置规则序列和注入序列的步骤如下:

  1. 选择转换通道:确定需要转换的通道,可以是单个通道或多个通道的组合。
  2. 设置转换顺序:为规则序列和注入序列设置转换通道的顺序,通常通过相关的寄存器进行配置。
  3. 配置转换触发源:选择触发转换的条件,可以是软件触发或外部触发等。
  4. 设置转换参数:包括采样时间、对齐方式、分辨率等转换参数的设置。
  5. 启动转换:根据需求启动规则序列和注入序列的转换。

具体的配置步骤和寄存器设置会根据具体的STM32系列和型号而有所不同,需要查阅相应系列的参考手册或技术文档以获取详细信息。

2.5、触发源

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

ADON位触发转换 和 外部事件触发转换

对于STM32系列中的ADC模块,确实存在这两种触发转换的方法。

  1. ADON位触发转换(仅限于某些旧的STM32系列,如F1系列):

    • 在旧的STM32系列中,可以通过设置 ADC_CR2 寄存器的 ADON 位来启动ADC转换。
    • 当 ADON 位为 1 时,表示ADC处于启动状态,再次将 ADON 位设置为 1,可以触发规则组转换。
  2. 外部事件触发转换

    • 外部事件触发转换是一种更为灵活和常见的触发方式,适用于各种STM32系列。
    • 外部事件触发可以分为规则组外部触发和注入组外部触发两种:
      • 规则组外部触发:可以由外部信号触发规则序列的转换,例如外部GPIO引脚的信号变化、定时器的触发等。
      • 注入组外部触发:类似地,注入序列的转换也可以由外部事件触发,且在某些情况下具有更高的优先级。

对于外部事件触发转换,需要配置相应的触发源,并在启动ADC时等待外部事件的发生以触发转换。这种方法通常更加灵活,并且适用于各种应用场景。

规则组外部触发使用方法 和 注入组外部触发使用方法

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
规则组外部触发和注入组外部触发的使用方法如下:

规则组外部触发使用方法:

  1. 配置触发源: 首先,需要选择外部事件作为触发源,例如外部GPIO引脚的信号变化、定时器的触发等。这通常需要配置相关的GPIO或定时器,并将其与ADC模块的触发输入信号相连。

  2. 配置ADC触发模式:初始化ADC时,需要设置触发模式为外部触发。这可以通过配置 ADC_CR2 寄存器中的 EXTEN 和 EXTSEL 位来实现。EXTEN 用于配置触发边沿,EXTSEL 用于选择触发源。

  3. 启动ADC转换: 当外部事件发生时,会触发ADC转换。在启动ADC时,ADC会等待外部触发事件,并在触发后执行规则组中的转换。

注入组外部触发使用方法:

  1. 配置触发源: 类似于规则组外部触发,首先需要选择外部事件作为触发源,并将其与注入组的触发输入信号相连。

  2. 配置注入组触发模式: 在初始化ADC时,需要设置注入组的触发模式为外部触发。这可以通过配置 ADC_CR2 寄存器中的 JEXTEN 和 JEXTSEL 位来实现。JEXTEN 用于配置注入组触发边沿,JEXTSEL 用于选择触发源。

  3. 启动ADC转换: 当外部事件触发时,注入组会执行相应的转换。在启动ADC时,注入组会等待外部触发事件,并在触发后执行注入组中的转换。

通过以上步骤,可以实现规则组和注入组的外部触发转换,从而灵活地应对各种采样需求。

2.6、转换时间

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
设置ADC时钟和转换时间的方法如下:

  1. 设置ADC时钟:

    • 首先,选择适当的时钟源和分频系数来配置ADC的时钟。在STM32中,ADC的时钟源通常为系统时钟(SYSCLK)或者外部时钟源(如HSE或HSI)。
    • 使用相关的寄存器(如RCC_CFGR 寄存器)来配置时钟源和分频系数,以获得所需的ADC时钟频率。例如,如果使用SYSCLK作为时钟源,并希望得到14MHz的ADC时钟,则需要设置合适的分频系数。
  2. 设置ADC转换时间:

    • 转换时间由采样时间和转换周期组成。采样时间是ADC在进行转换之前对输入信号进行采样的时间,而转换周期是ADC完成一次转换所需的时间。
    • 采样时间可以通过设置 SMPx[2:0] 位来实现,其中 x 表示对应的通道编号。不同的STM32系列有不同的通道编号和对应的寄存器。通常,SMPx[2:0] 位设置为0~7对应不同的采样时间,从而决定了转换过程中对输入信号的采样时间长度。
    • 转换周期固定为12.5个ADC时钟周期。因此,转换时间可以通过以下公式计算得到: T C O N V = 采样时间 + 12.5 个 A D C 时钟周期 T_{CONV} = 采样时间 + 12.5个ADC时钟周期 TCONV=采样时间+12.5ADC时钟周期

通过以上步骤,可以根据需求灵活地设置ADC的时钟和转换时间,以满足具体的应用需求。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

2.7、数据寄存器

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
数据寄存器用于存储ADC转换的结果,其中规则数据寄存器 ADCx_DR 存储规则组转换的结果,而注入数据寄存器 ADCx_JDRy 存储注入组转换的结果,其中 y 可以是1到4,对应着注入组中不同的转换通道。

在读取数据之前,需要根据应用的要求设置数据的对齐方式。这可以通过 ADCx_CR2 寄存器的 ALIGN 位来实现。具体地:

  • 右对齐模式(默认模式):转换结果的最低有效位存储在数据寄存器的最低位,而最高有效位存储在数据寄存器的最高位。这种模式适用于大多数应用场景。
  • 左对齐模式:转换结果的最高有效位存储在数据寄存器的最低位,而最低有效位存储在数据寄存器的最高位。这种模式适用于一些特定应用场景。

根据具体需求选择合适的对齐模式,然后读取数据寄存器中的转换结果即可。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

2.8、中断

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
在STM32的F1/F4/F7系列中,ADC模块支持多种中断事件,以及对应的事件标志和使能控制位。这些中断事件包括:

  1. 规则通道转换结束(End of Conversion, EOC):表示规则组中的一个转换通道完成了转换。

    • 事件标志:EOC
    • 使能控制位:EOCIE
  2. 注入通道转换结束(Injected End of Conversion, JEOC):表示注入组中的一个转换通道完成了转换。

    • 事件标志:JEOC
    • 使能控制位:JEOCIE
  3. 模拟看门狗状态位(Analog Watchdog, AWD):表示转换结果超出预设阈值,可以用于检测输入信号的异常状态。

    • 事件标志:AWD
    • 使能控制位:AWDIE
  4. 溢出(Overflow, OVR):表示转换结果溢出,即转换结果超出了数据寄存器的容量。在F1系列中没有此中断。

    • 事件标志:OVR
    • 使能控制位:OVRIE

此外,对于规则组转换结束后,还可以产生DMA请求,可以使用DMA及时传输转换好的数据到指定的内存地址,防止数据被覆盖。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

2.9、单次转换模式和连续转换模式

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
单次转换模式和连续转换模式是ADC的两种工作模式,具体特点如下:

单次转换模式:

  • 在单次转换模式下,ADC只进行一次转换,即完成一次转换后就停止工作。
  • 对于规则组,转换结果存储在ADC_DR寄存器中,并且转换结束标志位EOC被置为1。如果设置了EOCIE位,则会产生中断。
  • 对于注入组,转换结果存储在ADC_DRJx寄存器中,并且转换结束标志位JEOC被置为1。如果设置了JEOCIE位,则会产生中断。

连续转换模式:

  • 在连续转换模式下,ADC会连续进行转换,即完成一次转换后会自动触发下一次转换,直到外部停止触发或者软件停止。
  • 对于规则组,转换结果同样存储在ADC_DR寄存器中,并且转换结束标志位EOC被置为1。如果设置了EOCIE位,则会产生中断。
  • 对于注入组,转换结果同样存储在ADC_DRJx寄存器中,并且转换结束标志位JEOC被置为1。如果设置了JEOCIE位,则会产生中断。
  • 自动注入功能可以使得注入组自动进行转换,通过设置JAUTO位来启用。

总之,单次转换模式适用于只需要进行一次转换的场景,而连续转换模式适用于需要连续监测的场景。

2.10、扫描模式

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
扫描模式是指ADC进行转换时是否扫描多个通道的模式。具体特点如下:

关闭扫描模式:

  • 在关闭扫描模式下,ADC只会转换由ADC_SQRx或ADC_JSQR选中的第一个通道。也就是说,只会转换一个通道的数据。

使用扫描模式:

  • 在使用扫描模式下,ADC会扫描所有被ADC_SQRx或ADC_JSQR选中的通道,依次进行转换。
  • 这意味着,ADC会逐个转换所有被选中的通道,并将转换结果存储在相应的数据寄存器中。

总之,扫描模式可以让ADC在一次转换中依次转换多个通道的数据,适用于需要监测多个信号的场景。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

三、单通道ADC采集实验

3.1、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
根据配置描述,通过ADC1通道1(PA1)采集电位器的电压,并显示ADC转换的数字量及换算后的电压值。以下是实现该功能的步骤:

  1. 确定最小刻度: 对于STM32系列,如果ADC的参考电压(VREF+)为3.3V,且分辨率为12位,则最小刻度为 ( f r a c 3.3 V 2 12frac{3.3V}{2^{12}} frac3.3V212) ,即 ( f r a c 3.3 V 4096 frac{3.3V}{4096} frac3.3V4096) 。这是ADC可以分辨的最小电压变化。

  2. 确定转换时间: 根据您提供的采样时间239.5个ADC时钟周期,转换时间为21μs。

  3. 模式组合: 您选择了单次转换模式,并且不使用扫描模式。

  4. 编程实现:

    • 启用ADC1通道1(PA1)作为输入通道。
    • 配置ADC为单次转换模式,禁用扫描模式。
    • 启动ADC转换并等待转换完成。
    • 读取转换结果,换算为电压值。
    • 显示转换结果及电压值。

下面是一个简单的示例代码,用于实现这些步骤:

#include \"stm32f4xx_hal.h\"#include ADC_HandleTypeDef hadc;void SystemClock_Config(void);static void MX_GPIO_Init(void);static void MX_ADC1_Init(void);int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); uint32_t adc_value; float voltage; HAL_ADC_Start(&hadc); HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY); adc_value = HAL_ADC_GetValue(&hadc); voltage = adc_value * 3.3 / 4096; printf(\"ADC Value: %lu\\n\", adc_value); printf(\"Voltage: %.2fV\\n\", voltage); while (1) { }}void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);}static void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc.Instance = ADC1; hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc.Init.Resolution = ADC_RESOLUTION_12B; hadc.Init.ScanConvMode = DISABLE; hadc.Init.ContinuousConvMode = DISABLE; hadc.Init.DiscontinuousConvMode = DISABLE; hadc.Init.NbrOfDiscConversion = 0; hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc.Init.NbrOfConversion = 1; hadc.Init.DMAContinuousRequests = DISABLE; hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; HAL_ADC_Init(&hadc); sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&hadc, &sConfig);}static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);}

请根据您的实际硬件配置和需求进行适当的修改。

3.2、ADC寄存器介绍

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

3.3、单通道ADC采集实验配置步骤

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
单通道ADC采集实验配置步骤,以下是对每个步骤的简要说明:

  1. 配置ADC工作参数、ADC校准:

    • 使用 HAL_ADC_Init() 初始化 ADC,并使用 HAL_ADCEx_Calibration_Start() 进行校准。这两个函数将配置 ADC 的基本工作参数,并确保其准确性。
  2. ADC MSP初始化:

    • 实现 HAL_ADC_MspInit() 函数,其中包括配置 ADC 相关的中断向量表、时钟和 GPIO 等外设。这是为了确保 ADC 的正常工作,包括时钟和中断的配置。
  3. 配置ADC相应通道相关参数:

    • 使用 HAL_ADC_ConfigChannel() 配置 ADC 的输入通道及其相关参数,如采样时间等。通过这一步,您可以选择要采集的通道以及采样参数。
  4. 启动A/D转换:

    • 使用 HAL_ADC_Start() 启动 ADC 转换。一旦启动,ADC 将开始转换输入通道的模拟信号,并将其转换为数字值。
  5. 等待规则通道转换完成:

    • 使用 HAL_ADC_PollForConversion() 函数等待 ADC 转换完成。在这个步骤中,您可以选择等待转换完成或设置超时时间。
  6. 获取规则通道A/D转换结果:

    • 使用 HAL_ADC_GetValue() 函数获取转换结果。一旦转换完成,您可以使用该函数获取 ADC 转换的数字值,然后进行后续处理,比如显示或存储。

通过以上步骤,您可以完成单通道ADC采集的实验配置。确保根据您的具体硬件和应用需求进行适当的配置和修改。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

3.4、编程实战:单通道ADC采集实验

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
adc.c

#include \"./BSP/ADC/adc.h\"ADC_HandleTypeDef g_adc_handle;/* ADC单通道初始化函数 */void adc_init(void){ ADC_ChannelConfTypeDef adc_ch_conf; // 配置 ADC 句柄参数 g_adc_handle.Instance = ADC1; 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; g_adc_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_handle.Init.NbrOfDiscConversion = 0; g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 初始化 ADC HAL_ADC_Init(&g_adc_handle); // 进行 ADC 校准 HAL_ADCEx_Calibration_Start(&g_adc_handle); // 配置 ADC 通道 adc_ch_conf.Channel = ADC_CHANNEL_1; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能GPIOA和ADC1时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 配置GPIOA引脚为模拟模式 gpio_init_struct.Pin = GPIO_PIN_1; gpio_init_struct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &gpio_init_struct);  // 配置 ADC 时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* 获取ADC转换后的结果函数 */uint32_t adc_get_result(void){ // 启动 ADC 转换 HAL_ADC_Start(&g_adc_handle); // 等待转换完成 HAL_ADC_PollForConversion(&g_adc_handle, 10); // 获取转换结果 return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);}

adc.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc_init(void);/* ADC单通道初始化函数 */uint32_t adc_get_result(void);/* 获取ADC转换后的结果函数 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./USMART/usmart.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc.h\"int main(void){ uint16_t adcx; float temp; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200);  /* 串口初始化为115200 */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ adc_init(); /* 初始化ADC */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"ADC TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 110, 200, 16, 16, \"ADC1_CH1_VAL:\", BLUE); lcd_show_string(30, 130, 200, 16, 16, \"ADC1_CH1_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ while (1) { adcx = adc_get_result(); lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE); /* 显示ADCC采样后的原始值 */ temp = (float)adcx * (3.3 / 4096);  /* 获取计算后的带小数的实际电压值,比如3.1111 */ adcx = temp;  /* 赋值整数部分给adcx变量,因为adcx为u16整形 */ lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */ temp -= adcx; /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */ temp *= 1000; /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */ lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */ LED0_TOGGLE(); delay_ms(100); }}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

四、单通道ADC采集(DMA读取)

4.1、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

4.2、单通道ADC采集(DMA读取)实验配置步骤

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

#include \"./BSP/ADC/adc.h\"ADC_HandleTypeDef g_adc_handle;DMA_HandleTypeDef g_dma_handle;/* ADC单通道初始化函数 */void adc_init(void){ ADC_ChannelConfTypeDef adc_ch_conf; // 配置 ADC 句柄参数 g_adc_handle.Instance = ADC1; 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; g_adc_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_handle.Init.NbrOfDiscConversion = 0; g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 初始化 ADC HAL_ADC_Init(&g_adc_handle); // 进行 ADC 校准 HAL_ADCEx_Calibration_Start(&g_adc_handle); // 配置 ADC 通道 adc_ch_conf.Channel = ADC_CHANNEL_1; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能GPIOA和ADC1时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 配置GPIOA引脚为模拟模式 gpio_init_struct.Pin = GPIO_PIN_1; gpio_init_struct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &gpio_init_struct);  // 配置 ADC 时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* DMA初始化函数 */void dma_init(void){ // 初始化 DMA 句柄 g_dma_handle.Instance = DMA1_Channel1; g_dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; g_dma_handle.Init.MemInc = DMA_MINC_ENABLE; g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; g_dma_handle.Init.Mode = DMA_CIRCULAR; g_dma_handle.Init.Priority = DMA_PRIORITY_HIGH; HAL_DMA_Init(&g_dma_handle);}/* 将 DMA 和 ADC 句柄联系起来 */void adc_dma_link(void){ // 将 DMA 与 ADC 关联 __HAL_LINKDMA(&g_adc_handle, DMA_Handle, g_dma_handle);}/* 使能 DMA 数据流传输完成中断 */void dma_interrupt_enable(void){ // 配置 DMA 数据流传输完成中断 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);}/* DMA 数据流中断服务函数 */void DMA1_Channel1_IRQHandler(void){ // 处理 DMA 数据流中断事件 HAL_DMA_IRQHandler(&g_dma_handle);}/* 启动 DMA,开启传输完成中断 */void dma_start(void){ // 启动 DMA,开启传输完成中断 HAL_DMA_Start_IT(&g_dma_handle, (uint32_t)&ADC1->DR, (uint32_t)&adc_value, 1);}/* 触发 ADC 转换,DMA 传输数据 */void adc_start_dma(void){ // 启动 ADC 转换,DMA 传输数据 HAL_ADC_Start_DMA(&g_adc_handle, (uint32_t*)&adc_value, 1);}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

4.3、编程实战:单通道ADC采集(DMA读取)实验

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
adc.c

#include \"./BSP/ADC/adc.h\"DMA_HandleTypeDef g_dma_adc_handle;ADC_HandleTypeDef g_adc_dma_handle;uint8_t g_adc_dma_sta;/* ADC DMA读取 初始化函数 */void adc_dma_init(uint32_t mar){ ADC_ChannelConfTypeDef adc_ch_conf; // 使能 DMA1 时钟 __HAL_RCC_DMA1_CLK_ENABLE(); // 配置 DMA 句柄参数 g_dma_adc_handle.Instance = DMA1_Channel1; g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE; g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE; g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; g_dma_adc_handle.Init.Mode = DMA_NORMAL; g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM; HAL_DMA_Init(&g_dma_adc_handle); // 将 DMA 与 ADC 关联 __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle); // 配置 ADC 句柄参数 g_adc_dma_handle.Instance = ADC1; g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; g_adc_dma_handle.Init.ContinuousConvMode = ENABLE; g_adc_dma_handle.Init.NbrOfConversion = 1; g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_dma_handle.Init.NbrOfDiscConversion = 0; g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; HAL_ADC_Init(&g_adc_dma_handle); // 进行 ADC 校准 HAL_ADCEx_Calibration_Start(&g_adc_dma_handle); // 配置 ADC 通道 adc_ch_conf.Channel = ADC_CHANNEL_1; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf); // 配置 DMA1_Channel1 中断优先级 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3); // 使能 DMA1_Channel1 中断 HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // 启动 DMA,开启传输完成中断 HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0); // 启动 ADC 转换,DMA 传输数据 HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar ,0);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能 GPIOA 和 ADC1 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 配置 GPIOA 引脚为模拟模式 gpio_init_struct.Pin = GPIO_PIN_1; gpio_init_struct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &gpio_init_struct);  // 配置 ADC 时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* 使能一次ADC DMA传输函数 */void adc_dma_enable(uint16_t cndtr){ // 关闭 ADC1 和 DMA1_Channel1 ADC1->CR2 &= ~(1 << 0); DMA1_Channel1->CCR &= ~(1 << 0); while (DMA1_Channel1->CCR & (1 << 0)); // 设置 DMA1_Channel1 传输数据长度 DMA1_Channel1->CNDTR = cndtr; // 启动 DMA1_Channel1 和 ADC1 DMA1_Channel1->CCR |= 1 << 0; ADC1->CR2 |= (1 << 0) | (1 << 22);// 禁用 ADC1//__HAL_ADC_DISABLE(&g_adc_dma_handle);// 禁用 DMA1_Channel1,并等待传输完成//__HAL_DMA_DISABLE(&g_dma_adc_handle);//while (__HAL_DMA_GET_FLAG(&g_dma_adc_handle, __HAL_DMA_GET_TC_FLAG_INDEX(&g_dma_adc_handle)));// 重新设置 DMA1_Channel1 的传输数据长度//DMA1_Channel1->CNDTR = cndtr;// 重新使能 DMA1_Channel1//__HAL_DMA_ENABLE(&g_dma_adc_handle);// 使能 ADC1//__HAL_ADC_ENABLE(&g_adc_dma_handle);// 启动 ADC 转换//HAL_ADC_Start(&g_adc_dma_handle);}/* ADC DMA采集中断服务函数 */void DMA1_Channel1_IRQHandler(void){ // 判断 DMA1 传输完成中断事件是否发生 if (DMA1->ISR & (1<<1)) { // 标记 ADC DMA 状态 g_adc_dma_sta = 1; // 清除 DMA1 传输完成中断标志 DMA1->IFCR |= 1 << 1; }}

adc.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc_dma_init(uint32_t mar);/* ADC DMA读取 初始化函数 */void adc_dma_enable(uint16_t cndtr);/* 使能一次ADC DMA传输函数 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc.h\"#define ADC_DMA_BUF_SIZE 100 /* ADC DMA采集 BUF大小 */uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA BUF */extern uint8_t g_adc_dma_sta;  /* DMA传输状态标志, 0,未完成; 1, 已完成 */int main(void){ uint16_t i; uint16_t adcx; uint32_t sum; float temp; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200);  /* 串口初始化为115200 */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"ADC DMA TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 110, 200, 16, 16, \"ADC1_CH1_VAL:\", BLUE); lcd_show_string(30, 130, 200, 16, 16, \"ADC1_CH1_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动ADC DMA采集 */ while (1) { if (g_adc_dma_sta == 1) { /* 计算DMA 采集到的ADC数据的平均值 */ sum = 0; for (i = 0; i < ADC_DMA_BUF_SIZE; i++) /* 累加 */ { sum += g_adc_dma_buf[i]; } adcx = sum / ADC_DMA_BUF_SIZE;  /* 取平均值 */ /* 显示结果 */ lcd_show_xnum(134, 110, adcx, 4, 16, 0, BLUE); /* 显示ADCC采样后的原始值 */ temp = (float)adcx * (3.3 / 4096);  /* 获取计算后的带小数的实际电压值,比如3.1111 */ adcx = temp;  /* 赋值整数部分给adcx变量,因为adcx为u16整形 */ lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */ temp -= adcx;  /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */ temp *= 1000;  /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */ lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE); /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */ g_adc_dma_sta = 0; /* 清除DMA采集完成状态标志 */ adc_dma_enable(ADC_DMA_BUF_SIZE);  /* 启动下一次ADC DMA采集 */ } LED0_TOGGLE(); delay_ms(100); }}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

五、多通道ADC采集(DMA读取)

5.1、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

5.2、编程实战:多通道ADC采集(DMA读取)实验

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
adc.c

#include \"./BSP/ADC/adc.h\"DMA_HandleTypeDef g_dma_nch_adc_handle;ADC_HandleTypeDef g_adc_nch_dma_handle;uint8_t g_adc_dma_sta;/* ADC N通道(6通道) DMA读取 初始化函数 */void adc_nch_dma_init(uint32_t mar){ ADC_ChannelConfTypeDef adc_ch_conf; // 使能 DMA1 时钟 __HAL_RCC_DMA1_CLK_ENABLE(); // 配置 DMA 句柄参数 g_dma_nch_adc_handle.Instance = DMA1_Channel1; g_dma_nch_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; g_dma_nch_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE; g_dma_nch_adc_handle.Init.MemInc = DMA_MINC_ENABLE; g_dma_nch_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; g_dma_nch_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; g_dma_nch_adc_handle.Init.Mode = DMA_NORMAL; g_dma_nch_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM; HAL_DMA_Init(&g_dma_nch_adc_handle); // 将 DMA 句柄与 ADC 句柄关联 __HAL_LINKDMA(&g_adc_nch_dma_handle, DMA_Handle, g_dma_nch_adc_handle); // 配置 ADC 句柄参数 g_adc_nch_dma_handle.Instance = ADC1; g_adc_nch_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; g_adc_nch_dma_handle.Init.ScanConvMode = ADC_SCAN_ENABLE; g_adc_nch_dma_handle.Init.ContinuousConvMode = ENABLE; g_adc_nch_dma_handle.Init.NbrOfConversion = 6; // 6个通道的转换 g_adc_nch_dma_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_nch_dma_handle.Init.NbrOfDiscConversion = 0; g_adc_nch_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; HAL_ADC_Init(&g_adc_nch_dma_handle); // 启动 ADC 校准 HAL_ADCEx_Calibration_Start(&g_adc_nch_dma_handle); // 配置 ADC 通道参数 adc_ch_conf.Channel = ADC_CHANNEL_0; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&g_adc_nch_dma_handle, &adc_ch_conf); // 这里依次配置了6个通道,示例中只展示了第一个通道的配置 // 配置 DMA1_Channel1 的中断优先级和使能 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3); HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // 启动 DMA 传输并开启 ADC DMA 模式 HAL_DMA_Start_IT(&g_dma_nch_adc_handle, (uint32_t)&ADC1->DR, mar, 0); HAL_ADC_Start_DMA(&g_adc_nch_dma_handle, &mar ,0);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能 GPIOA 和 ADC1 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 配置 GPIOA 引脚为模拟输入模式 gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5; gpio_init_struct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &gpio_init_struct);  // 配置 ADC 时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* 使能一次ADC DMA传输函数 */void adc_dma_enable(uint16_t cndtr){ // 关闭 ADC1 ADC1->CR2 &= ~(1 << 0); // 关闭 DMA1_Channel1,并等待传输完成 DMA1_Channel1->CCR &= ~(1 << 0); while (DMA1_Channel1->CCR & (1 << 0)); // 重新设置 DMA1_Channel1 的传输数据长度 DMA1_Channel1->CNDTR = cndtr; // 重新使能 DMA1_Channel1 DMA1_Channel1->CCR |= 1 << 0; // 使能 ADC1 ADC1->CR2 |= 1 << 0; // 启动 ADC 转换 ADC1->CR2 |= 1 << 22;}/* ADC DMA采集中断服务函数 */void DMA1_Channel1_IRQHandler(void){ // 检查 DMA 传输完成标志位 if (DMA1->ISR & (1<<1)) { // 设置 DMA 传输完成标志 g_adc_dma_sta = 1; // 清除 DMA 传输完成标志位 DMA1->IFCR |= 1 << 1; }}

adc.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc_nch_dma_init(uint32_t mar);/* ADC N通道(6通道) DMA读取 初始化函数 */void adc_dma_enable(uint16_t cndtr);/* 使能一次ADC DMA传输函数 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc.h\"#define ADC_DMA_BUF_SIZE 50 * 6 /* ADC DMA采集 BUF大小, 应等于ADC通道数的整数倍 */uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE]; /* ADC DMA BUF */extern uint8_t g_adc_dma_sta;  /* DMA传输状态标志, 0,未完成; 1, 已完成 */int main(void){ uint16_t i,j; uint16_t adcx; uint32_t sum; float temp; HAL_Init();  /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init();  /* 初始化LED */ lcd_init();  /* 初始化LCD */ adc_nch_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"ADC 6CH DMA TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 110, 200, 12, 12, \"ADC1_CH0_VAL:\", BLUE); lcd_show_string(30, 122, 200, 12, 12, \"ADC1_CH0_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ lcd_show_string(30, 140, 200, 12, 12, \"ADC1_CH1_VAL:\", BLUE); lcd_show_string(30, 152, 200, 12, 12, \"ADC1_CH1_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ lcd_show_string(30, 170, 200, 12, 12, \"ADC1_CH2_VAL:\", BLUE); lcd_show_string(30, 182, 200, 12, 12, \"ADC1_CH2_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ lcd_show_string(30, 200, 200, 12, 12, \"ADC1_CH3_VAL:\", BLUE); lcd_show_string(30, 212, 200, 12, 12, \"ADC1_CH3_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ lcd_show_string(30, 230, 200, 12, 12, \"ADC1_CH4_VAL:\", BLUE); lcd_show_string(30, 242, 200, 12, 12, \"ADC1_CH4_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ lcd_show_string(30, 260, 200, 12, 12, \"ADC1_CH5_VAL:\", BLUE); lcd_show_string(30, 272, 200, 12, 12, \"ADC1_CH5_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动ADC DMA采集 */ while (1) { if (g_adc_dma_sta == 1) { /* 循环显示通道0~通道5的结果 */ for(j = 0; j < 6; j++) /* 遍历6个通道 */ { sum = 0; /* 清零 */ for (i = 0; i < ADC_DMA_BUF_SIZE / 6; i++) /* 每个通道采集了50次数据,进行50次累加 */ {  sum += g_adc_dma_buf[(6 * i) + j]; /* 相同通道的转换数据累加 */ } adcx = sum / (ADC_DMA_BUF_SIZE / 6); /* 取平均值 */ /* 显示结果 */ lcd_show_xnum(108, 110 + (j * 30), adcx, 4, 12, 0, BLUE); /* 显示ADC采样后的原始值 */ temp = (float)adcx * (3.3 / 4096); /* 获取计算后的带小数的实际电压值,比如3.1111 */ adcx = temp; /* 赋值整数部分给adcx变量,因为adcx为u16整形 */ lcd_show_xnum(108, 122 + (j * 30), adcx, 1, 12, 0, BLUE); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */ temp -= adcx; /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */ temp *= 1000; /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */ lcd_show_xnum(120, 122 + (j * 30), temp, 3, 12, 0X80, BLUE);/* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */ } g_adc_dma_sta = 0;/* 清除DMA采集完成状态标志 */ adc_dma_enable(ADC_DMA_BUF_SIZE); /* 启动下一次ADC DMA采集 */ } LED0_TOGGLE(); delay_ms(100); }}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

六、单通道ADC过采样实验

6.1、怎么用过采样和求均值的方式提高ADC的分辨率?

用过采样和求均值提高ADC分辨率 方程推导过程:https://max.book118.com/html/2018/0506/165038217.shtm
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
过采样和求均值的方式可以有效提高ADC的分辨率。下面是具体步骤:

  1. 确定过采样率

    • 过采样率是指在一定时间内采集的样本数。
    • 根据要增加的分辨率位数和初始采样频率,可以使用以下方程确定过采样率:

    f o s = 2 2 w × f s f_{os} = 2^{2w} \\times f_s fos=22w×fs

    其中,(f_{os}) 是过采样频率,(w) 是希望增加的分辨率位数,(f_s) 是初始采样频率。这是因为信噪比(SNR)的改善与过采样率的平方根成正比,而每增加一位分辨率,理论上需要4倍的过采样率来实现。

  2. 求均值

    • 在得到过采样率之后,进行多次采样,并将采样结果进行求和。
    • 例如,对于12位分辨率的ADC要提高4位分辨率,需要进行16倍的过采样( 2 4 = 16 2^4 = 16 24=16)。
    • 然后将这16次采样结果求和,最后右移4位(即除以16),即可得到提高分辨率后的结果。

注意:提高N位分辨率,需要将求和结果右移N位。

通过这种方法,可以在不增加硬件成本的情况下提高ADC的分辨率,提高系统对信号的精确度和灵敏度。

6.2、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

6.3、编程实战:单通道ADC过采样(16位分辨率)实验

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
adc.c

#include \"./BSP/ADC/adc.h\"DMA_HandleTypeDef g_dma_adc_handle;ADC_HandleTypeDef g_adc_dma_handle;uint8_t g_adc_dma_sta;/* ADC DMA读取 初始化函数 */void adc_dma_init(uint32_t mar){ ADC_ChannelConfTypeDef adc_ch_conf; // 使能DMA时钟 __HAL_RCC_DMA1_CLK_ENABLE(); // 配置DMA句柄 g_dma_adc_handle.Instance = DMA1_Channel1; g_dma_adc_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; g_dma_adc_handle.Init.PeriphInc = DMA_PINC_DISABLE; g_dma_adc_handle.Init.MemInc = DMA_MINC_ENABLE; g_dma_adc_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; g_dma_adc_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; g_dma_adc_handle.Init.Mode = DMA_NORMAL; g_dma_adc_handle.Init.Priority = DMA_PRIORITY_MEDIUM; HAL_DMA_Init(&g_dma_adc_handle); // 将DMA与ADC句柄联系起来 __HAL_LINKDMA(&g_adc_dma_handle, DMA_Handle, g_dma_adc_handle); // 配置ADC句柄 g_adc_dma_handle.Instance = ADC1; g_adc_dma_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; g_adc_dma_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; g_adc_dma_handle.Init.ContinuousConvMode = ENABLE; g_adc_dma_handle.Init.NbrOfConversion = 1; g_adc_dma_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_dma_handle.Init.NbrOfDiscConversion = 0; g_adc_dma_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; HAL_ADC_Init(&g_adc_dma_handle); // ADC校准 HAL_ADCEx_Calibration_Start(&g_adc_dma_handle); // 配置ADC通道 adc_ch_conf.Channel = ADC_CHANNEL_1; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; // 采样时间为1.5个ADC时钟周期 HAL_ADC_ConfigChannel(&g_adc_dma_handle, &adc_ch_conf); // 配置DMA传输中断 HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 3); HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn); // 启动DMA传输 HAL_DMA_Start_IT(&g_dma_adc_handle, (uint32_t)&ADC1->DR, mar, 0); // 启动ADC DMA传输 HAL_ADC_Start_DMA(&g_adc_dma_handle, &mar ,0);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能GPIOA时钟和ADC1时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); // 配置ADC引脚为模拟输入模式 gpio_init_struct.Pin = GPIO_PIN_1; gpio_init_struct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &gpio_init_struct);  // 配置ADC时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* 使能一次ADC DMA传输函数 */void adc_dma_enable(uint16_t cndtr){ // 禁止ADC和DMA传输 ADC1->CR2 &= ~(1 << 0); DMA1_Channel1->CCR &= ~(1 << 0); while (DMA1_Channel1->CCR & (1 << 0)); // 配置DMA传输数量 DMA1_Channel1->CNDTR = cndtr; // 使能DMA传输和ADC DMA1_Channel1->CCR |= 1 << 0; ADC1->CR2 |= 1 << 0; ADC1->CR2 |= 1 << 22; // 等待DMA传输完成// __HAL_DMA_DISABLE(&g_dma_adc_handle);// while (__HAL_DMA_GET_FLAG(&g_dma_adc_handle, __HAL_DMA_GET_TC_FLAG_INDEX(&g_dma_adc_handle)));// DMA1_Channel1->CNDTR = cndtr;// __HAL_DMA_ENABLE(&g_dma_adc_handle); // 启动ADC// __HAL_ADC_ENABLE(&g_adc_dma_handle);// HAL_ADC_Start(&g_adc_dma_handle);}/* ADC DMA采集中断服务函数 */void DMA1_Channel1_IRQHandler(void){ if (DMA1->ISR & (1<<1)) { g_adc_dma_sta = 1; DMA1->IFCR |= 1 << 1; }}

adc.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc_dma_init(uint32_t mar);/* ADC DMA读取 初始化函数 */void adc_dma_enable(uint16_t cndtr);/* 使能一次ADC DMA传输函数 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc.h\"/* ADC过采样技术, 是利用ADC多次采集的方式, 来提高ADC精度, 采样速度每提高4倍 * 采样精度提高 1bit, 同时, ADC采样速度降低4倍, 如提高4bit精度, 需要256次采集 * 才能得出1次数据, 相当于ADC速度慢了256倍. 理论上只要ADC足够快, 我们可以无限 * 提高ADC精度, 但实际上ADC并不是无限快的, 而且由于ADC性能限制, 并不是位数无限 * 提高结果就越好, 需要根据自己的实际需求和ADC的实际性能来权衡. */#define ADC_OVERSAMPLE_TIMES 256 /* ADC过采样次数, 这里提高4bit分辨率, 需要256倍采样 */#define ADC_DMA_BUF_SIZE ADC_OVERSAMPLE_TIMES * 10 /* ADC DMA采集 BUF大小, 应等于过采样次数的整数倍 */uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];  /* ADC DMA BUF */extern uint8_t g_adc_dma_sta; /* DMA传输状态标志, 0,未完成; 1, 已完成 */int main(void){ uint16_t i; uint32_t adcx; uint32_t sum; float temp; HAL_Init();  /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init();  /* 初始化LED */ lcd_init();  /* 初始化LCD */ adc_dma_init((uint32_t)&g_adc_dma_buf); /* 初始化ADC DMA采集 */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"ADC OverSample TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 110, 200, 16, 16, \"ADC1_CH1_VAL:\", BLUE); lcd_show_string(30, 130, 200, 16, 16, \"ADC1_CH1_VOL:0.000V\", BLUE); /* 先在固定位置显示小数点 */ adc_dma_enable(ADC_DMA_BUF_SIZE);  /* 启动ADC DMA采集 */ while (1) { if (g_adc_dma_sta == 1) { /* 计算DMA 采集到的ADC数据的平均值 */ sum = 0; for (i = 0; i < ADC_DMA_BUF_SIZE; i++) /* 累加 */ { sum += g_adc_dma_buf[i]; } adcx = sum / (ADC_DMA_BUF_SIZE / ADC_OVERSAMPLE_TIMES); /* 取平均值 */ adcx >>= 4; /* 除以2^4倍, 得到12+4位 ADC精度值, 注意: 提高 N bit精度, 需要 >> N */ /* 显示结果 */ lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE); /* 显示ADC采样后的原始值 */ temp = (float)adcx * (3.3 / 65536);  /* 获取计算后的带小数的实际电压值,比如3.1111 */ adcx = temp;  /* 赋值整数部分给adcx变量,因为adcx为u16整形 */ lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE); /* 显示电压值的整数部分,3.1111的话,这里就是显示3 */ temp -= adcx;  /* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */ temp *= 1000;  /* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */ lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE); /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */ g_adc_dma_sta = 0; /* 清除DMA采集完成状态标志 */ adc_dma_enable(ADC_DMA_BUF_SIZE);  /* 启动下一次ADC DMA采集 */ } LED0_TOGGLE(); delay_ms(100); }}

七、内部温度传感器实验

7.1、STM32内部温度传感器简介

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

7.2、温度计算方法

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
温度计算方法如下所示:

  1. 首先,从ADC采集到的内部温度传感器的电压值 V S E N S E V_{SENSE} VSENSE开始。

  2. 计算温度与 V S E N S E V_{SENSE} VSENSE曲线的平均斜率,通常以毫伏/摄氏度( m v / ℃ mv/℃ mv/℃)或微伏/摄氏度( u v / ℃ uv/℃ uv/℃)为单位表示。典型值约为4.3mv/℃。

  3. 确定25°C时的 V S E N S E V_{SENSE} VSENSE值,典型值为1.43。

  4. 使用以下公式计算温度(以摄氏度为单位):

    T ( ℃ ) = ( V 25 − V S E N S E A v g _ S l o p e ) + 25 T(℃) = \\left(\\frac{V25 - V_{SENSE}}{Avg\\_Slope}\\right) + 25 T()=(Avg_SlopeV25VSENSE)+25

其中, V 25 V_{25} V25 是在25°C时的 V S E N S E V_{SENSE} VSENSE值, Avg_Slope Avg\\_Slope Avg_Slope 是温度与 V S E N S E V_{SENSE} VSENSE曲线的平均斜率。
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

7.3、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

7.4、编程实战:内部温度传感器实验

adc.c

#include \"./BSP/ADC/adc.h\"ADC_HandleTypeDef g_adc_handle;/* ADC 内部温度传感器 初始化函数 */void adc_temperature_init(void){ ADC_ChannelConfTypeDef adc_ch_conf; // 初始化 ADC 句柄 g_adc_handle.Instance = ADC1; 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; g_adc_handle.Init.DiscontinuousConvMode = DISABLE; g_adc_handle.Init.NbrOfDiscConversion = 0; g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; HAL_ADC_Init(&g_adc_handle); // 进行 ADC 校准 HAL_ADCEx_Calibration_Start(&g_adc_handle); // 配置 ADC 通道 adc_ch_conf.Channel = ADC_CHANNEL_16; adc_ch_conf.Rank = ADC_REGULAR_RANK_1; adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf);}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC1) { RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能 ADC1 时钟 __HAL_RCC_ADC1_CLK_ENABLE(); // 配置 ADC1 时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); }}/* 获得ADC转换后的结果函数 */uint32_t adc_get_result(void){ // 启动 ADC 转换 HAL_ADC_Start(&g_adc_handle); // 等待转换完成 HAL_ADC_PollForConversion(&g_adc_handle, 10); // 返回转换结果 return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);}/* 获取内部温度传感器温度值 */short adc_get_temperature(void){ uint32_t adcx; short result; double temperature; // 获取 ADC 转换结果 adcx = adc_get_result(); // 计算温度值 temperature = adcx * (3.3 / 4096); temperature = (1.43 - temperature) / 0.0043 + 25; // 转换成整型 result = temperature *= 100; // 返回温度值 return result;}

adc.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc_temperature_init(void);/* ADC 内部温度传感器 初始化函数 */uint32_t adc_get_result(void);/* 获得ADC转换后的结果函数 */short adc_get_temperature(void);/* 获取内部温度传感器温度值 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./USMART/usmart.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc.h\"int main(void){ short temp; HAL_Init();  /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init();  /* 初始化LED */ lcd_init();  /* 初始化LCD */ adc_temperature_init();  /* 初始化ADC内部温度传感器采集 */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"Temperature TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 120, 200, 16, 16, \"TEMPERATE: 00.00C\", BLUE); while (1) { temp = adc_get_temperature(); /* 得到温度值 */ if (temp < 0) { temp = -temp; lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, \"-\", BLUE); /* 显示负号 */ } else { lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, \" \", BLUE); /* 无符号 */ } lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE); /* 显示整数部分 */ lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE); /* 显示小数部分 */ LED0_TOGGLE(); /* LED0闪烁,提示程序运行 */ delay_ms(250); }}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

八、光敏传感器实验

8.1、光敏二极管简介

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
光敏二极管(Photodiode)是一种特殊的二极管,其核心是一个PN结。与普通二极管不同的是,光敏二极管对光的强度非常敏感。它在工作时需要加上反向电压。

光敏二极管的暗电流非常小,通常小于0.1微安,这是因为在无光照的情况下,几乎没有光子撞击PN结,因此几乎没有电流通过。

当有光照射到光敏二极管上时,光的强度越大,反向电流也越大,形成光电流。这种光电流的变化是非线性的,即光强增大时,电流不是简单地线性增加,而是呈现出一种非线性的变化。

为了利用光敏二极管来检测光的强度变化,通常会串联一个电阻。当光照强度发生变化时,光敏二极管的电流也会随之变化,导致电阻两端的电压发生变化。通过连接至ADC(模数转换器)的方式,可以读取电压的变化,从而得知光强的变化情况。

总的来说,光敏二极管可以通过测量其反向电流的变化来检测光的强度变化,从而实现对光的检测和测量。

8.2、实验原理

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
光敏传感器实验的原理是利用光敏二极管(或其他类型的光敏传感器)的光敏特性来检测光照强度的变化,并将其转换为电信号输出。以下是光敏传感器实验的基本原理:

  1. 光敏传感器选择:选择适合实验需求的光敏传感器,如光敏二极管、光敏电阻等。根据实验要求和预期的光强范围选择合适的传感器。

  2. 传感器连接:将光敏传感器连接到适当的电路中。通常,光敏传感器需要与电源和负载电阻相连接。光敏传感器的输出端可以连接到微控制器、模数转换器(ADC)或其他电子设备,用于读取和处理光强信号。

  3. 光照实验:将光敏传感器放置在待测的光源附近,观察传感器输出信号随着光照强度的变化而变化。可以尝试不同光照条件下的实验,比如改变光源的亮度或距离,以及改变光源的颜色。

  4. 数据采集与分析:利用连接的微控制器或ADC等设备采集传感器输出的电信号,并进行分析处理。可以将光照强度与传感器输出信号之间的关系绘制成曲线或图表,以便更直观地理解传感器的响应特性。

  5. 实验验证与应用:根据实验结果对光敏传感器的性能进行验证,并根据实验需求进行进一步的应用。例如,可以将光敏传感器用于制作光控开关、光敏电压表、光强监测系统等。

通过光敏传感器实验,可以深入了解光敏传感器的工作原理、特性和应用场景,为光电转换和光控系统的设计提供实验基础和参考。

8.3、实验简要

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

8.4、编程实战:光敏传感器实验

adc3.c

#include \"./BSP/ADC/adc3.h\"ADC_HandleTypeDef g_adc_handle;/* ADC单通道初始化函数 */void adc3_init(void){ ADC_ChannelConfTypeDef adc_ch_conf; // 设置ADC句柄实例 g_adc_handle.Instance = ADC3; // 设置数据对齐方式为右对齐 g_adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 禁用扫描模式 g_adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; // 禁用连续转换模式 g_adc_handle.Init.ContinuousConvMode = DISABLE; // 设置转换通道数量为1 g_adc_handle.Init.NbrOfConversion = 1; // 禁用不连续转换模式 g_adc_handle.Init.DiscontinuousConvMode = DISABLE; // 不启用不连续转换 g_adc_handle.Init.NbrOfDiscConversion = 0; // 设置外部触发转换模式为软件触发 g_adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 初始化ADC HAL_ADC_Init(&g_adc_handle); // 执行ADC校准 HAL_ADCEx_Calibration_Start(&g_adc_handle); // 配置ADC通道 adc_ch_conf.Channel = ADC_CHANNEL_6; // 设置通道为ADC_CHANNEL_6 adc_ch_conf.Rank = ADC_REGULAR_RANK_1; // 设置通道在规则组中的排位 adc_ch_conf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 设置采样时间 HAL_ADC_ConfigChannel(&g_adc_handle, &adc_ch_conf); // 配置ADC通道}/* ADC MSP初始化函数 */void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc){ if(hadc->Instance == ADC3) { GPIO_InitTypeDef gpio_init_struct; RCC_PeriphCLKInitTypeDef adc_clk_init = {0}; // 使能GPIOF时钟 __HAL_RCC_GPIOF_CLK_ENABLE(); // 使能ADC3时钟 __HAL_RCC_ADC3_CLK_ENABLE(); // 配置GPIOF引脚为模拟输入模式 gpio_init_struct.Pin = GPIO_PIN_8; // 设置引脚为GPIO_PIN_8 gpio_init_struct.Mode = GPIO_MODE_ANALOG; // 设置引脚模式为模拟输入 HAL_GPIO_Init(GPIOF, &gpio_init_struct); // 初始化GPIOF引脚 // 配置ADC时钟 adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC; // 设置ADC时钟源 adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6; // 设置ADC时钟分频 HAL_RCCEx_PeriphCLKConfig(&adc_clk_init); // 配置ADC时钟 }}/* 获得ADC转换后的结果函数 */uint32_t adc_get_result(void){ // 启动ADC转换 HAL_ADC_Start(&g_adc_handle); // 等待转换完成 HAL_ADC_PollForConversion(&g_adc_handle, 10); // 获取转换结果并返回 return (uint16_t)HAL_ADC_GetValue(&g_adc_handle);}/* 读取光敏传感器值 */uint8_t lsens_get_val(void){ uint32_t temp_val; // 获取ADC转换结果 temp_val = adc_get_result(); // 将ADC转换结果除以40(假设这是一个适当的缩放系数),并进行阈值处理 temp_val /= 40; if (temp_val > 100) temp_val = 100; // 阈值处理,确保结果不超过100 // 返回光敏传感器值 return (uint8_t)(100 - temp_val); // 返回取反后的结果}

adc3.h

#ifndef __ADC_H#define __ADC_H#include \"./SYSTEM/sys/sys.h\"void adc3_init(void);/* ADC单通道初始化函数 */uint32_t adc_get_result(void);/* 获得ADC转换后的结果函数 */uint8_t lsens_get_val(void);/* 读取光敏传感器值 */#endif

main.c

#include \"./SYSTEM/sys/sys.h\"#include \"./SYSTEM/usart/usart.h\"#include \"./SYSTEM/delay/delay.h\"#include \"./USMART/usmart.h\"#include \"./BSP/LED/led.h\"#include \"./BSP/LCD/lcd.h\"#include \"./BSP/ADC/adc3.h\"int main(void){ short adcx; HAL_Init();  /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init();  /* 初始化LED */ lcd_init();  /* 初始化LCD */ adc3_init(); /* 初始化ADC3 */ lcd_show_string(30, 50, 200, 16, 16, \"STM32\", RED); lcd_show_string(30, 70, 200, 16, 16, \"LSENS TEST\", RED); lcd_show_string(30, 90, 200, 16, 16, \"ATOM@ALIENTEK\", RED); lcd_show_string(30, 110, 200, 16, 16, \"LSENS_VAL:\", BLUE); while (1) { adcx = lsens_get_val(); lcd_show_xnum(30 + 10 * 8, 110, adcx, 3, 16, 0, BLUE); /* 显示ADC的值 */ LED0_TOGGLE();  /* LED0闪烁,提示程序运行 */ delay_ms(250); }}

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

九、总结

【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)
【正点原子STM32】ADC模数转换器(ADC类型、ADC工作原理、单通道ADC采集、单通道ADC采集(DMA读取)、多通道ADC采集(DMA读取)、单通道ADC过采样、内部温度传感器、光敏传感器)

宜昌