系统相关
首页 > 系统相关> > 红外遥控器-IR-linux驱动

红外遥控器-IR-linux驱动

作者:互联网

IR驱动使用指南


NEC红外协议说明

NEC红外编码

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

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

CarMp3红外遥控对应按键码


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地址

资源git地址.

标签:ir,IR,rc,dev,遥控器,pulse,linux,gpio,Event
来源: https://blog.csdn.net/wllw7176/article/details/110506677