Linux紅外驅(qū)動(dòng)重點(diǎn)解析
紅外遙控是我們經(jīng)常見到的一種無線收發(fā)設(shè)備,比如電視遙控,空調(diào)遙控,現(xiàn)在電視遙控有些慢慢變成了藍(lán)牙裝置。昨天是在知識星球里面看到有人提問,今天來解析一份網(wǎng)友寫的驅(qū)動(dòng)程序。
調(diào)試紅外需要注意幾個(gè)細(xì)節(jié)
1、我們發(fā)射的遙控器用肉眼是看不到的,需要拿相機(jī)來觀察。
2、紅外接收管和普通的二極管不同,如果用錯(cuò)物料也是不行的。
1.NEC協(xié)議無線傳輸數(shù)據(jù)原理
NEC協(xié)議的特征:
1、8位地址和8位指令長度;
2、地址和命令兩次傳輸;(確??煽啃裕?nbsp;
3、PWM脈沖寬度調(diào)制,以發(fā)射紅外載波的占空比代表“0”和“1”;
4、載波頻率為38KHz
5、位時(shí)間為1.125ms和2.25ms
NEC碼位的定義:一個(gè)脈沖對應(yīng)560us的連續(xù)載波,一個(gè)邏輯1傳輸需要2.25ms(560us脈沖+1680us低電平),一個(gè)邏輯0的 傳輸需要1.125ms(560us脈沖+560us低電平)。
而遙控接收頭在收到脈沖時(shí)為低電平,在沒有收到脈沖時(shí)為高電平,因此, 我們在接收頭端收到的信號為:邏輯1應(yīng)該是560us低+1680us高,邏輯0應(yīng)該是560us低+560us高。
如下圖:
硬件
2. Linux下的驅(qū)動(dòng)接收程序
參考原文:
https://blog.csdn.net/wllw7176/article/details/110506677
兩個(gè)驅(qū)動(dòng)文件
- 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 <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;
- };
- #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;
- 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->gpiogpio_nr = gpio;
- pdata->active_low = (flags & OF_GPIO_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;
- return 0;
- }
- 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;
- 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;
- ir_raw_event_handle(gpio_dev->rcdev);
- err_get_value:
- return IRQ_HANDLED;
- }
- 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;
- if (pdata->allowed_protos)
- rcdev->allowed_protocols = pdata->allowed_protos;
- else
- rcdev->allowed_protocols = RC_BIT_ALL;
- rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
- gpio_dev->rcdevrcdev = rcdev;
- gpio_dev->gpio_nr = pdata->gpio_nr;
- gpio_dev->active_low = pdata->active_low;
- 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;
- rc = rc_register_device(rcdev);
- if (rc < 0) {
- dev_err(&pdev->dev, "failed to register rc device\n");
- goto err_register_rc_device;
- }
- 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);
- 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);
- rc_unregister_device(gpio_dev->rcdev);
- gpio_free(gpio_dev->gpio_nr);
- kfree(gpio_dev);
- 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-nec-decoder.c
- /* ir-nec-decoder.c - handle NEC 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 NEC_NBITS 32
- #define NEC_UNIT 562500 /* ns */
- #define NEC_HEADER_PULSE (16 * NEC_UNIT)
- #define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */
- #define NEC_HEADER_SPACE (8 * NEC_UNIT)
- #define NEC_REPEAT_SPACE (4 * NEC_UNIT)
- #define NEC_BIT_PULSE (1 * NEC_UNIT)
- #define NEC_BIT_0_SPACE (1 * NEC_UNIT)
- #define NEC_BIT_1_SPACE (3 * NEC_UNIT)
- #define NEC_TRAILER_PULSE (1 * NEC_UNIT)
- #define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
- #define NECX_REPEAT_BITS 1
- enum nec_state {
- STATE_INACTIVE,
- STATE_HEADER_SPACE,
- STATE_BIT_PULSE,
- STATE_BIT_SPACE,
- STATE_TRAILER_PULSE,
- STATE_TRAILER_SPACE,
- };
- /**
- * ir_nec_decode() - Decode one NEC 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_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
- {
- struct nec_dec *data = &dev->raw->nec;
- u32 scancode;
- u8 address, not_address, command, not_command;
- bool send_32bits = false;
- if (!(dev->enabled_protocols & RC_BIT_NEC))
- return 0;
- if (!is_timing_event(ev)) {
- if (ev.reset)
- data->state = STATE_INACTIVE;
- return 0;
- }
- IR_dprintk(2, "NEC decode started at state %d (%uus %s)\n",
- data->state, TO_US(ev.duration), TO_STR(ev.pulse));
- switch (data->state) {
- case STATE_INACTIVE:
- if (!ev.pulse)
- break;
- if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT * 2)) {
- data->is_nec_x = false;
- data->necx_repeat = false;
- } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_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, NEC_HEADER_SPACE, NEC_UNIT)) {
- data->state = STATE_BIT_PULSE;
- return 0;
- } else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) {
- if (!dev->keypressed) {
- IR_dprintk(1, "Discarding last key repeat: event after key up\n");
- } else {
- rc_repeat(dev);
- IR_dprintk(1, "Repeat last key\n");
- data->state = STATE_TRAILER_PULSE;
- }
- return 0;
- }
- break;
- case STATE_BIT_PULSE:
- if (!ev.pulse)
- break;
- if (!eq_margin(ev.duration, NEC_BIT_PULSE, NEC_UNIT / 2))
- break;
- data->state = STATE_BIT_SPACE;
- return 0;
- case STATE_BIT_SPACE:
- if (ev.pulse)
- break;
- if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
- geq_margin(ev.duration,
- NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
- IR_dprintk(1, "Repeat last key\n");
- rc_repeat(dev);
- data->state = STATE_INACTIVE;
- return 0;
- } else if (data->count > NECX_REPEAT_BITS)
- data->necx_repeat = false;
- data->bits <<= 1;
- if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)
- data->bits |= 1;
- else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2))
- break;
- data->count++;
- if (data->count == NEC_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, NEC_TRAILER_PULSE, NEC_UNIT / 2))
- break;
- data->state = STATE_TRAILER_SPACE;
- return 0;
- case STATE_TRAILER_SPACE:
- if (ev.pulse)
- break;
- if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_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) {
- IR_dprintk(1, "NEC checksum error: received 0x%08x\n",
- data->bits);
- send_32bits = true;
- }
- if (send_32bits) {
- /* NEC transport, but modified protocol, used by at
- * least Apple and TiVo remotes */
- scancode = data->bits;
- IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode);
- } else if ((address ^ not_address) != 0xff) {
- /* Extended NEC */
- scancode = address << 16 |
- not_address << 8 |
- command;
- IR_dprintk(1, "NEC (Ext) scancode 0x%06x\n", scancode);
- } else {
- /* Normal NEC */
- scancode = address << 8 | command;
- IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
- }
- if (data->is_nec_x)
- data->necx_repeat = true;
- rc_keydown(dev, RC_TYPE_NEC, scancode, 0);
- data->state = STATE_INACTIVE;
- return 0;
- }
- IR_dprintk(1, "NEC 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,
- .decode = ir_nec_decode,
- };
- static int __init ir_nec_decode_init(void)
- {
- ir_raw_handler_register(&nec_handler);
- printk(KERN_INFO "IR NEC protocol handler initialized\n");
- return 0;
- }
- static void __exit ir_nec_decode_exit(void)
- {
- ir_raw_handler_unregister(&nec_handler);
- }
- module_init(ir_nec_decode_init);
- module_exit(ir_nec_decode_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Mauro Carvalho Chehab");
- MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
- MODULE_DESCRIPTION("NEC IR protocol decoder");
參考文章中的dts文件:
- gpio-ir-receiver {
- compatible = "gpio-ir-receiver";
- gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>; //連接紅外的中斷引腳
- active_low = <1>; //紅外接收器是否將信號取反,有些紅外接收器會(huì)將接收到的高低電平信號反向輸出,比如我使用的hx1838紅外接收器
- linux,rc-map-name = "rc-hx18380-carmp3"; //紅外scancode與實(shí)際input_evnent code映射表名稱,要在rc_register_device注冊,具體見gpio-ir-recv.c
- allowed_protos = <0x100>; /*NEC protocol*/ //保留,驅(qū)動(dòng)中并未使用
- };
另一個(gè)文件里面調(diào)用的
ir-nec-decoder.c
這個(gè)函數(shù)是Linux內(nèi)核中的紅外處理申請函數(shù)
這里主要是注冊一個(gè)解碼的結(jié)構(gòu)體
ir-nec-decoder.c
3.中斷處理程序解析
gpio-ir-recv.c
ir_raw_event_store_edge() 這個(gè)函數(shù)用來計(jì)算電平的持續(xù)時(shí)間。
ir_raw_event_handle() 用來處理這個(gè)電平表示什么含義。
驅(qū)動(dòng)程序里面,首先是判斷當(dāng)前GPIO電平,如果是低電平,就進(jìn)入紅外解析,如果不是,或者獲取失敗,就退出程序。
4.紅外數(shù)據(jù)處理程序解析
內(nèi)核專門開了一個(gè)線程來處理數(shù)據(jù)解析
rc-ir-raw.c
處理函數(shù)其實(shí)就是處理電平時(shí)間長短來決定數(shù)字信號
ir-nec-decoder.c
這里是判斷頭,這個(gè)時(shí)間和9ms進(jìn)行比較
9ms 從哪里來的,可以看看這里
ir-nec-decoder.c
拿到頭后,這個(gè)switch函數(shù)就繼續(xù)往下跑
ir-nec-decoder.c
然后就是判斷 1 和 0 的時(shí)候了
ir-nec-decoder.c
上面那個(gè)就是1,下面那個(gè)就是0。
4.然后數(shù)據(jù)怎么上報(bào)呢?
ir-nec-decoder.c
這里是在另一個(gè)模塊中注冊的映射
不同的紅外鍵值對應(yīng)不同的上報(bào)按鍵鍵值
rc-trekstor.c