简介:这份资源是2024年全国大学生电子设计竞赛G题「简易超声波录音屏蔽系统」的完整参赛作品,面向电子信息、通信工程、自动化等专业的在校学生与指导教师,也适合作为课程设计、毕业设计或项目立项的参考范例。压缩包共880个文件,约13.24MB,以C语言源码(278个.c)与头文件(345个.h)为核心,辅以汇编启动文件、STM32CubeMX工程配置、Keil工程文件、原理图与实物照片等,覆盖从底层驱动到系统集成的完整工程结构。目前已有3001人学习下载,代码均经作者实测运行成功后才上传。读者可获取赛题方案思路、可直接编译的工程源码与配置方法,并在此基础上修改扩展功能,用于备赛、课设或毕设等场景,具备较高的学习与复用价值。
1. 从 2024 电赛 G 题说起:简易超声波录音屏蔽系统到底在屏蔽什么
2024 年电赛 G 题把「简易超声波录音屏蔽系统」推到了很多参赛队面前,也把一堆人卡在了换能器选型上。先把概念钉死:它要做的不是把录音设备弄坏,而是用超声频段的强信号去干扰麦克风前端的拾音链路,让录音结果变成一段听不清、认不出的噪声。核心器件是超声波换能器,常见中心频率落在 25kHz 到 40kHz 之间,人耳听不见,但麦克风的振膜和前置放大电路照样会响应。系统通常由 STM32 主控、DDS 或 PWM 信号源、功率放大级、超声波换能器阵列和电源管理几块组成。适合谁看?正在备赛电赛的学生、想复现超声波干扰方案的嵌入式工程师,以及需要理解超声波换能器等效电路图的人。这一章不堆公式,先把「屏蔽」的边界讲清楚,后面再落到配置方法和源代码结构。
2. 超声波换能器与驱动链路:从等效电路图到功放选型
2.1 换能器等效电路图里真正要看的三个参数
很多人拿到超声波换能器等效电路图就懵,其实对做录音屏蔽系统来说,只需要盯住三个量:谐振频率、静态电容和等效阻抗。谐振频率决定你发射信号的中心频率,偏离它,输出声压会断崖式下跌;静态电容决定驱动电路是当容性负载还是阻性负载来匹配;等效阻抗决定功放需要输出多大电流。常见的 25kHz 换能器静态电容在 2000pF 到 4000pF 量级,40kHz 的会小一些。等效电路图里那串 RLC 并联再串一个电容的结构,本质是在告诉你:只有在谐振点附近,换能器才表现为一个低阻抗的纯阻性负载,这时候能量转换效率最高。
选型时我一般会先确认中心频率,再确认额定功率,最后看封装尺寸能不能塞进结构件。电赛现场常见的是开放型换能器,直径 16mm 或 20mm,额定功率 1W 到 5W 不等。别一上来就买最大功率的,功放和电源跟不上反而更容易翻车。
2.2 用 STM32 定时器生成 25kHz 到 40kHz 驱动信号
驱动信号可以用 STM32 的定时器 PWM 模式产生,也可以用 DDS 芯片。电赛场景下 PWM 方案更省事,因为不需要额外芯片,频率也够用。下面这段代码用 TIM1 的通道 1 在 PA8 上输出 40kHz、占空比 50% 的方波,系统时钟按 72MHz 算。
// STM32F103 TIM1 PWM 输出 40kHz 方波 // 系统时钟 72MHz,预分频 0,自动重装载值 = 72M / 40k / 2 - 1 = 899 void Ultrasonic_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE); // PA8 复用推挽输出,接功放输入端 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = 899; // ARR TIM_TimeBaseStructure.TIM_Prescaler = 0; // PSC TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 450; // CCR,50% 占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM1, ENABLE); // 高级定时器必须开 TIM_Cmd(TIM1, ENABLE); }逻辑说明:TIM1 是高级定时器,输出 PWM 前必须调用TIM_CtrlPWMOutputs使能主输出,否则 PA8 上量不到波形,这是新手最常踩的坑。参数说明:TIM_Period决定频率,改频率就改这个值,公式是72M / (PSC+1) / (ARR+1) / 2;TIM_Pulse决定占空比,50% 时取(ARR+1)/2。如果你用的是 25kHz 换能器,把 ARR 改成 1439 即可。
2.3 功放级选型:为什么 MOS 管推挽比运放更常见
PWM 信号出来只有 3.3V,推不动换能器。中间必须加功放。常见做法是 MOS 管推挽或者专用超声驱动芯片。MOS 管推挽的好处是效率高、成本低,缺点是死区控制不好会直通烧管。我一般会在栅极驱动上串 10Ω 到 22Ω 电阻,抑制振铃。电源部分建议单独给功放供电,别和 STM32 共用一路 LDO,否则大电流一拉,MCU 直接复位。
换能器两端最好并联一个匹配电感,和换能器的静态电容在谐振频率上形成 LC 谐振,能明显提升声压输出。电感值按f = 1/(2π√(LC))反推,25kHz 配 3000pF 大概需要 13mH 左右。这个电感不是必须,但加了之后效果差别很大。
3. 源代码结构与配置方法:让系统跑起来的最小闭环
3.1 工程目录怎么分:源代码管理从第一天就要做
电赛时间紧,很多人代码写到一个 main.c 里,最后改都改不动。我建议从第一天就按模块分目录,哪怕只是几个文件。常见结构是:Core/放 main 和中断,Drivers/放 STM32 标准库或 HAL,BSP/放换能器驱动、按键、OLED,App/放屏蔽逻辑和状态机。源代码管理用 Git 本地仓库就够,每次烧录前 commit 一次,改崩了能回退。别小看这个习惯,电赛现场因为改错一行代码找不到上一版而崩盘的情况,我见过不止一次。
配置方法上,时钟树先用 CubeMX 配好,生成一次工程,之后手动改代码。如果全程手写寄存器,时间不够。CubeMX 里把 TIM1 通道 1 设为 PWM Generation,预分频和计数周期按上面算的值填,生成后直接调用HAL_TIM_PWM_Start就行。
3.2 主循环状态机:待机、发射、间歇三种模式怎么切
录音屏蔽系统不是一直满功率发射就最好。持续发射会让功放和换能器发热,电赛现场电池也扛不住。常见做法是间歇发射,比如发射 100ms、停 50ms,既省电又能保持干扰效果。下面是一个简化的状态机骨架。
typedef enum { MODE_IDLE = 0, MODE_BURST, MODE_CONTINUOUS } SysMode_t; SysMode_t g_mode = MODE_IDLE; uint32_t g_tick = 0; void App_Loop(void) { switch (g_mode) { case MODE_IDLE: // 关闭 PWM 输出,等待按键 TIM_Cmd(TIM1, DISABLE); if (Key_Scan() == KEY_START) { g_mode = MODE_BURST; g_tick = 0; } break; case MODE_BURST: // 间歇发射:100ms 开,50ms 关 if (g_tick % 150 < 100) { TIM_Cmd(TIM1, ENABLE); } else { TIM_Cmd(TIM1, DISABLE); } g_tick++; if (Key_Scan() == KEY_STOP) { g_mode = MODE_IDLE; } break; case MODE_CONTINUOUS: TIM_Cmd(TIM1, ENABLE); if (Key_Scan() == KEY_STOP) { g_mode = MODE_IDLE; } break; } }逻辑说明:g_tick每毫秒加一次,靠取模实现占空比可调的间歇发射。参数说明:100 和 50 这两个数可以按实测效果调,发热严重就缩短开启时间,干扰距离不够就加长。注意TIM_Cmd只是开关计数器,PWM 输出使能状态由TIM_CtrlPWMOutputs控制,两者别搞混。
3.3 配置方法:从烧录到实测的五个步骤
第一步,用 ST-Link 把编译好的 hex 烧进 STM32,确认 PA8 上有波形,示波器探头用 10x 档。第二步,接上功放和换能器,用手机录音功能做初步测试,录音回放里应该能听到明显的噪声或失真。第三步,调整换能器朝向,超声波指向性很强,偏 30 度效果就掉一半。第四步,测电流,待机应该在 20mA 以内,发射时根据功放不同在 200mA 到 1A 之间。第五步,连续跑 10 分钟,摸功放和换能器温度,烫手就降占空比或改间歇模式。
提示:测试录音效果时,手机不要贴着换能器,距离 30cm 到 1m 更能反映真实屏蔽范围。
4. 避坑与排查:电赛现场最容易翻车的五个点
4.1 换能器不响,示波器却有波形
现象:PA8 上波形正常,换能器一点声音都没有,录音也测不出干扰。原因:功放没有真正工作,或者换能器接在了功放输出和地之间但功放本身没供电。解决:先用示波器量功放输出端,如果还是 3.3V 方波,说明功放没导通,检查 MOS 管栅极电压和电源。如果功放输出有几十伏摆幅但换能器不响,多半是换能器坏了或者频率偏得太远。
4.2 一发射 STM32 就复位
现象:待机正常,一开 PWM 主控就重启。原因:功放瞬间电流把共用电源拉垮,或者地线走线太细导致地弹。解决:功放和 MCU 分开供电,至少加 470uF 电解电容在功放电源入口;地线用星型单点接地,别让大电流经过 MCU 的地。
4.3 录音里只有轻微底噪,屏蔽效果不明显
现象:能听到一点变化,但录音内容还是能辨认。原因:换能器数量不够或者功率太小,单只换能器覆盖角度有限。解决:用多只换能器组成阵列,朝不同角度发散;或者提高功放电压,但注意别超过换能器额定功率。实测中,三只 25kHz 换能器呈 120 度分布,比单只效果好很多。
4.4 频率对了但干扰距离只有几厘米
现象:贴着才能干扰,稍微远一点就无效。原因:超声波在空气中衰减很快,40kHz 比 25kHz 衰减更明显。解决:优先选 25kHz 换能器,或者加大驱动功率。另外,换能器前面不要有遮挡物,网罩太密也会挡声波。
4.5 源代码编译通过但下载后没反应
现象:Keil 或 CubeIDE 编译零错误,烧录也成功,但板子没动静。原因:启动文件选错、时钟没配、或者中断向量表偏移不对。解决:确认芯片型号和启动文件匹配,检查SystemInit里的时钟配置,用调试器单步看能不能进 main。电赛现场换板子后忘记改芯片型号,是血泪经验里排前三的翻车原因。
5. 进阶技巧:用扫频法找换能器真实谐振点
换能器标称 25kHz,实际谐振点可能在 24.3kHz 或 25.8kHz,差几百赫兹输出就掉一截。我一般会写一段扫频代码,从 23kHz 扫到 27kHz,每步停 200ms,同时用示波器看功放输出电流或者用声压计看读数,峰值对应的频率就是真实谐振点。下面这段代码用定时器更新中断改 ARR,实现自动扫频。
// 扫频:23kHz 到 27kHz,每 200ms 步进 100Hz void Sweep_Start(void) { uint16_t arr; for (uint32_t f = 23000; f <= 27000; f += 100) { arr = (uint16_t)(72000000UL / f / 2 - 1); TIM1->ARR = arr; TIM1->CCR1 = (arr + 1) / 2; // 保持 50% 占空比 Delay_ms(200); // 等待声压稳定 // 此处可加 ADC 采样,记录当前频率下的电流或声压 } }逻辑说明:直接改 ARR 会立即生效,但 CCR 要同步改,否则占空比会跳变。参数说明:步进 100Hz 是精度和速度的折中,想更细可以改 50Hz,但扫完时间翻倍。实测时把 ADC 接在功放电流采样电阻上,记录每个频点的值,最大值对应的就是谐振点。找到之后把 ARR 固定在这个值,屏蔽效果通常能提升 20% 到 30%。
另一个技巧是给换能器加背腔。开放型换能器向后辐射的声波和向前的是反相的,会抵消。用一段 PVC 管或者 3D 打印一个后盖,把背面封住,前向声压会明显增强。这个改动成本极低,但效果立竿见影。
我自己的习惯是:每次改完硬件,先用扫频确认谐振点,再调间歇发射的占空比,最后才动源代码里的状态机逻辑。顺序反了,变量太多,根本不知道是哪个改动起了作用。希望帮到你。
本文还有配套的精品资源,点击获取