T2-U开发板实现红外遥控接收与发送
红外遥控是利用近红外光进行数据传输的一种控制方式,广泛应用在工业控制、家电、照明等多个领域,很多产品在加入了智能控制功能后,依然还会保留红外控制方式。
这次我们就是使用Tuya T2-U开发板开发智能产品的基础上,加入红外接收、发送功能。不仅可以通过APP、语音等方式控制设备,还可以使用红外遥控器控制。
一、红外概况
近红外光波长0.76um1.5um,红外遥控收发器件波长一般为0.8um ~ 0.94um,具有传输效率高,成本低,电路实现简单,抗干扰强等特点。
红外遥控一般有发射和接收两部分组成,发射元件为红外发射管,接收一般采用一体化红外接收头,但发射载波频率与接收头固定频率需一致才能正确接收。
二、发射
1. 调制
红外遥控是以调制方式发射数据,将数据调制到固定的载波上发送,调制发送抗干扰能力更强,传送距离也更远。
红外发送首先要解决的就是调制问题,目前主流的调制方式有PPM和PWM。
- PPM:脉冲位置调制,调制脉冲宽度不变,用脉冲间隔来区分0和1。如下图所示,脉冲宽度不变都是560us,脉冲间隔改变。逻辑1总时间为2.25ms,逻辑0中时间长度为1.12ms。
图1:PPM调制
- PWM:脉冲宽度调制,脉冲间隔不变,调制脉冲宽度改变。如下图所示,脉冲间隔 为600us,脉冲宽度不同。逻辑1高电平时间为1.2ms,逻辑0高电平时间为0.6ms。
图2:PWM调制
调制载波频率一般在30KHz到60KHz之间,常用的载波有33K,36K,36.6K,38K,40K,56K等,其中38K使用最多。
常用占空比有1/3、1/2,1/3最多。
2. 红外传输协议
常用的红外传输协议有ITT协议、NEC协议、Nokia NRC协议、Sharp协议、Philips RC-5、RC-6 RECS-80协议、Sony SIRC协议等,其中最常见的为NEC协议。
常见NEC协议分析:
- 载波38KHz,逻辑1为2.25ms,脉冲时间560us;逻辑0为1.12ms,脉冲时间560us
图3:NEC逻辑’0’与逻辑’1’
- 协议格式
图4:NEC红外载波发送协议
(1) 首先发送9ms的载波脉冲
(2) 然后发送4.5ms的低电平
(3) 接下来是8bit的地址码(低位在前)
(4) 然后是8bit的地址反码,用于检验地址码是否出错
(5) 接下来的是8bit的命令码(低位在前)
(6) 然后是8bit的命令反码,用于检验命令码是否出错。
- 重复码
图5:NEC重复发送载波协议
如果一直按着一个键,将以110ms为周期发送重复码,重复码由9ms载波、2.25ms低电平及560us载波组成。
图6:NEC重复码
3. 编码
虽然不同协议都对各自的协议格式做了不同定义,但总体而言还是有高低电平组成的一串数据。
对于红外发射,就是按照协议规定高电平时间内,在红外输出口输出固定频率载波;低电平则直接输出低。红外接收头接到载波时输出高电平,没有载波时输出低电平,完成数据解码。
图7:NEC解码后协议
三、接收
红外接收常采用一体化红外接收头,集红外接收、放大、滤波、比较器输出等功能,并输出MCU可识别的TTL信号的。常用的一体化红外接收头有SCR638、HS0038、VS1838等。
SCR638
HS0038
图8:一体化接收头
红外接收应用电路图:
图9:红外接收典型应用
四、T2-U开发板硬件连接
这次使用的是T2-U2开发板配套红外遥控功能板,实现设备红外接收与发送功能。
红外遥控功能板介绍:
https://developer.tuya.com/cn/docs/iot/tuya-sandwich-infrared-remote-control-board?id=K97o1wfxi7v9l
红外遥控功能板
将遥控接收 study
管脚连接到T2开发板GPIO8,连接3.3V电源。
如需要红外发射功能将CRTL管脚连接到T2开发板GPIO7,同时连接5V电源。
五、TuyaOS红外功能介绍
TuyaOS红外接收目前支持NEC码、红外学习时间码(可用于万能红外遥控器)、红外发送功能。
TuyaOS红外接收采用中断+硬件定时器方式,可使用所有支持外部中断的GPIO,无 GPIO 管脚限制,给产品设计带来很大的便利。
红外接收功能:
支持查询模式和中断模式,可应用与不同的场景;如查询模式目前多用于万能红外遥控器学习状态,中断模式多用于设备红外控制。
支持NEC解码,支持
高位之前/低位在前
,引导码最大误差率
,高电平最大误差率
,低电平最大误差率
,重复码最大误差率
设置支持按下、松开通知,在中断模式下,可在回调中通过
s_frame_finish
获取,
is_frame_finish
为 1 表示这帧数据接收完成,处于松开状态,为 0 表示这帧数据可能还没有结束还在接收中。支持长按、短按通知,在中断模式下,可在回调函数中通过recv_data中断的repeat_cnt判断当前遥控器重复发送次数。
中断模式下回调函数通过tdl_ir_config(ir_handle, IR_CMD_RECV_CB_REGISTER, app_ir_recv_cb);
注册。
T2开发板上已经提供 tuyaos_ir_driver_demo_quickstart
工程可以快速实现红外接收与发送。红外接收目前支持 NEC 码、红外学习时间码(可用于万能红外遥控器)。tuyaos_ir_driver_demo_quickstart
可通过 wind_ide
或者 github
仓库先下载。
六、红外功能代码使用流程
初始流程为:硬件注册->查找设备->打开设备->操作设备(红外发送,介绍)
1. 硬件注册
选择驱动方式,支持下列三种驱动方式:
单硬件定时器驱动
双硬件定时器驱动
PWM 捕获驱动
捕获方式驱动不一定所有硬件都支持,其中除单硬件定时器驱动方式外,其他两种驱动方式都支持自发自收,这里选择的定时器必须是微妙级(us)的定时器。发送引脚应支持 38KHz 的 PWM 输出。
硬件注册示例代码如下:
Code: Select all
char dev_name[] = {"IR_DEVICE"};
IR_DRV_CFG_T ir_hw_cfg = {
.send_pin = GPIO_NUM_7,
.recv_pin = GPIO_NUM_8,
.send_timer = TIMER_NUM_0,
.recv_timer = TIMER_NUM_0,
.send_duty = 50,
};
TUYA_CALL_ERR_RETURN(tdd_ir_driver_register(IR_DEV_NAME, IR_DRV_SINGLE_TIMER, ir_hw_cfg));
2. 设备查找
根据设备名称,获取设备操作句柄。示例代码如下:
Code: Select all
IR_HANDLE_T ir_handle;
op_ret = tdl_ir_dev_find(dev_name, &ir_handle);
if (OPRT_OK != op_ret) {
PR_NOTICE("tdl_ir_dev_find error:%d", op_ret);
goto __EXIT;
}
3. 打开设备
这里可以通过配置,通过不同的协议方式开启设备,目前支持下列两种方式:
- 时间码类型
- NEC 协议类型
配置红外设备模式,支持以下三种模式配置:
- 仅发送
- 仅接收
- 发送接收同时支持
时间码类型(timecode),是将红外码值按照发送时的高低电平时间进行发送和接收的。示例代码如下:
Code: Select all
ir_cfg.ir_mode = IR_MODE_SEND_RECV; // 红外设备模式
ir_cfg.recv_queue_num = 3; // 最多接收缓存红外数据条数
ir_cfg.recv_buf_size = 1024; // 单条红外码值最大支持空间大小
ir_cfg.recv_timeout = 1000; // 接收超时时间
ir_cfg.prot_opt = IR_PROT_TIMECODE; // 时间码协议类型
op_ret = tdl_ir_dev_open(ir_handle, &ir_cfg);
if (OPRT_OK != op_ret) {
PR_ERR("open failed, %d", op_ret);
goto __EXIT;
}
NEC 协议初始化示例代码:
Code: Select all
ir_cfg.ir_mode = IR_MODE_SEND_RECV;
ir_cfg.recv_queue_num = 3;
ir_cfg.recv_buf_size = 1024;
ir_cfg.recv_timeout = 1000;
ir_cfg.prot_opt = IR_PROT_NEC; // NEC 协议类型
ir_cfg.prot_cfg.nec_cfg.is_nec_msb = 0,
ir_cfg.prot_cfg.nec_cfg.lead_err = 70; // 引导码最大误差率
ir_cfg.prot_cfg.nec_cfg.logics_err = 50; // 逻辑 0/1 高电平最大误差率
ir_cfg.prot_cfg.nec_cfg.logic0_err = 45; // 逻辑 0 低电平最大误差率
ir_cfg.prot_cfg.nec_cfg.logic1_err = 70; // 逻辑 1 低电平最大误差率
ir_cfg.prot_cfg.nec_cfg.repeat_err = 50; // 重复码最大误差率
op_ret = tdl_ir_dev_open(ir_handle, &ir_cfg);
if (OPRT_OK != op_ret) {
PR_ERR("open failed, %d", op_ret);
goto __EXIT;
}
4. 红外发送
这里需要注意的是,红外发送采用的是阻塞的方式,因为发送时使用数据是发送传入的数据的空间,所以必须等红外码值发送完成后才会执行后面的程序。
这里的发送是和 tdl_ir_dev_open
时设备的协议有关的, ir_cfg.prot_opt = IR_PROT_TIMECODE
时红外发送示例如下:
Code: Select all
UINT32_T data[] = {560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1690, 1690, 1690};
ir_send_data.timecode.data = data;
ir_send_data.timecode.len = strlen(data);
op_ret = tdl_ir_dev_send(ir_handle, 38000, ir_send_data, 1);
if (OPRT_OK != op_ret) {
PR_ERR("ir send error, %d", op_ret);
}
PR_DEBUG("ir send start");
ir_cfg.prot_opt = IR_PROT_NEC
时红外发送示例如下:
Code: Select all
ir_send_data.nec_data.addr = 0x807F;
ir_send_data.nec_data.cmd = 0x1DE2;
ir_send_data.nec_data.repeat_cnt = 1;
op_ret = tdl_ir_dev_send(ir_handle, 38000, ir_send_data, 1);
if (OPRT_OK != op_ret) {
PR_ERR("ir send error, %d", op_ret);
}
PR_DEBUG("ir send start");
5. 红外接收
红外接收默认也是阻塞的方式,但也支持通过回调的方式进行操作。如果选择的是 IR_PROT_TIMECODE
协议方式,那接收的数据将直接是解码后的数据。红外接收示例如下:
Code: Select all
op_ret = tdl_ir_dev_recv(ir_handle, &ir_recv_data, 3000);
if (OPRT_OK == op_ret) {
#if USE_NEC_DEMO
PR_DEBUG("demo addr:%04x, cmd:%04x, cnt:%d", ir_recv_data->nec_data.addr, ir_recv_data->nec_data.cmd, ir_recv_data->nec_data.repeat_cnt);
#else
PR_DEBUG("demo addr:%p, len:%d", ir_recv_data, ir_recv_data->timecode.len);
for (i=0; i<ir_recv_data->timecode.len; i++) {
PR_DEBUG_RAW("%d ", ir_recv_data->timecode.data[i]);
}
PR_DEBUG_RAW("\r\n");
#endif
tdl_ir_dev_recv_release(ir_handle, ir_recv_data);
}
6. 接收回调注册
在 硬件注册->查找设备->打开设备 之后就可以注册接收回调后,红外接收到数据会立马通过注册的回调函数通知应用,无法再使用 tdl_ir_dev_recv()
获取数据。
在接收到有效数据后回调函数会传出两个参数 is_frame_finish
和 recv_data
,is_frame_finish
为 1 表示这帧数据接收完成,为 0 表示这帧数据可能还没有结束还在接收中;recv_data
表示接收的数据。回调函数类型如下:
Code: Select all
typedef void (*IR_APP_RECV_CB)(UINT8_T is_frame_finish, IR_DATA_U *recv_data);
这里的数据类型也是根据您初始化的时候选择的协议格式进行配置。比如:在初始化时您配置红外协议为 NEC 编码格式的话,那么红外接收到数据后会将接收到的红外码转换成 NEC 格式之后再通过回调函数将数据传出。
使用示例如下:
Code: Select all
void app_ir_recv_cb(UINT8_T is_frame_finish, IR_DATA_U *recv_data)
{
TAL_PR_NOTICE("is_finish: %d, addr: 0x%04x, cmd: 0x%04x, repeat: %d", \
is_frame_finish, recv_data->nec_data.addr, recv_data->nec_data.cmd, recv_data->nec_data.repeat_cnt);
}
tdl_ir_config(ir_handle, IR_CMD_RECV_CB_REGISTER, app_ir_recv_cb);
日志如下:
Code: Select all
[01-01 00:00:29 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 0
[01-01 00:00:29 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 1
[01-01 00:00:29 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 2
[01-01 00:00:29 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 3
[01-01 00:00:30 ty N][app_main.c:43] is_finish: 1, addr: 0x807f, cmd: 0x0cf3, repeat: 3
[01-01 00:00:31 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 0
[01-01 00:00:31 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 1
[01-01 00:00:31 ty N][app_main.c:43] is_finish: 1, addr: 0x807f, cmd: 0x0cf3, repeat: 1
[01-01 00:00:33 ty N][app_main.c:43] is_finish: 0, addr: 0x807f, cmd: 0x0cf3, repeat: 0
[01-01 00:00:33 ty N][app_main.c:43] is_finish: 1, addr: 0x807f, cmd: 0x0cf3, repeat: 0
在应用上可以通过 repeat_cnt
去判断红外按键是按下,长按或释放等动作。