溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux紅外驅(qū)動是什么

發(fā)布時間:2021-06-18 16:30:17 來源:億速云 閱讀:460 作者:chen 欄目:系統(tǒng)運維

這篇文章主要講解了“Linux紅外驅(qū)動是什么”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Linux紅外驅(qū)動是什么”吧!

紅外遙控是我們經(jīng)常見到的一種無線收發(fā)設備,比如電視遙控,空調(diào)遙控,現(xiàn)在電視遙控有些慢慢變成了藍牙裝置。昨天是在知識星球里面看到有人提問,今天來解析一份網(wǎng)友寫的驅(qū)動程序。

調(diào)試紅外需要注意幾個細節(jié)

1、我們發(fā)射的遙控器用肉眼是看不到的,需要拿相機來觀察。

2、紅外接收管和普通的二極管不同,如果用錯物料也是不行的。

Linux紅外驅(qū)動是什么

1.NEC協(xié)議無線傳輸數(shù)據(jù)原理

NEC協(xié)議的特征: 

1、8位地址和8位指令長度;

2、地址和命令兩次傳輸;(確保可靠性)

3、PWM脈沖寬度調(diào)制,以發(fā)射紅外載波的占空比代表“0”和“1”;

4、載波頻率為38KHz

5、位時間為1.125ms和2.25ms

NEC碼位的定義:一個脈沖對應560us的連續(xù)載波,一個邏輯1傳輸需要2.25ms(560us脈沖+1680us低電平),一個邏輯0的 傳輸需要1.125ms(560us脈沖+560us低電平)。

而遙控接收頭在收到脈沖時為低電平,在沒有收到脈沖時為高電平,因此, 我們在接收頭端收到的信號為:邏輯1應該是560us低+1680us高,邏輯0應該是560us低+560us高。

如下圖:

Linux紅外驅(qū)動是什么

硬件

Linux紅外驅(qū)動是什么

2. Linux下的驅(qū)動接收程序

參考原文:

https://blog.csdn.net/wllw7176/article/details/110506677

兩個驅(qū)動文件

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*/ //保留,驅(qū)動中并未使用  };

另一個文件里面調(diào)用的

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

這個函數(shù)是Linux內(nèi)核中的紅外處理申請函數(shù)

Linux紅外驅(qū)動是什么

這里主要是注冊一個解碼的結(jié)構體

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

3.中斷處理程序解析

gpio-ir-recv.c

Linux紅外驅(qū)動是什么

ir_raw_event_store_edge() 這個函數(shù)用來計算電平的持續(xù)時間。

ir_raw_event_handle() 用來處理這個電平表示什么含義。

Linux紅外驅(qū)動是什么

驅(qū)動程序里面,首先是判斷當前GPIO電平,如果是低電平,就進入紅外解析,如果不是,或者獲取失敗,就退出程序。

4.紅外數(shù)據(jù)處理程序解析

內(nèi)核專門開了一個線程來處理數(shù)據(jù)解析

rc-ir-raw.c

Linux紅外驅(qū)動是什么

Linux紅外驅(qū)動是什么

處理函數(shù)其實就是處理電平時間長短來決定數(shù)字信號

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

這里是判斷頭,這個時間和9ms進行比較

Linux紅外驅(qū)動是什么

9ms 從哪里來的,可以看看這里

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

Linux紅外驅(qū)動是什么

拿到頭后,這個switch函數(shù)就繼續(xù)往下跑

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

Linux紅外驅(qū)動是什么

然后就是判斷 1 和 0  的時候了

Linux紅外驅(qū)動是什么

Linux紅外驅(qū)動是什么

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

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

4.然后數(shù)據(jù)怎么上報呢?

ir-nec-decoder.c

Linux紅外驅(qū)動是什么

這里是在另一個模塊中注冊的映射

不同的紅外鍵值對應不同的上報按鍵鍵值

rc-trekstor.c

Linux紅外驅(qū)動是什么

感謝各位的閱讀,以上就是“Linux紅外驅(qū)動是什么”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對Linux紅外驅(qū)動是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI