系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
一、基本介绍
红外编码传输的本质是将已知信号进行数字编码、调制到红外光波段,再进行发送的信号传输方式。在接收端以同频进行解调,即可得到传输结果。常见的传输载波频率如38kHz。
被调制的已知信号可根据不同红外传输协议进行不同的定义和编码。如常见的NEC编码,其标准编码为4字节(32位),为1byte地址+1byte地址反码+1byte数据+1byte数据反码。
红外传输具有很多优势。首先,红外光为肉眼不可见光段,在使用时可以避免产生不必要的光束,不会影响人类正常生活。其本质与可见光LED相同,正常使用下也不会对人眼造成损伤。其次红外发光二极管制造简单,价格便宜,功耗极低。因此基于红外编码传输的遥控手段被广泛应用于许多消费电子产品中。
红外传输也具有劣势。由于太阳等因素,环境中本身存在一定红外光,若频率相近,会干扰红外遥控信号。且红外光穿透性差,几乎无穿墙能力。
红外传输的编码方式简单,易于实现“学习”功能。这同时也代表其安全性差,易被劫持。
参考来自:红外遥控原理及实现 [电子森林]
二、硬件连接
1.红外接收模块
本次使用的红外接收模块如下图所示。
前面银白色的是红外接收头,整个模块的引脚定义如下表所示:
左(-) | 中 | 右(S) |
---|---|---|
GND | VCC | 信号线 |
相同定义方式还适用于其他标有(S)与(-)的传感器模块。
2.与Arduino UNO连线
本红外模块需使用5V供电,连接至UNO板上对应位置的5V与GND即可。红外模块的信号线无特殊要求,连接至任一数字引脚即可,在本文中红外模块的信号线连接至2号管脚。
GND | VCC | 信号线 |
---|---|---|
GND | +5V | 2 |
三、软件调试
1.在ARDUINO IDE中安装IRremote库
如何安装库:
本文使用的是IRremote库,其包含了许多封装好的红外控制相关的功能函数,其包含了接收、发送、解码、编码等功能,对于AVR单片机、ESP系列、RP2040都有很好的支持。
开源库地址:https://github.com/Arduino-IRremote/Arduino-IRremote
函数文档地址:https://arduino-irremote.github.io/Arduino-IRremote/classIRrecv.html
Arduino库说明:https://www.arduino.cc/reference/en/libraries/irremote/
2.部分类与函数介绍
因为我本身也没有特别完整地了解这个IRremote函数库的全部,再加上本来我发文章就比较随意。这里就主要挑了接收过程中主要用到的类和函数,想看完整文档手册的点击上方函数文档连接即可。(讲道理我感觉这种开源的组件最重要的是要会用,也没必要全看下来)
2-1 IRrecv 类
首先是最重要的IRrecv
类,它主要用于接收红外的信号。
2-1-1 构建 IRrecv 类
定义 IRrecv 类的方式有三种:
IRrecv name();
//直接定义 说实话我也不太清楚怎么用
IRrecv name(IR_Recv_Pin);
//带着接收引脚定义 IR_Recv_Pin:IR模块的信号引脚
IRrecv name(IR_Recv_Pin,Feedback_LED_Pin);
//带着接收引脚和反馈灯 也不太清楚没怎么用过
常用的还是第二种,name就是这个类实例化的名字,自己任起即可。IR_Recv_Pin
是指IR模块的信号线所所接的引脚。
在本文中IR_Recv_Pin = 2
。
2-1-2 IRrecv::begin
这个函数是开始红外数据接收,其函数定义如下:
void IRrecv::begin
( uint8_t aReceivePin,
bool aEnableLEDFeedback = false,
uint8_t aFeedbackLEDPin = USE_DEFAULT_FEEDBACK_LED_PIN
)
//上面是参数
{
setReceivePin(aReceivePin);
#if !defined(NO_LED_FEEDBACK_CODE)
bool tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
if (aEnableLEDFeedback)
{
tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_RECEIVE;
}
setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
#else
(void) aEnableLEDFeedback;
(void) aFeedbackLEDPin;
#endif
// Set pin mode once
pinMode(irparams.IRReceivePin, INPUT);
start();
}
参数1:aReceivePin
是IR模块的信号引脚,和前文的IR_Recv_Pin
意思相同。
参数2:aEnableLEDFeedback
是否启用反馈灯,缺省值为false。
参数3:aFeedbackLEDPin
反馈灯引脚号,缺省值的USE_DEFAULT_FEEDBACK_LED_PIN 默认为 0引脚 。
这个函数主要就是定义一遍引脚和要不要启动反馈灯,最后开始数据接收。个人感觉这个函数主要是为了给前面不带引脚直接定义的IRrecv 类确定引脚和开始接收的。那个反馈灯我没有用过,
使用的话就是:
name.begin();
//或
name.begin(IR_Recv_Pin,false); //不使用反馈灯
//或
name.begin(IR_Recv_Pin,true,13); //使用反馈灯 接在13号引脚上
name就是前面实例化类的那个name
但这不是唯一的启动数据接收方式,如果在前面定义的时候就已经指定好了红外接收引脚,也可以用下面这个函数启动数据接收。
2-1-3 IRrecv::enableIRIn
这个函数是使能IR数据接收,定义如下:
void IRrecv::enableIRIn()
{
start();
}
与begin函数相比只保留了最后的start,所以如果之前确定好了使用引脚。就可以直接用这个函数也可以启动数据接收。
使用方式:
name.enableIRIn(); //启动红外接收
(小声bb:其实直接start就行但我不知道为什么好多例程里都用的enableIRIn这个函数,所以其实使用下面这么启动数据转换也是可以的)
name.start(); //启动红外接收
2-1-4 IRrecv::disableIRIn
这个函数是失能IR数据接收,与2-1-3函数相对应。通过该两者配合可以实现仅在特定条件下开启数据接收。
void IRrecv::disableIRIn()
{
stop();
}
和2-1-3类似,里面也就是个stop所以理论上直接stop或者用end函数(里面也只有一个stop)也可以实现停止接收的效果。
使用方式:
name.disableIRIn(); //中止红外接收
//或
name.stop(); //中止红外接收
//或
name.end(); //中止红外接收
2-1-5 IRrecv::decode
这个函数是进行红外数据的解码,同名函数有两种定义,一种不带参数,一种带一个指针参数。带参数的为较老版本的IR解码函数。(在最新版本IRremote库中,作者已经强烈不推荐使用,但是目前网上很多教程仍用的旧版decode,我个人尝使用新旧版本效果上没有什么明显的区别)
定义如下:(整个函数太长了里面一堆各种解码的内容,有兴趣的自己去IRReceive.hpp头文件里面翻吧)
bool IRrecv::decode() //新版解码函数
bool IRrecv::decode (decode_results *aResults) //旧版解码函数
相同点是bool类型的返回值代表有没有接收到红外发射码并完成解码。true是完成!
不同之处在于旧版的decode需要我们先创建一个用于接收解码结果的decode_results
类型变量,然后在解码的时候作为参数传进去,解码结果就会在这个decode_results
类型变量里。
而新版函数方式则是,在IRrecv这个类(也就是前面定义为name的)中已定义了有一个IRData
类型的成员数据,用来储存解码结果,叫做decodedIRData。(通过name.decodedIRData
可以访问到这个数据成员)
新版decode会将接收到的数据直接转换完并存入decodedIRData
这个成员中。
总的来说就是,旧版用decode_results
类型储存解码结果,但需要在程序中提前定义,并通过指针传递参数。新版用IRData
类型储存解码结果,已包含在预先定义的IRrecv类中了,叫decodedIRData
,直接调用即可。
另外,decode_results
和IRdata
类型里的数据成员名还不太一样,后面会提到。
使用方式:
//旧版本decode (不推荐!)
decode_results resultname; //定义一个decode_results类型变量 最好是在全局里
name.decode(&resultname); //接收并解码 解码结果存入resultname
//新版本decode (推荐!)
name.decode() //接收并解码 解码结果存入name.decodedIRData
2-2 IRData类
这个类用于存储红外解码结果,可由解码器进行填充。数据成员可以被打印或其他函数调取。
我觉得的常用到的就是protocol
, address
,command
, numberOfBits
, decodedRawData
.
2-2-1 protocol
解码协议,这个通过decode函数会自动确认这部分红外协议的类型并且填充这个值。
通过name.decodedIRData.protocol
可以访问到该值。它是enum枚举类型。若通过打印函数得出枚举值后,可通过 编码枚举值 查看对应的编码类型。
2-2-2 address
解码出的地址值
通过name.decodedIRData.address
可以访问到该值
2-2-3 command
解码出的按键命令值
通过name.decodedIRData.command
可以访问到该值
2-2-4 numberOfBits
数据接收的位数
通过name.decodedIRData.numberOfBits
可以访问到该值
2-2-5 decodedRawData
32位的解码原始数据。
通过name.decodedIRData.decodedRawData
可以访问到该值
在NCE协议中按
8位按键值补码 8位按键值 8位地址补码 8位地址码 排列
为方便观察建议使用十六进制读取并显示
如IRCode: E718FF00
中
按键值就是0x18 地址值就是0x00
通过前文提到的address和command也可以分别读取到相应结果
2-3 decode_results类
这个类同样也用于存储红外解码结果,由旧版的decode命令进行填充。数据成员可以被打印或其他函数调取。
主要是,确实写不动了,里面常用的也就是value,这个值和 2-2 里面的decodedRawData
一样,只不过顺序相反了。在NEC红外协议下是,8位地址值 8位地址值补码 8位按键值 8位按键值补码。
其他的就看下面的这段吧,从库的头文件里摘出来的,讲了每一项的意义和与IRData的关系。
struct decode_results
{
decode_type_t decode_type; // deprecated, moved to decodedIRData.protocol ///< UNKNOWN, NEC, SONY, RC5, ...
uint16_t address;
uint32_t value; // deprecated, moved to decodedIRData.decodedRawData ///< Decoded value / command [max 32-bits]
uint8_t bits; // deprecated, moved to decodedIRData.numberOfBits ///< Number of bits in decoded value
uint16_t magnitude; // deprecated, moved to decodedIRData.extra ///< Used by MagiQuest [16-bits]
bool isRepeat; // deprecated, moved to decodedIRData.flags ///< True if repeat of value is detected
// next 3 values are copies of irparams values - see IRremoteint.h
uint16_t *rawbuf; // deprecated, moved to decodedIRData.rawDataPtr->rawbuf ///< Raw intervals in 50uS ticks
uint16_t rawlen; // deprecated, moved to decodedIRData.rawDataPtr->rawlen ///< Number of records in rawbuf
bool overflow; // deprecated, moved to decodedIRData.flags ///< true if IR raw code too long
};
顺便学个词 deprecate v. 不推荐 反对
3.按键编码获取程序
4.进一步开发
四、常见问题
1.接收到“FFFFFFFF”
在NEC协议的遥控器下,当你摁住一个键时,遥控器不会重复发送编码结果,而是会发送FFFFFFFF
,也就是“重复码”。因此,接收RAW格式时会发现是FFFFFFFF。
若采用IRremote库带的解码,程序会正确识别重复码,并解析成上一次接收到正常编码结果。
声明
该文章是自己学习摸索过程中得出的一些结论,内容不一定百分之百正确,描述语言也不一定专业,若有错误请大佬评论区指正,谢谢!