【STM32项目实战系列】基于STM32G474的ADC驱动配置

前言:最近项目中又使用了一次ADC用于检测温度与电流,配置驱动的时候也比较简单,基本上一气呵成,由于前面还没有介绍ADC相关的文章,所以这里就记录一下配置的过程,ADC全称模数转换器,顾名思义就是将一个模拟信号转换成一个数字信号,使用的场景也比较广泛,估计搞嵌入式的小伙伴都知道ADC的使用频率之高,几乎每一个嵌入式的产品都或多或少的使用到ADC。所以,话不多说,直接上正文吧。


目录

1,ADC使用场景

2,ADC外设资源简介

3,ADC的CUBEMX配置

4,ADC的代码API编写


1,ADC使用场景

STM32微控制器中的ADC(模数转换器)模块广泛应用于各种场景,主要用于将模拟信号转换为数字信号,主要应用于电压的采集,有了最原始的电压数据就可以根据欧姆定律扩展计算得到电流与电阻,然后一些使用到阻值或者电流的传感器就可以接到这个外设上面,最后换算得到需要检测的参数数据,比如温湿度传感器、烟雾传感器、霍尔传感器、热敏电阻等等。

本篇文章是基于STM32G474芯片主控用于检测一个系统的温度数据与电流数据和一个供电电压的参数数据,使用到了6个ADC的通道。

另外上面也提到每一个嵌入式的产品都会使用ADC的原因是因为市场上面的智能嵌入式产品基本都用到内部供电电池,而检测电量的其中一种方式就是检测电压,这就会用到ADC。

总结来看呢就是下面的几个方面,使用AI生成的,虽然就是冷冰冰的知识,但还是比较全的,大家简单认识了解一下就可以了。。。

1. 传感器数据采集

  • 温度传感器:如NTC、PTC、热电偶等,ADC将模拟温度信号转换为数字信号。

  • 光强传感器:如光敏电阻、光电二极管等,ADC用于测量环境光强度。

  • 压力传感器:如气压传感器、压力变送器等,ADC用于测量压力变化。

  • 湿度传感器:如电容式湿度传感器,ADC用于测量环境湿度。

2. 电池管理

  • 电池电压监测:ADC用于实时监测电池电压,确保电池在安全范围内工作。

  • 电流检测:通过测量电流检测电阻上的电压,ADC可以计算电池的充放电电流。

3. 音频处理

  • 音频信号采集:ADC用于采集麦克风或其他音频源的模拟信号,转换为数字信号进行后续处理。

  • 音频信号处理:在音频处理系统中,ADC用于将模拟音频信号转换为数字信号,便于数字信号处理(DSP)。

4. 工业控制

  • 模拟信号监控:在工业自动化中,ADC用于监控各种模拟信号,如电压、电流、压力、流量等。

  • 闭环控制:ADC用于反馈控制系统中,实时采集传感器数据,调整控制参数。

5. 医疗设备

  • 生物信号采集:如心电图(ECG)、脑电图(EEG)等,ADC用于采集生物电信号。

  • 血氧监测:通过ADC采集光电传感器的信号,计算血氧饱和度。

6. 通信系统

  • 信号调制解调:在无线通信系统中,ADC用于将接收到的模拟信号转换为数字信号,便于解调和处理。

  • 信号强度检测:ADC用于测量接收信号的强度,调整发射功率或天线方向。

7. 汽车电子

  • 发动机控制:ADC用于采集发动机温度、压力、氧气浓度等信号,优化发动机性能。

  • 车载传感器:如加速度传感器、陀螺仪等,ADC用于采集车辆运动状态数据。

8. 消费电子

  • 触摸屏控制:ADC用于检测触摸屏上的触摸位置,将模拟触摸信号转换为数字坐标。

  • 电源管理:ADC用于监测电源电压和电流,确保设备稳定运行。

9. 环境监测

  • 空气质量检测:ADC用于采集气体传感器的信号,监测空气中的有害气体浓度。

  • 水质监测:ADC用于采集水质传感器的信号,监测水中的pH值、溶解氧等参数。

10. 科学研究

  • 实验数据采集:在科学实验中,ADC用于采集各种模拟信号,如电压、电流、温度等,进行数据分析和处理。


2,ADC外设资源简介

由于本篇文章使用到的主控芯片是STM32G474,所以第一件事情就需要找到官网上面的参考手册进行查看

STM32G474RE - 主流Arm Cortex-M4内核MCU,工作频率170 MHz,具有512 KB Flash存储器,数学加速器,HR定时器,高模拟集成度 - 意法半导体STMicroelectronics

当然了,正所谓举一反三,触类旁通,基于其他主控MCU的ADC配置也是需要先找到对应的官网上面的参考手册,先查看其基本信息在进行合理的配置使用。

 

由于官网上面的下载的都是英文的,一定会有些小伙伴读不下去,我下面就充当一下翻译进行简单介绍一下吧。

 ST官方系列的主控芯片每一个型号都有其不同的外设资源配置,并且每一种外设也都有不同的通道或者对应的管脚的索引关系。

此款MCU所有ADC的资源简介:
1,ADC1和ADC2紧密耦合,可以在双模式(ADC1为主)下运行。ADC3 和 ADC4 紧密耦合,可以以双模式运行(ADC3是主设备)。ADC5是独立控制的。,

2,每个ADC由一个12位的逐次逼近式模数转换器组成。

3,每个ADC最多有19个多路复用通道。各种通道的A/D转换可以以单通道、连续、扫描或非连续模式进行。

4,ADC的结果存储在左对齐或右对齐的16位数据寄存器中。

5,ADC被映射到AHB总线上,以允许快速数据处理。

6,模拟看门狗功能允许应用程序检测输入电压是否超出用户定义的高或低值。

7,内置的硬件过采样器可以改善模拟性能,同时从CPU卸载相关的计算负担。实现了一种高效的低功耗模式,以便在低频下实现非常低的功耗。

ADC主要功能:
1,最多5个ADC,其中4个(成对)可以以双模式运行,ADC1连接到14个外部通道+4个内部通道,ADC2连接到16个外部通道+2个内部通道,ADC3连接到15个外部通道+3个内部通道,ADC4连接到16个外部通道+2个内部通道,ADC5连接到13个外部通道+5个内部通道

2,12、10、8或6位 可配置分辨率

3,ADC转换时间与AHB总线时钟频率无关,可以通过降低分辨率加快转换时间

4,管理单端或差分输入AHB从机总线接口,允许快速数据处理,

5,自校准

6,通道可编程采样时间灵活的采样时间控制最多四个注入通道(将模拟输入分配给常规或注入通道是完全可配置的)

7,硬件助手准备注入通道的上下文,以允许快速上下文切换

8,数据与内置数据一致性对齐,数据可以通过DMA管理以进行常规通道转换4个专用数据寄存器,用于注入通道

9,过采样器:16位数据寄存器;超采样比率可调节从2到256;可编程数据移位可达8位,

10,数据预处理:获得补偿;偏移补偿

11,低功耗特性:速度自适应低功耗模式,可在低频下降低ADC功耗,允许在低速总线频率下应用,同时保持最佳的ADC性能-提供自动控制,以避免ADC在低AHB总线时钟频率下溢出

12,每个 ADC 的外部模拟输入通道数量:从GPI0引脚最多5个快速通道;从GPIO引脚最多可达13个慢通道。

13,此外,还有几个内部专用频道:内部参考电压(VREFINT),连接到ADC1、3、4和5
;内部温度传感器(VTS),连接到ADC1和5;VBAT监测通道(VBAT/3),连接到ADC1、3(仅适用于第3类设备)和5;OPAMP1的内部输出连接到ADC1;OPAMP2和OPAMP3的内部输出连接到ADC2
;OPAMP3的内部输出连接到ADC3;OPAMP6的内部输出连接到ADC4(用于第3类设备)或ADC3(用于第4类设备);OPAMP4和OPAMP5的内部输出连接到ADC5

14,转换的开始可以由以下方式启动:通过软件进行常规和注入转换;通过可配置极性的硬件触发器(内部定时器事件或GPI0输入事件)进行常规和注入转换。

15,转换模式:每个ADC可以转换单个通道或扫描一系列通道;单模式在触发一次时转换选定的输入;连续模式连续转换选定的输入;不连续模式,

16,ADC1、ADC2、ADC3和ADC4的双ADC模式

17,ADC就绪时中断生成,采样结束,转换结束(常规或注入),序列转换结束(常规或注入),模拟看门狗1、2或3或超时事件

18,每个ADC有3个模拟看门狗-看门狗可以进行过滤,忽略超出范围的数据

19,ADC输入范围:VREF-≤ VIN≤VREF+(这个电压一般是3.3V

下面显示了一个ADC的框图可以了解下其内部的基本实现原理:嘿嘿,其实这个东西就是在使用非常熟练的情况下在进行研究的,对于初级工程师而言会配置实现就可以了,有兴趣的小伙伴可以下载一下这个参考手册进行查看学习。

ADC的管脚与内部信号描述:

就简单介绍上面的那些吧,当然限于篇幅没有完全介绍完成,还有诸如ADC时钟特征、ADC低功耗模式、ADC中断、ADC寄存器的配置的介绍有兴趣的小伙伴就去参考手册那里学习吧。


3,ADC的CUBEMX配置

这里就不过多介绍了,在我的前面的文章也非常详细的介绍,包括且不限于ADC外设资源

【STM32项目实战系列】基于CUBEMX创建一个ST项目工程(详细版)_smbus-alert-mode-优快云博客


4,ADC的代码API编写

使用了6个通道进行数据采集,都是ADC1,没有使用中断,就是简单的数据循环读取,配置如下,代码仅供参考学习使用。。。

1,3路电流检测

    PC0     ------> ADC1_IN6  ----> ISENDA

    PC1     ------> ADC1_IN7  ----> ISENDC

    PC2     ------> ADC1_IN8  ----> ISENDB

2,两路热敏电阻检测

    PC3     ------> ADC1_IN9  ----> ADC T [NTC]

    PA0     ------> ADC1_IN1  ----> ADC M [MF51]

3,一路直接电压的检测

    PA1     ------> ADC1_IN2  ----> 24~48V

 adc.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    adc.c
  * @brief   This file provides code for the configuration
  *          of the ADC instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "adc.h"

/* USER CODE BEGIN 0 */

// 电压计算常量
#define VREF 3.3f        // 假设 VREF 为 3.3V
#define ADC_RESOLUTION 4095  // 12-bit ADC 分辨率 (0-4095)

/* USER CODE END 0 */

ADC_HandleTypeDef hadc1;
ADC_ChannelConfTypeDef sConfig;

/* ADC1 init function */
void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */
  ADC_MultiModeTypeDef multimode = {0};
  // ADC_ChannelConfTypeDef sConfig = {0};

  // 配置 ADC1 的基本设置
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;  // 设置 ADC 时钟为 2 分频
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;  // 12 位分辨率
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;  // 数据右对齐
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;  // 启用扫描模式(支持多个通道)
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;  // 每次转换完成后触发中断
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;  // 连续转换模式
  hadc1.Init.NbrOfConversion = 1;  // 设置转换通道数为1个
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;  // 软件触发启动
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = DISABLE;  // 禁用 DMA 请求
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.OversamplingMode = DISABLE;
  
  // 初始化 ADC1
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
      Error_Handler();  // 如果初始化失败,则调用错误处理函数
  }

  // 配置 ADC 多模模式
  multimode.Mode = ADC_MODE_INDEPENDENT;  // 独立模式
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
      Error_Handler();  // 配置失败,调用错误处理函数
  }

  // 配置 ADC 通道 1 (ADC_CHANNEL_1)
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;                    // 第1个通道
  sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;     // 设置采样时间
  sConfig.SingleDiff = ADC_SINGLE_ENDED;                // 单端输入,另一个可选的是差分模式
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
      Error_Handler();  // 配置失败,调用错误处理函数
  }

  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */

  /** Initializes the peripherals clocks
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
    PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* ADC1 clock enable */
    __HAL_RCC_ADC12_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration
    PC0     ------> ADC1_IN6
    PC1     ------> ADC1_IN7
    PC2     ------> ADC1_IN8
    PC3     ------> ADC1_IN9
    PA0     ------> ADC1_IN1
    PA1     ------> ADC1_IN2
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
  if (adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspDeInit 0 */

  /* USER CODE END ADC1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_ADC12_CLK_DISABLE();

    /**ADC1 GPIO Configuration
    PC0     ------> ADC1_IN6
    PC1     ------> ADC1_IN7
    PC2     ------> ADC1_IN8
    PC3     ------> ADC1_IN9
    PA0     ------> ADC1_IN1
    PA1     ------> ADC1_IN2
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);

    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1);

    /* ADC1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(ADC1_2_IRQn);
  /* USER CODE BEGIN ADC1_MspDeInit 1 */

  /* USER CODE END ADC1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
/**************************************************** */
// 输入通道号,输出计算的电压
    // PC0     ------> ADC1_IN6  ----> ISENDA
    // PC1     ------> ADC1_IN7  ----> ISENDC
    // PC2     ------> ADC1_IN8  ----> ISENDB
    // PC3     ------> ADC1_IN9  ----> ADC T [NTC]
    // PA0     ------> ADC1_IN1  ----> ADC M [MF51]
    // PA1     ------> ADC1_IN2  ----> 24~48V
/**************************************************** */

float get_voltage(uint32_t channel)
{
    // ADC_ChannelConfTypeDef sConfig = {0};
    float voltage = 0;

    // 配置 ADC通道
    sConfig.Channel = channel;                          // 输入指定的通道号
    sConfig.Rank = ADC_REGULAR_RANK_1;                  // 设置通道排名
    sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5;   // 设置采样时间
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);            // 配置 ADC 通道

    // 启动 ADC 转换并等待完成
    HAL_ADC_Start(&hadc1);
    if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK) {
        // 获取 ADC 转换结果
        uint32_t adc_value = HAL_ADC_GetValue(&hadc1);
        
        // printf("HAL_ADC_GetValue:%d\r\n", HAL_ADC_GetValue(&hadc1));

        // 根据 ADC 值计算电压
        voltage = adc_value * VREF / ADC_RESOLUTION;
        HAL_ADC_Stop(&hadc1);
    }
    else {
        HAL_ADC_Stop(&hadc1);
        // 错误处理,如果 ADC 转换失败
        return -1.0f;  // 返回错误值
    }

    return voltage;
}

//PA1 检测连接的输入电源的电压值
float get_power_voltage(void)
{
    return get_voltage(ADC_24_48V) * 16;
}

/* USER CODE END 1 */

 adc.h

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    adc.h
  * @brief   This file contains all the function prototypes for
  *          the adc.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __ADC_H__
#define __ADC_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern ADC_HandleTypeDef hadc1;

/* USER CODE BEGIN Private defines */
#define ISENDA ADC_CHANNEL_6
#define ISENDC ADC_CHANNEL_7
#define ISENDB ADC_CHANNEL_8
#define ADC_M  ADC_CHANNEL_1
#define ADC_T  ADC_CHANNEL_9
#define ADC_24_48V ADC_CHANNEL_2
/* USER CODE END Private defines */

void MX_ADC1_Init(void);

/* USER CODE BEGIN Prototypes */

float get_voltage(uint32_t channel);
float get_power_voltage(void);
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __ADC_H__ */

具体的效果就不再展示了,如果哪位小伙伴的ADC配置无法达到效果的可以评论区留言。。。完结。。。

### 使用 STM32CubeMX 配置 STM32G474 进行 ADC 电压采集 #### 配置硬件参数 在 STM32CubeMX 中启动项目并选择目标微控制器型号为 `STM32G474RET6` 或其他适用版本。进入 Pinout & Configuration 页面设置 ADC 功能。 对于希望使用的每个 ADC 输入通道,需指定相应的 GPIO 引脚作为模拟输入,并将其连接到内部 ADC 模块上[^1]。 #### 设置 ADC 参数 转至 Middleware/Drivers 下找到 Analog-to-Digital Converter (ADC),点击打开配置窗口: - **Mode**: 单次转换模式适合于偶尔采样;循环扫描模式适用于持续监控多个通道的情况。 - **Resolution**: 设定分辨率为 12 bits 符合该系列 MCU 的特性。 - **Data Alignment**: 数据对齐方式通常保持默认右对齐即可满足大多数应用需求。 - **External Trigger Conversion Source**: 如果不需要外部触发,则应禁用此选项以简化逻辑流程。 - **DMA Request After Last Transfer (DMAT)**: 启用此项允许每次完成全部序列后的 DMA 请求传输操作,从而减少 CPU 干预频率提高效率. #### 初始化 DMA 控制器 为了实现高效的数据处理,在同一页面内还需调整 Direct Memory Access(DMA)的相关属性: - 添加一个用于服务所选 ADC 的专用 DMA 流; - 将其方向设为外设到内存(peripheral to memory); - 缓冲区大小依据具体应用场景而定; - 地址增量模式针对源地址固定不变、目的地址自动增加的情形; 以上步骤完成后保存并生成初始化代码框架供后续开发环境编译链接使用。 ```c // 示例代码片段展示如何通过 HAL 库函数发起一次基于 DMA 的 ADC 转换请求 HAL_StatusTypeDef status = HAL_ADC_Start_DMA(&hadc1, (uint32_t*)AdcBuffer, BUFFER_SIZE); if(status != HAL_OK){ // 错误处理机制... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是星凡呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值