当前位置:   article > 正文

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

stm32中adc

一、ADC简介

二、ADC工作原理

三、单通道ADC采集实验

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

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

六、单通道ADC过采样实验

七、内部温度传感器实验

八、光敏传感器实验

九、总结

一、ADC简介

1.1、什么是ADC?

在这里插入图片描述
ADC(模数转换器) 是一种电子设备,用于将连续的模拟信号转换为相应的离散数字数据。这在许多应用中非常重要,特别是在嵌入式系统和数据采集系统中。

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

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

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

1.2、常见的ADC类型

常见ADC类型:https://blog.csdn.net/whereismatrix/article/details/81814431
在这里插入图片描述
常见的ADC类型包括并行比较型和逐次逼近型。它们各有优点和缺点:

  1. 并行比较型 ADC

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

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

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

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

在这里插入图片描述

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

在这里插入图片描述
逐次逼近型 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的特性参数

在这里插入图片描述
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 的主要特性:

  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)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ADC(模拟-数字转换器)的框图结构通常包括以下部分:

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

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

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

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

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

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

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

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

这些部分共同构成了 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的参考电压(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、输入通道

在这里插入图片描述

2.4、转换序列

在这里插入图片描述

规则组和注入组

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

  1. 规则转换组

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

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

通过规则组和注入组的灵活组合,STM32的ADC可以满足各种不同的转换需求,从而更好地适应不同的应用场景。
在这里插入图片描述
规则组和注入组的执行优先级如下所示:

  1. 规则组执行优先级

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

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

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

规则序列和注入序列

在这里插入图片描述
在这里插入图片描述
规则序列和注入序列是STM32中ADC转换序列的两种类型,它们的设置可以通过相关的寄存器进行配置。

  1. 规则序列

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

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

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

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

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

2.5、触发源

在这里插入图片描述

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

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

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

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

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

对于外部事件触发转换,需要配置相应的触发源,并在启动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、转换时间

在这里插入图片描述
在这里插入图片描述
设置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的时钟和转换时间,以满足具体的应用需求。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7、数据寄存器

在这里插入图片描述
数据寄存器用于存储ADC转换的结果,其中规则数据寄存器 ADCx_DR 存储规则组转换的结果,而注入数据寄存器 ADCx_JDRy 存储注入组转换的结果,其中 y 可以是1到4,对应着注入组中不同的转换通道。

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

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

根据具体需求选择合适的对齐模式,然后读取数据寄存器中的转换结果即可。
在这里插入图片描述
在这里插入图片描述

2.8、中断

在这里插入图片描述
在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及时传输转换好的数据到指定的内存地址,防止数据被覆盖。
在这里插入图片描述

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

在这里插入图片描述
单次转换模式和连续转换模式是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、扫描模式

在这里插入图片描述
扫描模式是指ADC进行转换时是否扫描多个通道的模式。具体特点如下:

关闭扫描模式:

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

使用扫描模式:

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

总之,扫描模式可以让ADC在一次转换中依次转换多个通道的数据,适用于需要监测多个信号的场景。
在这里插入图片描述

三、单通道ADC采集实验

3.1、实验简要

在这里插入图片描述
根据配置描述,通过ADC1通道1(PA1)采集电位器的电压,并显示ADC转换的数字量及换算后的电压值。以下是实现该功能的步骤:

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

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

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

  4. 编程实现:

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

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

#include "stm32f4xx_hal.h"
#include <stdio.h>

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

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

3.2、ADC寄存器介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3、单通道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采集的实验配置。确保根据您的具体硬件和应用需求进行适当的配置和修改。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.4、编程实战:单通道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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

在这里插入图片描述

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

4.1、实验简要

在这里插入图片描述

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

在这里插入图片描述

#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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

在这里插入图片描述

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

5.1、实验简要

在这里插入图片描述

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

在这里插入图片描述
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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、单通道ADC过采样实验

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

用过采样和求均值提高ADC分辨率 方程推导过程:https://max.book118.com/html/2018/0506/165038217.shtm
在这里插入图片描述
过采样和求均值的方式可以有效提高ADC的分辨率。下面是具体步骤:

  1. 确定过采样率: 过采样率是指在一定时间内采集的样本数。根据要增加的分辨率位数和初始采样频率,可以使用以下方程确定过采样率:
    f o s = 4 w × f s f_{os} = 4^w \times f_s fos=4w×fs
    其中, f o s f_{os} fos是过采样频率, w w w是希望增加的分辨率位数, f s f_s fs是初始采样频率。

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

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

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

6.2、实验简要

在这里插入图片描述

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

在这里插入图片描述
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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

七、内部温度传感器实验

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

在这里插入图片描述

7.2、温度计算方法

在这里插入图片描述
温度计算方法如下所示:

  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值, A v g _ S l o p e Avg\_Slope Avg_Slope 是温度与 V S E N S E V_{SENSE} VSENSE曲线的平均斜率。
在这里插入图片描述
在这里插入图片描述

7.3、实验简要

在这里插入图片描述

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

在这里插入图片描述

八、光敏传感器实验

8.1、光敏二极管简介

在这里插入图片描述
光敏二极管(Photodiode)是一种特殊的二极管,其核心是一个PN结。与普通二极管不同的是,光敏二极管对光的强度非常敏感。它在工作时需要加上反向电压。

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

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

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

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

8.2、实验原理

在这里插入图片描述
光敏传感器实验的原理是利用光敏二极管(或其他类型的光敏传感器)的光敏特性来检测光照强度的变化,并将其转换为电信号输出。以下是光敏传感器实验的基本原理:

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

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

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

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

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

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

8.3、实验简要

在这里插入图片描述

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); // 返回取反后的结果
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

九、总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号