第29卷第9期2008年9月
微 计 算 机 应 用
M I CROCOMP UTER APP L I CATI O NS
Vol129No19
Sep12008基于嵌入式L i n ux的红外遥控驱动程序设计3
周海泉1,2 倪 宏2
(1中国科学院研究生院 北京 100190 2中国科学院声学所国家网络新媒体工程技术研究中心 北京 100190)
摘要:提出了一种基于嵌入式L inux的通用红外遥控驱动程序设计方法。在L inux官方组织L I RC p r oject开源代码的基础上,引入了L inux内核中的tasklet机制,可以使硬件中断迅速被响应,而且不会丢失同时发生的其它中断,对开发或移植类似的嵌入式L inux红外遥控驱动程序具有一定的借鉴作用,已应用在国家下一代互联网(CNGI)示范工程项目“视频多媒体点播系统”中。
关键词:L i n ux L I RC 休眠 唤醒 t a sklet
D esi gn of I nfrared Rem ote Con trol D r i ver Ba sed on Em bedded L i n ux
ZHOU Hai quan1,N I Hong2
(1Graduate School,Chinese Academy of Sciences,Beijing,100190,China;
2Nati onal Net w ork Ne w Media Engineering&Technol ogy Research Center,
I nstitute of Acoustics,Chinese Acade my of Sciences,Beijing,100190,China)
Abstract:To p resent a universal infrared re mote contr ol device driver design method based on e mbedded L inux1Tasklet mechanis m of L inux kernel is intr oduced on the basis of open s ource code p r ovided by L inux official organizati on L I RC p r oject1It makes the hard ware interrup t res ponse very fast and other interrup ts would not be l ost at the sa me ti m e1There will have s ome reference value for I nfrared Re mote Contr ol driver design in e mbedded L inux1It has been used in CNGI“VOD syste m”1
Keywords:L inux,L I RC,sleep,wake up,tasklet
L inux操作系统因其开源、兼容性好、移植性高的特性在嵌入式平台上得到了越来越广泛的应用;尤其
在竞争激烈的家电产业中,实用且内核免费的L inux无疑会大大降低产品成本。各厂商都倾向于加强L inux 在信息家电领域的应用,L inux正在成为数字电视、机顶盒等信息家电的软件中枢。
红外遥控器相当于多媒体终端的输入设备,终端对红外指令的处理性能直接关系到平台运行时的灵敏度与稳定性,也影响到用户体验的舒适程度。
L inux相关机构L I RC p r oject(L inux I nfrared Re mote Contr ol)针对红外遥控提供开源代码,供开发者进行二次开发。L I RC软件包包括针对不同接口硬件的驱动程序,以及后台程序和应用程序。后台程序通过s ocket将驱动程序解码信息发送至应用程序,如果要适用不同协议的遥控器只需更改配置文件。虽然这种架构设计的好处是为了移植方便,但用起来比较复杂、程序代码量大,不适用于某些资源紧张的嵌入式平台,可能存在系统响应不及和不稳定等问题。
本文于2007-11-03收到,2008-05-09收到修改稿。
3基金项目:国家发改委“下一代互联网示范工程”项目“视频多媒体点播系统”(CNGI-04-15-2A)。
9期 周海泉等:基于嵌入式L inux 的红外遥控驱动程序设计本文基于L I RC 包中的开源程序,针对嵌入式系统加入了有效的内核机制,实现了嵌入式L inux 下通用红外遥控驱动的设计(以下简称L I RC )。
1
基本原理
图1 NEC 协议红外遥控信号的调制111 红外遥控信号编码方式
遥控码是一串二进制数字信号,该信
号通过脉冲编码形成脉冲序列,并被调制
到一个固定频率载波(例如:38KHz )上,
最后通过红外发射管,以光脉冲的形式发
射出去。所谓脉冲编码,就是将“1”和
“0”用一个脉冲来标识。以笔者使用的
NEC 红外协议为例,用脉宽560us,周期4
×560us 代表二进制“1”;用脉宽560us,
周期2×560us 代表二进制“0”
(如图1所示)。图2显示的是一个典型NEC 协议的红外遥控码。
112 红外遥控接收原理
接收端通过一体化接收头器件接收红外信号,解调后送给CP U 内负责红外通信的模块(I P 核)。该模块通过测量两个沿之间的时间长度,将脉宽数据(9m s 、415m s 、560us 等数值)记录在模块内的F I F O (硬件
)图2 NEC 协议红外遥控编码
中,F I F O 中有数据时即可
产生中断,驱动程序响应中
断,进入中断例程,从F I F O
中读取脉宽数据存入内存
缓冲区中,根据脉宽组合
解码。2 驱动程序设计
211 L i n ux 设备驱动程序简
L inux 设备驱动程序是
为特定的设备提供给应用程序的一组标准化接口,为应用程序屏蔽了具体的硬件工作细节。应用程序是构筑在内核之上的,驱动程序实际上就是内核的一部分,它的目的就是实现一个简单的管理设备的接口,内核用这个接口请求驱动程序控制设备的I/O 操作。
驱动程序从总体上看可分为两部分:
11驱动与内核接口层,它实现驱动在内核的注册加载与卸载清除工作。主要任务就是在模块加载时向内核注册驱动,以及实现虚拟文件系统的设备操作接口。对于采用中断处理的设备,此部分还包括中断处理函数的注册与注销,用于硬件中断资源的申请和释放。
21硬件设备接口层,这部分主要描述驱动程序与设备的交互,它主要包括硬件初始化以及设备的读写访问和中断处理。可以把它理解为对硬件设备的实际操作,也就是为应用程序屏蔽了的那部分。212 L I RC 实现
L inux 设备驱动程序分三种:字符设备(红外等)、块设备(硬盘等)和网络设备(网卡)。红外设备属于字符设备,因此L I RC 的设计遵循L inux 字符设备驱动程序设计标准。L I RC 的流程及接口如图3所示。
字符设备驱动的加载主要由三部分组成:11向内核注册字符设备;21初始化硬件;31申请中断资源。
95
微 计 算 机 应 用 2008年模块加载成功后,开中断等待硬件接收数据后,F I F O 产生中断,进入中断处理例程lirc_interrup t 。中断处理例程的功能就是将有关中断接收的信息反馈给设备,并根据正在服务的中断的含义处理数据,这些都是标准的字符设备驱动设计方法,可参考L I RC 软件包,在此不做赘述
。
图3 L I RC 设计流程及接口
lirc_interrup t 将F I F O 中的数据储存到缓冲区链表
中,然后唤醒读取进程,告诉该进程现在有新的数据可
以读取,读取进程与驱动的接口是lirc_read,应该是阻塞
型的,将在411节中做详细说明。
值得注意的是,这样的情况下应用程序读取的是脉
宽数值,而不是遥控码值。这就需要一个始终运行着的
后台进程来将这些脉宽数据翻译成遥控码值。为求软
件架构简单,节省嵌入式处理器资源,可在驱动程序内
再开辟一个键值缓冲区,先译码再让应用程序读取,这
种方式比较适用于嵌入式系统。
仅使用标准字符驱动设计方法来设计红外驱动是
无法保证系统服务质量的,因此需要为红外驱动加入有
效的内核机制。为响应现代硬件和应用程序的需求,
L inux 内核已经发展到同时处理更多事情的时代。这种变革使得内核性能及伸缩性得到了相当大的提高,然而也极大提高了内核编程的复杂性。设备驱动程序开发者必须在开始设计时就考虑到并发因素,并且还必须对内核提供的并发管理机制有坚实的理解。213 并发问题
由前文可知,红外遥控信号二进制“0”的持续时间长度是1112m s,“1”的长度是2125m s,NEC 红外编码是32bit 的,再加上9m s +415m s 的引导码,硬件获取一个完整的遥控码的时间至少是1112×32+9+415=49134m s 。从本质上讲,设备中断是异步事件,中断处理例程和其他代码并发运行。在此期间内,L I RC 要处理很多次中断,如果响应太久而使并发执行的其他硬件中断(比如高速网卡)得不到响应,就会引起并发问题。最好的中断处理是快进快出,这样可以提高整个系统的处理效率。但是有些中断处理不能满足这个条件,需要做某些特定的操作,甚至是一些延时。在中断处理中应该避免延时以及大量操作的出现,否则会造成系统缺陷。
内核提供了许多可延迟代码执行的机制,比如工作队列、小任务(tasklet )以及定时器等,这些机制使得代码可在任何时刻执行,而不管当前进程在做什么。L I RC 设计的关键是内核中的同步和任务调度,用到的有关机制将在下一章节里做详细介绍。
3 设计的关键机制
311 阻塞型I/O
L I RC 的解码是一个相对处理器运行较慢的过程,如果用户程序访问设备但是驱动程序无法立即满足请求,该如何响应?进程通常不会关心此类问题,只会简单调用read 和write,然后等待必要的工作结束后返回调用。以L I RC 为例,如果解码没有完成时,read 被调用时应该是被阻塞的,即不能从内核空间获得数据。因此,在这种情况下,我们的驱动程序应该(默认)阻塞该进程,将其置入休眠状态直到请求可继续。
当一个进程被置入休眠时,它会被标记为一种特殊状态并从调度器的运行队列中移走。直到某些情况下修改了这个状态,进程才会在CP U 上调度,也即运行该进程。休眠中的进程被搁置在一边,等待将来的某个事件发生--被唤醒。
完成唤醒任务的代码必须能够到我们的进程,这样才能唤醒休眠的进程。为确保唤醒发生,需清楚
06
9期
周海泉等:基于嵌入式L inux的红外遥控驱动程序设计
地直到对每个休眠而言哪些事件序列会结束休眠。能够到休眠的进程意味着,需要维护一个称为等待队列的数据结构。
等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制,它包含了等待某个特定事件的所有进程。
一个等待队列通过一个“等待队列头(wait queue head)”来管理,等待队列头是一个类型为wait_queue_ head_t的结构体,定义在<linux/wait1h>中。可通过如下方法定义并初始化一个等待队列头: DECLARE_WA I T_QUE UE_HEAD(na me);
当进程休眠时,它将期待某个条件会在未来成真。当一个休眠进程被唤醒时,它将再次检查它所等待的条件的确为真。L inux内核中最简单的休眠方式是称为wait_event的宏(以及它的几个变种);在实现休眠的同时,它也检查进程等待的条件。wait_event的形式是:
wait_event(queue,conditi on);
queue是等待队列头,conditi on是任意一个布尔表达式,上面的宏在休眠前后都要对该表达式求职;在条件为真之前,进程会保持休眠。
在我们的程序里,选择使用wait_event_interrup tible,interrup tible表示可被信号中断。返回值为整型,非零值表示休眠被某个信号中断,这时候将不能被用户read。
整个过程的另外一半是唤醒。其他的某个执行进程(可能是另一个进程或者中断处理例程)必须为我们执行唤醒,因为我们的进程正在休眠中。用来唤醒休眠进程的基本函数是wake_up,它也有多种形式,对应wait_event_interrup tible的是wake_up_interrup tible。
等待队列的应用涉及两个进程,假设为A和B。A是资源的消费者,B是资源的生产者。A在消费的时候必须确保资源已经生产出来,为此定义一个资源等待队列。这个队列同时要被进程A和进程B使用,在L I RC程序里,A就是应用程序需要的红外码值,是read调用;B就是驱动程序得到的硬件信息,是interrup t 处理。以L I RC为例,等待队列的用法如下:
DEC LARE_WA I T_QUE UE_HE AD(lirc_read_queue);/3声明一个等待队列,静态全局变量3/
在系统调用进程A(read)中,执行逻辑如下:
while(lirc is unavaiable){/3如果没有新数据到达3/ wait_event_interrup tible(&lirc_read_queue);}/3阻塞,
进入休眠,等待B唤醒3/
consu me_res ource();
linux内核设计与实现 pdf/3唤醒后,处理到达的数据3/
在中断处理B(interrup t)中,执行逻辑如下:
p r oduce_res ource();
/3新的数据到达3/
wake_up_interrup tible(&lirc_read_queue);
/3通知进程A所等待的事件已经发生,唤醒在该设备上休眠的A3/
312 t a sklet机制
中断处理的一个主要问题是怎样在处理例程内完成耗时的任务。响应一次设备中断需要完成一定数量的工作,但是中断处理例程需要尽快结束而不能使中断阻塞的时间过长,这两个需求(工作和速度)彼此冲突。为了最大程度的避免中断处理时间过长而导致并发中断丢失,有时候我们需要把一些在中断处
理中不是非常紧急的任务放在后面执行,而让中断处理程序尽快返回。L inux(连同很多其他的系统)的解决方法是通过将中断处理例程分成两部分--顶半部和底半部。利用顶半部处理中断必须处理的任务,而底半部处理不是太紧急的任务。
称为“顶半部”的部分,是实际响应中断的例程,也就是用request_irq注册的中断例程;而所谓的“底半部”是一个被顶半部调度,并在稍后更安全的时间内执行的例程。顶半部处理例程和底半部处理例程之间最大的不同,就是当底半部处理例程执行时,所有的中断都是打开的———这就是所谓的在更安全时间内运行。典型的情况是顶半部保存设备的数据到一个设备特定的缓冲区并调度它的底半部,然后退出———这个操作是非常快的。然后,底半部执行其他必要的工作,例如唤醒休眠进程、启动另外的I/O操作等等。这种
16
微 计 算 机 应 用
2008年
方式允许在底半部工作器件,顶半部还可以继续为新的中断服务。
几乎每一个严格的中断处理例程都是以这种方式分成两部分的。例如,当一个网络接口报告有新数据
包到达时,处理例程仅仅接收数据并将它推到协议层上,而实际的数据包处理过程是在底半部执行的,L I RC 也是如此,实际的解码过程应在底半部处理。
tasklet通常是底半部处理的优选机制,因为这种机制非常快,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成是一个可以由系统决定的安全时刻在软件中断上下文被调度运行的特殊函数。
tasklet以数据结构的形式存在,并在使用前必须初始化。可以使用特定的宏来声明该结构,即可完成tasklet的初始化:
DECLARE_T ASK LET(na me,functi on,data);
na me是给tasklet起的名字,functi on是执行tasklet时调用的函数(实际上就是底半部处理),data是一个用来传递给tasklet函数的unsigned l ong类型的值。
函数tasklet_schedule用来调度一个tasklet运行。L I RC是这样在中断处理例程中调度tasklet的:
DECLARE_T ASK LET(lirc_tasklet,lirc_do_tasklet,0);
static irqreturn_t lirc_interrup t(int irq,void3dev_id, struct p t_regs3regs)
{
clear_irq_flag(); /3清中断标志位3/
tasklet_schedule(&lirc_tasklet); return I RQ_HANDLE D;
}
实际的tasklet例程,即lirc_do_tasklet,将会在系统方便时得到执行。这个例程执行中断处理的大多数任务,一般是这样实现的:
static void lirc_do_tasklet(unsigned l ong ir_data)
{
do_s ome_necessary_work();
/3底半部处理,从硬件F I F O读取数据到缓冲区链表3/
wake_up_interrup tible(&lirc_read_queue);/3唤醒读取进程3/
return;
}
tasklet可以看作是软中断或者任务调度。当系统产生中断的时候做一个标识,调度处理这个中断的相应的例程。当系统相对空闲的时候,再运行这个具体的处理。这样可以保证其他的中断能够及时响应。
4 结束语
本文介绍了一种基于嵌入式L inux的红外遥控驱动程序设计方法。这种方法使用了有效的内核机制使驱动程序更加符合嵌入式系统的需求,目前已应用于视频多媒体点播系统及高性能宽带信息网示范区的终端产品中,系统综合测试及实际使用表明,其响应速度和稳定性均取得满意的效果。
参考文献
1 NEC I R Pr ot ocol[E B/OL]1htt p://www1sbp r ojects1com/knowledge/ir/nec1ht m
2 L inux I nfrared Re mote Contr ol OfficialW ebsite1www1lirc1org[E B/OL]1
3 JONAT HAN CORBET,ALESS ANDRO RUB I N I,GREG KROAH-HART MAN1L inux Device D rivers13rd ed1[S1l1]:O’R eilly, 2005
作者简介
周海泉,男,(1982-),硕士研究生,主要研究方向为嵌入式系统和数字家庭
倪宏,男,(1964-),研究员、博导,主要研究方向为网络多媒体通信、嵌入式系统等。
26
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论