Linux内核红外遥控子系统LIRCWord下载.docx
- 文档编号:20463457
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:14
- 大小:167.83KB
Linux内核红外遥控子系统LIRCWord下载.docx
《Linux内核红外遥控子系统LIRCWord下载.docx》由会员分享,可在线阅读,更多相关《Linux内核红外遥控子系统LIRCWord下载.docx(14页珍藏版)》请在冰豆网上搜索。
脉冲宽度1200微秒表示逻辑1,宽度600微秒表示逻辑0.
幀格式
当按下遥控器上的按键时,遥控器会发送一个命令信号,这个信号就是一个幀,它包含了命令字段和地址(设备)字段,以及扩展字段。
当按住按键不放时,遥控器会不断的发送这一命令信号,直到松开。
SIRC协议的幀格式有12位、15位、20位三种,如下所示:
一个幀以一个起始标志(图中的红色)开始,它是一个2400微秒的脉冲并跟一个间隔。
之后是7字节的命令字段(图中的橙色),这个字段用于识别按下了遥控器上的哪个按键;
然后是地址字段(图中的蓝色),用于识别控制的是什么类型的设备;
对于20位宽格式,还有一个扩展字段,用于传输其他信息。
址值和设备类型映射表
一种命令值和具体按键的映射表
示例:
Sony电视遥控器上“音量-”按键对应的脉冲波形
对于脉冲波形的解码,一般用一个专门的硬件单元完成,也可以在CPU中利用如GPIO等检测接收器输出的波形然后使用软件的方式解码,但这种方式效率显然很低。
2.Linux对红外遥控的支持
Linux上通过LIRC子系统对红外控制提供支持,它包含几个部分:
lirc核心、协议原始脉冲解码器、按键映射表、红外输入设备驱动。
LIRC代码在:
drivers/media/IR
2.1.协议原始脉冲解码器模块
解码器模块实现用软件的方法对原始脉冲进行解码。
解码器用一个ir_raw_handler结构表示。
structir_raw_handler
{
structlist_headlist;
int(*decode)(structinput_devinput_dev,structir_raw_eventevent);
/*解码函数*/
int(*raw_register)(structinput_devinput_dev);
/*注册函数*/
int(*raw_unregister)(structinput_devinput_dev);
/*卸载函数*/
};
解码器通过注册和卸载函数:
intir_raw_handler_register(structir_raw_handler*ir_raw_handler)
voidir_raw_handler_unregister(structir_raw_handler*ir_raw_handler)
当注册解码器时,ir_raw_handler的raw_register函数被调用,所以可在其进行一些解码器初始化工作。
相应的,卸载时raw_unregister函数被调用。
所有注册的解码器放在一个全局链表ir_raw_handler_list中,lirc会便利每个解码器对报告的波形进行解码,注意,此时当任何一个解码器返回一个错误,后面的解码器不会被执行,所以不要将不使用的解码器模块同时加载到内核中。
解码器的主体就是decode函数,lirc核心会将驱动报告的每个脉冲一次一次的传递到decode函数,而decode函数的实现就是一个状态机,每一次输入导致进入下一状态,直到一次解码完成,然后返回起始状态进行下次解码。
再LIRC中,每个脉冲(包括脉冲间隔)用一个ir_raw_event结构表示:
structir_raw_event
unsignedpulse:
1;
/*是脉冲还是间隔*/
unsignedduration:
31;
/*宽度,以ns为单位,一个0ns的脉冲表示重新开始解码*/
lirc中对于脉冲宽度的比较使用eq_margin()、geq_margin()函数,它允许宽度值在二分之一单元上下波动。
boolgeq_margin(unsignedd1,unsignedd2,unsignedmargin)
booleq_margin(unsignedd1,unsignedd2,unsignedmargin)
解码器的完整实现可参考sonysirc解码器实现:
ir-sony-decoder.c
2.2.按键映射表模块
按键映射模块都放在keymaps目录下。
不同的遥控器有不同的按键映射,按键映射模块的作用就是将扫描码与Linuxinput系统标准事件对应起来。
映射表的注册和卸载:
intir_register_map(structrc_keymap*map)
voidir_unregister_map(structrc_keymap*map)
按键映射模块的主体就是一个ir_scancode结构数组,每个元素是一对按键映射。
2.3.红外输入设备驱动
红外输入设备驱动负责向LIRC核心报告脉冲或直接报告扫描码事件。
红外输入设备用ir_input_dev结构描述:
structir_input_dev
structdevicedev;
/*device*/
char*driver_name;
/*Nameofthedrivermodule*/
structir_scancode_tablerc_tab;
/*scan/keytable*/
unsignedlongdevno;
/*devicenumber*/
conststructir_dev_props*props;
/*Deviceproperties*/
structir_raw_event_ctrl*raw;
/*forrawpulse/spaceevents*/
structinput_dev*input_dev;
/*theinputdeviceassociatedwiththisdevice*/
/*keyinfo-neededbyIRkeycodehandlers*/
spinlock_tkeylock;
/*protectsthebelowmembers*/
boolkeypressed;
/*currentstate*/
unsignedlongkeyup_jiffies;
/*whenshouldthecurrentkeypressbereleased?
*/
structtimer_listtimer_keyup;
/*timerforreleasingakeypress*/
u32last_keycode;
/*keycodeoflastcommand*/
u32last_scancode;
/*scancodeoflastcommand*/
u8last_toggle;
/*toggleoflastcommand*/
设备注册和卸载:
intir_input_register(structinput_dev*dev,constchar*map_name,
conststructir_dev_props*props,constchar*driver_name)
voidir_input_unregister(structinput_dev*input_dev)
注册流程:
(1)分配一个input设备,input_allocate_device();
(2)对input设备进行一些初始化设置,但事件掩码不需要设置;
(3)将分配的input设备结构的地址作为参数调用ir_input_register(),以后与lirc核心的交互都是通过这个input设备结构的地址进行的。
ir_input_register()函数的map参数指定要使用的按键映射表,所有映射表定义在rc-map.h中,比如RC_MAP_RC5_TV。
props参数可以为NULL,但若要使用解码器模块对原始脉冲解码(比如无法直接从硬件获得扫描码时),则要设置。
比如:
staticstructir_dev_propsirc_props={
.driver_type=RC_DRIVER_IR_RAW,/*指定需要软件解码*/
.allowed_protos=IR_TYPE_SONY,
input_dev=input_allocate_device();
if(!
irc_input_dev){
ret=-ENOMEM;
gotoerr_input_allocate_device;
}
input_dev->
name="
IRC"
;
ret=ir_input_register(input_dev,RC_MAP_RC5_TV,&
irc_props,NULL);
if(ret){
gotoerr_ir_input_register;
input_dev->
rep[REP_DELAY]=400;
rep[REP_PERIOD]=33;
对于可直接从硬件读取到扫描码的设备,可用以下函数包括扫描码事件:
voidir_keydown(structinput_dev*dev,intscancode,u8toggle)
对于只能获得原始脉冲的设备,需先调用下面函数报告每个脉冲和脉冲间隔:
intir_raw_event_store(structinput_dev*input_dev,structir_raw_event*ev);
intir_raw_event_store_edge(structinput_dev*input_dev,enumraw_event_typetype)
第一个函数要求驱动自己填充ir_raw_event结构,并且在报告第一个脉冲前,需要调用ir_raw_event_reset()函数重置解码器,当驱动认为一个完成的波形已报告完毕后,调用ir_raw_event_handle()启动解码;
第二个函数自动生成一个ir_raw_event结构,脉冲宽度根据前后两次调用ir_raw_event_store_edge()函数的时间间隔字段计算,并且会自动调用ir_raw_event_reset()和ir_raw_event_handle()函数。
type参数指定是何脉冲。
enumraw_event_type
IR_SPACE=(1<
<
0),
IR_PULSE=(1<
1),
IR_START_EVENT=(1<
2),
IR_STOP_EVENT=(1<
3),
其他两个驱动常用的接口:
voidir_repeat(structinput_devdev)/*重复上次按键*/
u32ir_g_keycode_from_table(structinput_devinput_dev,u32scancode)/*获得扫描码对应的按键*/
2.4.LIRC对按住按键时重复事件的处理
首先,重复是自动的,它使用了input子系统的REP功能。
当第一次向lirc报一个扫描码事件时,lirc会向input子系统报告相应的按键事件,并启动一个定时器,该定时器在超时后会上报对应的按键松开事件;
之后若在250ms(这个值等于input子系统的rep延时的默认值)内该设备又报告了同一个扫描码,这时只是将定时器再推后250ms,并不报告新的按键事件,也就是说按键的重复由input系统处理。
这种设计主要是考虑到遥控器的限制,有的遥控器没有按键按下和松开之分(虽然在按下和松开时都有脉冲,但没有区分字段,而且有时可能会丢失信号)。
3.附使用GPIO接收遥控命令驱动代码
#include<
linux/init.h>
linux/module.h>
linux/interrupt.h>
linux/gpio.h>
linux/platform_device.h>
linux/workqueue.h>
linux/time.h>
linux/irq.h>
linux/spinlock.h>
media/ir-core.h>
asm/irq.h>
#defineGPIO_IRC_MAPRC_MAP_SONY_SIRC12_TV
//#defineDEBUG
staticstructinput_dev*irc_input_dev;
staticstructtimer_listirc_timer;
staticatomic_tf_restart;
externu64gpt_get_cycles(void);
externunsignedlonggpt_delta_to_ns(u64delta);
staticvoidgpio_irc_raw_event_reset()
ir_raw_event_reset(irc_input_dev);
atomic_set(&
f_restart,1);
}
staticvoidgpio_irc_timer_func(unsignedlongdata)
structir_raw_eventev;
/*endpulse*/
ev.pulse=false;
ev.duration=~0u;
ir_raw_event_store(irc_input_dev,&
ev);
ir_raw_event_handle(irc_input_dev);
gpio_irc_raw_event_reset();
#ifdefDEBUG
printk("
gpio_ircrestartevent\n"
);
#endif
staticirqreturn_tgpio_irc_rx_irq(intirq,void*dev_id)
staticbooltriger_falling=1;
staticunsignedlonglast;
if(atomic_read(&
f_restart)){
atomic_set(&
f_restart,0);
}else{
ev.pulse=!
triger_falling;
ev.duration=gpt_delta_to_ns((unsignedlong)gpt_get_cycles()-last);
ir_raw_event_store(irc_input_dev,&
last=(unsignedlong)gpt_get_cycles();
if(triger_falling)
set_irq_type(irq,IRQF_TRIGGER_RISING);
else
set_irq_type(irq,IRQF_TRIGGER_FALLING);
triger_falling=!
mod_timer(&
irc_timer,jiffies+HZ/50);
returnIRQ_HANDLED;
staticstructir_dev_propsgpio_irc_props={
.driver_type=RC_DRIVER_IR_RAW,
staticintgpio_irc_probe(structplatform_device*pdev)
intgpio,gpio_irq;
intret;
gpio=pdev->
resource[0].start;
gpio_irq=gpio_to_irq(gpio);
if(gpio_request(gpio,"
gpio_irc"
)){
dev_err(&
pdev->
dev,"
rxpinnotavailable\n"
return-1;
gpio_direction_input(gpio);
setup_timer(&
irc_timer,gpio_irc_timer_func,gpio_irq);
irc_input_dev=input_allocate_device();
irc_input_dev->
MXCGPIOIRreceiver"
ret=ir_input_register(irc_input_dev,GPIO_IRC_MAP,&
gpio_irc_props,NULL);
pr_err("
gpio_irc:
ir_input_register()failed\n"
gotoerr_ir_input_register;
ret=request_irq(gpio_irq,gpio_irc_rx_irq,0,"
pdev);
dev,"
unabaletorequestirq\n"
gotoerr_request_irq;
set_irq_type(gpio_irq,IRQF_TRIGGER_FALLING);
return0;
err_request_irq:
ir_input_unregister(irc_input_dev);
err_ir_input_register:
input_free_device(irc_input_dev);
err_input_allocate_device:
gpio_free(gpio);
returnret;
staticintgpio_irc_remove(structplatform_device*pdev)
del_timer_sync(&
irc_timer);
free_irq(gpio_irq,NULL);
staticintgpio_irc_suspend(structplatform_device*pdev,pm_message_tstate)
staticintgpio_irc_resume(structplatform_device*pdev)
staticstructplatform_driverirc_driver={
.driver={
.name="
},
.probe=gpio_irc_probe,
.remove=gpio_irc_remove,
.suspend=gpio_irc_suspend,
.resume=gpio_irc_resume,
staticintgpio_irc_init(void)
returnplatform_driver_register(&
irc_driver);
staticvoidgpio_irc_exit(void)
platform_driver_unregister(&
late_initcall_sync(gpio_irc_init);
module_exit(gpio_irc_exit);
MODULE_LICENSE("
GPL"
MODULE_DESCRIPTION("
infraredremotecontrolthroughgpio"
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 内核 红外 遥控 子系统 LIRC