ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于ES32F365x的低频脉冲理疗仪DIY设计与实现

基于ES32F365x的低频脉冲理疗仪DIY设计与实现 把手头一堆国产芯片的开发板用起来给家里人做一台低频脉冲理疗仪是我去年底就有的想法。老人腰椎不好隔三差五要去理疗科报到不方便不说开销也不小。网上一两百块的家用理疗仪拆开看过几款内部方案其实大同小异都是“MCU高压升压H桥输出”这几大件成本并不高。既然自己能画板写代码干脆直接做一台顺便把整个设计过程和踩坑经历整理出来给想动手的朋友一条清晰的路。这篇文章以国产东软载波微电子的ES32F365x为主控完整讲清楚硬件架构、原理图设计、关键代码逻辑以及我在实测中遇到的各种问题希望能帮大家少走弯路。1. 起因为什么我会用国产芯片做一台低频脉冲理疗仪低频脉冲理疗仪通俗讲就是通过电极片向人体特定部位输出低频脉冲电流刺激神经和肌肉达到缓解酸痛、放松肌肉的效果。它在电学层面做的事情并不复杂产生一个频率从零点几赫兹到上百赫兹可调、脉宽从几十微秒到几百微秒可调、幅度从十几伏到上百伏可调的脉冲信号再通过两个电极片作用于皮肤表面。这台设备有几个硬性需求一是输出必须是双相脉冲也就是电流方向要交替变化否则电荷会在皮肤电极界面持续堆积轻则刺痛重则造成电化学灼伤二是要有足够高的输出电压因为人体皮肤阻抗通常在几千欧到几十千欧要用较低的电流穿过表皮必须用几十伏甚至上百伏的电压来“压”进去三是整个系统要能用电池供电既要省电又要体积小。我选ES32F365x思路很直接。首先它是国产芯片供货比很多小众型号稳定价格也合适其次它的资源刚好卡在这个项目的需求上48MHz主频的ARM Cortex-M0内核、12位ADC、多个16位定时器、I2C/SPI/UART接口做脉冲控制、升压闭环、OLED显示完全不会被资源卡脖子最重要的是上手门槛低有完整的库和参考手册不需要像某些冷门芯片那样对着数据手册啃寄存器。对于DIY项目来说能不能快速跑通比性能极限重要得多。整个项目做下来主线可以分成四块系统架构与芯片选型、原理图设计、代码实现、实测调试。下面从第一块开始讲。2. 系统架构与芯片选型理疗仪的真实硬件需求2.1 “电疗”在硬件上到底要做什么很多人第一次接触低频脉冲理疗仪会以为它像万用表测电压一样简单实际上硬件设计里藏着两个关键矛盾。第一是电压要高但电流要小。普通USB接口只有5V直接从5V输出到皮肤根本产生不了可感知的刺激效果。于是需要一级升压电路把电压抬到30V到100V范围同时通过串联电阻和限流设计把输出电流控制在安全范围内。这个“高压小电流”的特性决定了电源拓扑的选择。第二是脉冲方向要交替。如果一直单方向输出电极片下面的汗液和电解质会发生电解反应时间一长皮肤就会发红刺痛。所以必须在脉冲间隙切换输出极性让正反向的电荷量大致相等。这就需要一套极性切换电路常见方案是用H桥。硬件架构拆开来看就清晰了电源部分负责给整个系统供电并产生高压母线输出部分负责把高压母线变成可控的双相脉冲控制部分负责生成各种脉冲参数和管理人机交互。2.2 高压母线、输出桥路和信号链的划分整个系统可以画成一条信号链电池电压 - 高压母线 - H桥 - 电极 - 人体。高压母线是这台设备的核心。它决定了你能输出多强的刺激。家用理疗仪一般把高压母线做到30V到80V再配合脉宽调节来控制实际刺激强度。设计时我把高压母线做成可调的可以通过旋钮或者软件设定上限这样同一台设备既可以做温和的按摩模式也可以做较强力的镇痛模式。H桥放在高压母线和电极之间。它由四个开关管组成MCU通过控制开关管的通断组合让流过电极的电流方向交替变化。这一级的设计直接决定了波形质量和安全性是整台设备最容易出问题的环节。信号链的最后是电极片和人体皮肤的接触界面。这个界面不是纯电阻而是一个带有电容特性的复杂负载所以输出端的保护电路不能省至少要有串联限流电阻和必要的TVS管。2.3 为什么ES32F365x刚好能用管脚、定时器、ADC资源盘点选芯片的时候我列了一个清单把项目必需的外设资源全部列出来再拿ES32F365x逐项核对定时器资源是这个项目的重点。生成低频脉冲需要一个周期定时器控制脉宽需要一个单次定时器切换方向需要另一个单次定时器升压PWM又需要一个带PWM输出的定时器。ES32F365x提供了多个16位定时器其中一个高级定时器支持PWM输出和互补通道正好可以用来做升压开关控制也可以用来生成精准的延时中断。ADC资源同样关键。高压母线的输出电压需要通过电阻分压后送到ADC采样MCU根据采样结果调节升压PWM的占空比构成一个软件闭环。ES32F365x的12位ADC在这种场景下够用采样速度也完全来得及。GPIO数量和复用关系也是考量的点。OLED用I2C接口占两个脚三个按键占三个脚H桥控制占两个脚升压PWM占一个脚ADC采样占一个脚再加上UART串口留作调试总共用到的引脚不到十五个。用LQFP32封装的型号就足够不会出现引脚不足的尴尬。3. 原理图设计从5V电池到电击片的电路细节3.1 电源树设计充电、稳压与高压产生先看整体供电结构。设备用3.7V锂电池供电充电管理用最常见的TP4056充电电流设在500mA左右保护板用标配的DW01加8205A方案。电池输出分为两路一路经过低压差稳压器ME6211得到3.3V专门给MCU、OLED和按键等数字电路供电另一路直接进高压产生模块。高压产生模块是这个设计里弹性最大的一块。我试过两种方案一是直接用市面上常见的DC-DC高压升压模块输入3.7V到5V输出最高能到100V以上模块自带电位器可调简单粗暴二是用MCU输出PWM驱动一个反激变压器通过软件控制占空比来调节输出电压。第一种方案适合快速验证整个系统我在前期调软件时就用它。第二种方案适合追求体积和成本的量产思维也是我在最终版本里采用的方式。这里给出第二种方案的简化电路逻辑MCU的PWM输出经过一个三极管或MOSFET驱动级推动一个匝数比大约1比10的EE13变压器次级用快恢复二极管整流再经过一个1uF/100V的电容滤波就得到了高压母线。变压器次级出线后再接一个100k和4.7k的电阻分压网络分压点送入MCU的ADC引脚用来实时监测高压值。要注意的是这种反激拓扑的空载电压会偏高所以输出端一定要并联一个泄放电阻否则空载时电容上可能积累非常高的电压我第一次测试时就是因为没加泄放电阻空载电压飙到了180V差点把后端的H桥打穿。3.2 输出H桥与安全保护H桥部分我用了四个N沟道MOSFET组合。高边两个管子需要电平移位直接用MCU的3.3V GPIO去驱动高边在高压场景下是不行的因为MOSFET的源极接了高压母线栅极必须比源极低足够电压才能可靠导通。这里我用光耦加小信号三极管做一个简单的电平移位MCU输出高电平时光耦导通把高边MOSFET的栅极拉到地让栅源之间形成反向偏压管子就导通了。H桥四只管子的逻辑关系是这样正向脉冲时左高边管和右低边管同时导通电流从高压母线经过左管流入电极再从右管回到地。反向脉冲时右高边管和左低边管同时导通电流方向反过来。两个控制信号必须严格互斥不能出现同一侧高低边同时导通的情况否则就是直接短路瞬间烧管。安全保护方面我在H桥的每个输出端都串联了一个150Ω的电阻这个电阻决定实际刺激电流的上限。同时在地和输出之间并联了一个ESD二极管和一个双向TVS管防止人体静电和外部干扰打坏后级电路。输出端还可以预留一对端子接成人体的导电橡胶电极片。3.3 人机交互与接线说明人机交互模块我用的是0.96寸I2C接口的OLED屏加三个轻触按键一个切换模式两个调节参数。OLED的SCL和SDA分别接到MCU的I2C引脚I2C上拉电阻用4.7k接到3.3V。三个按键一端接GPIO另一端接地GPIO配置成上拉输入按下时读到低电平。每个按键都加了0.1uF电容做硬件去抖软件里再加几十毫秒的去抖过滤双保险。指示方面我还加了一个LED灯每次输出脉冲时同步闪烁这样在示波器不方便接的时候肉眼就能判断脉冲有没有输出。3.4 关键元器件的BOM与替换建议下面这张表是我最终确定的BOM把每个器件在电路里的角色标清楚了大家在抄作业的时候可以直接对号入座。模块器件型号/参数备注充电TP4056模块PCB模块带DW018205A保护稳压ME62113.3V/300mA低压差给MCU供电主控ES32F365xLQFP32国产Cortex-M0高压产生EE13变压器匝比1:10初级匝数12次级120高压整流UF40071A/1000V快恢复反激整流专用高压滤波陶瓷电容1uF/100V母线储能H桥IRLR024N55V/17A N-MOS低边也可换AO3400光电隔离PC817直插DIP4驱动高边管输出限流电阻150Ω/0.25W串联在电极回路显示SSD1306 OLED0.96寸I2C128x64按键轻触开关6x6mm三个替换建议放在这里高压产生模块如果不想自己绕变压器直接买“3.7V升100V高压模块”大约十块钱一个跟这个电路兼容性很好把模块输出接到H桥母线即可H桥的MOSFET如果手头有IRF540N、FQP30N06这类管子也能用只是需要注意栅极电容变大后开关损耗会增加驱动能力要跟得上。4. 代码实现双相脉冲、升压闭环与交互界面4.1 时间基准和输出引脚初始化软件代码的风格跟硬件划分一样保持模块化。入口处我把整个项目的初始化理成四件事系统时钟、GPIO复用、定时器、I2C外设。系统的时基我用SysTick做1ms中断作为主循环的时间基准和按键去抖的计数依据。PWM和脉冲定时器用硬件定时器生成不占用CPU周期。引脚初始化参照下面这段逻辑void gpio_init(void) { // H桥控制引脚 gpio_set_mode(H_BRIDGE_FWD, GPIO_MODE_OUTPUT_PP); gpio_set_mode(H_BRIDGE_REV, GPIO_MODE_OUTPUT_PP); // 升压PWM输出引脚 gpio_set_mode(PIN_BOOST_PWM, GPIO_MODE_AF_PP); // ADC采样引脚 gpio_set_mode(PIN_HV_SENSE, GPIO_MODE_ANALOG); // OLED的I2C引脚 gpio_set_mode(PIN_I2C_SCL, GPIO_MODE_AF_OD); gpio_set_mode(PIN_I2C_SDA, GPIO_MODE_AF_OD); // 按键 gpio_set_mode(PIN_KEY_MODE, GPIO_MODE_INPUT_PU); gpio_set_mode(PIN_KEY_UP, GPIO_MODE_INPUT_PU); gpio_set_mode(PIN_KEY_DOWN, GPIO_MODE_INPUT_PU); }4.2 治疗的“心脏”用三个定时器搭一个脉冲状态机生成双相脉冲是整个软件里最关键的部分。我用了三个定时器配合一个状态机来实现比在中断里数延时靠谱得多因为主循环即使被UI刷新卡住脉冲时序也不受影响。三个定时器的分工是TIM1产生治疗频率周期就是脉冲之间的间隔比如50Hz就是20ms一个周期TIM2作为脉宽定时器工作在单次模式负责控制单个脉冲宽度TIM3作为方向切换的间隔定时器负责正反向脉冲之间的死区。状态机有四个状态空闲、正向脉冲、死区、反向脉冲。typedef enum { ST_IDLE 0, ST_FWD_PULSE, ST_GAP, ST_REV_PULSE } stim_state_t; volatile stim_state_t stim ST_IDLE; volatile uint16_t pulse_us 150; // 默认脉宽150us volatile uint16_t freq_hz 50; // 默认频率50HzTIM1的更新中断是整个刺激过程的起点void TIM1_IRQHandler(void) { if (tim_get_flag(TIM1, TIM_EV_UPDATE)) { tim_clear_flag(TIM1, TIM_EV_UPDATE); if (stim ST_IDLE) { bridge_forward(); // H桥正向导通 stim ST_FWD_PULSE; tim_start_oneshot(TIM2, pulse_us); // 启动脉宽定时器 } } }TIM2的更新中断代表一个方向的脉冲结束void TIM2_IRQHandler(void) { if (tim_get_flag(TIM2, TIM_EV_UPDATE)) { tim_clear_flag(TIM2, TIM_EV_UPDATE); if (stim ST_FWD_PULSE) { bridge_idle(); // 正向脉冲结束先关断H桥 stim ST_GAP; tim_start_oneshot(TIM3, 20); // 死区20us防止直通 } else if (stim ST_REV_PULSE) { bridge_idle(); // 反向脉冲结束回到空闲 stim ST_IDLE; } } }TIM3的更新中断用于启动反向脉冲void TIM3_IRQHandler(void) { if (tim_get_flag(TIM3, TIM_EV_UPDATE)) { tim_clear_flag(TIM3, TIM_EV_UPDATE); if (stim ST_GAP) { bridge_reverse(); // H桥反向导通 stim ST_REV_PULSE; tim_start_oneshot(TIM2, pulse_us); // 同样脉宽形成对称双相脉冲 } } }这个状态机的妙处在于它只用三个硬件定时器就完成了“正向脉冲-死区-反向脉冲-等待下一周期”的完整循环而且中间没有多余的软延时时序精度完全由硬件保证。H桥控制函数本身也做了互锁保护即使软件状态机出错也不会出现同臂直通static void bridge_off(void) { gpio_write(H_BRIDGE_FWD, 0); gpio_write(H_BRIDGE_REV, 0); } static void bridge_forward(void) { bridge_off(); delay_us(2); gpio_write(H_BRIDGE_FWD, 1); } static void bridge_reverse(void) { bridge_off(); delay_us(2); gpio_write(H_BRIDGE_REV, 1); }4.3 高压闭环ADC采样与简单PI控制高压母线如果不能稳定刺激强度就会忽大忽小体验很差。我在最终版本里对着分压电阻网络做了软件闭环。分压计算很简单100k电阻和4.7k电阻串联分压比大约1比21。如果高压是60VADC引脚上的电压就是60*4.7/(1004.7)2.69V折合12位ADC的数值大约3340。目标值存成一个全局变量用户通过按键调整目标值相当于调节刺激强度。PI控制器的实现精简版如下int32_t hv_pid_update(uint16_t target_adc, uint16_t current_adc) { static int32_t err_acc 0; int32_t err (int32_t)target_adc - (int32_t)current_adc; err_acc err; if (err_acc 300) err_acc 300; if (err_acc -300) err_acc -300; int32_t duty err * 2 err_acc; if (duty 900) duty 900; if (duty 50) duty 50; return duty; // 单位是0.1% }这个函数放在主循环里每2ms执行一次算出来的占空比直接更新到升压PWM寄存器。实测下来电压稳定度在±1V以内完全够用。如果你用的是自带电位器的高压模块这部分的软件闭环可以跳过直接把模块输出调到固定高压再用脉宽来控制强度效果差异不大。4.4 菜单和OLED显示逻辑交互逻辑我做得很简单三键配合OLED。主界面分成三行第一行显示当前模式和频率第二行显示脉宽和强度等级第三行显示输出电压实际值。按键逻辑用状态机实现模式键在“频率设置”“脉宽设置”“强度设置”三个菜单间切换。上键和下键在对应菜单里加减参数。void ui_process(void) { uint8_t key key_scan(); if (key KEY_MODE) { menu_id (menu_id 1) % 3; oled_refresh_menu(); } if (key KEY_UP) { if (menu_id 0) { freq_hz clamp_s16(freq_hz 5, 1, 150); } else if (menu_id 1) { pulse_us clamp_s16(pulse_us 10, 40, 400); } else if (menu_id 2) { hv_target 50; // 每次提高约1V if (hv_target 4000) hv_target 4000; } oled_refresh_value(menu_id); } if (key KEY_DOWN) { if (menu_id 0) { freq_hz clamp_s16(freq_hz - 5, 1, 150); } else if (menu_id 1) { pulse_us clamp_s16(pulse_us - 10, 40, 400); } else if (menu_id 2) { hv_target - 50; if (hv_target 500) hv_target 500; } oled_refresh_value(menu_id); } }OLED刷新的频率不需要太高我把它放到主循环用SysTick计数每100ms刷新一次避免反复刷屏干扰脉冲输出的稳定性。5. 调试实录我踩过的波形问题和发热问题5.1 脉冲正负不对称问题出在死区第一次把电路全搭起来用示波器夹在电极两端看波形发现正向脉冲和反向脉冲的幅度明显不一样正向有60V反向只有45V左右。一开始怀疑是变压器次级绕组两端的负载不均后来仔细想明白了高边PMOS和低边NMOS的导通速度不一样。低边NMOS由MCU直推导通很快高边PMOS要走光耦导通延迟大所以反向通路建立得慢后半段的脉冲还没等波形稳定就到了设定的脉宽时间看起来幅度就小了。解决办法是在方向切换时把死区从20us增加到50us同时把高边光耦LED驱动的限流电阻从470Ω减小到220Ω让光耦导通更快一点。改完之后再次测正反向波形基本对称。5.2 H桥发热和短路瞬间状态机跑通之后我用手去摸H桥的MOSFET发现温度高得不对劲才工作了十几秒就明显烫手。用示波器测了MOSFET的栅源波形发现问题出在栅极驱动不足。高边管子靠光耦拉低栅极导通时栅源电压只有几伏管子工作在放大区和饱和区之间导通压降大自然发热。低边管子虽然由MCU直推但跑的是5V系统还是3.3V系统栅极最高也只有3.3V对IRLR024N这种逻辑电平管子来说刚刚够用但余量不多。最终方案是给低边管子换成AO3400虽然耐压低但导通电阻小3.3V栅极驱动也能彻底饱和导通高边则把光耦输出级的电阻重新调整让栅极可以充分拉到地。同时把门极电阻从10Ω改成了4.7Ω加快开关速度。调整后再测满载工作五分钟外壳温度也只有四十度上下。5.3 高压从80V变到60V之后ADC采样波动高压闭环调好之后又出现一个新问题负载变化时输出电压会短暂掉下来然后又恢复但恢复过程有大约500ms的明显波动。我最初把PI参数调得比较激进Kp取大了导致输出有小过冲。后来把Kp降下来Ki保持不变再把ADC采样从单次改成连续采样取平均值用最近八次的平均值参与计算波动明显减小。还有一个细节高压母线上的开关噪声会通过分压电阻耦合进ADC引脚。我在ADC引脚和地之间加了一个0.1uF电容形成一阶低通滤波采样值干净了很多。5.4 实测中的EMI和小提醒这台设备在工作时有明显的开关噪声OLED屏幕在脉冲输出的瞬间偶尔会出现闪烁。原因很简单高压变压器的开关频率和I2C时钟产生了一定的相互干扰。处理办法是调整布局OLED的I2C线远离变压器和高压母线并在OLED供电端加一个10uF电容。另外高压母线到H桥之间的走线尽量短粗降低寄生电感。改完之后闪烁问题基本消失OLED显示稳定。实测过程中还发现用示波器探头直接夹电极两端测波形会受到很强的电磁干扰波形上有很多毛刺。这不是设备故障而是测量方式的问题换成差分探头或者用电缆加衰减器测量毛刺会小很多。没有差分探头的话可以用两个绝缘好的鳄鱼夹线把示波器地线接在电路板的地上把探头缩短减少环路面积。6. 使用安全边界给所有DIY理疗仪爱好者的忠告6.1 电气安全设计低压脉冲理疗仪虽然输出电压高但实际电流被限制在几十毫安以内正常使用不会对人体造成严重伤害。不过这并不意味着可以掉以轻心毕竟这玩意的母线电压动不动就上百伏。设计上要做好三件事一是输出端必须串联固定限流电阻这个电阻值要经过计算保证短路时电流也不会超过安全范围二是在高压母线上加一个手动复位保险丝或者可恢复保险丝万一某个管子被击穿短路保险丝能断开母线三是最关键的一点H桥控制逻辑必须在软件和硬件两个层面做互锁杜绝同一侧高低边同时导通。我自己在调试时吃过一次教训用手拿着电极在没插输出线的情况下按下强度键输入端悬空导致高压模块空载电压飙升瞬间击穿了H桥的一个高边管。所以强烈建议大家在测试时先把输出电阻接上或者接一个1kΩ的假负载不要空载调压。6.2 人体使用禁忌之前在某论坛看到有人把电刺激贴片贴在脖子两侧这属于极度危险的位置。低频脉冲电流如果流过颈动脉窦区域可能引起血压骤降或心率异常。以下几条边界请务必记牢电极片绝对不要跨过心脏放置比如胸口左右两侧同时贴电流穿过心脏区域的风险极高。电极片不要贴在头部、咽喉部位会对中枢神经和呼吸肌造成不可预期的刺激。孕妇、装有心脏起搏器的人、癫痫患者严禁使用任何类型的电刺激设备。皮肤破损、有皮疹或者感觉迟钝的区域也不能贴否则可能因感觉不到刺激强度而烫伤或刺激过度。使用时间每次控制在20到30分钟以内强度从最低档逐步增加不要一上来就开最大档。最后补充一句非常重要的经验不要把DIY设备当医用设备来用。我自己做这台机器目的更多是研究电路和嵌入式控制偶尔给家人做肌肉放松症状严重时该去医院还是要去医院。低频电刺激理疗仪本身只能作为辅助放松工具不能替代正规的医学诊断和治疗。这套方案从原理图到代码都是可以复现的硬件成本加起来不超过五十块钱调试工具只需要一个万用表加一个示波器。如果你想在这个项目基础上继续深入可以往两个方向走一是把波形做得更丰富增加扫频、间歇、调制等多种刺激模式二是加入蓝牙模块用手机App来控制参数我会在后续的文章里继续分享这两部分的实现思路。
返回列表