手把手教你使用STM32的ADC(原创)

本文详细介绍了STM32单片机ADC的使用场景,包括其优点和局限性,以及如何在Cubemx和HAL库环境下配置ADC、DMA和滑动窗口滤波。通过实例演示了如何利用ADC实现PI控制器控制Buck电路输出电压,同时探讨了高级ADC解决方案和采样率计算的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模数转换器ADC是STM32单片机芯片自带的关键外设,其将模拟信号转换为数字信号,从而使单片机能够测量电压信号。本文将主要介绍STM32 ADC的使用情景、基于cube mx+hal库平台的使用、基本程序设计模式。

本文采用的是最经典常用的方案,即cube mx+hal库+ADC+DMA+滑动窗口滤波,最后基于这一方案实现了一个经典案例,即设计一个PI控制器控制Buck电路的输出电压。

1. STM32 ADC的使用情景

当你所开发的硬件设备需要测量电压信号时,STM32单片机自带的ADC貌似是一个最低成本与最高效的选择。但实际上STM32 ADC性能很有限:

  • 测量范围只有0V-3.3V;
  • 分辨率理论上最高为12位,也就是3.3/2^12=8e-4V,但实测误差显然远比这大;
  • 采样率理论上最高为 ADC时钟频率/12,也就是6位,9+3个周期。假设你的ADC时钟频率为8MHz,那么最高采样率为0.67MHz。处理这样的数据流对于STM32的CPU来说也是一个很大的挑战。

尽管如此,STM32 ADC方案比外接专用采样芯片、或者换成DSP来实现便宜的多。而且,STM32的ADC有个显著优势就是其通道数特别多,对于STM32F429来说,它有3个ADC外设,每个都可以测量15个通道。这也正是大多数学生项目都喜欢使用STM32 ADC的原因。

然而,如果你的项目需要很高频率、或者幅值精度较高的电压采样的话,比如实现一个数字示波器、要实时测量10kHz的正弦信号中的纹波、输入音频数据等。那就可能需要采用其他专用的ADC芯片了,STM32 的ADC的表现往往令人失望。在此列举一下其他更高级的ADC方案:

  • 音频处理:Analog Devices ADAU1761,Texas Instruments PCM5242
  • 数字示波器:Texas Instruments ADS54J60,Analog Devices AD9680
  • 对于更自由的数据处理需求,使用直接接电脑PCIe接口的DAQ数据采样板也是不错的选择,例如使用NI的PCIe-6320等。

有部分场景,比如做过零检测、测PWM波占空比、测范围在0-3.3V之外的信号等,这些都可以通过额外设计电路实现,还是可以使用STM32 ADC的。

2. 在Cube MX中配置ADC+DMA

本文采用的是最经典常用的方案,即cube mx+hal库+ADC+DMA+滑动窗口滤波。 DMA是一种快速将ADC转换结果读入内存的外设,在需要高速采样的时候格外重要。滑动窗口滤波是一种常见滤波手段,用于减少ADC高速采样时的噪声信号。

2.1 打开ADC选项

在左侧Analog中选择ADC1,右侧勾选你想要的通道。最右边就可以显示对应的引脚。

以下是基本参数设置,对照着设置即可,注意Number of conversion指通道数,选择之后要配置各通道的优先级。

2.2. DMA 设置

按照如下配置即可:

2.3. ADC 采样率计算与设置

笔者有个师兄在基于STM32 ADC实现控制器的时候,直接把ADC采样语句和后续控制都写在了主函数大while语句里面,导致最终无法确定ADC的具体采样速率,审稿人问控制频率的时候束手无策。因此需要引以为鉴。

计算并设置ADC采样频率是很关键的,因为其直接影响我们后续做处理与控制的节奏。在此给出基本公式:

f_s = \frac{f_\mathrm{PCLK2}/\mathrm{ClockPrescaler}}{(\mathrm{Resolution+SamplingTime})(\mathrm{ChannelNum}))}

下面给出该式中每一项对应的值的设置。

2.3.1 ADC时钟频率,PCLK2与ClockPrescaler

STM32 ADC挂载在APB2外设总线上,其采样率与APB2总线时钟频率f_PCLK2有关,看中文参考手册P250:

f_PCLK2在STM32 cubemx中有以下时钟设置:

这里看出APB2总线时钟频率f_PCLK2是16MHz。ADC的时钟是在其基础上分出来的,预分频器ClockPrescaler对应于以下选项:

2.3.2 Resolusion与SamplingTime

不同的分辨率对应不同的采样周期,对应于以下选项:

SamplingTime与通道数对应于以下选项:

如果按照以上的选项,那么最终ADC的采样率就是

f_s=\frac{16e6/8}{(15+480)\times2}\approx2020\mathrm{Hz}

实践中,应当依照自己想要的采样率配置这些参数。如果后续的操作比较消耗时间,那么采样率不宜设置太大。

3. 在代码中使用ADC

3.1 设置结果数组并开启ADC

在cubemx中生成代码之后。首先,在main.c中的私有变量部分添加一个32位usigned int数组作为全局变量,用于储存测量值:

/* USER CODE BEGIN PD */
#define ADC_CHANNEL_NUM 2
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint32_t measures[ADC_CHANNEL_NUM];
MovingAverageFilter filter;  //滤波器
...

注意,这里C语言定义数组用的是栈内存,STM32下不建议使用malloc动态申请堆内存。

接着,在主函数中的用户代码2添加启动语句:

/* USER CODE BEGIN 2 */
InitMovingAverageFilter(&filter); // 初始化滤波器
HAL_ADC_Start_DMA(&hadc1, measures, ADC_CHANNEL_NUM);

STM32外设的启动语句往往很长,加上IDE代码提示功能很辣鸡,十分容易忘记。但其实这一方法在在stm32f4xx_hal_adc.c中有所实现。除此以外还有另外几个函数:

// Enables ADC and starts conversion of the regular channels.
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);

// Enables the interrupt and starts ADC conversion of regular channels.
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);

// Enables ADC DMA request after last transfer (Single-ADC mode) and enables ADC peripheral  
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);

// Gets the converted value from data register of regular channel.
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc)

3.2 编写回调函数

STM32 ADC回调函数的签名在stm32f4xx_hal_adc.c中都有所定义,使用的是一种弱实现的方式,就像java里的抽象函数一样。

在主函数中实现这个回调函数,每次ADC对所有通道采样一遍的时候都会执行这个函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) 
{
    HAL_ADC_Start_DMA(&hadc1, measures, ADC_CHANNEL_NUM);
	UpdateMovingAverageFilter(&filter, measures[0]);  //滑动窗口滤波,只过滤1通道
	voltage_mea = (float) ComputeMovingAverage(&filter) * 3.3f / 4095.0f;  // 12bits 4095 real voltage
	
}

在回调函数开头必须要把之前的启动语句重新写一遍,否则这个回调函数可能只会执行一遍。

3.3 编写滑动窗口滤波

滑动窗口滤波是一种常见滤波手段,用于减少ADC高速采样时的噪声信号。首先定义一个结构体:

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define WINDOW_SIZE 100
typedef struct {
  uint32_t buffer[WINDOW_SIZE];
  uint32_t index;
} MovingAverageFilter;

再定义滑动窗口滤波相关方法:

uint32_t ComputeMovingAverage(MovingAverageFilter *filter)  
{
  uint32_t sum = 0;
  for (int i = 0; i < WINDOW_SIZE; ++i) 
  {
    sum += filter->buffer[i];
  }
  return sum / WINDOW_SIZE;
}

void InitMovingAverageFilter(MovingAverageFilter *filter) 
{
  filter->index = 0;
  for (int i = 0; i < WINDOW_SIZE; ++i) 
  {
    filter->buffer[i] = 0;
  }
}

void UpdateMovingAverageFilter(MovingAverageFilter *filter, uint32_t newValue)
{
  filter->buffer[filter->index] = newValue;
  filter->index = (filter->index + 1) % WINDOW_SIZE;
  if (filter->index % WINDOW_SIZE == 0) filter->index = 0;
}

其实这可以封装成一个滤波器类,但遗憾的是C语言中没有类和对象,所以只能定义一个结构体和几个独立的方法。

3.4 异步或同步地处理结果

如果要同步地处理结果,只需要在ADC的回调函数中执行后续任务。

但若后续操作需要的时间比采样周期长,则需要异步地处理结果。这涉及配置一个定时器,周期性地执行后续任务。有关定时器的内容我会在以后更新。

### 使用STM32G474实现数字Buck转换器设计 #### 主要特性概述 STM32G474是一款基于ARM Cortex-M4内核的高性能微控制器,具备浮点运算单元(FPU),支持DSP指令集扩展。该芯片拥有丰富的外设资源,包括定时器、ADC、DAC以及多种通信接口,这些特点使其成为理想的选择来构建高效的数字Buck转换控制系统[^2]。 #### 控制架构说明 为了完成对Buck变换电路的有效管理,通常采用闭环反馈机制。通过集成于MCU内部的高度精确模拟到数字转换器(ADC)采集输出电压信号,并将其送入软件算法处理模块;随后依据预定义的目标值调整脉宽调制(PWM)占空比参数,从而达到稳定供电的目的。此过程涉及到快速响应时间与高精度调节的要求,而STM32G474凭借其卓越性能完全可以胜任这一角色。 #### 关键组件配置 - **PWM发生器**: 利用高级定时器(TIMx)生成可编程频率及宽度变化规律性的方波形驱动信号给外部MOSFET开关元件; - **电流采样电阻Rshunt**: 安置于低压侧串联路径上用于实时监测流经电感L上的瞬态峰值大小情况; - **输入滤波电容Cin & 输出平滑电容Co**: 减少纹波干扰影响并维持稳定的直流水平供给负载端使用; - **温度传感器NTC/PTC thermistor (optional)**: 可选配热敏电阻监控环境温升状况防止过热损坏风险。 #### 初始化设置流程 针对上述提到的各项硬件设施,在程序启动阶段需执行必要的初始化操作: ```c // 配置GPIO引脚模式为复用推挽输出形式以便连接至TIMx_CHy通道 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 设置指定编号通用异步收发传输UART串口波特率等属性 MX_USART1_UART_Init(); // 开启对应组别DMA请求使能位允许自动读取多路AD采样数值 __HAL_RCC_DMA1_CLK_ENABLE(); ``` #### PID控制策略实施 对于动态条件下保持良好线性和负载暂态特性的需求来说,引入比例积分微分(PID)校正环节显得尤为重要。具体做法是在每次中断服务例程(ISR)触发时刻重新评估误差项e(t)=Vref-Vout并与历史累积偏差Esum共同作用决定新的PWM周期内的开启持续期Ton_new: \[ T_{on\_new} = K_p \cdot e(t)+K_i\int_0^{t}{e(\tau)d\tau }+K_d{\frac{de(t)}{dt}} \] 其中\(Kp\)代表比例系数,\(Ki\)表示积分增益因子,\(Kd\)则是微分校正权重。这三项参数的具体取值取决于实际应用场景下的经验积累或是借助MATLAB/Simulink仿真工具辅助优化获得最佳匹配组合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值