news 2026/5/1 9:58:44

别再只会读ADC值了!STM32F103的ADC实战:从电压采集到串口波形显示(附CubeMX配置)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再只会读ADC值了!STM32F103的ADC实战:从电压采集到串口波形显示(附CubeMX配置)

STM32F103 ADC实战:从硬件搭建到波形可视化的全流程解析

在嵌入式开发中,ADC(模数转换器)是将模拟世界与数字系统连接的关键桥梁。许多开发者虽然掌握了ADC的基本原理,却苦于无法将其转化为实际项目中的有效工具。本文将带你从零开始,使用STM32F103C8T6(BluePill开发板常见型号)构建一个完整的电压采集与可视化系统,涵盖CubeMX配置、DMA传输优化、串口协议设计以及Python实时波形显示的全套解决方案。

1. 硬件准备与CubeMX工程创建

在开始编码前,正确的硬件连接和开发环境配置是项目成功的基础。我们需要准备以下硬件组件:

  • STM32F103C8T6最小系统板(BluePill)
  • 10kΩ电位器(用于模拟电压输入)
  • USB转TTL串口模块(如CH340G)
  • 杜邦线若干

硬件连接示意图:

STM32引脚连接目标备注
PA0电位器中间引脚ADC1通道0输入
3.3V电位器一端供电电压
GND电位器另一端接地
PA9(TX)USB-TTL模块RXUSART1发送端
PA10(RX)USB-TTL模块TXUSART1接收端
3.3VUSB-TTL模块VCC可选,部分模块需供电
GNDUSB-TTL模块GND共地

打开STM32CubeMX,按照以下步骤创建工程:

  1. 选择MCU型号:STM32F103C8T6
  2. 系统核心配置:
    • SYS: Serial Wire (用于ST-Link调试)
    • RCC: High Speed Clock (HSE) 选择Crystal/Ceramic Resonator
  3. ADC1配置:
    • 启用IN0通道(对应PA0)
    • 参数设置:
      Resolution = 12Bits Data Alignment = Right Scan Conversion Mode = Disabled Continuous Conversion Mode = Enabled Discontinuous Conversion Mode = Disabled DMA Continuous Requests = Enabled End Of Conversion Selection = EOC flag at the end of single conversion
  4. USART1配置:
    • Mode: Asynchronous
    • Baud Rate: 115200
    • Word Length: 8Bits
    • Parity: None
    • Stop Bits: 1
  5. DMA配置:
    • 添加DMA通道:ADC1
    • Mode: Circular
    • Priority: High
    • Memory Increment: Enable
  6. 时钟树配置:
    • 确保PCLK2不超过72MHz(ADC时钟不超过14MHz)
    • 推荐配置:
      HCLK = 72MHz PCLK1 = 36MHz PCLK2 = 72MHz ADC Prescaler = PCLK2/6 = 12MHz
  7. 生成代码:
    • Toolchain/IDE选择适合你的开发环境(MDK-ARM/IAR/STM32CubeIDE)

提示:CubeMX生成的HAL库代码已经包含了ADC和USART的初始化,但我们需要在用户代码区域添加应用逻辑。

2. ADC采集与DMA传输优化

传统轮询方式采集ADC会占用大量CPU资源,而中断方式在高采样率时也会导致频繁中断。使用DMA可以直接将ADC转换结果搬运到内存缓冲区,完全解放CPU资源。

DMA缓冲区定义与初始化:

main.c文件中添加以下全局变量:

#define ADC_BUF_SIZE 256 uint16_t adcBuffer[ADC_BUF_SIZE]; float voltageBuffer[ADC_BUF_SIZE];

main()函数中的/* USER CODE BEGIN 2 */部分添加DMA启动代码:

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuffer, ADC_BUF_SIZE);

ADC值到实际电压的转换:

STM32F103的ADC为12位分辨率,参考电压通常为3.3V。创建转换函数:

void ADC_ConvertToVoltage(uint16_t* adcBuf, float* voltBuf, uint32_t size) { for(uint32_t i = 0; i < size; i++) { voltBuf[i] = (float)adcBuf[i] * 3.3f / 4095.0f; } }

定时采样控制:

为了实现固定采样率,我们可以使用定时器触发ADC转换。在CubeMX中配置TIM2:

  1. 时钟源:Internal Clock
  2. 预分频器:72-1 (1MHz)
  3. 计数器周期:1000-1 (1kHz采样率)
  4. 触发输出(TRGO):Update Event

然后在ADC配置中:

  • 选择"Timer 2 Trigger Out event"作为外部触发源
  • 外部触发边沿:上升沿触发

注意:DMA循环模式会持续覆盖缓冲区数据,因此需要合理设计数据处理节奏,避免数据丢失。

3. 串口数据传输协议设计

直接将原始ADC数据通过串口发送效率低下且难以解析。我们需要设计一个简单高效的通信协议。

协议帧结构设计:

字节位置内容说明
00xAA帧头,用于同步
10x55帧头第二字节
2数据长度N后续数据字节数
3~N+2数据内容实际传输的数据
N+3校验和前面所有字节的和的低8位

数据打包函数实现:

void USART_SendDataPacket(uint8_t* data, uint16_t size) { uint8_t packet[256 + 4]; // 最大256字节数据+4字节协议头尾 uint8_t checksum = 0; packet[0] = 0xAA; packet[1] = 0x55; packet[2] = size; for(int i = 0; i < size; i++) { packet[3 + i] = data[i]; } // 计算校验和 for(int i = 0; i < size + 3; i++) { checksum += packet[i]; } packet[size + 3] = checksum; HAL_UART_Transmit(&huart1, packet, size + 4, HAL_MAX_DELAY); }

电压数据发送实现:

在主循环中定期发送电压数据:

/* USER CODE BEGIN WHILE */ while (1) { static uint32_t lastTick = 0; if(HAL_GetTick() - lastTick >= 100) { // 每100ms发送一次数据 lastTick = HAL_GetTick(); ADC_ConvertToVoltage(adcBuffer, voltageBuffer, ADC_BUF_SIZE); USART_SendDataPacket((uint8_t*)voltageBuffer, ADC_BUF_SIZE * sizeof(float)); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */

4. Python上位机波形显示

使用Python的pySerial和Matplotlib库可以快速构建一个实时波形显示工具。

Python端代码实现:

import serial import struct import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from collections import deque # 串口配置 ser = serial.Serial('COM3', 115200, timeout=1) # 替换为你的串口号 plt.style.use('ggplot') # 显示配置 MAX_POINTS = 500 x_data = deque(maxlen=MAX_POINTS) y_data = deque(maxlen=MAX_POINTS) fig, ax = plt.subplots() line, = ax.plot([], [], 'b-') ax.set_xlim(0, MAX_POINTS/10) ax.set_ylim(0, 3.3) ax.set_xlabel('Sample Point') ax.set_ylabel('Voltage (V)') ax.set_title('STM32 ADC Real-time Waveform') def parse_packet(data): if len(data) < 4 or data[0] != 0xAA or data[1] != 0x55: return None length = data[2] if len(data) < length + 4: return None checksum = sum(data[:-1]) & 0xFF if checksum != data[-1]: print(f"Checksum error: {checksum} != {data[-1]}") return None return data[3:3+length] def update(frame): # 读取串口数据 while ser.in_waiting > 0: header = ser.read(1) if header == b'\xAA': rest = ser.read(3) # 读取协议头剩余部分 if len(rest) == 3 and rest[0] == 0x55: length = rest[1] data = ser.read(length + 1) # 数据+校验和 packet = b'\xAA' + rest + data payload = parse_packet(packet) if payload: # 解析浮点数据 num_floats = len(payload) // 4 voltages = struct.unpack(f'<{num_floats}f', payload) # 更新显示数据 for i, v in enumerate(voltages): x_data.append(len(x_data)) y_data.append(v) # 更新曲线 line.set_data(x_data, y_data) # 调整X轴范围 if len(x_data) > MAX_POINTS/10: ax.set_xlim(x_data[-1] - MAX_POINTS/10, x_data[-1]) return line, ani = FuncAnimation(fig, update, blit=True, interval=50) plt.show()

功能增强建议:

  1. 多通道支持:修改协议格式,包含通道信息
  2. 数据保存:添加按钮将当前数据保存为CSV
  3. 采样率显示:计算实际采样率并显示
  4. 触发功能:添加边沿触发功能稳定波形显示

5. 系统优化与调试技巧

一个健壮的数据采集系统需要考虑多方面因素。以下是几个关键优化点:

电源噪声抑制:

  • 在ADC参考电压引脚添加10μF和0.1μF去耦电容
  • 使用独立的LDO为模拟部分供电
  • 避免数字信号线与模拟信号线平行走线

软件滤波算法:

  • 移动平均滤波(简单有效)
    #define FILTER_WINDOW 5 float movingAverageFilter(float newValue) { static float buffer[FILTER_WINDOW] = {0}; static uint8_t index = 0; static float sum = 0; sum -= buffer[index]; buffer[index] = newValue; sum += buffer[index]; index = (index + 1) % FILTER_WINDOW; return sum / FILTER_WINDOW; }
  • 中值滤波(适合消除脉冲噪声)
  • 卡尔曼滤波(动态系统最优估计)

DMA双缓冲技术:当处理速度跟不上采集速度时,可以使用双缓冲技术:

#define BUF_SIZE 256 uint16_t adcBuffer1[BUF_SIZE]; uint16_t adcBuffer2[BUF_SIZE]; volatile uint8_t activeBuffer = 0; // 0 for buffer1, 1 for buffer2 // 在DMA完成中断中切换缓冲区 void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) { activeBuffer = 1; // 处理adcBuffer1 } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { activeBuffer = 0; // 处理adcBuffer2 }

常见问题排查:

  1. 无数据或数据全零

    • 检查CubeMX中ADC通道配置是否正确
    • 测量实际输入电压是否在0-3.3V范围内
    • 确认DMA配置为循环模式
  2. 数据不稳定

    • 检查电源稳定性
    • 适当增加采样时间(ADC_SMPR寄存器)
    • 添加软件滤波
  3. 串口数据错乱

    • 确认双方波特率一致
    • 检查地线连接
    • 降低传输速率或缩短数据包长度
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 9:54:33

2025届毕业生推荐的十大降AI率方案实际效果

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 要降低AIGC在内容生产里的占比&#xff0c;就得从源头优化工作流程&#xff0c;先是优先运用…

作者头像 李华
网站建设 2026/5/1 9:49:19

Go数学算法库终极指南:从基础运算到高级数学函数完整教程

Go数学算法库终极指南&#xff1a;从基础运算到高级数学函数完整教程 【免费下载链接】Go Algorithms and Data Structures implemented in Go for beginners, following best practices. 项目地址: https://gitcode.com/GitHub_Trending/go2/Go GitHub推荐项目精选中的…

作者头像 李华