分类: 嵌入式
2010-05-21 11:56:42
1、红外通信协议
红外通信是传统的线缆通信方式的替代,被用来进行设备之间点对点的数据传输。目前作为一种广泛使用的无线连接技术,被众多的硬件和软件平台支
持。它具有小角度、短距离、传输速率高等特点。
红外通信技术发展的早期,存在有几种不同的通信标准,不同标准之间的通信设备并不能进行红外通信,为了统一红外通信标准,于1993年,一些厂商成立了IrDA(红外数据协会)。IrDA是目前红外无线通信的工业标准,你可以使用它
与打印机、传真机、modem、其它计算机进行通信。按照速率IrDA红外通信可以分为下面四种:
l SIR:2.4 kbit/s 到 115.2 kbit/s,最基本的红外标准。串口连接时必定使用的是SIR。
l MIR:576 kbit/s 到 1152 kbit/s
l FIR:4 Mbit/s
l VFIR:16 Mbit/s
2、红外通信装置
为了进行红外通信,我们需要有发送和接收红外
信号的装置,笔记本一般都带有IrDA的红外接口,而台式机一般需要购买专门的红外适配器。目前市场上的红外适配器主要有串口红外适配器、USB红外适配器、主板接口红外适配器等。
利用我们的计算机进行红外通信的方式主要有:
l 利用IrDA接口与其它具有红外通信接口的设备,如打印
机、传真机、计算机等通信。
l 使用专用的红外遥控器和接收器。
l 使用红外接收器再加上普通的电视、DVD等的遥控器。还需要专门的控制软件。
第一种主要是不同设备之间的红外数据传输,后两种是对计算机的红外遥控。Linux针对这两种使用方式有不同的驱动和软件包。
3、 Linux红外支持
Linux/IrDA project于1997年开始,因为与IrDA协会的名称产生冲突,项目名称更改为Linux/IR,并从1999年开始成为IrDA的官方成员之一。相关的软件包主要有IrDA-Utils。
LIRC project(Linux Infrared Remote
Control)的主要目标是通过红外红外信号进行计算机的远程控制。LIRC包支持解码并发送最普通的IR远程控制信号。早期版本只支持串口和并口接口
的硬件,但是现在已经能够支持很多不同的硬件。LIRC包包括了针对不同接口硬件的设备驱动,不过最重要的还是 lircd 后台程序,它负责解码设备驱动接收到的IR信号并将解码后的信息通过 socket 进行发送,另外还有一个 lircmd 后台程序,负责连接 lircd
并将解码后的IR信号转换为鼠标移动信息。此外LIRC还包括了一些用户空间的应用程序,为我们使用IR来控制计算机提供方便。因为这里讨论的是红外
远程控制驱动,所以LIRC将是下面阐述的重点。的有关LIRC
project的详细信息可以浏览它的项目主页
4、LIRC结构
下图是LIRC包的驱动模型(Driver Model)
主要涉及了三个devfs接口,/dev/lirc、/dev/lircd、/dev/lircmd。
l /dev/lirc
依赖于使用的ioctl
LIRC_MODE_MODE2
输出的packet包括了描述IR信号的整型值。这个整型值的位0-23表示pulse/space的长度;位24为0时表示space,为1时表示pulse;所有剩余的位没有意义。
LIRC_MODE_CODE
outputs chars (8 bits)
LIRC_MODE_LIRCCODE outputs
codes of configurable length in big endian byte order
可参考文件drivers/lirc_serial/lirc_serial.c、drivers/lirc_parallel/lirc_parallel.c、tools/mode2.c(mode工具,将这个驱动的输出显示在终端上)。
l /dev/lircd
输出包括了所有remote和pressed button信息的字符串。
l /dev/lircm
鼠标后台。支持三种鼠标协议:MouseSystems, IntelliMouse 和 IMPS/2。可参考文件daemons/lircmd.c
4、LIRC配置
LIRC的配置主要涉及到三个配置文件,lircd.conf、lircmd.conf、.lircrc。
l lircd.conf
lircd.conf是lircd后台程序的配置文件。LIRC项目主页上已经有很多针对不同牌子遥控器的配
置文件,如果在上面找不到匹配自己遥控器的lircd.conf文件,则需要使用irrecord工具进行手工配置。配置过程在另一篇文章“Linux下
遥控器的配置与使用”里已经有所阐述,下面的示例是一个典型的配置文件,其中定
义了1~9,上、下、左、右和开关键的矩形脉冲信息。
begin remote
name test
bits 16
flags SPACE_ENC|CONST_LENGTH
eps 30
aeps 100
header 9092 4460
one 632 1628
zero 632 496
ptrail 630
repeat 9118 2181
pre_data_bits 16
pre_data 0xFF
gap 108483
toggle_bit 0
begin codes
1 0x
2 0xB847
3 0xF807
4 0x9867
5 0xD827
6 0x8877
7 0xE817
8 0x48B7
9 0x
up 0x
down 0x22DD
left 0xE21D
right 0x02FD
power 0x28D7
mute 0x
end codes
end remote
l lircmd.conf
lircmd后台程序的配置文件,主要配置了遥控按键与鼠标消息的对应关系。
l .lircrc
LIRC的客户端应用使用的配置文件,应该位于你的用户目录下面。也可以命名为为/etc/lircrc,此时它为所有应用所共用。
begin
prog
= lirckeyd
remote
= new.conf
button
= power
repeat
= 0
config
= power
end
begin~end表示一个独立的配置块。
prog域的值表示使用该配置块的程序名称。
Remote的值表示使用的遥控器名称(参见中的定义)。
Button
的值表示遥控器按键名称(参见中的定义)。
Repeat
的值表示按键的重复次数。
5、编写LIRC的客户端应用
有两种类型的LIRC应用,一种为发送IR命令的应用,比如xrc、irsend等,另一种为接收IR命令的应用,比如irexec,
irxevent 和 irpty等。它们都将通过位于/dev/lircd的socket连接到lircd后台,通信的数据为普通的可读格式。
无论什么时候lircd接收到一个IR信号,它将广播下面的字符串到每个客户端(client):
< repeat count >
code是IR信号的64位编码。repeat count表示用户按下按键的时间,每当接收到一个新的IR信号时,它将增加1。button name和remote control name在lircd的配置文件里定义。
如果你仅仅希望使自己的应用接收IR命令,可以使用lirc_client库,通常是/usr/lib/liblirc_client.a。可以使用它编写LIRC的客户端应用,要记得包含lirc/lirc_client.h文件。lirc_client库比较重要的函数有:
l int lirc_init(char *prog,int verbose)
库的初始化,它连接lircd后台做一些内部的初始化。prog与.lircrc中的prog项对应,verbose表示是否打印错误信息
到stderr,0表示不打印,1表示打印。返回值为连接到lircd的socket描述符,有错误发生时
返回-1。
l
int
lirc_readconfig(char *file,struct lirc_config **config,int (check)(char
*s))
读取配置文件。file表示配置文件名称,NULL表示使用默认的.lircrc,也可以进行多次调用加载多个配置文件。config表示配置文件数据结构(struct config)的指针,将在函数内部
填充,lirc_code2char函数将需要它。check函数指针指向一个对file进行解析的用户自定义函数。
l
int
lirc_nextcode(char **code)
阻塞直到socket上有数据可用。可以在程序的主循环里使用。如果有错误发生返回-1,否则返回0和指向数据流里下一个
字符串的指针code,注意code必须使用free函数释放。此函数使得code中获得prog对应的配置信息,若无
按键信息则阻塞进程。
l
int
lirc_code2char(struct lirc_config *config,char *code,char **string)
获得配置文件里与按键
对应的配置字符串。config和code分别由是前面两个函数填充的,string中将被.lircrc中的config域值填充。
l
void
lirc_freeconfig(struct lirc_config *config)
释放config结构。
l
int
lirc_deinit()
lirc销毁函数。
下面是LIRC项目主页上的一个简单示例
/* $Id: technical.html,v 1.26 2005/12/10 22:12:02 lirc
Exp $ */
/****************************************************************************
** irexec.c
****************************************************************
****************************************************************************
*
* irexec - execute programs
according to the pressed remote control buttons
*
* Copyright (C) 1998 Trent Piepho
* Copyright (C) 1998 Christoph Bartelmus
*/
#ifdef
HAVE_CONFIG_H
#
include
#endif
#include
#include
#include
#include
#include
#include
#include
"lirc_client.h"
char
*progname;
int
main(int argc, char *argv[])
{
struct lirc_config *config;
progname=argv[0];
if(argc>2)
{
fprintf(stderr,"Usage: %s
exit(EXIT_FAILURE);
}
if(lirc_init("irexec",1)==-1)
exit(EXIT_FAILURE);
if(lirc_readconfig(argc==2 ?
argv[1]:NULL,&config,NULL)==0)
{
char *code;
char *c;
int ret;
while(lirc_nextcode(&code)==0)
{
if(code==NULL) continue;
while((ret=lirc_code2char(config,code,&c))==0
&&
c!=NULL)
{
#ifdef
DEBUG
printf("Execing
command \"%s\"\n",c);
#endif
system(c);
}
free(code);
if(ret==-1) break;
}
lirc_freeconfig(config);
}
lirc_deinit();
exit(EXIT_SUCCESS);
6、红外信号映射为X事件
如果我们希望使用遥控器远程遥控进行视频、音频等播放操作,则需
要将上面解码后的红外信号映射为X事件(当然指的是针对基于X的图形应用时)。这个过程主要涉及到下面几个函数:
l XKeysymToKeycode( *display, keysym)
将转换为。Display指定连接的X server。表示键盘按键的字符编码,如XK_Up、XK_Down等,定义的列表在X11/keysymdef.h文件里,多数应用通常
只包括X11/keysym.h文件。代表了物理按键。如果指定的没有对应任何,则返回0。
l XTestFakeKeyEvent(display, keycode, True, 0);
使用XTEST扩展发送按键事件的接
口。第三个参数表示按键被按下或释放,如果按下,则发送KeyPress事件,否则发送KeyRelease。使用这个函数时,不能够指定事件发送的目的地,它将按照真实
键盘按键的事件传送机制来传送。
l XSync( *display, Bool discard)
flush输出缓冲,并等待所有
的请求已经被X server接收和处理。对于X server接收到的每个协议错误,XSync将调用客户端应用的错误处理函数。server生成的都依次添加在xlib的事件队列里。如果设置discard为False,则XSync不丢弃这个事件队列里
的事件,否则,丢弃所有队列里的事件。
在前面示例里添加下面的定义:
static struct KeyMapEntry {
char
*config;
KeySym
keysym;
} keymap[] = {
{"Escape", XK_Escape},
{"Backspace", XK_BackSpace},
{"1", XK_1},
{"2", XK_2},
{"3", XK_3},
{"4", XK_4},
{"5", XK_5},
{"6", XK_6},
{"7", XK_7},
{"8", XK_8},
{"9", XK_9},
{"0", XK_0},
{"Up", XK_Up},
{"Down", XK_Down},
{"Left", XK_Left},
{"Right", XK_Right},
};
void
dispatchEvent(char *msg)
{
struct
KeyMapEntry entry;
KeyCode keycode;
int
i;
for (
i = 0; keymap[i].config; i++) {
entry
= keymap[i];
if
(strcmp(entry.config, msg) == 0) {
keycode
= XKeysymToKeycode(display, entry.keysym);
XTestFakeKeyEvent(display,
keycode, True, 0);
XTestFakeKeyEvent(display,
keycode, False, 0);
XSync(display,
False);
break;
}
}
if
(!keymap[i].config)
print("
}
然后将lirc_code2char循环里的语句
system(c);
替换为