原
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
最近在做一个智能家居的项目,其核心部分是使用红外遥控各种家电,包括电视,风扇,空调等等。在制作学习型红外遥控的过程中,遇到了许多问题,也颇有感悟,特此记录下来与大家分享。
首先先介绍一下红外线的基本收发。

发射端

红外线发送不同于一般的数据传输协议,在此与串口比较说明。
串口是最简单的数据传输协议,学过单片机的人都知道,单片机通过串口发送协议时,其发送的波形是一系列高低变化的电平(这不是废话吗?)。
一般串口一次发送只能发送1个字节,即8个位的数据。若排除校验位、其实为或是其他xx位,若是从最高位开始发,0101 0001B 这个数据的波形就是先低电平,后高电平,再低电平…. 一直到最低位发送完毕(或者是校验位)。以下是串口发送的波形图。
而红外线的发射有两种状态,一种是载波信号,另一种是空闲信号。什么是载波信号?按照我最直观的理解,载波信号就是一组连续的,高频率的方波。一般世面上的家电红外线发射都是使用38kHz频率的载波,这个还牵扯到红外线接收头的问题,在后面接收端会提到。与载波信号相对,空闲信号就是一段连续的高电平或低电平。注意,是空闲信号期间是不会发生波形变化的。
上面就是我在使用红外线发射时相关IO口输出的高低电平变化,是不是奇怪为社么有黑色的粗条。那就是载波信号,一系列连续的高低电平变化,只不过频率太高,电平跳变的线都挤在一起,成为了一根粗条。
而两根粗条之间的间隔就是空闲信号,有可能是高电平,也可能是低电平,一般没什么影响,只要其间不发生电平跳变就行。
不同的厂家有的红外线发送协议都不尽相同,但是都类似。例如NEC协议,
当某个位为1时,就先发送560us的载波信号,后发送1.68ms的空闲信号。当某个位为0时,就先发送560us的载波信号,后发送560us的空闲信号。
按照NEC协议,上面的波形图发送的数据就是 0000 0000B。
另外,NEC协议在正式数据发送前还有个引导码,先是9ms的载波,后是4.5ms的低电平。引导码,顾名思义,就是告诉接受设备我要开始发送数据了,你做好准备接受。
当然这只是一个特定的协议,你完全可以自己制定一个协议,不过还要在接收端上也要根据这个协议进行接收。

接收端

单片机的接收端是通过一个红外线接收头来实现的。这个红外线接收头很神奇,能够识别38kHz的载波信号与空闲信号。
当接收到的是载波信号时,其引脚就输出低电平。当接收到的是空闲信号时,期引脚就输出高电平。
这是与之前相对应的接收端波形图,可以看到,发送的载波经红外接收头输出成了低电平,空闲信号输出成了高电平。
那这就好办了,之前我们不是说过红外传输间有个协议吗,只要我在接收端根据这个协议进行接收就行了,继续拿NEC作为例子说明:
当NEC的引导码发送过来时,由于有载波信号,红外接收头引脚输出由高电平跳变为低电平。那么只要我们设置一个下降沿中断就可以随时地进行数据接收。而且,我们要检测接受到的载波信号和空闲信号的持续时间是否与发送的引导相同(都接近9ms和4.5ms),只有当通过了引导码的检验后才能进行数据接收,否则直接结束接收,避免收到错误的数据。
数据的接受也是一样,按照接收到高低电平的持续时间识别发送过来的是1还是0。
下面贴上用使用NEC协议作发送和接收测试时相应引脚的波形图,因为屏幕太小的缘故,只记录到了引导码+用户码。而NEC剩余的用户反码、按键码和按键反码都没记录到。
学习型红外的实现
上面阐述了红外收发的基本原理,然而若根据协议来制定接受协议与发送协议,那么世界上那么多个厂家,那么多种红外协议,难道要没种协议都写一个发送接收函数吗?这当然是不现实的,所以我们有了另一种思路。
既然红外线是以波形的形式发送出去的,只要两段波形相同,那么接受端才不会管你的程序是怎么写的,有没有按照协议发送呢,通通照收了再说。
所以,如果我们有了一种未知协议的遥控器发射设备,我只需要拿个一个红外线接收器将你发送的波形记录下来(解码),那不就学习到你的波形了吗?怎么记录呢,可以参考一下NEC协议接受端的操作。
基于NEC协议的接受函数主要是检测发送过来的波形符不符合NEC协议的标准。例如,引导码的载波信号是否为9ms,空闲是否为4.5ms,560us载波后的空闲是560us还是1.68ms。
换一种思路,假如我知不道究竟持续多久的载波+空闲才是正确的引导,但我能够确定你发送的信号是准确的。那么只要我将从你开始发送(进入中断后)的每一次的载波信号的时间和空闲信号的时间都保存在一个长度足够的数组里,那么当我想发送一段和你相同的红外编码时,只要根据这个数组里的时间发送载波信号和空闲信号即可,这样即使我知道你的协议,但我照样能发射出和你一样的波形。
然而,这个方法的缺点就是需要牺牲空间,因为要使用数组记录每一次高电平低电平的时间,发射端红外编码协议越复杂,那么储存一个这样的红外编码需要的空间就越多。
我测试过NEC协议和美的空调R51协议的波形,保存一个以NEC编码的红外波形需要用到71个16位的整型空间,共142个字节。而每存一个R51协议的命令要用到200个16位整型的空间,即400字节。要知道,像电视、风扇这种家电的红外编码还算简洁,但对于空调来说,每一个温度对应的各种操作(例如开机,升温、降温)的红外编码都不同,在不知道编码规律单纯记录波形的情况下,这种方式对空间的消耗是巨大的。
上图是我在测试美的空调R51协议编码发射端时完整记录的波形图。
有木有注意到,R51的波形图横坐标是以25ms为单位,之前NEC是以2.5ms为单位,所以空调的波形数据占那么大的空间就不足为怪了。
总结
学习型红外遥控的原理基本是这些了。上面我只是介绍了这个实现这个功能的大概思路而忽略了实现过程。对于stm32这款单片机,可以使用定时器PWM功能输出38kHz的载波信号;测量高低电平持续时间,可以使用systick的精确延时来实现等等。
附上我制作学习型红外时的调试流程图:
因为之前在51上做过NEC的红外遥控,所以直接使用了51板作为红外发射端。
最后贴上源代码地址,学习型红外的功能都封装好了,直接使用函数即可,不过要注意引脚的设置。
刚刚学会用git和github,越用越觉得爽,也希望大家有空也一起学学。有的地方写的不够好,希望大家多多指教。
学习红外时参考了这篇博客,也推荐给大家看看:NEC协议红外遥控器
之前已经写过了一个使用定时器普通计时功能来识别红外遥控数据的文章。本次是使用定时器输入捕获来实现,这种方法比起定时器普通计数来说要更加复杂一些,不过效果会更好。