1、Linux内核红外遥控子系统LIRCLinux Infrared Remote Control System修订历史版本日期作者描述0.12011-10-12 杜昌彬初稿1.红外遥控基础知识1.1红外遥控简介红外遥控协议有有很多,比如RC-5,RC-6,NEC,SIRC等,不过协议都比较简单,基本上都是以脉冲宽度或脉冲间隔来编码。当遥控器上按下按键时,遥控器逻辑单元会产生一个完整的逻辑脉冲波形,这个波形上包含了遥控命令的信息,他是红外传输的基带信号。这个波形被送到遥控器的调制单元,经调制单元调制成高频的红外电磁波信号,并由发光二极管发射出去。如下图的左边模块。红外遥控的信号的产生和接收红外电磁
2、波信号现在一般使用一体化接收头接收,接收头同时完成了信号的解调和放大,其输出信号就是红外的基带脉冲信号。解调后的信号可直接送入信号处理器中由处理器对脉冲波形进行解码,也就是将经编码的脉冲信号翻译成逻辑数字。根据不同的控制协议,解码方式不同。红外接收头,一跟线用于输出脉冲信号,其他两根是电源线和地线1.2红外遥控协议 下面已sony的SIRC协议为列说明。编码 SIRC协议使用脉冲宽度对每一比特位进行编码,编码规则如下:SIRC协议编码首先,每一个脉冲后跟一个固定宽为600微秒的间隔,而每一个脉冲便是一个逻辑数字,并由脉冲的宽度决定是0还是1:脉冲宽度1200微秒表示逻辑1,宽度600微秒表示逻
3、辑0.幀格式当按下遥控器上的按键时,遥控器会发送一个命令信号,这个信号就是一个幀,它包含了命令字段和地址(设备)字段,以及扩展字段。当按住按键不放时,遥控器会不断的发送这一命令信号,直到松开。SIRC协议的幀格式有12位、15位、20位三种,如下所示: 一个幀以一个起始标志(图中的红色)开始,它是一个2400微秒的脉冲并跟一个间隔。之后是7字节的命令字段(图中的橙色),这个字段用于识别按下了遥控器上的哪个按键;然后是地址字段(图中的蓝色),用于识别控制的是什么类型的设备;对于20位宽格式,还有一个扩展字段,用于传输其他信息。 址值和设备类型映射表 一种命令值和具体按键的映射表示例:Sony电视
4、遥控器上“音量-”按键对应的脉冲波形对于脉冲波形的解码,一般用一个专门的硬件单元完成,也可以在CPU中利用如GPIO等检测接收器输出的波形然后使用软件的方式解码,但这种方式效率显然很低。2.Linux 对红外遥控的支持Linux上通过LIRC子系统对红外控制提供支持,它包含几个部分:lirc核心、协议原始脉冲解码器、按键映射表、红外输入设备驱动。LIRC代码在:drivers/media/IR2.1.协议原始脉冲解码器模块解码器模块实现用软件的方法对原始脉冲进行解码。解码器用一个ir_raw_handler结构表示。struct ir_raw_handler struct list_head
5、list; int (*decode)(struct input_dev input_dev, struct ir_raw_event event); /* 解码函数 */ int (*raw_register)(struct input_dev input_dev); /* 注册函数 */ int (*raw_unregister)(struct input_dev input_dev); /* 卸载函数 */; 解码器通过注册和卸载函数: int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) void ir_r
6、aw_handler_unregister(struct ir_raw_handler *ir_raw_handler) 当注册解码器时, ir_raw_handler的 raw_register函数被调用,所以可在其进行一些解码器初始化工作。相应的,卸载时 raw_unregister函数被调用。所有注册的解码器放在一个全局链表ir_raw_handler_list中,lirc会便利每个解码器对报告的波形进行解码,注意,此时当任何一个解码器返回一个错误,后面的解码器不会被执行,所以不要将不使用的解码器模块同时加载到内核中。 解码器的主体就是 decode函数,lirc核心会将驱动报告的每个脉
7、冲一次一次的传递到decode 函数,而decode 函数的实现就是一个状态机,每一次输入导致进入下一状态,直到一次解码完成,然后返回起始状态进行下次解码。再LIRC中,每个脉冲(包括脉冲间隔)用一个ir_raw_event结构表示:struct ir_raw_event unsigned pulse:1; /* 是脉冲还是间隔 */ unsigned duration:31; /* 宽度,以ns为单位,一个0ns的脉冲表示重新开始解码 */;lirc中对于脉冲宽度的比较使用eq_margin()、geq_margin()函数,它允许宽度值在二分之一单元上下波动。 bool geq_margi
8、n(unsigned d1, unsigned d2, unsigned margin) bool eq_margin(unsigned d1, unsigned d2, unsigned margin) 解码器的完整实现可参考sony sirc解码器实现:ir-sony-decoder.c2.2.按键映射表模块 按键映射模块都放在keymaps目录下。 不同的遥控器有不同的按键映射,按键映射模块的作用就是将扫描码与Linux input系统标准事件对应起来。 映射表的注册和卸载: int ir_register_map(struct rc_keymap *map) void ir_unreg
9、ister_map(struct rc_keymap *map) 按键映射模块的主体就是一个ir_scancode结构数组,每个元素是一对按键映射。2.3.红外输入设备驱动 红外输入设备驱动负责向LIRC核心报告脉冲或直接报告扫描码事件。 红外输入设备用ir_input_dev结构描述:struct ir_input_dev struct device dev; /* device */ char *driver_name; /* Name of the driver module */ struct ir_scancode_table rc_tab; /* scan/key table */
10、 unsigned long devno; /* device number */ const struct ir_dev_props *props; /* Device properties */ struct ir_raw_event_ctrl *raw; /* for raw pulse/space events */ struct input_dev *input_dev; /* the input device associated with this device */ /* key info - needed by IR keycode handlers */ spinlock_
11、t keylock; /* protects the below members */ bool keypressed; /* current state */ unsigned long keyup_jiffies; /* when should the current keypress be released? */ struct timer_list timer_keyup; /* timer for releasing a keypress */ u32 last_keycode; /* keycode of last command */ u32 last_scancode; /*
12、scancode of last command */ u8 last_toggle; /* toggle of last command */;设备注册和卸载: int ir_input_register(struct input_dev *dev,const char *map_name, const struct ir_dev_props *props,const char *driver_name) void ir_input_unregister(struct input_dev *input_dev)注册流程:(1)分配一个input设备,input_allocate_device
13、();(2)对input设备进行一些初始化设置,但事件掩码不需要设置;(3)将分配的input设备结构的地址作为参数调用ir_input_register(),以后与lirc核心的交互都是通过这个input设备结构的地址进行的。ir_input_register ()函数的map参数指定要使用的按键映射表,所有映射表定义在rc-map.h中,比如RC_MAP_RC5_TV。props参数可以为NULL,但若要使用解码器模块对原始脉冲解码(比如无法直接从硬件获得扫描码时),则要设置。比如:static struct ir_dev_props irc_props = .driver_type =
14、RC_DRIVER_IR_RAW, /* 指定需要软件解码 */ .allowed_protos = IR_TYPE_SONY,;input_dev = input_allocate_device(); if (!irc_input_dev) ret = -ENOMEM; goto err_input_allocate_device; input_dev-name = IRC; ret = ir_input_register(input_dev, RC_MAP_RC5_TV, &irc_props, NULL); if (ret) goto err_ir_input_register; inp
15、ut_dev-repREP_DELAY = 400;input_dev-repREP_PERIOD = 33;对于可直接从硬件读取到扫描码的设备,可用以下函数包括扫描码事件: void ir_keydown(struct input_dev *dev, int scancode, u8 toggle)对于只能获得原始脉冲的设备,需先调用下面函数报告每个脉冲和脉冲间隔: int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev); int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type)第一个函数要求驱动自己填充 ir_raw_event结构,并且在报告第一个脉冲前,需要调用ir_raw
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1