当前位置: 首页 > news >正文

51单片机基础-红外遥控(NEC协议)

第十八章 红外遥控实验(NEC协议)

1. 导入

红外遥控是家电最常见的人机交互方式。常用接收头(如 VS1838B、HS0038、TSOP1738)将38kHz调制的载波解调为电平脉冲,单片机只需“测时间、看长短”即可解码。本章以最普及的 NEC 协议为例,采用外部中断 + 定时器测时,完成地址/命令码解码与按键响应。

目标:

  • 认识红外接收头与NEC时序;
  • 配置 INT0(P3.2)捕获下降沿;
  • 使用 Timer0 计时并按阈值解码0/1位;
  • 识别引导码、重复码、32位数据,并验证反码;
  • 用LED/串口显示解码结果,或触发功能。

2. 硬件设计

  • 器件:51单片机(STC89C52 等)、红外接收头(VS1838B/HS0038/TSOP1738)、LED若干(或串口)。
  • 连接(典型接法):
    • 接收头 OUTP3.2/INT0
    • 接收头 VCC → +5V
    • 接收头 GND → GND
    • 建议近 VCC/GND 处加 0.1µF 去耦电容。
  • 注意:接收头输出为解调后信号,空闲高,收到载波时输出低电平脉冲。

3. NEC 协议速览

  • 帧结构:
    • 引导码:9ms 低 + 4.5ms 高(两次下降沿间隔约 13.5ms)
    • 数据:32位(LSB先传)= 地址(8) + 地址反码(8) + 命令(8) + 命令反码(8)
    • 每位:0/1 皆为 560µs 低,随后空闲高为
      • “0”:约 560µs
      • “1”:约 1.69ms
    • 重复码(长按):9ms 低 + 2.25ms 高 + 560µs 低(相邻下降沿约 11.25ms),不再发送32位数据。
  • 实际解码技巧:
    • 仅用“相邻下降沿时间间隔”即可区分:引导(≈13.5ms)、重复(≈11.25ms)、位0(≈1.125ms)、位1(≈2.25ms)。

4. 解码思路

  • 配置 INT0 为下降沿触发。每次下降沿进入中断:

    • 读取 Timer0 计数并清零,得到“与上次下降沿的间隔”;
    • 首先判断是否为“引导码间隔”→ 进入接收状态,准备收32位;
    • 接着的每个间隔判断位宽:≈1.1ms 记为 0,≈2.25ms 记为 1(LSB先);
    • 收满32位后按反码校验,得到地址与命令;
    • 若识别到重复码,则认为是“上一个命令的长按重复”。
  • 计时基准:

    • 11.0592MHz 时,定时器每计数≈1.085µs。
    • 经验阈值(单位:计数tick):
      • 引导:1100014000(≈1214ms)
      • 重复: 950012500(≈1012.5ms)
      • 位0: 9001500(≈1.01.5ms)
      • 位1: 17002600(≈1.82.6ms)
    • 不同晶振/接收头可能略有差异,阈值可微调。

5. 完整示例代码(Keil C51,NEC解码 + 串口打印 + LED示例)

#include <reg52.h>
#include <intrins.h>/* 硬件定义 */
sbit LED = P1^0;      // 指示灯
// 红外接收头 OUT 接 P3.2(INT0),VCC=5V,GND=GND/* 串口(用于打印解码结果,可选) */
void uart_init(void) {TMOD |= 0x20;     // T1方式2TH1  = 0xFD;      // 9600bps @11.0592MHzTL1  = 0xFD;TR1  = 1;SCON = 0x50;      // 8N1,允许接收ES   = 0;         // 此处不使用串口中断EA   = 1;
}
void uart_send_byte(unsigned char ch) {SBUF = ch; while(!TI); TI = 0;
}
void uart_send_str(const char *s){ while(*s) uart_send_byte(*s++); }
void uart_send_hex8(unsigned char v){const char hx[]="0123456789ABCDEF";uart_send_byte(hx[(v>>4)&0xF]); uart_send_byte(hx[v&0xF]);
}/* 简单延时(演示用) */
void delay_ms(unsigned int ms) {unsigned int i,j;for(i=0;i<ms;i++)for(j=0;j<125;j++);
}/* -------- NEC 解码相关 -------- */
/* 阈值(单位:Timer0 tick,~1.085us每tick) */
#define TH_LEAD_MIN  11000u
#define TH_LEAD_MAX  14000u#define TH_REP_MIN    9500u
#define TH_REP_MAX   12500u#define TH_BIT0_MIN    900u
#define TH_BIT0_MAX   1500u
#define TH_BIT1_MIN   1700u
#define TH_BIT1_MAX   2600u#define TH_FRAME_GAP 20000u   // >20ms 视为无效/超时/* 状态机 */
typedef enum {IR_IDLE = 0,IR_RECV_BITS
} ir_state_t;volatile ir_state_t ir_state = IR_IDLE;
volatile unsigned char  ir_bit_index = 0;
volatile unsigned long  ir_shift = 0;   // 按LSB先方式装入
volatile bit            ir_new = 0;     // 获取到完整新码
volatile bit            ir_repeat = 0;  // 重复码标志
volatile unsigned char  ir_addr = 0, ir_addr_n = 0;
volatile unsigned char  ir_cmd  = 0, ir_cmd_n  = 0;
volatile unsigned char  last_cmd = 0, last_addr = 0;/* 定时器与外部中断初始化:T0计时,INT0测下降沿 */
void ir_hw_init(void) {// Timer0: 16位方式1,清零启动作自由计数TMOD = (TMOD & 0xF0) | 0x01;TH0 = 0; TL0 = 0;TR0 = 1;// 外部中断0:下降沿触发IT0 = 1;    // 1=下降沿触发EX0 = 1;    // 允许INT0中断EA  = 1;    // 开总中断
}/* INT0 中断服务:每次下降沿,读取间隔与状态机解码 */
void ext0_isr(void) interrupt 0 {unsigned int dur;// 读取本次距离上一次下降沿的tick数TR0 = 0;dur = ((unsigned int)TH0 << 8) | TL0;TH0 = 0; TL0 = 0;TR0 = 1;// 过长间隔,复位状态if (dur > TH_FRAME_GAP) {ir_state = IR_IDLE;ir_bit_index = 0;return;}// 1) 先判断是否引导码if (dur >= TH_LEAD_MIN && dur <= TH_LEAD_MAX) {ir_state = IR_RECV_BITS;ir_bit_index = 0;ir_shift = 0;return;}// 2) 判断是否重复码(长按)if (dur >= TH_REP_MIN && dur <= TH_REP_MAX) {ir_repeat = 1;ir_new = 1;      // 交给主循环处理(沿用上次命令)return;}// 3) 接收位数据if (ir_state == IR_RECV_BITS) {if (dur >= TH_BIT0_MIN && dur <= TH_BIT0_MAX) {// 位0:装入0// ir_shift |= (0UL << ir_bit_index); // 无需写ir_bit_index++;} else if (dur >= TH_BIT1_MIN && dur <= TH_BIT1_MAX) {// 位1:装入1ir_shift |= (1UL << ir_bit_index);ir_bit_index++;} else {// 无效脉宽,丢弃ir_state = IR_IDLE;ir_bit_index = 0;return;}// 收满32位,拆分与校验if (ir_bit_index >= 32) {unsigned char a, an, c, cn;a  =  (unsigned char)( ir_shift        & 0xFF);an =  (unsigned char)((ir_shift >> 8)  & 0xFF);c  =  (unsigned char)((ir_shift >> 16) & 0xFF);cn =  (unsigned char)((ir_shift >> 24) & 0xFF);// 反码校验(NEC:an=~a, cn=~c)if ((unsigned char)(a ^ an) == 0xFF && (unsigned char)(c ^ cn) == 0xFF) {ir_addr = a; ir_addr_n = an;ir_cmd  = c; ir_cmd_n  = cn;last_addr = a; last_cmd = c;ir_repeat = 0;ir_new = 1;}ir_state = IR_IDLE;ir_bit_index = 0;}}
}/* -------- 应用层示例 -------- */
void main(void) {LED = 1;              // 低有效LED请按实际调整uart_init();          // 打开串口查看解码结果(可不用)ir_hw_init();uart_send_str("NEC IR Decode Start\r\n");while (1) {if (ir_new) {ir_new = 0;if (ir_repeat) {// 重复码:认为长按上次键uart_send_str("REPEAT CMD: 0x"); uart_send_hex8(last_cmd);uart_send_str(" ADDR: 0x");       uart_send_hex8(last_addr);uart_send_str("\r\n");// 示例:长按同样执行上次命令动作// 这里简单闪烁LED以示响应LED = !LED;} else {// 新的完整32位码uart_send_str("ADDR="); uart_send_hex8(ir_addr);uart_send_str(" CMD="); uart_send_hex8(ir_cmd);uart_send_str("\r\n");// 示例:根据命令码执行// 不同遥控器命令码不同,请先观察打印再映射// 例如:若某键CMD=0x45,切换LEDif (ir_cmd == 0x45) {LED = !LED;}}}// 其他任务……}
}

说明:

  • 将接收头 OUT 接到 P3.2(INT0)。若需改口,请同步修改外部中断配置。
  • 阈值为经验值,若解码不稳,请轻微调整 TH_* 宏。
  • 大多数遥控器不同键的命令码不同,先用串口观察,再按需映射到功能。

6. 调试与验证

  • 步骤:
    • 确认接收头接线正确、5V供电,靠近板上避免环境光干扰;
    • 打开串口助手(9600-8N1),复位单片机;
    • 对准接收头按遥控器按键,应看到形如 “ADDR=xx CMD=yy” 的打印;
    • 记录常用键的 CMD 值,在代码里映射到相应功能(LED/蜂鸣器/电机等)。
  • 常见问题:
    • 无打印:检查 INT0 是否使能,是否设置下降沿;接收头OUT是否接到P3.2。
    • 乱码/不稳定:阈值过松或过紧;接收头附近电源滤波不足;强光干扰。
    • 只有重复码:遥控器按下过快或只识别到重复帧;靠近一些再试,或加大引导阈值容差。
    • 反码校验失败:非NEC遥控或时序判定误差;尝试微调阈值,尤其是位0/1边界。

7. 扩展建议

  • 用定时器中断驱动数码管,实时显示 CMD 十六进制;
  • 支持多协议(如 NEC/RC5/Sony),通过首段时序特征判定选择解码器;
  • 将命令映射为系统菜单控制(模式切换、数值增减);
  • 加入按键学习功能:长按本地键将当前CMD存入 EEPROM,下次上电仍有效。

通过本章,你已经掌握了红外遥控的核心解码技巧:用外部中断采边沿、用定时器测时间、用阈值分辨位型。结合此前的显示与执行部件,你可以快速构建完整的遥控控制系统。


http://www.dtcms.com/a/532770.html

相关文章:

  • 响应式网站开发设计网站怎么做推广和宣传语
  • 大气网站背景图江苏建筑网站建设
  • mingw64 动态库的制作与使用
  • webrtc代码走读(八)-QOS-FEC-flexfec rfc8627
  • Linux内核RDMA连接管理(CMA)驱动深度解析:高性能网络的基石
  • 网站建设网站自助建设wordpress迁移安装
  • [sam2图像分割] 提示编码器 | PositionEmbeddingRandom
  • 晋江网站建设联系电话迁安做网站中的cms润强
  • 【图像处理基石】图像匹配技术:从原理到实践,OpenCV实现与进阶方向
  • JavaScript性能优化:reduce方法的巧妙运用
  • Go语言数组和切片
  • 静安做网站公司wordpress 做社区
  • SVD分解在MIMO系统中的应用:从信道建模到信号恢复
  • chp03【组队学习】Post-training-of-LLMs
  • 网站建设大神级公司北京网站建设价位
  • HCIE云计算题超长解析
  • MobaXterm 全面使用指南:从入门到高效运维
  • 律师网站模板wordpress建站侵权
  • 昆明网站设计制作公司如何注册微信公众平台账号
  • 深入浅出 SSE:实现服务器向客户端的单向实时通信
  • Datawhale AI夏令营--构建一个面向应急管理领域的智能问答系统task2
  • 论gRPC:基于 TCP/IP 的通用网络模式,以及基于 Unix Domain Sockets (UDS) 的同机进程间通信 (IPC) 模式
  • C语言习题~day27
  • AI之智能体agent与dify的搭建
  • 哪个网站可以接图纸做哪个行业建设网站的需求多
  • 做微信的网站叫什么米WordPress多重筛选功能
  • C++与Python:内存管理与指针的对比
  • 辽宁专业模板网站建设公司网站开发顶岗周记
  • X红书AI发布助手 - 自动化内容创作与发布工具
  • html5网站建设中模板网站改版 seo