资讯动态

单片机NEC红外解码与按摩椅电机控制系统设计详解

发布时间:2026/9/12 0:34:42 来源:尧图企业网站定制
简介这是一份面向NEC 78K系列单片机开发者的按摩椅控制程序源码包以日本FAMILY W-1按摩椅为对象主控芯片为NEC UPD784218。资源围绕按摩强度调节、模式选择、时间设定等实际功能覆盖电机驱动、VFD真空荧光显示、按键扫描、定时中断等嵌入式开发核心环节适合嵌入式初学者或家电控制器开发人员研究参考。压缩包内共138个文件大小仅421KB包含22个C源文件、19个H头文件、34个BAK备份、19个REL可重定位文件以及ASM汇编、HEX固件等类型便于对照源码、生成文件与最终烧录镜像进行学习。通过系统初始化、VFD显示驱动、按键处理等模块的工程组织可以直观了解整机控制流程与软硬件接口关系。资源当前已有125人学习下载。对想要了解NEC单片机在家电产品中的真实工程结构、或需要参考C与汇编混合编程和VFD显示驱动的开发者这是一份体量轻但信息密度较高的参考材料。1. 按摩椅程序里的 NEC 与单片机按摩椅的遥控和电控有两套东西配合红外接收端用 NEC 协议38kHz 载波解码后得到 32 位数据帧帧里带地址和命令反码单片机端则把帧翻译成揉捏电机、敲打电机、气囊电磁阀的动作。拿到带“nec”标签的按摩椅程序工程先读红外解码部分比读电机控制更有价值因为后面所有映射和状态机都在消费解码输出这一层出错动作全是垃圾输入。普通家电按一下动一下按摩椅的一个动作要保持几十秒再换下一个核心是状态机而不是按键分发。下面讲的 NEC 解码、电机控制、看门狗和现场排查对做按摩椅、理疗床、康复器械的固件工程师以及毕设选“按摩椅控制系统”的学生都适用先找到执行机构再跳到对应章节。2. 单片机 NEC 红外解码从时间片到位映射2.1 NEC 协议在按摩椅遥控器里的时间参数NEC 协议在单片机端看到的是一串宽窄不同的脉冲。引导码是 9ms 的 38kHz 载波突发加 4.5ms 空闲数据位是约 560µs 的载波突发加一段空闲空闲长度在 1690µs 左右是逻辑 1560µs 左右是逻辑 0。对下降沿中断来说从一次下降沿到下一次下降沿逻辑 1 约为 2.25ms逻辑 0 约为 1.125ms差了一倍阈值取 1.5ms 时基本不挑器件。按摩椅遥控器按键多常见做法是走扩展 NEC 或自定义命令段把地址固定成厂内编号命令码对应到具体按键。标准 NEC 的数据帧一共 32 位按 LSB 在前发送8 位地址、8 位地址反码、8 位命令、8 位命令反码。只要接收端把地址反码和命令反码都校验基本能挡住同频段其它遥控器的偶然干扰。NEC 帧参数数值解码用途载波频率38kHz一体化接收头解调引导码9ms 载波 4.5ms 空闲判定帧头逻辑 0560µs 载波 560µs 空闲下降沿间隔约 1.125ms逻辑 1560µs 载波 1690µs 空闲下降沿间隔约 2.25ms数据帧总长32 位LSB 在前地址反码命令反码重复码9ms 载波 2.25ms 空闲长按自动发送提示一体化红外接收头收到载波时输出低电平、空闲输出高电平单片机看到的是反相波形外部中断建议配下降沿或双边沿触发。2.2 用定时器捕获实现 NEC 解码的完整代码在 51 内核上做 NEC 解码我一般不用定时中断去轮询电平而是让一个 16 位定时器自由跑外部中断每次触发时读一次计数器算差值。这样中断里只有几次减法和比较时间上能控制在几微秒内解码过程中主循环还能同时处理电机任务。// NEC 解码适配 STC15W / 标准 5112MHz 主频 // 定时器 0 自由计数INT0 下降沿触发 sbit IR_IN P3^2; // 接收头 OUT 接 P3.2 volatile unsigned int last_tick 0; volatile unsigned char bit_cnt 0; volatile unsigned char frame[4] {0}; volatile unsigned char frame_ok 0; void ir_int0_isr(void) interrupt 0 { unsigned int now (TH0 8) | TL0; unsigned int diff now - last_tick; last_tick now; if (diff 12000) { // 两帧间的空白清空缓冲 bit_cnt 0; return; } if (diff 7000) { // 引导码9ms 4.5ms 或重复码 bit_cnt 0; return; } if (diff 400 diff 5000 bit_cnt 32) { if (diff 1500) { // 2.25ms 为逻辑 1 frame[bit_cnt 3] | (1u (bit_cnt 7)); } bit_cnt; } if (bit_cnt 32) { frame_ok 1; bit_cnt 0; } }定时器初始化只要保证TH0和TL0在启动后不再被主程序赋初值让它一直自由运行即可。diff now - last_tick利用无符号整数回绕的特性即使两次中断之间发生了定时器溢出也能得到正确的间隔不需要额外处理溢出标志。这个解码器里没有用中断屏蔽中断函数里没有阻塞操作NEC 一位数据的间隔在 1.1ms 以上单片机完全来得及响应。阈值选择上引导码判定取了 7000µs这是因为它同时覆盖数据帧引导码 13.5ms 和重复码 11.25ms 两种间隔。数据位的下限取 400µs是为了挡住边沿抖动或极窄的杂波。若实际遥控器晶振偏差大位宽漂移超过 10%把 1500µs 阈值调整到 1300µs 即可代码逻辑不用改。2.3 从 NEC 功能码到按摩椅按键动作的映射表解码器输出裸的 4 字节帧按键语义要单独建一层。按摩椅按键通常包括电源、自动、揉捏、敲打、推拿、气囊、加热、强度加、强度减和停止。项目上做协议定义时我会先把键值表写进需求文档固件里也只在唯一的地方维护这张表。按键名命令码动作电源0x45待机/运行切换自动0x46进入自动按摩程序揉捏0x47揉捏电机启动敲打0x44敲打电机启动气囊0x40气泵和电磁阀循环加热0x43加热继电器吸合停止0x42全部电机停转并回位这里的 0x45、0x46 不是 NEC 协议的强制值是按摩椅行业内比较常见的一组遥控码。不同厂家、不同批次遥控器的码表可能完全不同所以项目开始时必须先烧一个打印原始帧的测试固件把每个键对应的字节记录下来再填表不要直接套用别人工程里的键值。2.4 NEC 帧校验与主循环取帧帧里第二个字节是地址反码、第四个字节是命令反码校验规则很简单frame[1]应等于~frame[0]frame[3]应等于~frame[2]。还有一部分方案会把整个数据帧取反发送在初始化字节不为 0 时两者有对应差异程序里统一按“校验失败就丢帧”处理。#define IR_ADDR 0x00 // 按摩椅本机接收地址 void process_nec_cmd(void) { unsigned char tmp[4]; if (!frame_ok) return; EA 0; // 关中断拷贝防 32 位帧被中途更新 tmp[0] frame[0]; tmp[1] frame[1]; tmp[2] frame[2]; tmp[3] frame[3]; frame_ok 0; EA 1; if (tmp[0] ! (unsigned char)~tmp[1] || tmp[2] ! (unsigned char)~tmp[3]) { return; // 反码不匹配丢弃 } if (tmp[0] ! IR_ADDR) return; // 地址不是本机丢弃 dispatch_key(tmp[2]); // 只把命令码交给业务层 }关中断拷贝是因为frame[4]是多字节组合主循环读它时外部中断可能正好写入新帧造成前一半是旧帧、后一半是新帧的错位数据。校验在这里就是最后一道闸。按帧周期 40ms 到 110ms 统计NEC 遥控器按住不松会反复发重复码。按摩椅几乎每个按键都是自锁式长按没有额外语义所以解码器直接把重复码当作噪声丢弃如果将来要做长按连续调档在收到重复码时记住当前命令值再走一次dispatch_key即可。3. 按摩椅电机控制驱动选型、PWM 参数与状态机编排3.1 按摩机芯的执行机构与驱动方案按摩椅的执行器分四类揉捏电机、敲打电机、行走电机以及气泵加电磁阀的气囊系统。揉捏和敲打电机常是 24V 直流有刷电机要正反转和调速驱动用 H 桥。气泵功率大但只需要开关用继电器控制。电磁阀数量多、电流小用 ULN2803 这类达林顿阵列最省事一块芯片能带七路正好覆盖常见的气囊路数。执行机构电压驱动控制方式关键参数揉捏电机24V DCH 桥PWM 方向连续运行敲打电机24V DCH 桥PWM 方向频繁启停行走电机24V DCH 桥方向 限位两端微动开关气泵12V/24V继电器开/关大电流浪涌电磁阀12VULN2803开/关7 路加热丝220V继电器开/关需要安规距离H 桥选型上常用集成驱动芯片如 BTS7960两路 PWM 输入占空比决定速度方向由哪一路给 PWM 决定。继电器控制气泵时要用带灭弧能力的继电器不要把 5V 单片机引脚直接顶到继电器线圈上中间加三极管或 ULN2803。提示继电器线圈和电磁阀都是感性负载断电瞬间会产生反电动势。线圈两端并续流二极管电磁阀靠近驱动芯片的输出端也要加否则轻则复位重则打坏 IO。3.3V 单片机驱动达林顿模块时输入高电平往往不够要把输入侧拉高到 5V 或加一级电平转换。3.2 电机正反转与 PWM 调速的最小实现以 BTS7960 为例片子的 RPWM 和 LPWM 输入分别对应两个方向。正转时给 RPWM 一个占空比可调的信号、LPWM 拉低反转时反过来。占空比范围 0 到 100。PWM 频率取 16kHz 到 20kHz避开人耳敏感区同时给电流采样留出稳定窗口。// 揉捏电机控制dir1 正转dir0 反转duty 范围 0~100 void knead_motor(unsigned char dir, unsigned char duty) { if (dir 1) { RPWM_PIN 1; // 方向引脚置高 LPWM_PIN 0; pwm_set_duty(0, duty); // 通道 0 输出 PWM } else { RPWM_PIN 0; LPWM_PIN 1; pwm_set_duty(0, 100 - duty); } }这里pwm_set_duty是对底层 PWM 外设的封装STC15 用 PCA 模块、STM32 用定时器通道函数接口保持一致换芯片时只改底层。反向时的100 - duty是为了统一“有效占空比”的语义实际硬件的输出波形正好反过来不用过度理解。电机最容易被忽视的是启动电流。揉捏电机带负载静止起步时电流能冲到额定值的好几倍如果一上来就给 100% 占空比过流保护必然触发。项目里普遍加软启动前 300~500ms 把占空比从 20% 线性抬到目标值既避免过流误动作也让用户感觉动作更柔和。3.3 用状态机编排揉捏、敲打、气囊动作按摩椅的“自动模式”是把多个动作按时间顺序拼接。写这种逻辑最容易掉进 if 嵌套的坑动作一多就变成面条代码。我会用一个枚举状态机每个状态负责一个动作和一个持续时长到点后先清理再切换。typedef enum { ST_IDLE, ST_HOME, ST_KNEAD, ST_TAP, ST_AIRBAG, ST_STRETCH } MachineState; MachineState g_state ST_IDLE; unsigned char g_sec 0; void mode_tick(void) { switch (g_state) { case ST_KNEAD: knead_motor(1, 60); // 揉捏正向 60% if (g_sec 30) { // 30 秒后切到敲打 motor_stop_all(); g_state ST_TAP; g_sec 0; } break; case ST_TAP: tap_motor(1, 50); // 敲打正向 50% if (g_sec 20) { // 20 秒后切到气囊 motor_stop_all(); g_state ST_AIRBAG; g_sec 0; } break; // 其余状态类似 } }关键点是g_sec只由定时器中断里的秒计数驱动mode_tick在 10ms 任务里周期调用不自己延时。这样即使某个任务卡顿模式切换的时间也不受影响。切换动作前先motor_stop_all()是为了避免两个电机同时启动造成电流叠加这在低成本电源方案上很容易触发过流。3.4 电流采样与电机堵转识别按摩椅机芯被织物卡住时电机会堵转如果程序不识别电机会持续输出大扭矩把机芯烧毁。常见做法是低侧采样电阻加 ADC。采样电阻放在 H 桥下桥臂和 GND 之间阻值越小损耗越低一般 5~20mΩ 配合运放放大单片机再读 ADC。// 每 10ms 采集一次电机电流连续 3 次超限判定堵转 void current_check(void) { unsigned int adc adc_read(3); unsigned long mv (unsigned long)adc * 3300 / 1023; unsigned int ma (unsigned int)(mv / 5); // 采样电阻 5mΩ放大 50 倍 if (ma 2500) { if (over_cnt 3) { motor_stop_all(); over_cnt 0; } } else { over_cnt 0; } }换算系数要在实际板子上用钳流表标定代码里的 3300 和 5 只是示意。连续三次超限才动作是为了滤掉软启动瞬间或气囊动作带来的电流尖峰。真正的堵转是持续的三到五次采样就能稳定识别单次超限直接停机气囊挤压引起的波动反而会频繁误杀。4. 按摩椅程序可靠性主循环、看门狗与断电记忆4.1 裸机主循环按时间片调度按摩椅不上操作系统但所有任务混在一个循环里写也不行。常见做法是把时间切成 1ms、10ms、100ms 三个基本节拍。1ms 级任务在定时器中断里处理10ms 级以上放主循环。中断里只做计时和标记具体业务全在任务函数里执行避免中断嵌套把电机 PWM 时序打乱。节拍任务执行位置1ms红外边沿计时、PWM 占空比更新定时器中断10ms按键扫描、电流采样、限位检查主循环100msLED 刷新、加热检测主循环每轮循环NEC 帧处理、模式切换主循环主循环不能停。我一般把等待按键这类阻塞逻辑全部改成状态查询循环里只调用各种_task()函数。这样看门狗能及时喂遥控器按任意时刻都能被处理。要注意的是取余判断调度在每个循环里都会执行如果某次任务执行时间抖动任务不会被严格均分更稳的方式是用计数器递减每个任务独立维护自己的下次执行时刻。4.2 看门狗的正确喂法看门狗的作用是发现主循环卡死并自动复位。不能在中断里喂狗因为中断照常执行能骗过看门狗主循环实际已经卡死。喂狗点要放在主循环几条关键调用链之后确保一旦某个调用死循环看门狗必然超时。void feed_wdt(void) { WDT_CONTR WDT_EN | WDT_CLR | WDT_PS; // STC15 使能并清除计时 } void main_loop(void) { while (1) { key_task(); // 可能阻塞的 IO 任务 motor_task(); process_nec_cmd(); feed_wdt(); // 放在最远端 } }STC 系列看门狗的溢出时间由预分频决定具体公式要看选型手册主频不同同一预分频的超时时间差好几倍。工程上把喂狗周期放在看门狗超时时间的一半以内最稳妥。按摩椅这种需要频繁启停电机的设备电机换向瞬间的干扰容易让程序跑飞喂狗周期不要和电机控制周期绑在一起要用独立逻辑判断。4.3 用 IAP/EEPROM 保存档位和断电记忆按摩椅关机再开机用户一般希望记忆上次的模式、速度和加热状态。STC15 没有独立 EEPROM只能把用户程序区的某个扇区划出来用 IAP 方式读写。写入前要先擦除整个扇区擦除后该块全为 0xFF。写数据时只有“1”能变成“0”所以不能直接修改某个字节必须先擦后写。IAP 地址保存内容默认0x0000上次模式0x000x0001速度档0x020x0002加热开关0x010x0010出厂校准标记0xAAvoid eeprom_write(unsigned int addr, unsigned char val) { IAP_CONTR 0x80; // 使能 IAP IAP_CMD 0x02; // 字节写 IAP_ADDRH (addr 8) 0xFF; IAP_ADDRL addr 0xFF; IAP_DATA val; IAP_TRIG 0x5A; // 触发序列时序苛刻 IAP_TRIG 0xA5; }IAP 操作必须配合EA0或把中断关闭否则写入过程中断到来会造成 IAP 时序被打断。擦写寿命按万次计算所以项目上严格控制写次数只有模式真正变化时才写关机时判断当前值是否与上次写入值不同不同才擦写空闲时绝对不去定时刷参数。4.4 固定串口通道与调试帧设计按摩椅装整机后看不到内部状态现场排查全靠预留串口。我会在固件里固定开一个 115200 的调试串口每 100ms 输出 8 字节定长帧帧头、状态机枚举、NEC 原始帧、电流高几位和限位状态。定长帧的好处是上位机和串口助手都能按偏移解析不用处理粘包。// 100ms 周期调用发送 8 字节调试帧 void debug_report(void) { unsigned char buf[8] {0xAA, 0x55, g_state, frame[0], frame[1], frame[2], frame[3], (unsigned char)(g_current_ma 4)}; uart_send(buf, 8); }生产测试时这个调试帧可以被测试治具直接用。控制板通上电治具通过 USB 转串口读取数据流只要帧同步头稳定出现、帧内状态是预置的待机态基本能确认主板电源、单片机最小系统和红外接收通道正常。整机状态下的异常也大多能靠这 8 个字节定位到是解码层还是动作层的问题。5. NEC 解码波形与现场按键失灵排查5.1 用逻辑分析仪看 NEC 波形再动代码按键没反应先别怀疑程序。逻辑分析仪单通道接红外接收头 OUT 和 GND采样率 2MHz触发方式选下降沿抓 50ms。波形里有 9ms 引导码说明遥控器芯片在工作没有就换电池或检查导电橡胶。引导码正常而数据段宽度明显不对时才去看解码阈值。试波时注意接收头供电电压的纹波。如果供电上叠加了电机换向产生的尖峰解码会在某几帧随机失败波形看起来就是忽好忽坏。给接收头电源并一个 100nF 加 47µF 电容是按摩椅主板上最便宜的稳定性投资。5.2 按键失灵与误触发的现场排查表现场故障分很多种我把最常见的几类列成了一张排查表。排查顺序永远是从接收端往动作端走不要先拆机器看电机。现象优先检查所有键无反应接收头供电、接收头输出电平、遥控器电池单个键无反应遥控器导电橡胶、该键码值解码有帧但动作错乱映射表、LSB 位序、地址校验偶发误触发阳光/灯光干扰、重复码处理动作错乱十有八九是位序问题。NEC 是 LSB 在前如果解码时按串口收字节的 MSB 习惯写入命令码会整体颠倒波形却完全正常。这类问题只能在调试固件里逐个按键打印原始帧直接和经验值对比才能暴露。5.3 调试固件打印原始 NEC 帧的 30 行工具按键正确但动作错乱最难查因为普通万用表量不出逻辑错误。我的做法是单独写一个测试固件只做一件事把解码后的 4 字节原始帧从串口打出来同时把全部电机引脚置低避免测试时板子突然动作造成安全事故。// 测试固件上电后不停打印 NEC 原始帧电机输出全部禁止 void test_main(void) { uart_init(115200); motor_pins_low(); while (1) { if (frame_ok) { uart_printf(RAW %02X %02X %02X %02X\r\n, frame[0], frame[1], frame[2], frame[3]); frame_ok 0; } } }烧进板子后逐个按键按一遍把串口记录下来的字节填进正式固件的映射表按键与动作的对应关系就永远不会错。这颗测试固件在批量生产前的老化工序里同样好用板子通电接串口测试台自动发遥控码比对打印结果就能判断红外接收通道。老化工序发现的问题越早越好解决等整机装好再接串口拆一次外壳的成本早就超过一颗芯片的价格了。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价