第二十一章 INFRARED_RECEPTION实验
本章,我们将介绍ESP32-S3对红外遥控器的信号解码。ESP32-S3板子上标配的红外接收头和一个小巧的红外遥控器。我们将利用管脚输入功能,解码开发板标配的红外遥控器的编码信号,并将编码后的键值在LCD屏中显示出来。 本章分为如下几个小节: 21.1 红外遥控介绍 21.2 硬件设计 21.3 软件设计 21.4 下载验证
21.1 红外遥控介绍 21.1.1 红外遥控技术介绍 红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。 由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方便。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。 21.1.2 红外器件特性 红外遥控的情景中,必定会有一个红外发射端和红外接收端。在本实验中,正点原子的红外遥控器作为红外发射端,红外接收端就是板载的红外接收器,实物图可以查看21.2.3小节原理图部分。要使两者通信成功,收/发红外波长与载波频率需一致,在这里波长就是940nm,载波频率就是38kHz。 红外发射管也是属于二极管类,红外发射电路通常使用三极管控制红外发射器的导通或者截至,在导通的时候,红外发射管会发射出红外光,反之,就不会发射出红外光。虽然我们用肉眼看不到红外光,但是我们借助手机摄像头就能看到红外光。但是红外接收管的特性是当接收到红外载波信号时,OUT引脚输出低电平;假如没有接收到红外载波信号时,OUT引脚输出高电平。 红外载波信号其实就是由一个个红外载波周期组成。在频率为38KHz下,红外载波周期约等于26.3us(1s / 38KHz ≈ 26.3us)。在一个红外载波发射周期里,发射红外光时间8.77us和不发射红外光17.53us,发射红外光的占空比一般为1/3。相对的,整个周期内不发射红外光,就是载波不发射周期。在红外遥控器内已经把载波和不载波信号处理好,我们需要做的就是识别遥控器按键发射出的信号,信号也是遵循某种协议的。 21.1.3 红外编解码协议介绍 红外遥控的编码方式目前广泛使用的是:PWM(脉冲宽度调制)的NEC协议和Philips PPM(脉冲位置调制)的RC-5协议的。开发板配套的遥控器使用的是NEC协议,其特征如下: 1,8 位地址和 8 位指令长度; 2,地址和命令 2 次传输(确保可靠性); 3,PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”; 4,载波频率为 38Khz; 5,位时间为 1.125ms 或 2.25ms; 在NEC协议中,如何为协议中的数据‘0’或者‘1’?这里分开红外接收器和红外发射器。 红外发射器:发送协议数据‘0’ = 发射载波信号560us + 不发射载波信号560us 发送协议数据‘1’ = 发射载波信号560us + 不发射载波信号1680us 红外发射器的位定义如下图所示。
图21.1.3.1 红外发射器位定义图 红外接收器:接收到协议数据‘0’ = 560us低电平 + 560us高电平 接收到协议数据‘1’ = 560us低电平 + 1680us高电平 红外接收器的位定义如下图所示。 图21.1.3.2 红外接收器位定义图 NEC遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。 我们遥控器的按键“ALIENTEK”按下时,从红外接收头端收到的波形如下图所示。 图21.1.3.3 按键“ALIENTEK”所对应的红外波形 从上图中可以看到,其地址码为0,控制码为0x47(正确解码后11100010)。可以看到在100ms之后,我们还收到了几个脉冲,这是NEC码规定的连发码(由9ms低电平+2.25ms高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码可以通过统计连发码的次数来标记按键按下的长短/次数。 21.1.4 IRremoteESP8266库介绍 基于对红外编解码协议介绍,我们是可以通过识别IO的状态进而解码。但是在使用Arduino开发时,更推荐使用别人写好的库,这里我们要使用的是“IRremoteESP8266”库。 IRremoteESP8266库支持接收并解码红外遥控信号,也支持编码并发送红外信号。该库支持多种不同的红外协议,包括NEC、Sony、RC5和RC6等。 接下来,我们就来看一下如何下载IRremoteESP8266库包。 该库可以在Arduino IDE中库管理搜索到,具体下载操作如下图所示。 图21.1.2.1 安装IRremoteESP8266库包步骤 下载不成功的话,请到“资料盘à6,软件资料à2,Arduino软件包”下,把“IRremoteESP8266-master.zip”压缩包在Arduino IDE上手动安装。 若要使用IRremoteESP8266库的函数,需要先定义一个IRrecv对象,具体操作如下: IRrecv irrecv(pin); 其中pin是连接到红外接收器输出引脚的数字引脚号。 此外,还需要定义一个用于存储接收红外按键值的变量,如下: decode_results results; 接下来,介绍本例程用到IRremoteESP8266库的函数。 第一个函数:enableIRIn函数,该函数功能是初始化红外解码,启动红外接收功能。 void IRrecv::enableIRIn(const bool pullup); 参数pullup为是否设置红外接收引脚上拉,默认可以不填该传参表示不用上拉。 无返回值。 第二个函数:decode函数,该函数的功能是解码从红外遥控器接收到的信号,并将结果存储在一个decode_results类型的对象中。 bool IRrecv::decode(decode_results *result, irparams_t *save, uint8_t max_skip, uint16_t noise_floor); 只需要关注参数results即可,results为一个decode_results类的对象。 返回值:解码成功返回1,失败返回0。 第三个函数:resume函数,该函数的功能是接收下一个编码。 void IRrecv::resume(void); 无返回值。 注意:resume不可忽略,应与decode函数配对使用,否则,只能读取一个红外按键键值,而不再接收新的按键值。 21.2 硬件设计 1. 例程功能 在LCD上显示一些实验信息之后,即进入等待红外触发,如果接收到正确的红外信号,则解码,并在LCD上显示键值和所代表的意义。LED闪烁用于提示程序正在运行。 2. 硬件资源 1)LED灯 LED-IO1 2)USART0 U0TXD-IO43 U0RXD-IO44 3)XL9555 IIC_SDA-IO41 IIC_SCL-IO42 4)SPILCD CS-IO21 SCK-IO12 SDA-IO11 DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连) PWR- IO1_3(XL9555) RST- IO1_2(XL9555) 5)红外接收头 REMOTE_IN-IO2 6)正点原子红外遥控器 3. 原理图 红外遥控接收头相关原理图,如下图所示。 图21.2.1 红外遥控接收头原理图 需要注意:REMOTE_IN和SD卡片选共用了IO2,所以它们不可以同时使用。 开发板配套的红外遥控器外观如图21.2.2所示: 图21.2.2 红外遥控器 开发板上接收红外遥控器信号的红外管外观如图21.2.3所示。使用时需要遥控器有红外管的一端对准开发板上的红外管才能正确收到信号。 图21.2.3 开发板上的红外接收管位置 21.3 软件设计 21.3.1 程序流程图 下面看看本实验的程序流程图: 图21.3.1 程序流程图 21.3.2 程序解析 1. REMOTE驱动代码这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。REMOTE驱动源码包括两个文件:remote.cpp和remote.h。 下面我们先解析remote.h的程序。对红外接收管的引脚做了相关定义。 #define RMT_RX_PIN 2 我们选择使用IO2作为红外接收管的引脚。 下面我们再解析remote.cpp的程序,前面提及到需要定义红外接收对象以及接收红外数据的变量,代码如下: IRrecv irrecv(RMT_RX_PIN); decode_results results; 接下来,看一下红外接收初始化函数remote_init,代码如下: /** * @param 无 * @retval 无 */ void remote_init(void) { irrecv.enableIRIn(); } remote_init函数主要调用enableIRIn函数初始化红外解码,启动红外接收。 接下来,介绍一下红外按键扫描函数remote_scan,代码如下: /** * @brief 处理红外按键 * @param 无 * @retval 0:没有任何按键按下 * 其他:按下的按键键值 */ uint8_t remote_scan(void) { uint8_t rmt_data = 0; /* 红外遥控按键键值 */ uint8_t remote_key = 0; /* 提取控制码 */ uint32_t raw_data = 0; /* 原始数据 */ if (irrecv.decode(&results)) /* 对红外数据进行解码 */ { raw_data = results.value; remote_key = (uint8_t)(raw_data >> 8); /* 提取控制码 */ if ((remote_key != 0xFF) && (remote_key != 0)) /* 证明已经获得有效键值 */ { rmt_data = remote_key; } irrecv.resume(); /* 接收下一个值 */ return rmt_data; } return 0; } 该函数主要就是调用decode函数获取红外数据,然后进行解析,提取控制码,最终控制码作为返回值进行返回。 2. 15_infrared_reception.ino代码在15_infrared_reception.ino里面编写如下代码: #include "led.h" #include "uart.h" #include "xl9555.h" #include "spilcd.h" #include "remote.h" uint8_t rmt_key; char *str = "0"; /** * @brief 当程序开始执行时,将调用setup()函数,通常用来初始化变量、函数等 * @param 无 * @retval 无 */ void setup() { led_init(); /* LED初始化 */ uart_init(0, 115200); /* 串口0初始化 */ xl9555_init(); /* IO扩展芯片初始化 */ lcd_init(); /* LCD初始化 */ remote_init(); /* 红外接收初始化 */ lcd_show_string(30, 50, 200, 16, LCD_FONT_16, "ESP32-S3", RED); lcd_show_string(30, 70, 200, 16, LCD_FONT_16, "REMOTE TEST", RED); lcd_show_string(30, 90, 200, 16, LCD_FONT_16, "ATOM@ALIENTEK", RED); lcd_show_string(30, 110, 200, 16, LCD_FONT_16, "KEYVAL:", RED); lcd_show_string(30, 130, 200, 16, LCD_FONT_16, "SYMBOL:", RED); } /** * @brief 循环函数,通常放程序的主体或者需要不断刷新的语句 * @param 无 * @retval 无 */ void loop() { rmt_key = remote_scan(); if (rmt_key) { lcd_show_num(86, 110, rmt_key, 3, LCD_FONT_16, BLUE); /* 显示键值 */ switch (rmt_key) /* 判断键值 */ { case 0: str = "ERROR"; break; case 162: str = "POWER"; break; case 98: str = "UP"; break; case 2: str = "PLAY"; break; case 226: str = "ALIENTEK"; break; case 194: str = "RIGHT"; break; case 34: str = "LEFT"; break; case 224: str = "VOL-"; break; case 168: str = "DOWN"; break; case 144: str = "VOL+"; break; case 104: str = "1"; break; case 152: str = "2"; break; case 176: str = "3"; break; case 48: str = "4"; break; case 24: str = "5"; break; case 122: str = "6"; break; case 16: str = "7"; break; case 56: str = "8"; break; case 90: str = "9"; break; case 66: str = "0"; break; case 82: str = "DELETE"; break; } lcd_fill(86, 130, 116 + 8 * 8, 170 + 16, WHITE); /* 清楚之前的显示 */ lcd_show_string(86, 130, 200, 16, LCD_FONT_16, str, BLUE); /* 显示标记 */ } LED_TOGGLE(); delay(500); } 在setup函数中,调用led_init函数完成LED初始化,uart_init函数完成串口初始化,调用xl9555_init函数完成XL9555初始化,调用lcd_init函数完成LCD屏初始化,调用remote_init函数完成红外解码初始化,然后LCD显示实验信息。 在loop函数中,间隔500毫秒调用remote_scan函数获取红外数据并进行解析,然后在LCD进行显示。 21.4 下载验证 下载代码后,可以看到LCD显示如下图所示。 图21.4.2 红外接收实验测试图 此时,我们通过遥控器按下不同的按键,则可以看到LCD上显示了不同按键的键值和对应遥控器上的符号,如下图所示。 图21.4.2 红外接收实验测试图
|