赞
踩
红外发射器将信号通过载波发送出来,红外接收器将接收到的红外信号进行电平解码,红外驱动根据这个解码后的电平信号进行解码操作,上图显示了NEC编码的时序规则
gpio-ir-recv.c
将红外信号解码器产生的中断信号转变为ir-event,gpio中断函数根据电平判断为pulse/space,然后调用解码器进行解码
//1. 红外接收器接收到信号会产生中断,中断为双边缘触发类型,中断处理函数如下 static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { enum raw_event_type type = IR_SPACE; // type = IR_SPACE 对应上面时序图的High,IR_PULSE 对应时序图的Low gval = gpio_get_value(gpio_dev->gpio_nr);//获取红线接收器电平信号 /* active_low =1 实际的电平信号与上图协议规定的为取反关系 将中断脉冲低电平沿作为IR_PULSE处理,脉冲高电平沿作为IR_SPACE 处理 IR_SPACE 作为数据识别 */ if (gpio_dev->active_low) gval = !gval; if (gval == 1) type = IR_PULSE; /*存储获取到的电平信号,这个电平信号会在解码器中解析并处理*/ ir_raw_event_store_edge(gpio_dev->rcdev, type); /* 开启timer,检测电平的持续时间, flush_timer()为定时器超时函数 flush_timer()会存储获取到的电平的持续时间,并调用解码器进行解析处理 */ mod_timer(&gpio_dev->flush_timer, jiffies + nsecs_to_jiffies(gpio_dev->rcdev->timeout)); ir_raw_event_handle(gpio_dev->rcdev); //调用解码器解析处理 } static void flush_timer(unsigned long arg) { struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)arg; DEFINE_IR_RAW_EVENT(ev); ev.timeout = true; ev.duration = gpio_dev->rcdev->timeout; ir_raw_event_store(gpio_dev->rcdev, &ev); ir_raw_event_handle(gpio_dev->rcdev); } 2. 红外信号解码处理过程很简单 2.1 触发中断,中断处理函数获取电平信号,设置timer统计电平持续时间,调用解码器解析 2.2 timer统计电平持续时间,调用解码器解析 2.3 不断重复2.1~2.2过程,直到解码器完成所有数据的解析
将gpio-ir-recv 驱动存储的ir-event进行事件解码,然后通过input子系统进行事件上报
/*下面的宏定义根据上面的时序图的不同阶段识别标签,定义了不同的宏定义,方便后续使用,解释如下*/ #define HX1838_NBITS 32 #define HX1838_UNIT 562500 /* ns */ #define HX1838_HEADER_PULSE (16 * HX1838_UNIT) /*起始信号低电平时长NEC协议为9mS*/ #define HX1838X_HEADER_PULSE (8 * HX1838_UNIT) /*起始信号高电平时长NEC协议为4.5mS*/ #define HX1838_REPEAT_SPACE (4 * HX1838_UNIT) /*重复信号高电平时长NEC协议为2.25mS*/ #define HX1838_BIT_PULSE (1 * HX1838_UNIT) /*数据比特低电平时长NEC协议为0.56 mS*/ #define HX1838_BIT_0_SPACE (1 * HX1838_UNIT) /*数据比特0高电平时长NEC协议为0.56mS*/ #define HX1838_BIT_1_SPACE (3 * HX1838_UNIT) /*数据比特1高电平时长NEC协议为0.56mS*/ #define HX1838_TRAILER_PULSE (1 * HX1838_UNIT) #define HX1838_TRAILER_SPACE (65 * HX1838_UNIT) /*帧结束信号,根据实际测量实际测试hx1838接收到的这个space大概35~37ms */ #define HX1838X_REPEAT_BITS 1 /*enum 列举了红外解码系的几种状态,一次完整的红外信号解码过程会在以下几种状态间切换*/ enum nec_state { STATE_INACTIVE, STATE_HEADER_SPACE, STATE_BIT_PULSE, STATE_BIT_SPACE, STATE_TRAILER_PULSE, STATE_TRAILER_SPACE, }; /*解码器的核心函数,将不同的信号根据红外时序图进行解码*/ /** * ir_hx1838_decode() - Decode one HX1838 pulse or space */ static int ir_hx1838_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct nec_dec *data = &dev->raw->nec; u32 scancode; enum rc_type rc_type; u8 address, not_address, command, not_command; bool send_32bits = false; static int repeat_cnt = 0; if (!is_timing_event(ev)) { if (ev.reset) data->state = STATE_INACTIVE; //不是有效的起始信号不做处理直接返回 return 0; } /*解码状态机状态*/ switch (data->state) { case STATE_INACTIVE: //0 非激活状态,要等待9ms_Low+4.5ms_Hight作为引导信号 if (!ev.pulse) break; /*根据时序图,解析信号是否为Start*/ if (eq_margin(ev.duration, HX1838_HEADER_PULSE, HX1838_UNIT * 2)) { data->is_nec_x = false; data->necx_repeat = false; } else if (eq_margin(ev.duration, HX1838X_HEADER_PULSE, HX1838_UNIT / 2)) data->is_nec_x = true; else break; data->count = 0; data->state = STATE_HEADER_SPACE; //1 STATE_INACTIVE->STATE_HEADER_SPACE return 0; case STATE_HEADER_SPACE: if (ev.pulse) break; /*2. Start信号后,信号应为PULSE 根据PULSE信号持续时间长度,判断信号为 Repeat/Data */ if (eq_margin(ev.duration, HX1838_HEADER_SPACE, HX1838_UNIT)) { //Data信号,STATE_HEADER_SPACE-> STATE_BIT_PULSE data->state = STATE_BIT_PULSE; repeat_cnt = 0; return 0; } else if (eq_margin(ev.duration, HX1838_REPEAT_SPACE, HX1838_UNIT / 2)) { //这里我们加入计数数器当重复信号出现3次时我们才认为是真的重复信号 //这样更符合人的感觉,否则容易导致误触发信号 repeat_cnt++; if (3 == repeat_cnt) { rc_repeat(dev); repeat_cnt = 0; return 0; } return 0; } break; /* 3.PULSE 转态,下一状态为SPACE */ case STATE_BIT_PULSE: if (!ev.pulse) break; if (!eq_margin(ev.duration, HX1838_BIT_PULSE, HX1838_UNIT / 2)) break; data->state = STATE_BIT_SPACE; //STATE_BIT_PULSE->STATE_BIT_SPACE return 0; case STATE_BIT_SPACE: if (ev.pulse) { //printk(KERN_WARNING "error ev.pulse in STATE_BIT_SPACE\n"); break; } /*如果不是Repat信号,则表示数据0/1信号位*/ if (data->necx_repeat && data->count == HX1838X_REPEAT_BITS && geq_margin(ev.duration, HX1838_TRAILER_SPACE, HX1838_UNIT / 2)) { //Reeat 信号 printk(KERN_WARNING "Repeat last key2\n"); rc_repeat(dev); data->state = STATE_INACTIVE; return 0; } else if (data->count > HX1838X_REPEAT_BITS) data->necx_repeat = false; //如果为Data信号,则存储data的数据位 data->bits <<= 1; if (eq_margin(ev.duration, HX1838_BIT_1_SPACE, HX1838_UNIT / 2)) data->bits |= 1; else if (!eq_margin(ev.duration, HX1838_BIT_0_SPACE, HX1838_UNIT / 2)) break; data->count++; //获取到的数据位数, /* data->count=HX1838_NBITS(32) 代表本次Data的解析完成,后面将进行数据解码 */. if (data->count == HX1838_NBITS) data->state = STATE_TRAILER_PULSE; //STATE_BIT_SPACE-> STATE_TRAILER_PULSE else data->state = STATE_BIT_PULSE; return 0; /*4 STATE_TRAILER_PULSE 代表数据捕获完成*/ case STATE_TRAILER_PULSE: if (!ev.pulse) break; if (!eq_margin(ev.duration, HX1838_TRAILER_PULSE, HX1838_UNIT / 2)) break; data->state = STATE_TRAILER_SPACE; //STATE_TRAILER_PULSE->STATE_TRAILER_SPACE return 0; /*5 STATE_TRAILER_SPACE 代表数据捕获完成,将进行数据解码*/ case STATE_TRAILER_SPACE: if (ev.pulse) break; if (!geq_margin(ev.duration, HX1838_TRAILER_SPACE, HX1838_UNIT / 2)) break; /*根据时序图的红外协议Data部分进行数据解码*/ address = bitrev8((data->bits >> 24) & 0xff); not_address = bitrev8((data->bits >> 16) & 0xff); command = bitrev8((data->bits >> 8) & 0xff); not_command = bitrev8((data->bits >> 0) & 0xff); /*6. 这个是红外协议加入的校验机制,确保Data传输的准确性*/ if ((command ^ not_command) != 0xff) { printk(KERN_WARNING "HX1838 checksum error: received 0x%08x\n", data->bits); send_32bits = true; } /*7 针对处理的32 bit Data,进行更细化的处理*/ if (send_32bits) { /* HX1838 transport, but modified protocol, used by at * least Apple and TiVo remotes */ scancode = data->bits; rc_type = RC_TYPE_NEC32; } else if ((address ^ not_address) != 0xff) { /* Extended HX1838 */ scancode = address << 16 | not_address << 8 | command; rc_type = RC_TYPE_NECX; } else { /* Normal HX1838 */ scancode = address << 8 | command; rc_type = RC_TYPE_NEC; } if (data->is_nec_x) data->necx_repeat = true; /*8 将7解析处理的scancode,通过Linux系统的Input子系统上报到用户空间。 到这里一次完整的红外通讯过程得到处理,将状态设置为STATE_INACTIVE,准备进行下一次的数据处理 */ rc_keydown(dev, rc_type, scancode, 0); data->state = STATE_INACTIVE; } } /* 每一种红外解码驱动都有自己支持的红外编码类型 .protocols定义了该编码器能支持的协议类型,只有支持的红外协议才会进行解码 */ static struct ir_raw_handler nec_handler = { .protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32, .decode = ir_hx1838_decode, };
ir-hx1838-decode.c
static struct ir_raw_handler nec_handler = {
.protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32,
.decode = ir_hx1838_decode,
};
gpio-ir-recv.c
rcdev->enabled_protocols = RC_BIT_NEC;
[1]linux lirc驱动框架通过 rcdev->enabled_protocols 和 nec_handler.protocols,将红外信号接收驱动与解码器驱动关联起来
[2]只要两者有相同的bit位置1,就会调用解码器驱动进行相关的解码操作。
gpio-ir-receiver {
compatible = "gpio-ir-receiver"; // 设置为gpio-ir-receiver 中的compatible
gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>; //连接红外的中断引脚
active_low = <1>; //红外接收器是否将信号取反,有些红外接收器会将接收到的高低电平信号反向输出,比如我使用的hx1838红外接收器
linux,rc-map-name = "rc-hx18380-carmp3"; //红外scancode与实际input_evnent code映射表名称,要在rc_register_device注册,具体见gpio-ir-recv.c
allowed_protos = <0x100>; /*NEC protocol*/ //保留,驱动中并未使用
};
更改Makefile相关的变量后就可以编译所有需要的驱动及测试app,详细见代码中的Makefile
红外接收模块连接到正确的硬件后进行如下操作 insmod gpio-ir-recv.ko insmod ir-hx1838-decoder.ko ./evtest /dev/input/by-path/platform-gpio-ir-receiver-event #Path不同的内核可能会不同 一切正常会用如下输出 #./evtest /dev/input/by-path/platform-gpio-ir-receiver-event Input driver version is 1.0.1 Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100 Input device name: "gpio_ir_recv" Supported events: Event type 0 (Sync) Event type 1 (Key) Event code 2 (1) Event code 3 (2) Event code 4 (3) Event code 5 (4) Event code 6 (5) Event code 7 (6) Event code 8 (7) Event code 9 (8) Event code 10 (9) Event code 13 (Equal) Event code 24 (O) Event code 59 (F1) Event code 60 (F2) Event code 114 (VolumeDown) Event code 115 (VolumeUp) Event code 164 (PlayPause) Event code 363 (Channel) Event code 402 (ChannelUp) Event code 403 (ChannelDown) Event code 407 (Next) Event code 412 (Previous) Event type 4 (Misc) Event code 4 (ScanCode) Event type 20 (Repeat) Testing ... (interrupt to exit) Event: time 3873.099064, type 4 (Misc), code 4 (ScanCode), value 45 #ScanCode value 45 为红外遥控实际发送的按键编码 Event: time 3873.099064, type 1 (Key), code 403 (ChannelDown), value 1 Event: time 3873.099064, -------------- Report Sync ------------ Event: time 3873.358824, type 1 (Key), code 403 (ChannelDown), value 0 Event: time 3873.358824, -------------- Report Sync ------------ Event: time 3888.029036, type 4 (Misc), code 4 (ScanCode), value 45 Event: time 3888.029036, type 1 (Key), code 403 (ChannelDown), value 1 Event: time 3888.029036, -------------- Report Sync ------------ Event: time 3888.288804, type 1 (Key), code 403 (ChannelDown), value 0 Event: time 3888.288804, -------------- Report Sync ------------ Event: time 3891.288905, type 4 (Misc), code 4 (ScanCode), value 45 Event: time 3891.288905, type 1 (Key), code 403 (ChannelDown), value 1 #按键按下, ChannelDown 对应inputcode的字符解释 Event: time 3891.288905, -------------- Report Sync ------------ Event: time 3891.548819, type 1 (Key), code 403 (ChannelDown), value 0 #按键松开 Event: time 3891.548819, -------------- Report Sync ------------ Event: time 3896.208367, type 4 (Misc), code 4 (ScanCode), value 45 #按键按住不放重复事件 Event: time 3896.208367, -------------- Report Sync ------------ Event: time 3896.532041, type 4 (Misc), code 4 (ScanCode), value 45 Event: time 3905.948268, -------------- Report Sync ------------ Event: time 3907.030803, type 4 (Misc), code 4 (ScanCode), value 45 Event: time 3907.030803, type 1 (Key), code 403 (ChannelDown), value 1 Event: time 3907.030803, -------------- Report Sync ------------ Event: time 3907.288822, type 1 (Key), code 403 (ChannelDown), value 0 Event: time 3907.288822, -------------- Report Sync ------------ Event: time 3908.628166, type 4 (Misc), code 4 (ScanCode), value 45
[1]lirc_dev 会在/dev/ 下创建 /dev/lircx的设备
[2]ir-lirc-code 驱动利用lirc_dev导出的接口将接收到的ir-event进行pulse/space编码,并通过ioctl/read/write char驱动接口向应用层报告该编码后的信号
[3]应用层结合lirc库,可以分析处理脉冲信号
注意:ir-lirc驱动,相当于通用的红外接口,理论上可以处理任意红外遥控器信号,用户要利用lirc库的配置文件对lirc 进行相关的配置才能实际使lirc驱动接收到信号得到正确的处理
ir-lirc-codec 驱动与ir-decoder(比如ir-hx1838-decode) 区别是它只负责编码信号pulse/space 时长,具体解码操作都放到应用去做了,并且一般是结合lirc库使用
lirc库下载地址:
https://gitee.com/wllw7176/self_100ask_imx6ull/tree/master/self_dir/third_part/lirc-0.9.0
lirc库编译方法查看里面的README
insmod lirc_dev.ko insmod ir-lirc-codec.ko insmod gpio-ir-recv.ko mode2 -m -d /dev/lirc0 按键按键会有如下输出,这个输出可以用于分析红外协议 #mode2 -m -d /dev/lirc0 16777215 9118 4402 672 459 688 445 678 467 661 460 670 464 674 463 668 458 671 462 669 1562 678 1563 675 1586 669 1544 674 1564 699 1539 674 1564 702 1545 693 1540 698 461 669 1537 701 460 666 467 666 466 694 1516 699 457 673 461 665 1546 693 465 668 1538 700 1539 693 1544 695 466 664 1546 696 39721 9147 2114 688 对上面输出进行分解: LowTime HighHoldTime NEC协议[1/0] bit 9105 4439 起始信号 Address:0x00 625 500 0 631 506 0 623 508 0 606 528 0 654 487 0 621 501 0 632 508 0 627 504 0 ~Address:0x0xff 646 1595 1 624 1608 1 632 1612 1 626 1611 1 632 1609 1 630 1607 1 629 1610 1 627 1613 1 627 1608 1 Command(ScaneCode):0x45 653 485 0 626 1611 1 631 502 0 659 474 0 624 509 0 626 1611 1 628 505 0 630 508 0 618 1621 1 ~Command(~ScanCode): 616 516 0 623 1613 1 626 1610 1 621 1620 0 626 503 0 681 1579 1 610 39802 #结束信号 9089 2199 600 #重复按键信号 Address 对应NEC解码时序的的Address Command 对应NEC解码时序的的Command 更直观的方式对信号进行解析,使用mode2工具执行下列命令 pulse 对应NEC协议低电平时长,space 对应NEC协议高电平时长为数据识别时长 #mode2 -d /dev/lirc0 pulse 9035 space 4478 起始信号 NEC[0/1] pulse 593 space 537 0 pulse 596 space 540 0 pulse 571 space 557 0 pulse 567 space 565 0 pulse 592 space 537 0 pulse 595 space 537 0 pulse 591 space 540 0 pulse 569 space 565 0 pulse 585 space 1651 1 pulse 593 space 1662 1 pulse 574 space 1640 1 pulse 571 space 1733 1 pulse 529 space 1643 1 pulse 603 space 1632 1 pulse 597 space 1638 1 pulse 599 space 1638 1 pulse 597 space 1636 1 pulse 574 space 563 pulse 594 space 1642 pulse 595 space 538 pulse 590 space 537 pulse 571 space 564 pulse 589 space 1642 pulse 598 space 539 pulse 591 space 536 pulse 594 space 1667 pulse 570 space 541 pulse 592 space 1640 pulse 597 space 1647 pulse 584 space 1650 pulse 591 space 536 pulse 567 space 1690 pulse 572 space 39832 帧结束信号 pulse 9023 space 2222 重复信号 pulse 594 space 96052 周期补全信号,因为NEC协议一个周期大概110ms 96052+9023+2222+594 =108ms pulse 8995 space 2268 pulse 572 space 96060 pulse 9019 space 2219 pulse 572 space 96079 pulse 9040 space 2201 pulse 599 space 96052
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。