2025寒假练 - 基于STM32的模拟电路伯特图
该项目使用了STM32,实现了模拟电路的伯特图的设计,它的主要功能为:通过ADC采集数据,利用UART发送到上位机,在上位机中对信号进行显示和处理,并绘制模拟电路的伯特图。。
标签
示波器
STM32
寒假练
伯德图
Dearjim
更新2025-03-17
65

系统介绍

本像是是使用STM32G031芯片的ADC采样功能,采集模拟电路的电压信号,通过UART发送到上位机;利用上位机强大的算力对数据进行FFT,计算幅频特性,计算相频特性,并绘制曲线显示。

系统框图

image.png

硬件介绍


  • 主控芯片:STM32G031(Cortex-M0+内核,主频64MHz,集成12位ADC)
  • 信号源:板载麦克风发生器模块(用于生成音频信号)
  • 数据传输:USB转串口模块(CH340或STM32内置USB-CDC)
  • 外设功能:TIM1定时器(用于精准定时触发ADC)、ADC通道(采集麦克风模拟信号)

   - 方案框图和项目设计思路介绍

image.png

设计思路

  1. 硬件配置:通过STM32CubeMX配置TIM1定时器(10kHz中断,0.1ms周期),ADC通道(单次采样模式),以及串口通信参数。
  2. 软件逻辑:TIM1中断触发ADC采集,数据通过DMA或轮询方式读取,经串口发送至PC。
  3. 上位机处理:Python脚本接收数据后,基于10kHz采样率进行FFT分析,计算幅频和相频响应,并通过GUI动态绘制伯特图。

软件流程图与关键代码

软件流程图

image.png




关键代码

- 软件流程图和关键代码介绍

  while (1)
{
/* USER CODE END WHILE */
counter_value = __HAL_TIM_GET_COUNTER(&htim1);
if(counter_value != counter_value_last)
{
time0_1ms ++;
counter_value_last=counter_value;
HAL_ADC_Start(&hadc1); // 启动ADC转换
if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK) // 等待转换完成
{
adc_value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1); // 停止ADC转换

// 明确指定传输的数据类型为 uint32_t
HAL_UART_Transmit(&huart2, (uint8_t*)&adc_value, sizeof(uint32_t), HAL_MAX_DELAY);

// 发�?�结束符
uint8_t end_marker = '\n';
HAL_UART_Transmit(&huart2, &end_marker, 1, HAL_MAX_DELAY);
}


/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

TIM配置函数

void MX_TIM1_Init(void)
{

/* USER CODE BEGIN TIM1_Init 0 */

/* USER CODE END TIM1_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 63999;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */

/* USER CODE END TIM1_Init 2 */

}

PYTHON代码(部分)

      
import serial
from collections import deque
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from threading import Thread, Lock

# 串口配置
SERIAL_PORT = 'COM6'
BAUD_RATE = 115200
SAMPLE_RATE = 10000 # 采样频率 1kHz
BUFFER_DURATION = 5 # 数据缓存时长(秒)

# 全局变量
raw_buffer = deque(maxlen=int(BUFFER_DURATION * SAMPLE_RATE)) # 原始数据缓冲
lock = Lock() # 线程锁

def serial_reader():
"""串口数据读取线程"""
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0.1)
while True:
try:
# 读取一行数据
line = ser.readline()
if line: # 如果读取到数据
# 将二进制数据转换为uint32_t
value = int.from_bytes(line.strip(), byteorder='little', signed=False)
with lock:
raw_buffer.append(value)
except (ValueError, serial.SerialException):
pass

def update_plot(frame):

"""实时更新伯德图"""
with lock:
data = np.array(raw_buffer)

if len(data) < 100: # 至少有100个数据点才更新
return

# FFT计算
N = len(data)
T = 1.0 / SAMPLE_RATE # 采样周期
yf = np.fft.rfft(data - np.mean(data)) # 去除直流分量
xf = np.fft.rfftfreq(N, T)

# 计算幅频特性
magnitude = 20 * np.log10(np.abs(yf) / N * 2) # 转换为dB

# 计算相频特性
phase = np.angle(yf) * 180 / np.pi # 转换为度数

# 绘制伯德图
ax1, ax2 = fig.axes # 获取子图的坐标轴
ax1.clear()
ax2.clear()

ax1.semilogx(xf[1:], magnitude[1:]) # 排除0Hz分量
ax1.set_xlabel('Frequency [Hz]')
ax1.set_ylabel('Magnitude [dB]')
ax1.set_title('Real-time Bode Diagram')
ax1.grid(True, which="both", ls="--")
ax1.set_xlim(1, SAMPLE_RATE//2) # 显示1Hz到Nyquist频率范围

ax2.semilogx(xf[1:], phase[1:]) # 排除0Hz分量
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Phase [degrees]')
ax2.grid(True, which="both", ls="--")
ax2.set_xlim(1, SAMPLE_RATE//2) # 显示1Hz到Nyquist频率范围

if __name__ == '__main__':
# 启动串口读取线程
Thread(target=serial_reader, daemon=True).start()

# 配置实时绘图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 12)) # 创建子图
ani = animation.FuncAnimation(fig, update_plot, interval=10000//SAMPLE_RATE)

# 显示窗口
plt.tight_layout()
plt.show()

功能展示图及说明

  1. 硬件连接图
    • STM32G031通过USB连接PC,麦克风信号接入ADC输入引脚(如PA0)。
  2. 数据波形图
    • Python实时显示ADC采样波形(时域信号)。
  3. 伯特图界面
    • 上位机界面显示幅频曲线(dB)和相频曲线(°),横轴为频率(1Hz-5kHz)。

项目中遇到的难题和解决方法

  1. 问题1:TIM1中断周期误差
    • 现象:0.1ms中断实际偏差较大。
    • 解决:调整预分频器(Prescaler)和重载值(Period),通过示波器校准。
  2. 问题2:ADC采样时间不足
    • 现象:高频信号采样失真。
    • 解决:降低ADC时钟分频(ADC_CLOCK_SYNC_PCLK_DIV2)并优化采样周期(SAMPLETIME_12CYCLES)。
  3. 问题3:串口数据丢失
    • 现象:高频率下通过字符串传输数据丢失。
    • 解决:直接通过DMA传输


仿真电路VS实际波形

https://www.eetree.cn/short/28ujfdrc ,如果访问失败 可以使用以下电路txt

$ 1 0.000005 10.20027730826997 50 5 43 5e-11
c 592 192 592 288 0 1e-10 3.2999174996347302 0.001
c 592 208 688 208 0 3.3e-7 1.802247240334509e-11 0.001
c 880 224 976 224 0 1e-7 -3.2672449663432546 0.001
c 928 128 928 192 0 0.000022 3.2999174996347307 0.001
c 784 352 864 352 0 3.3e-11 1.370472989259408e-8 0.001
r 592 128 592 192 0 4700
r 688 128 688 208 0 200000
r 928 128 1008 128 0 10
r 1008 128 1008 192 0 100000
r 768 400 864 400 0 10000
c 880 400 960 400 0 0.000009999999999999999 0.03275551440982856 0.001
r 768 304 880 304 0 100000
r 688 208 688 288 0 200000
409 768 224 880 224 3 0.6 0.06558508914802758 0.023100000000000002 0
w 688 208 768 208 0
w 880 224 880 304 0
w 768 304 768 240 0
w 880 304 880 352 0
w 864 352 880 352 0
w 784 352 768 352 0
w 768 352 768 304 0
w 768 352 768 400 0
w 864 400 880 400 0
w 880 400 880 352 0
g 960 400 992 400 0 0
w 592 128 688 128 0
g 592 320 624 320 0 0
w 592 288 592 320 0
w 688 288 592 288 0
g 928 192 960 192 0 0
w 688 128 928 128 0
R 1008 128 1008 80 0 0 40 3.3 0 0 0.5
w 976 224 1008 224 0
w 1008 224 1008 192 0
409 1008 240 1120 240 3 0.6 1.304583334693632e-7 0.023100000000000002 0
w 1008 256 1008 304 0
w 1008 304 1104 304 0
w 1104 304 1104 240 0
w 592 208 528 208 0
w 1120 240 1136 240 0
o 39 128 0 70656 5 0.1 0 2 39 3

未添加噪音输入:

image.png

添加噪音输入

image.png

实际测量:

image.png

实测幅频特性和相频特性

image.png

分析仿真结果和实际结果不同的原因可能是环境噪声对高频伯德图的影响机制

麦克风采集的信号本质上是声压信号,其易受环境噪声干扰,尤其是高频段差异可能与以下噪声因素相关:

  • 噪声类型
    • 宽带噪声:如空气流动、电子设备开关噪声(风扇、电源纹波)。
    • 窄带噪声:特定频率的干扰(如工频50/60Hz、WiFi/蓝牙射频干扰)。
    • 机械噪声:麦克风振动或接触噪声(如敲击、摩擦)。
  • 高频段敏感性
    高频信号(如>3kHz)的声压能量通常较低,若叠加环境噪声,信噪比(SNR)急剧下降,导致FFT分析时噪声能量掩盖真实信号,表现为幅频曲线波动或相位混乱。


心得体会与建议

  1. 收获
    • 掌握了STM32CubeMX快速配置外设的方法,深入理解了定时器与ADC的协同工作。
    • 实践了Python与嵌入式系统的数据交互,熟悉了FFT算法在频域分析中的应用。
  2. 改进建议
    • 可引入DMA+双缓冲模式提升ADC效率。
    • 上位机界面可改用PyQt或Tkinter实现更友好的交互。
  3. 意见
    • STM32CubeMX生成的代码需结合手动优化(如中断优先级),避免资源冲突。


附件下载
bode.py
python代码
STM32_Code.zip
STM32代码
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号