红外遥控器-IR-linux驱动
作者:互联网
IR驱动使用指南
NEC红外协议说明
红外发射器将信号通过载波发送出来,红外接收器将接收接收到的红外信号进行电平编码,红外驱动根据这个解码后的电平信号进行解码操作,上图显示了NEC编码的时序规则
实验使用CarMp3-21键红外遥控按键ScanCode(NEC协议中的Command)码,NEC编码
gpio-ir-recv 接收驱动
gpio-ir-recv.c
将红外信号解码器产生的中断信号转变为ir-event,gpio中断函数根据电平判断为pulse/space,然后调用解码器进行解码
gpio-ir-recv.c
/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <media/rc-core.h>
#include <linux/platform_data/media/gpio-ir-recv.h>
#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
struct gpio_rc_dev {
struct rc_dev *rcdev;
int gpio_nr;
bool active_low;
struct timer_list flush_timer;
};
static struct rc_map_table hx1838_carmp3[] = {
{ 0x45, KEY_CHANNELDOWN }, { 0x46, KEY_CHANNEL }, { 0x47, KEY_CHANNELUP },
{ 0x44, KEY_PREVIOUS }, { 0x40, KEY_NEXT }, { 0x43, KEY_PLAYPAUSE },
{ 0x07, KEY_VOLUMEDOWN }, { 0x15, KEY_VOLUMEUP }, {0x09, KEY_EQUAL },
{ 0x16, KEY_O }, { 0x19, KEY_F1 }, { 0x0D, KEY_F2 },
{ 0x0C, KEY_1 }, { 0x18, KEY_2 }, { 0x5E, KEY_3 },
{ 0x08, KEY_4 }, { 0x1C, KEY_5 }, { 0x5A, KEY_6 },
{ 0x42, KEY_7 }, { 0x52, KEY_8 }, { 0x4A, KEY_9 },
};
static struct rc_map_list hx1838_carmp3_map = {
.map = {
.scan = hx1838_carmp3,
.size = ARRAY_SIZE(hx1838_carmp3),
.rc_type = RC_TYPE_NEC,
.name = RC_MAP_HX1838_CARMP3,
}
};
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static int gpio_ir_recv_get_devtree_pdata(struct device *dev,
struct gpio_ir_recv_platform_data *pdata)
{
struct device_node *np = dev->of_node;
enum of_gpio_flags flags;
int gpio;
int rval = 0;
u64 allowed_protos = 0;
gpio = of_get_gpio_flags(np, 0, &flags);
if (gpio < 0) {
if (gpio != -EPROBE_DEFER)
dev_err(dev, "Failed to get gpio flags (%d)\n", gpio);
return gpio;
}
pdata->gpio_nr = gpio;
//pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW);
pdata->active_low = of_property_read_bool(np, "active_low");
/* probe() takes care of map_name == NULL or allowed_protos == 0 */
pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL);
pdata->allowed_protos = 0;
//rval = of_property_read_u64(np, "allowed_protos", &allowed_protos);
//pdata->allowed_protos = allowed_protos;
return rval;
}
static const struct of_device_id gpio_ir_recv_of_match[] = {
{ .compatible = "gpio-ir-receiver", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);
#else /* !CONFIG_OF */
#define gpio_ir_recv_get_devtree_pdata(dev, pdata) (-ENOSYS)
#endif
static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
{
struct gpio_rc_dev *gpio_dev = dev_id;
int gval;
int rc = 0;
enum raw_event_type type = IR_SPACE;
gval = gpio_get_value(gpio_dev->gpio_nr);
if (gval < 0)
goto err_get_value;
//printk(KERN_WARNING "---->[%s:%d] gval:%d\n", __func__, __LINE__, gval);
/*
active_low =1
将中断脉冲低电平沿作为IR_PULSE处理,脉冲高电平沿作为IR_SPACE 处理
IR_SPACE 作为数据识别
*/
if (gpio_dev->active_low)
gval = !gval;
if (gval == 1)
type = IR_PULSE;
rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
if (rc < 0)
goto err_get_value;
mod_timer(&gpio_dev->flush_timer,
jiffies + nsecs_to_jiffies(gpio_dev->rcdev->timeout));
ir_raw_event_handle(gpio_dev->rcdev);
err_get_value:
return IRQ_HANDLED;
}
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);
}
static int gpio_ir_recv_probe(struct platform_device *pdev)
{
struct gpio_rc_dev *gpio_dev;
struct rc_dev *rcdev;
const struct gpio_ir_recv_platform_data *pdata =
pdev->dev.platform_data;
int rc;
if (pdev->dev.of_node) {
struct gpio_ir_recv_platform_data *dtpdata =
devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL);
if (!dtpdata)
return -ENOMEM;
rc = gpio_ir_recv_get_devtree_pdata(&pdev->dev, dtpdata);
if (rc)
return rc;
pdata = dtpdata;
}
if (!pdata)
return -EINVAL;
if (pdata->gpio_nr < 0)
return -EINVAL;
gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
if (!gpio_dev)
return -ENOMEM;
rcdev = rc_allocate_device();
if (!rcdev) {
rc = -ENOMEM;
goto err_allocate_device;
}
rcdev->priv = gpio_dev;
rcdev->driver_type = RC_DRIVER_IR_RAW;
rcdev->input_name = GPIO_IR_DEVICE_NAME;
rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
rcdev->input_id.bustype = BUS_HOST;
rcdev->input_id.vendor = 0x0001;
rcdev->input_id.product = 0x0001;
rcdev->input_id.version = 0x0100;
rcdev->dev.parent = &pdev->dev;
rcdev->driver_name = GPIO_IR_DRIVER_NAME;
rcdev->min_timeout = 0;
rcdev->timeout = IR_DEFAULT_TIMEOUT;
rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
if (pdata->allowed_protos)
rcdev->allowed_protocols = pdata->allowed_protos;
else
rcdev->allowed_protocols = RC_BIT_ALL;
rcdev->enabled_protocols = RC_BIT_NEC;
rcdev->map_name = pdata->map_name ? pdata->map_name : RC_MAP_EMPTY;
gpio_dev->rcdev = rcdev;
gpio_dev->gpio_nr = pdata->gpio_nr;
gpio_dev->active_low = pdata->active_low;
setup_timer(&gpio_dev->flush_timer, flush_timer,
(unsigned long)gpio_dev);
rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
if (rc < 0)
goto err_gpio_request;
rc = gpio_direction_input(pdata->gpio_nr);
if (rc < 0)
goto err_gpio_direction_input;
//Add by Wuliang
rc = rc_map_register(&hx1838_carmp3_map);
if (rc < 0)
goto err_request_irq;
//printk(KERN_WARNING "----->%s:%d rcdev->enabled_protocols:%llu\n", __func__, __LINE__, rcdev->enabled_protocols);
rc = rc_register_device(rcdev);
if (rc < 0) {
dev_err(&pdev->dev, "failed to register rc device\n");
goto err_register_rc_device;
}
//printk(KERN_WARNING "----->%s:%d rcdev->enabled_protocols:%llu\n", __func__, __LINE__, rcdev->enabled_protocols);
platform_set_drvdata(pdev, gpio_dev);
rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
gpio_ir_recv_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"gpio-ir-recv-irq", gpio_dev);
printk(KERN_WARNING "----->%s:%d gpio_nr:%d allowed_protocols:%llu enabled_protocols:%llu active_low:%d rcdev->map_name:%s\n",
__func__, __LINE__,pdata->gpio_nr, rcdev->allowed_protocols,rcdev->enabled_protocols, gpio_dev->active_low, rcdev->map_name);
if (rc < 0)
goto err_request_irq;
return 0;
err_request_irq:
rc_unregister_device(rcdev);
rcdev = NULL;
err_register_rc_device:
err_gpio_direction_input:
gpio_free(pdata->gpio_nr);
err_gpio_request:
rc_free_device(rcdev);
err_allocate_device:
kfree(gpio_dev);
return rc;
}
static int gpio_ir_recv_remove(struct platform_device *pdev)
{
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
del_timer_sync(&gpio_dev->flush_timer);
rc_unregister_device(gpio_dev->rcdev);
gpio_free(gpio_dev->gpio_nr);
kfree(gpio_dev);
//Add by Wuliang
rc_map_unregister(&hx1838_carmp3_map);
return 0;
}
#ifdef CONFIG_PM
static int gpio_ir_recv_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
if (device_may_wakeup(dev))
enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
else
disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
return 0;
}
static int gpio_ir_recv_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
if (device_may_wakeup(dev))
disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
else
enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
return 0;
}
static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
.suspend = gpio_ir_recv_suspend,
.resume = gpio_ir_recv_resume,
};
#endif
static struct platform_driver gpio_ir_recv_driver = {
.probe = gpio_ir_recv_probe,
.remove = gpio_ir_recv_remove,
.driver = {
.name = GPIO_IR_DRIVER_NAME,
.of_match_table = of_match_ptr(gpio_ir_recv_of_match),
#ifdef CONFIG_PM
.pm = &gpio_ir_recv_pm_ops,
#endif
},
};
module_platform_driver(gpio_ir_recv_driver);
MODULE_DESCRIPTION("GPIO IR Receiver driver");
MODULE_LICENSE("GPL v2");
解码驱动 ir-hx1838-decode.c
将gpio-ir-recv 驱动存储的ir-event进行事件解码,然后通过input子系统进行事件上报
ir-hx1838-decode.c
/* ir-nec-decoder.c - handle HX1838 IR Pulse/Space protocol
*
* Copyright (C) 2010 by Mauro Carvalho Chehab
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitrev.h>
#include <linux/module.h>
#include "rc-core-priv.h"
#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 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
* @dev: the struct rc_dev descriptor of the device
* @duration: the struct ir_raw_event descriptor of the pulse/space
*
* This function returns -EINVAL if the pulse violates the state machine
*/
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: //非激活状态,要等待9ms低电平+4.5ms高电平作为引导信号
if (!ev.pulse)
break;
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;
return 0;
case STATE_HEADER_SPACE:
if (ev.pulse)
break;
if (eq_margin(ev.duration, HX1838_HEADER_SPACE, HX1838_UNIT)) {
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);
//printk(KERN_WARNING "Repeat last key1 %dus %s\n", TO_US(ev.duration), TO_STR(ev.pulse));
//data->state = STATE_TRAILER_PULSE;
repeat_cnt = 0;
return 0;
}
return 0;
}
break;
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;
return 0;
case STATE_BIT_SPACE:
if (ev.pulse) {
//printk(KERN_WARNING "error ev.pulse in STATE_BIT_SPACE\n");
break;
}
if (data->necx_repeat && data->count == HX1838X_REPEAT_BITS &&
geq_margin(ev.duration,
HX1838_TRAILER_SPACE, HX1838_UNIT / 2)) {
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;
//printk(KERN_WARNING "data_bit_count:%d data_bits:0x%08x\n", data->count, data->bits);
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++;
if (data->count == HX1838_NBITS)
data->state = STATE_TRAILER_PULSE;
else
data->state = STATE_BIT_PULSE;
return 0;
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;
return 0;
case STATE_TRAILER_SPACE:
if (ev.pulse)
break;
if (!geq_margin(ev.duration, HX1838_TRAILER_SPACE, HX1838_UNIT / 2))
break;
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);
if ((command ^ not_command) != 0xff) {
printk(KERN_WARNING "HX1838 checksum error: received 0x%08x\n",
data->bits);
send_32bits = true;
}
if (send_32bits) {
/* HX1838 transport, but modified protocol, used by at
* least Apple and TiVo remotes */
scancode = data->bits;
//printk(KERN_WARNING "HX1838 (modified) scancode 0x%08x\n", scancode);
//printk(KERN_WARNING "HX1838 (Ext) scancode 0x%06x\n", scancode);
rc_type = RC_TYPE_NEC32;
} else if ((address ^ not_address) != 0xff) {
/* Extended HX1838 */
scancode = address << 16 |
not_address << 8 |
command;
//printk(KERN_WARNING "HX1838 (Ext) scancode 0x%06x\n", scancode);
rc_type = RC_TYPE_NECX;
} else {
/* Normal HX1838 */
scancode = address << 8 | command;
//printk(KERN_WARNING "HX1838 scancode 0x%04x\n", scancode);
//printk(KERN_WARNING "HX1838 scancode 0x%04x\n", scancode);
rc_type = RC_TYPE_NEC;
}
if (data->is_nec_x)
data->necx_repeat = true;
rc_keydown(dev, rc_type, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
}
//printk(KERN_WARNING "HX1838 decode failed at count %d state %d (%uus %s)\n",
// data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
//printk(KERN_WARNING "HX1838 decode failed at count %d state %d (%uus %s)\n",
// data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
return -EINVAL;
}
static struct ir_raw_handler nec_handler = {
.protocols = RC_BIT_NEC | RC_BIT_NECX | RC_BIT_NEC32,
.decode = ir_hx1838_decode,
};
static int __init ir_hx1838_decode_init(void)
{
ir_raw_handler_register(&nec_handler);
printk(KERN_INFO "IR HX1838 protocol handler initialized, allowed_protos:%llu\n", nec_handler.protocols);
return 0;
}
static void __exit ir_hx1838_decode_exit(void)
{
ir_raw_handler_unregister(&nec_handler);
}
module_init(ir_hx1838_decode_init);
module_exit(ir_hx1838_decode_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wuliang jxustwl@163.com");
MODULE_DESCRIPTION("HX1838 IR protocol decoder");
gpio-ir-recv.c 联合 ir-hx1838-decode.c使用
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,就会调用解码器驱动进行相关的解码操作。解码器的decode函数就是一个状态机的处理,结合NEC时序很容易看懂
dts添加相关节点
gpio-ir-receiver {
compatible = "gpio-ir-receiver";
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*/ //保留,驱动中并未使用
};
编译gpio-ir-recv.ko ir-hx1838-decode.ko evtest
更改Makefile相关的变量后就可以编译所有需要的驱动及测试app
测试驱动
测试步骤
insmod gpio-ir-recv.ko
insmod ir-hx1838-decoder.ko
./evtest /dev/input/by-path/platform-gpio-ir-receiver-event
一切正常会用如下输出
#./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
Event: time 3908.628166, -------------- Report Sync ------------
Event: time 3908.951966, type 4 (Misc), code 4 (ScanCode), value 45
Event: time 3908.951966, -------------- Report Sync ------------
Event: time 3909.275690, type 4 (Misc), code 4 (ScanCode), value 45
Event: time 3909.275690, -------------- Report Sync ------------
Event: time 3909.599475, type 4 (Misc), code 4 (ScanCode), value 45
Event: time 3909.599475, -------------- Report Sync ------------
Event: time 3909.923257, 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
按键按键会有如下输出,这个输出可以用于分析nec 红外协议
#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
资源git地址
标签:ir,IR,rc,dev,遥控器,pulse,linux,gpio,Event 来源: https://blog.csdn.net/wllw7176/article/details/110506677