IR驱动使用指南


NEC红外协议说明

红外发射器将信号通过载波发送出来,红外接收器将接收到的红外信号进行电平解码,红外驱动根据这个解码后的电平信号进行解码操作,上图显示了NEC编码的时序规则

  1. Start 表示起始信号,9ms_Low+4.5ms_Hight,表示一次红外传输的开始
  2. Data表示要传输的数据,大小固定为4Bytes,每个数据位由0/1编码构成
    560us_Low + 560us_High 代表数据0
    560us_Low + 1680us_Hhg 代表数据1

    Data的4Bytes数据构成:Address+Address反码+Command+Command反码
    Address 厂商定义,一般为0x00
    Command 厂商定义,红外遥控建对应的键位码
  3. Repeat 信号 9ms_Low+2.25ms_High,当按键一直按着不放的时候会产生这个重复按键信号
    Repeat 在Data后面

实验使用CarMp3-21键红外遥控按键ScanCode(NEC协议中的Command)码,NEC编码


gpio-ir-recv 接收驱动

gpio-ir-recv.c将红外信号解码器产生的中断信号转变为ir-event,gpio中断函数根据电平判断为pulse/space,然后调用解码器进行解码

gpio-ir-recv.c 主要代码解析

//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 对应时序图的Lowgval = 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过程,直到解码器完成所有数据的解析

解码驱动 ir-hx1838-decode.c

    将gpio-ir-recv 驱动存储的ir-event进行事件解码,然后通过input子系统进行事件上报

ir-hx1838-decode.c 主要代码解析

/*下面的宏定义根据上面的时序图的不同阶段识别标签,定义了不同的宏定义,方便后续使用,解释如下*/
#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;elsebreak;data->count = 0;data->state = STATE_HEADER_SPACE; //1 STATE_INACTIVE->STATE_HEADER_SPACEreturn 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_PULSEdata->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_SPACEreturn 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_PULSEelsedata->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_SPACEreturn 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,
};

疑问:gpio-ir-recv.c 如何调用 ir-hx1838-decode.c中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,就会调用解码器驱动进行相关的解码操作。

红外接收器相对应dts添加相关节点

gpio-ir-receiver {compatible = "gpio-ir-receiver"; // 设置为gpio-ir-receiver 中的compatiblegpios = <&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.callowed_protos = <0x100>; /*NEC protocol*/ //保留,驱动中并未使用
};

编译gpio-ir-recv.ko ir-hx1838-decode.ko evtest

更改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

开发红外驱动之前可以通过 lirc_dev.c ir-lirc-code.c,结合lirc库来分析红外信号是否符合相关的协议说明

[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

结合lirc-0.9.0中mode2 分析红外信号解码过程

insmod lirc_dev.ko
insmod ir-lirc-codec.ko
insmod gpio-ir-recv.ko
mode2 -m -d /dev/lirc0按键按键会有如下输出,这个输出可以用于分析红外协议
#mode2 -m -d /dev/lirc0167772159118     4402      672      459      688      445678      467      661      460      670      464674      463      668      458      671      462669     1562      678     1563      675     1586669     1544      674     1564      699     1539674     1564      702     1545      693     1540698      461      669     1537      701      460666      467      666      466      694     1516699      457      673      461      665     1546693      465      668     1538      700     1539693     1544      695      466      664     1546696    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/lirc0pulse 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     0pulse 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    1pulse 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 536pulse 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 =108mspulse 8995 space 2268
pulse 572  space 96060pulse 9019 space 2219
pulse 572  space 96079pulse 9040 space 2201
pulse 599  space 96052

资源git地址

资源git地址.

红外遥控器-IR-linux驱动(基于百问网IMX6ULL_Pro平台)相关推荐

  1. 基于百问网IMX6ULL_PRO开发板移植LCD多点触摸驱动(GT911)

    引言 笔者这里使用的Linux内核是自己移植的4.1.15版本 添加驱动代码 进入drivers/input/touchscreen目录查找是否拥有gt911驱动 cd drivers/input/t ...

  2. 基于百问网IMX6ULL_PRO开发板的Uboot移植(Uboot-2017.03)

    文章目录 引言 Uboot下载 Uboot目录分析 目录分析 移植所需要关注的目录 首次编译下载Uboot 编译 下载 方法1 方法2 方法3 现象 移植Uboot 添加自己单板相关文件 添加修改单板 ...

  3. 单片机学习笔记1--资料下载、环境搭建(基于百问网STM32F103系列教程)

    第1篇 资料下载.环境搭建 第一章 百问网视频体系及学习路线 1.1课程视频变化 2011-2020:百问网录制了10年的Linux视频. 2021: 1.首次进入单片机领域,发布单片机课程! 2.重 ...

  4. linux 提取网卡驱动,韦东山-Linux下移植wifi网卡驱动(RTL8723) - 百问网嵌入式问答社区...

    硬件:韦老师的开发板IMX6ULL 内核版本:4.15(自己移植) 这次实验是在自己移植的内核上面加入RTL8723驱动 问题一:如何获取RTL8723驱动? 在韦老师给我们的程序中能够使用wifi, ...

  5. ARM架构与编程2--ARM架构(基于百问网ARM架构与编程教程视频)

    一.RISC和CISC 1.1 RISC 上一章介绍过,通过指针操作寄存器,可以选择操作内存,也可以选择直接操作外设.这样是因为在ARM中,对于内存和外设他们是位于同一块存储空间内的.CPU访问他们的 ...

  6. 单片机学习笔记9--常见的通信方式(基于百问网STM32F103系列教程)

    第九章 常见的通信方式 通信即数据传输,不同模块之间进行数据的发送或接收.所以通信方式也可以认为是数据传输方式.常用的通信方式有以下几种. 一.串行和并行通信 串行和并行简单的区别就是数据传输的线是一 ...

  7. Mastering Embedded Linux Programming 学习 (五)在百问网157开发板上,解决网络配置问题

    Mastering Embedded Linux Programming 学习 (五)在百问网157开发板上,解决网络配置问题 思考.参考 搜索发现,需要配置设备树,参考这个链接 修改设备树 找到百问 ...

  8. Mastering Embedded Linux Programming 学习 (二)在百问网157开发板上,编译构建u-boot

    Mastering Embedded Linux Programming 学习 (二)在百问网157开发板上,编译构建u-boot 一.下载u-boot源码 git clone https://git ...

  9. Mastering Embedded Linux Programming 学习 (三)在百问网157开发板上,编译构建linux内核

    Mastering Embedded Linux Programming 学习 (三)在百问网157开发板上,编译构建linux内核 一.下载内核源码 wget http://ftp.sjtu.edu ...

最新文章

  1. oracle procedures批量删除带索引条件数据很慢_见微知著,数据库应用设计优化浅谈...
  2. SAP MM 并非奇怪现象之MB5B报表查不到某一笔出库记录?
  3. C++ deque底层实现
  4. Python-OpenCV 处理图像(三):图像像素点操作
  5. 求平方根的算法 牛顿迭代法和二分法
  6. php5.2.3 zend3,windows 2008 R2 下 IIS7.5+PHP5.2.17+Mysql5.5.16+Zend3.3.3
  7. 如何利用ffmpeg提供的API函数进行多媒体文件的解封装
  8. [每日一题] OCP1z0-047 :2013-07-12 多表插入
  9. 选offer的5个维度
  10. AKM项目轶事之GBS同事转入GDC
  11. python安装scrapy教程_Python实用工具包Scrapy安装教程
  12. LintCode 842: Origami
  13. codesys的设备树重点详解
  14. 解决xshell中数字小键盘不能使用的问题
  15. BizTalk Server : 提高 BizTalk 编程能力的 8 点技巧和窍门
  16. Navicat注释乱码
  17. 虚函数与虚函数表剖析(动多态)
  18. NODE安装和使用说明,报错处理方法
  19. 2017年年终总结 韩俊强的博客
  20. RUA!ERROR!

热门文章

  1. WMI系列--关于WMI
  2. 什么是MyBatis?怎么操作MyBatis?
  3. CameraLink备忘录
  4. 计算机网络--万维网实验
  5. Vs2005中操作WORD
  6. mcnpf5输出结果_MCNP及用.ppt
  7. js replace使用方法
  8. MacPorts使用
  9. java人民币大小写转换_Java 实现人民币小写转大写[转载http://sunjun.blogchina.com/]
  10. LumaQQ2006的安装