ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32驱动CSB超声换能器:从等效电路到频率跟踪的完整实现

2026/9/11 20:21:58 拓冰建站 浏览量
STM32驱动CSB超声换能器:从等效电路到频率跟踪的完整实现 简介压缩包内是基于 STM32F103C8T6 驱动超声波换能器的完整工程源码面向嵌入式开发者与电子竞赛学生重点解决换能器驱动中的定时器 PWM 脉冲产生、信号放大及阻抗匹配等设计难题。资源共 130 个文件压缩后约 3MB包含 STM32 的 C/汇编源码、头文件、Keil 工程配置、编译生成的 hex/axf 文件以及工程清理脚本并集成 uC/OS-II 实时操作系统内核可按 USER、HARDWARE、SYSTEM 等目录模块逐层阅读。通过 USER 目录下的驱动实现可学习定时器输出比较与中断处理参考 HARDWARE 中的 GPIO 和功率放大电路配置可完成硬件连接再借助 UCOSII 源码理解多任务调度下的超声波发射与接收控制。已有 512 人学习适合希望从定时器原理到系统级应用完整搭建超声波测距或通信方案的开发者尤其是需要深入理解实时操作系统如何协调外设脉冲输出的进阶学习者。1. csb.zip 里的超声波驱动资料为什么照着接反而翻车下载一个 csb.zip 只需要几分钟解压后通常是一份 CSB 系列换能器的数据手册、几张原理图以及一段看起来能直接编译的 STM32 工程。把这些资源接到自己的板子上时最常见的结局却是换能器不出声或者响了几声就开始发烫。超声换能器驱动真正的门槛在于两点换能器本身是一个强容性负载驱动源要先做阻抗匹配同时 40kHz 只是标称谐振频率实际谐振点会随温度和负载漂移。csb.zip 里的文档可以当作器件参数参考但工程代码不能直接照搬。这篇内容以 CSB-40T/R 为例把 STM32 驱动超声换能器的等效电路、驱动级、频率跟踪和回波接收完整串一遍适合做液位计、流量计、避障模块以及正在做超声方向毕业设计的人对照落地。2. 先从 CSB 换能器等效电路入手谐振点、静态电容与匹配电感CSB 系列是超声测距里最常用的一类压电换能器发射头和接收头成对出现典型型号是 CSB-40T 和 CSB-40R。它标称 40kHz但这个数值只是出厂中心频率实际每一只的谐振点都会因为陶瓷片的厚度公差、温度和负载变化而偏移。这意味着驱动程序里的频率不能写死驱动电路也不能盲目抄网上的成品图。在动 STM32 代码之前先把换能器的电气模型和匹配参数算明白否则后面调程序只是在碰运气。2.1 拿到 CSB-40T/R 先测静态电容别急着写代码CSB-40T 和 CSB-40R 的电气参数差异很大发射头工作在高压驱动回路接收头输出的是几十毫伏级的微弱电压信号。两者不能互换接口电路也不能共用。使用前先用带电容档的万用表或 LCR 表在 1kHz 测试频率下量一下两只换能器的静态电容。数据手册给出的典型范围在 2000pF 到 2500pF但同一批次不同个体的离散度能达到 ±20%这个实测值直接决定后面的匹配电感取多少。参数CSB-40T发射CSB-40R接收对驱动方案的影响标称谐振频率40kHz ± 1kHz40kHz ± 1kHz决定 PWM 基频和扫频范围静态电容 C02000~2500pF2000~2300pF决定匹配电感值推荐驱动电压20~120Vpp—决定变压器匝比和 MOS 耐压接收灵敏度—约 -67dB决定接收链路增益波束角-6dB约 75°约 75°影响安装结构和声学隔离设计提示测静态电容要选 1kHz 而不是 40kHz。在低频下等效电路里的动态支路阻抗很高电流主要流经静态电容 C0测出来的值最接近真实 C0。如果拿 40kHz 下的读数去算匹配电感动态支路参与后会把读数带偏算出来的电感也就不对了。2.2 用 BVD 等效电路算匹配电感解释“有波形没声音”压电陶瓷换能器在谐振频率附近的行为常用 BVDButterworth-Van Dyke模型描述一条由 R1、L1、C1 串联组成的“机械臂”与静态电容 C0 并联。外部电源提供的能量一部分给 C0 充电一部分驱动机械臂振动。当驱动频率等于谐振频率 fs 时机械臂呈纯阻性电流最大声功率最高偏离 fs 越远能量越容易消耗在 C0 的反复充放电上。为了抵消 C0 的影响常见做法是在换能器两端并联一个匹配电感把静态电容的容性阻抗抵消掉让驱动桥看到接近纯阻的负载。匹配电感按这个公式计算Lm 1 / (4 × π² × f² × C0)取 f 40000Hz、C0 2200pF 代入算出来 Lm 约 7.2mH。实际取标称值 6.8mH 到 8.2mH 之间再通过电流波形微调因为 C0 本身的离散性很大。这个电感不是可选项没有它高压脉冲源的输出大部分消耗在 C0 充放电上换能器几乎不振动示波器上看波形却很正常这就是“有方波、没声音”的典型原因。2.3 连续波驱动和脉冲串驱动怎么选超声测距类应用普遍采用脉冲串驱动连续发出 8 到 16 个 40kHz 周期后停止发射切换到接收状态等回波。脉冲串越短测距盲区越小但声能量也越低脉冲串越长回波越强但盲区会变大。以 40kHz 计算12 个周期占 300μs单程对应空气中约 10cm这是液位计里比较常用的起点。如果做超声清洗、超声焊接这类功率应用则需要连续波驱动让换能器持续振动。这种情况下匹配电感的精度更关键电路里还要增加频率自动跟踪和过流保护因为换能器发热后谐振点会往低频漂固定 40kHz 连续驱动可能造成功率下降甚至换能器损坏。下一章把这些需求落到 STM32 的定时器配置和驱动电路上。3. 用 STM32 定时器生成 40kHz 互补 PWM把驱动级做扎实要让 CSB-40T 发出足够强的超声波需要给换能器两端施加高压交变脉冲。常见方案是半桥或全桥电路配合升压变压器STM32 输出 40kHz 互补 PWM驱动上下两个 MOS 管交替导通把直流电压切成方波经变压器升压后施加到换能器上。这一步有两个关键细节频率必须可调方便后续做谐振点跟踪互补 PWM 之间必须有死区时间防止上下管直通烧毁。3.1 用高级定时器 TIM1 输出带死区的互补 PWMSTM32 的通用定时器也能输出 PWM但只有 TIM1、TIM8 这类高级定时器能输出互补通道和硬件死区。以 STM32F103 在 72MHz 系统时钟下为例把 TIM1 的预分频设为 71计数周期 ARR 设为 24输出频率就是 72MHz / 72 / 25 40kHz。占空比通过 CCR1 控制Pulse 设为 12 时是 50%。实际半桥驱动里占空比建议略小于 50%给变压器磁通复位留出时间也让死区造成的波形跌落不至于影响有效电平。常用的 HAL 库初始化片段如下寄存器方式和标准外设库方式只是改接口参数逻辑一样TIM_HandleTypeDef htim1; TIM_OC_InitTypeDef sConfigOC; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig; htim1.Instance TIM1; htim1.Init.Prescaler 72 - 1; /* 72MHz / 72 1MHz */ htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 25 - 1; /* 1MHz / 25 40kHz */ htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; HAL_TIM_PWM_Init(htim1); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 12; /* 占空比 12/25 48% */ sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity TIM_OCNPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; sConfigOC.OCIdleState TIM_OCIDLESTATE_RESET; sConfigOC.OCNIdleState TIM_OCNIDLESTATE_RESET; HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1); sBreakDeadTimeConfig.OffStateRunMode TIM_OSSR_DISABLE; sBreakDeadTimeConfig.OffStateIDLEMode TIM_OSSI_DISABLE; sBreakDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime 72; /* 72 / 72MHz 1us */ sBreakDeadTimeConfig.BreakState TIM_BREAK_DISABLE; HAL_TIM_OC_ConfigBreakDeadTime(htim1, sBreakDeadTimeConfig); HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1);这段代码的关键参数是 Prescaler、Period 和 DeadTime。Prescaler 决定定时器计数频率Period 决定输出频率两者配合得到 40kHzDeadTime 的单位是系统时钟周期72 就是 1μs。死区时间不能设成 0常见的低压 MOS 驱动至少需要 500ns 以上频率越高、管子开关越慢死区就要适当加大。上电后如果发现 MOS 管发烫但波形看起来正常先怀疑死区不够其次是栅极电阻太小导致振铃。3.2 半桥驱动电路和变压器参数怎么定STM32 的 GPIO 在 3.3V 下只能提供几毫安驱动能力直接驱动 MOS 管会让管子长时间工作在放大区开关损耗剧增。常见做法是加一级栅极驱动芯片比如 TC4420、TC4427或者用两只三极管搭图腾柱电路。驱动电流越大栅极电压上升沿越陡开关损耗越小但过快的边沿也会带来 EMI 问题所以栅极串联电阻通常取 10Ω 到 22Ω 做阻尼。器件常见取值关键约束MOS 管IRLZ44N、AOD4184Vgs(th) 要低于 3V3.3V/5V 驱动才能完全导通栅极驱动TC4420 或推挽三极管峰值输出电流建议大于 1A升压变压器1:5 到 1:10按目标 Vpp 和供电电压反推隔直电容100nF~1μF 薄膜电容防止变压器偏磁饱和电流采样电阻10Ω、1%、无感电阻用于观测电流和频率跟踪变压器匝比的选择要算一下。半桥输出的交流幅值约为供电电压 VCC经过匝比 1:N 升压后换能器两端得到的峰峰值约 2 × VCC × N。12V 供电配 1:5 变压器得到约 120Vpp驱 CSB-40T 比较合适如果供电只有 5V就需要 1:10 以上但此时变压器次级分布电容和漏感的影响也会明显变大波形容易振铃。3.3 用重复计数器控制脉冲串发完自动停止超声测距要求精确控制发射脉冲个数。初学者容易在循环里反复调用 PWM Start 和 Stop这种写法会产生额外的边沿抖动脉冲个数也不可靠。用 TIM1 的重复计数器 RepetitionCounter 可以做到硬件级控制设置 RCR 为 11意味着每 12 个更新事件后产生一次更新中断在中断里关闭 PWM脉冲串就正好是 12 个周期。void ultrasonic_start_burst(uint16_t cycles) { __HAL_TIM_SET_AUTORELOAD(htim1, 25 - 1); /* 40kHz */ __HAL_TIM_SET_PRESCALER(htim1, 72 - 1); __HAL_TIM_SET_COUNTER(htim1, 0); htim1.Instance-RCR cycles - 1; /* 重复计数 */ HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); } void TIM1_UP_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim1, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(htim1, TIM_FLAG_UPDATE); HAL_TIM_PWM_Stop(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(htim1, TIM_CHANNEL_1); rx_gate_enable(); /* 关闭发射打开接收通道 */ } }这段代码的核心是利用 RCR 产生“发 N 个脉冲后立即停止”的硬件行为。中断里不需要做复杂运算只做停止输出和切换收发状态所以实时性有保证。注意在启动发射前要把计数器清零否则第一次脉冲宽度不完整。发射期间接收通道最好关闭避免高压脉冲串直接串入接收放大链路造成饱和恢复需要很长时间。4. 扫频跟踪谐振点用输入捕获读回波超声驱动的完整闭环驱动电路能在 40kHz 附近工作只是第一步。换能器的实际谐振点会随温度、负载、老化漂移回波信号本身又小又容易受振铃干扰所以工程上需要一套闭环发射前确认当前谐振频率发射后准确测量回波到达时间。STM32 的定时器、ADC 和输入捕获正好能覆盖这两件事。4.1 为什么固定 40kHz 不可靠压电陶瓷的谐振频率受温度影响明显温度升高陶瓷片的弹性模量和密度变化谐振频率会下降几十到几百赫兹。换能器表面沾水、结垢、覆冰时机械负载质量变大谐振点也会往低频移动。对测距应用来说发射效率下降 3dB 意味着测量距离明显缩短对连续波功率应用来说还可能造成换能器发热加剧。所以驱动代码里频率要留可调入口并且要有一套自动跟踪机制。4.2 用软件扫频找电流峰值自动锁定谐振频率最简单的频率跟踪方法是让定时器从 38kHz 扫到 42kHz每档发射一组脉冲同时用 ADC 采样发射回路里采样电阻上的电压电压峰值最大处就是当前谐振点。实现上不需要额外硬件发射电流经过采样电阻后接到 STM32 的 ADC 引脚把定时器的 ARR 改一下就能改变频率。#define F_START 38000UL #define F_END 42000UL #define F_STEP 100UL #define ADC_BUF_SIZE 32 static uint16_t adc_buf[ADC_BUF_SIZE]; static uint32_t best_freq 40000UL; static uint16_t measure_peak_adc(uint32_t freq) { uint16_t peak 0; uint32_t arr (72000000UL / 72UL) / freq - 1; __HAL_TIM_SET_AUTORELOAD(htim1, arr); __HAL_TIM_SET_PRESCALER(htim1, 72 - 1); HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_buf, ADC_BUF_SIZE); ultrasonic_start_burst(12); for (uint8_t i 0; i ADC_BUF_SIZE; i) { if (adc_buf[i] peak) peak adc_buf[i]; } HAL_ADC_Stop_DMA(hadc1); return peak; } void frequency_tracking_loop(void) { uint16_t max_value 0; for (uint32_t f F_START; f F_END; f F_STEP) { uint16_t v measure_peak_adc(f); if (v max_value) { max_value v; best_freq f; } } uint32_t arr (72000000UL / 72UL) / best_freq - 1; __HAL_TIM_SET_AUTORELOAD(htim1, arr); }这段代码里measure_peak_adc 每调用一次就完成一次指定频率下的发射和采样。ADC 缓冲区大小要覆盖至少一个 PWM 周期40kHz 对应 25μs如果 ADC 配置成 1Msps32 个点正好覆盖 32μs能抓到完整的电流波形。比较峰值而不是平均值是为了避开 PWM 占空比带来的直流偏置影响峰值直接反映激励电流强度。完整扫一遍 41 个频点需要大约几十毫秒放在启动时做一次之后每隔几分钟微扫一次即可。注意发射期间的高压开关动作会产生很强的电磁干扰ADC 采样尽量避免使用连续采样改为由定时器触发采样并且采样窗口放在脉冲串的中间部分。如果峰值出现明显毛刺可以在比较前对 ADC 数据做一次简单的中值滤波。4.3 回波接收链路与输入捕获测距发射结束后接收头会先输出一串振幅很大的振铃信号然后才是从目标反射回来的回波。接收链路通常由三部分组成低噪声运放做固定增益放大、有源带通滤波器选 40kHz 附近信号、比较器把模拟回波整形为方波最后送入 STM32 的输入捕获引脚。用 CSB-40R 时近处强回波可达数百毫伏远处弱回波只有几毫伏接收增益要留可调电位器。比较器输出接到 TIM2 的输入捕获通道后用两个捕获边沿分别记录回波到达的上升沿和下降沿就能算出回波时间。下面是一个以回波上升沿为停止条件的 HAL 库捕获回调volatile uint32_t echo_start; volatile uint32_t echo_stop; volatile uint32_t echo_width; volatile uint8_t echo_state 0; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if ((htim-Instance TIM2) (htim-Channel HAL_TIM_ACTIVE_CHANNEL_1)) { if (echo_state 0) { echo_start HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); echo_state 1; } else { echo_stop HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); echo_width echo_stop - echo_start; echo_state 0; } } }逻辑说明echo_state 为 0 时等待第一个上升沿捕获到后记录时间为 1 时等待下降沿得到回波脉宽。距离计算用声速换算空气中声速约 343m/s温度补偿公式 c 331.4 0.6T距离mm echo_widthμs× cmm/μs÷ 2。例如 echo_width 为 2000μsc 取 0.343mm/μs距离就是 343mm。实际工程里回波窗口一定要加使能控制发射结束后 1ms 内不打开捕获避开振铃超过 20ms 没有捕获就超时认为没有回波。否则电路噪声会让捕获回调进入死循环状态看似程序“卡死”其实是中断频率过高导致其他任务饿死。4.4 什么时候可以跳过扫频直接固定频率对低成本避障模块如果换能器是新的、工作温度变化不大固定 40kHz 也能工作代价只是发射效率损失几个 dB表现为测量距离变短。对液位计、流量计这类需要长时间可靠运行的产品扫频必须做至少要在启动时和温度变化超过一定阈值后重新扫一次。还有一种折中做法只在 39kHz、40kHz、41kHz 三个点检测峰值选择幅值最大的作为工作点计算量小又能覆盖大多数温度场景。5. 换时钟、换芯片包、换烧录方式之后三个必调的工程细节csb.zip 里的工程文件往往是在某一款特定板子上调通的换到自己板子上后代码可能不跑、烧录后没反应、或者运行几分钟就异常。这一章把最常见的三个工程细节讲清楚都是先于换能器本体需要排查的地方。5.1 换板子后晶振起不来按 CL 公式重算匹配电容外部 8MHz 晶振需要两个负载电容才能稳定起振取值要按晶振数据手册里的 CL 参数计算CL (C1 × C2) / (C1 C2) CstrayC1、C2 是两个引脚对地电容Cstray 是 PCB 走线和引脚寄生电容一般取 3pF 到 6pF。假如晶振标称 CL 18pFC1 C2 取 20pF代入公式后 CL 约 15pF偏小可能出现起振慢、时好时坏。此时应把 C1、C2 换成 22pF 或 27pF 再试。验证方法是用示波器探针看 OSC_OUT 引脚是否有完整正弦波或者读 RCC 时钟控制寄存器的 HSERDY 标志位是否置位。5.2 Keil 下生成 bin 文件用 ST-Link Utility 烧录有些 csb.zip 工程用的是旧版芯片包或 IAR 工程结构迁移到 Keil 后要先确认 Keil 里安装的 STM32F1 芯片包版本与新工程匹配否则外设库文件会报一堆头文件找不到的错误。编译通过后默认只生成 axf 文件烧录工具不一定认。在 Keil 的 User 页签里加一行命令就能生成 bin 文件fromelf --bin --output .\build\app.bin .\build\csb_drv.axf生成的 bin 文件可以用 ST-Link Utility 烧录到地址 0x08000000。如果目标板有 Bootloader要改成 Bootloader 之后的偏移地址比如 0x08004000并且在代码里设置中断向量表偏移 SCB-VTOR否则程序能下载但上电不运行。排查顺序是先确认烧录地址再确认 VTOR最后才怀疑换能器驱动。5.3 驱动级预留过流保护和温度测量两个后门量产的换能器 C0 离散性大匹配电感和驱动频率都要允许在固件里校准。建议板子上预留一个 10k 电位器接口用来校准电流采样增益同时用 NTC 热敏电阻接到 ADC 引脚测量换能器表面温度。过流保护不要只靠软件轮询应利用 STM32 高级定时器的刹车功能把电流比较器的输出直接接到 BKIN 引脚当采样电阻上的电压超过阈值时硬件在几微秒内封锁 PWM 输出然后在中断里置错误标志并执行恢复流程。这个硬连线保护比任何软件判断都快是超声驱动板里最值得保留的一个习惯。本文还有配套的精品资源点击获取