资讯详情

【物联网毕设基础】单片机:NEC 协议红外遥控器

文章目录

  • NEC 红外遥控器协议


NEC 红外遥控器协议

家电遥控器的通信距离往往不高,红外线的成本远低于其他无线设备,因此红外线在家电遥控器的应用中一直占有一席之地。遥控器的基带通信协议很多,大概有几十种,常用 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。最常用的是 NEC 因此,我们达成了协议 KST-51 配套开发板的遥控器直接使用 NEC 协议,我们这节课也以 NEC 解释协议标准。

NEC 协议的数据格式包括引导码、用户码、用户码(或用户码反码)、按键码和键码反码,最后一个停止位。停车位主要起隔离作用,一般不判断,编程时也不理会。总共有4个字节32位数据编码,如下图所示。第一个字节是用户代码,第二个字节也可能是用户代码,或用户代码的反向代码,具体由制造商决定,第三个字节是当前按钮的关键数据代码,第四个字节是关键数据代码的反向代码,可用于纠正数据错误。

在这里插入图片描述 这个 NEC 协议表味着数据的方式不像我们以前学到的那样 UART 如此直观,但每个数据本身也需要编码,然后编码载波调制。

  • 引导码:9 ms 的载波 4.5 ms 的空闲。
  • 比特值“0”:560 us 的载波 560 us 的空闲。
  • 560 us 的载波 1.68 ms 的空闲。

结合上图,我们可以看到前面的黑色段落是指导码 9 ms 载波,然后是引导码 4.5 ms 后面的数据码是许多载波和空闲交叉,它们的长度取决于它们想要传递的具体数据。HS0038B 红外到载波信号时,红外集成接收器将输出低电平,并在业余时间输出高电平。我们用逻辑分析仪抓住红外按钮HS0038B 解码后的图形,如下图所示。

从图中可以看出,首先是 9 ms 载波加 4.5 ms 至少闲开始,数据码在前面,在后面,数据码的第一个字节是8组 560 us 的载波加 560 us 空闲,也就是 0x第二个字节是8组 560 us的载波加 1.68 ms 可以看出,闲暇是 0xFF,这两个字节是用户码和用户码的反码。键码二进制是按键 0x0C,反码就是 0xF最后跟着一个 560 us 载波停止。键码二进制是按键 0x0C,反码就是 0xF最后跟着一个 560 us 载波停止位。对于我们的遥控器来说,不同的按钮是键码和键码反码的区别,用户码是一样的。这样,我们就可以通过单片机程序分析当前按键的键码。

当我们在前面学习中断时,我们学到了两个外部中断:51单片机外部中断0和外部中断1。我们的红外接收引脚收到了 P3.3 在引脚上,引脚的第二个功能是外部中断1。在寄存器TCON 中的 bit3 和 bit2 这两个,与外部中断1有关。其中 IE1 是外部中断标志位,当外部中断发生时,该标志位自动设置1和定时器中断 TF 类似地,中断后会自动清零,软件也可以清零。bit2 如果设置外部中断类型,则设置外部中断类型 bit2 只要0 P3.3 如果是低电平,可以触发中断 bit2 为1,那么 P3.3 只有从高电平到低电平的下降边缘才能触发中断。另外,外部中断1的使能位是 EX1。然后我们写下程序,用数字管显示遥控器的用户码和键码。

Infrared.c 文件主要用于检测红外通信。当外部中断发生时,进入外部中断。通过定时器1定时,首先判断引导码,然后逐个获得数据码的每个位置的高低电平时间,以了解每个位置是0还是1,最后解决数据码。虽然最终功能非常简单,但由于编码本身的复杂性,红外接收的中断程序在逻辑上更加复杂,所以我们首先提供中断函数的程序流程图,您可以比较流程图来理解程序代码

/***************************Infrared.c 文件程序源代码*****************************/ #include <reg52.h> sbit IR_INPUT = P3^3; //红外接收引脚 bit irflag = 0; ///红外接收标志,收到正确数据后置 1 unsigned char ircode[4]; ///红外代码接收缓冲区  /* 红外接收功能的初始化 */ void InitInfrared(){ 
             IR_INPUT = 1; ///确保红外接收引脚被释放     TMOD &= 0x0F; //清零 T1 的控制位     TMOD |= 0x10; //配置 T1 为模式 1     TR1 = 0; //停止 T1 计数     ET1 = 0; //禁止 T1 中断     IT1 
       
        = 
        1
        ; 
        //设置 INT1 为负边沿触发 EX1 
        = 
        1
        ; 
        //使能 INT1 中断 
        } 
        /* 获取当前高电平的持续时间 */ 
        unsigned 
        int 
        GetHighTime
        (
        )
        { 
          TH1 
        = 
        0
        ; 
        //清零 T1 计数初值 TL1 
        = 
        0
        ; TR1 
        = 
        1
        ; 
        //启动 T1 计数 
        while 
        (IR_INPUT
        )
        { 
          
        //红外输入引脚为 1 时循环检测等待,变为 0 时则结束本循环 
        //当 T1 计数值大于 0x4000,即高电平持续时间超过约 18ms 时, 
        //强制退出循环,是为了避免信号异常时,程序假死在这里。 
        if 
        (TH1 
        >= 
        0x40
        )
        { 
          
        break
        ; 
        } 
        } TR1 
        = 
        0
        ; 
        //停止 T1 计数 
        return 
        (TH1
        *
        256 
        + TL1
        )
        ; 
        //T1 计数值合成为 16bit 整型数,并返回该数 
        } 
        /* 获取当前低电平的持续时间 */ 
        unsigned 
        int 
        GetLowTime
        (
        )
        { 
          TH1 
        = 
        0
        ; 
        //清零 T1 计数初值 TL1 
        = 
        0
        ; TR1 
        = 
        1
        ; 
        //启动 T1 计数 
        while 
        (
        !IR_INPUT
        )
        { 
          
        //红外输入引脚为 0 时循环检测等待,变为 1 时则结束本循环 
        //当 T1 计数值大于 0x4000,即低电平持续时间超过约 18ms 时, 
        //强制退出循环,是为了避免信号异常时,程序假死在这里。 
        if 
        (TH1 
        >= 
        0x40
        )
        { 
          
        break
        ; 
        } 
        } TR1 
        = 
        0
        ; 
        //停止 T1 计数 
        return 
        (TH1
        *
        256 
        + TL1
        )
        ; 
        //T1 计数值合成为 16bit 整型数,并返回该数 
        } 
        /* INT1 中断服务函数,执行红外接收及解码 */ 
        void 
        EXINT1_ISR
        (
        ) interrupt 
        2
        { 
          
        unsigned 
        char i
        , j
        ; 
        unsigned 
        char byt
        ; 
        unsigned 
        int time
        ; 
        //接收并判定引导码的 9ms 低电平 time 
        = 
        GetLowTime
        (
        )
        ; 
        //时间判定范围为 8.5~9.5ms, 
        //超过此范围则说明为误码,直接退出 
        if 
        (
        (time
        <
        7833
        ) 
        || 
        (time
        >
        8755
        )
        )
        { 
          IE1 
        = 
        0
        ; 
        //退出前清零 INT1 中断标志 
        return
        ; 
        } 
        //接收并判定引导码的 4.5ms 高电平 time 
        = 
        GetHighTime
        (
        )
        ; 
        //时间判定范围为 4.0~5.0ms, 
        //超过此范围则说明为误码,直接退出 
        if 
        (
        (time
        <
        3686
        ) 
        || 
        (time
        >
        4608
        )
        )
        { 
          IE1 
        = 
        0
        ; 
        return
        ; 
        } 
        //接收并判定后续的 4 字节数据 
        for 
        (i
        =
        0
        ; i
        <
        4
        ; i
        ++
        )
        { 
          
        //循环接收 4 个字节 
        for 
        (j
        =
        0
        ; j
        <
        8
        ; j
        ++
        )
        { 
          
        //循环接收判定每字节的 8 个 bit 
        //接收判定每 bit 的 560us 低电平 time 
        = 
        GetLowTime
        (
        )
        ; 
        //时间判定范围为 340~780us, 
        //超过此范围则说明为误码,直接退出 
        if 
        (
        (time
        <
        313
        ) 
        || 
        (time
        >
        718
        )
        )
        { 
          IE1 
        = 
        0
        ; 
        return
        ; 
        } 
        //接收每 bit 高电平时间,判定该 bit 的值 time 
        = 
        GetHighTime
        (
        )
        ; 
        //时间判定范围为 340~780us, 
        //在此范围内说明该 bit 值为 0 
        if 
        (
        (time
        >
        313
        ) 
        && 
        (time
        <
        718
        )
        )
        { 
          byt 
        >>= 
        1
        ; 
        //因低位在先,所以数据右移,高位为 0 
        //时间判定范围为 1460~1900us, 
        //在此范围内说明该 bit 值为 1 
        }
        else 
        if 
        (
        (time
        >
        1345
        ) 
        && 
        (time
        <
        1751
        )
        )
        { 
          byt 
        >>= 
        1
        ; 
        //因低位在先,所以数据右移, byt 
        |= 
        0x80
        ; 
        //高位置 1 
        }
        else
        { 
          
        //不在上述范围内则说明为误码,直接退出 IE1 
        = 
        0
        ; 
        return
        ; 
        } 
        } ircode
        [i
        ] 
        = byt
        ; 
        //接收完一个字节后保存到缓冲区 
        } irflag 
        = 
        1
        ; 
        //接收完毕后设置标志 IE1 
        = 
        0
        ; 
        //退出前清零 INT1 中断标志 
        } 
       

大家在阅读这个程序时,会发现学长在获取高低电平时间的时候做了超时判断 if(TH1 >= 0x40),这个超时判断主要是为了应对输入信号异常(比如意外的干扰等)情况的,如果不做超时判断,当输入信号异常时,程序就有可能会一直等待一个无法到来的跳变沿,而造成程序假死。

另外补充一点,遥控器的单按按键和持续按住按键发出来的信号是不同的。我们先来对比一下两种按键方式的实测信号波形

红外单次按键时序图

红外持续按键时序图

单次按键的结果16-9和我们之前的图16-8是一样的,这个不需要再解释。而持续按键,首先会发出一个和单次按键一样的波形出来,经过大概 40 ms 后,会产生一个 9 ms 载波加 2.25 ms 空闲,再跟一个停止位的波形,这个叫做重复码,而后只要你还在按住按键,那么每隔约 108 ms 就会产生一个重复码。对于这个重复码我们的程序并没有对它单独解析,而是直接忽略掉了,这并不影响对正常按键数据的接收。如果你日后做程序时需要用到这个重复码,那么只需要再把对重复码的解析添加进来就可以了。


标签: itt连接器kpse08f24

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台