评论

收藏

[Linux] Linux红外驱动重点解析

服务系统 服务系统 发布于:2021-09-05 19:35 | 阅读数:482 | 评论:0

初次接触Linux内核,好仔细阅读顶层目录的readme文件,它是Linux内核的概述和编译命令说明。readme的说明侧重于X86等通用的平台,对于某些特殊的体系结构,可能有些特殊的说明。
DSC0000.jpg

红外遥控是我们经常见到的一种无线收发设备,比如电视遥控,空调遥控,现在电视遥控有些慢慢变成了蓝牙装置。昨天是在知识星球里面看到有人提问,今天来解析一份网友写的驱动程序。
调试红外需要注意几个细节
1、我们发射的遥控器用肉眼是看不到的,需要拿相机来观察。
2、红外接收管和普通的二极管不同,如果用错物料也是不行的。
DSC0001.gif

1.NEC协议无线传输数据原理
NEC协议的特征:
1、8位地址和8位指令长度;
2、地址和命令两次传输;(确保可靠性)
3、PWM脉冲宽度调制,以发射红外载波的占空比代表“0”和“1”;
4、载波频率为38KHz
5、位时间为1.125ms和2.25ms
NEC码位的定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的 传输需要1.125ms(560us脉冲+560us低电平)。
而遥控接收头在收到脉冲时为低电平,在没有收到脉冲时为高电平,因此, 我们在接收头端收到的信号为:逻辑1应该是560us低+1680us高,逻辑0应该是560us低+560us高。
如下图:
DSC0002.jpg

硬件
DSC0003.png

2. Linux下的驱动接收程序
参考原文:
https://blog.csdn.net/wllw7176/article/details/110506677
两个驱动文件

  • 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>;                     //红外接收器是否将信号取反,有些红外接收器会将接收到的高低电平信号反向输出,比如我使用的hx1838红外接收器  
  •     linux,rc-map-name = "rc-hx18380-carmp3"; //红外scancode与实际input_evnent code映射表名称,要在rc_register_device注册,具体见gpio-ir-recv.c  
  •     allowed_protos = <0x100>; /*NEC protocol*/ //保留,驱动中并未使用  
  • };
另一个文件里面调用的
ir-nec-decoder.c
DSC0004.png

这个函数是Linux内核中的红外处理申请函数
DSC0005.png

这里主要是注册一个解码的结构体
ir-nec-decoder.c
DSC0006.png

3.中断处理程序解析
gpio-ir-recv.c
DSC0007.png

ir_raw_event_store_edge() 这个函数用来计算电平的持续时间。
ir_raw_event_handle() 用来处理这个电平表示什么含义。
DSC0008.png

驱动程序里面,首先是判断当前GPIO电平,如果是低电平,就进入红外解析,如果不是,或者获取失败,就退出程序。
4.红外数据处理程序解析
内核专门开了一个线程来处理数据解析
rc-ir-raw.c
DSC0009.png

DSC00010.png

处理函数其实就是处理电平时间长短来决定数字信号
ir-nec-decoder.c
DSC00011.png

这里是判断头,这个时间和9ms进行比较
DSC00012.png

9ms 从哪里来的,可以看看这里
ir-nec-decoder.c
DSC00013.png

DSC00014.png

拿到头后,这个switch函数就继续往下跑
ir-nec-decoder.c
DSC00015.png

DSC00016.png

然后就是判断 1 和 0  的时候了
DSC00017.png

DSC00018.png

ir-nec-decoder.c
DSC00019.png

上面那个就是1,下面那个就是0。
4.然后数据怎么上报呢?
ir-nec-decoder.c
DSC00020.png

这里是在另一个模块中注册的映射
不同的红外键值对应不同的上报按键键值
rc-trekstor.c
DSC00021.png

原文链接:https://mp.weixin.qq.com/s/mwjY69oSF8WjnZ2B8a8Xeg

关注下面的标签,发现更多相似文章