這篇文章主要講解了“Linux紅外驅(qū)動是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Linux紅外驅(qū)動是什么”吧!
創(chuàng)新互聯(lián)公司主營澤庫網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都App定制開發(fā),澤庫h5小程序設(shè)計搭建,澤庫網(wǎng)站營銷推廣歡迎澤庫等地區(qū)企業(yè)咨詢
紅外遙控是我們經(jīng)常見到的一種無線收發(fā)設(shè)備,比如電視遙控,空調(diào)遙控,現(xiàn)在電視遙控有些慢慢變成了藍牙裝置。昨天是在知識星球里面看到有人提問,今天來解析一份網(wǎng)友寫的驅(qū)動程序。
調(diào)試紅外需要注意幾個細節(jié)
1、我們發(fā)射的遙控器用肉眼是看不到的,需要拿相機來觀察。
2、紅外接收管和普通的二極管不同,如果用錯物料也是不行的。
1、8位地址和8位指令長度;
2、地址和命令兩次傳輸;(確保可靠性)
3、PWM脈沖寬度調(diào)制,以發(fā)射紅外載波的占空比代表“0”和“1”;
4、載波頻率為38KHz
5、位時間為1.125ms和2.25ms
NEC碼位的定義:一個脈沖對應(yīng)560us的連續(xù)載波,一個邏輯1傳輸需要2.25ms(560us脈沖+1680us低電平),一個邏輯0的 傳輸需要1.125ms(560us脈沖+560us低電平)。
而遙控接收頭在收到脈沖時為低電平,在沒有收到脈沖時為高電平,因此, 我們在接收頭端收到的信號為:邏輯1應(yīng)該是560us低+1680us高,邏輯0應(yīng)該是560us低+560us高。
如下圖:
硬件
參考原文:
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#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 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
這個函數(shù)是Linux內(nèi)核中的紅外處理申請函數(shù)
這里主要是注冊一個解碼的結(jié)構(gòu)體
ir-nec-decoder.c
gpio-ir-recv.c
ir_raw_event_store_edge() 這個函數(shù)用來計算電平的持續(xù)時間。
ir_raw_event_handle() 用來處理這個電平表示什么含義。
驅(qū)動程序里面,首先是判斷當前GPIO電平,如果是低電平,就進入紅外解析,如果不是,或者獲取失敗,就退出程序。
4.紅外數(shù)據(jù)處理程序解析
內(nèi)核專門開了一個線程來處理數(shù)據(jù)解析
rc-ir-raw.c
處理函數(shù)其實就是處理電平時間長短來決定數(shù)字信號
ir-nec-decoder.c
這里是判斷頭,這個時間和9ms進行比較
9ms 從哪里來的,可以看看這里
ir-nec-decoder.c
拿到頭后,這個switch函數(shù)就繼續(xù)往下跑
ir-nec-decoder.c
然后就是判斷 1 和 0 的時候了
ir-nec-decoder.c
上面那個就是1,下面那個就是0。
ir-nec-decoder.c
這里是在另一個模塊中注冊的映射
不同的紅外鍵值對應(yīng)不同的上報按鍵鍵值
rc-trekstor.c
感謝各位的閱讀,以上就是“Linux紅外驅(qū)動是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Linux紅外驅(qū)動是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!