千家信息网

Linux红外驱动是什么

发表于:2025-12-02 作者:千家信息网编辑
千家信息网最后更新 2025年12月02日,这篇文章主要讲解了"Linux红外驱动是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Linux红外驱动是什么"吧!红外遥控是我们经常见到的一种无
千家信息网最后更新 2025年12月02日Linux红外驱动是什么

这篇文章主要讲解了"Linux红外驱动是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Linux红外驱动是什么"吧!

红外遥控是我们经常见到的一种无线收发设备,比如电视遥控,空调遥控,现在电视遥控有些慢慢变成了蓝牙装置。昨天是在知识星球里面看到有人提问,今天来解析一份网友写的驱动程序。

调试红外需要注意几个细节

1、我们发射的遥控器用肉眼是看不到的,需要拿相机来观察。

2、红外接收管和普通的二极管不同,如果用错物料也是不行的。

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高。

如下图:

硬件

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   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #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   #include   #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 0xx\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 0xx\n", scancode);    } else if ((address ^ not_address) != 0xff) {     /* Extended NEC */     scancode = address     << 16 |         not_address <<  8 |         command;     IR_dprintk(1, "NEC (Ext) scancode 0xx\n", scancode);    } else {     /* Normal NEC */     scancode = address << 8 | command;     IR_dprintk(1, "NEC scancode 0xx\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

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

这里主要是注册一个解码的结构体

ir-nec-decoder.c

3.中断处理程序解析

gpio-ir-recv.c

ir_raw_event_store_edge() 这个函数用来计算电平的持续时间。

ir_raw_event_handle() 用来处理这个电平表示什么含义。

驱动程序里面,首先是判断当前GPIO电平,如果是低电平,就进入红外解析,如果不是,或者获取失败,就退出程序。

4.红外数据处理程序解析

内核专门开了一个线程来处理数据解析

rc-ir-raw.c

处理函数其实就是处理电平时间长短来决定数字信号

ir-nec-decoder.c

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

9ms 从哪里来的,可以看看这里

ir-nec-decoder.c

拿到头后,这个switch函数就继续往下跑

ir-nec-decoder.c

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

ir-nec-decoder.c

上面那个就是1,下面那个就是0。

4.然后数据怎么上报呢?

ir-nec-decoder.c

这里是在另一个模块中注册的映射

不同的红外键值对应不同的上报按键键值

rc-trekstor.c

感谢各位的阅读,以上就是"Linux红外驱动是什么"的内容了,经过本文的学习后,相信大家对Linux红外驱动是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

红外 电平 驱动 处理 程序 脉冲 遥控 函数 就是 信号 数据 时间 逻辑 传输 不同 接收器 文件 载波 学习 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 戴尔t140服务器RAID1 高中信息技术操作题数据库示范 服务器安全组宝塔 重庆交友软件开发机构 成都博软软件开发公司 徐汇区自主可控网络技术优势 网络安全竖着画的手抄报简单漂亮 三星手机服务器错误怎么办 我的世界ec服务器跑酷攻略第7关 网络技术和软件设计有什么区别 服务器主机安全性 贤生网络技术工作室 重庆系统软件开发排行 扬州软件开发费用是多少 数据库输入出错 数据库分离更新系统信息 新荣区现代化网络安全常见问题 mt管理器为什么连接服务器 网安小讲堂网络安全法 华为公司最新开发5g网络技术 西安测试软件开发 有什么好的游戏软件开发学校 电脑网络技术与安全管理论文 入侵网站拿服务器 数据库runstarts 软件开发技术行业状况 浪潮服务器虚拟化建设 计算机网络技术培训目标 数据库课程设计考试预约系统 互联网是一场科技革命
0