




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
l 硬件原理OMAP5912处理器是由TI应用最为广泛的TMS320C55X DSP内核与低功耗、增强型ARM926EJS微处理器组成的双核应用处理器。用这样一种组合方式将2个处理器整合在1个芯片后,开发人员可以根据实际情况,利用DSP运行复杂度较高的数字信号处理任务,利用ARM运行通信、控制和人机接口方面的任务,从而使便携式设备在保持良好人机交互环境的基础上,有效地降低功耗。在外设方面,OMAP5912微处理器支持常用的各种接口,其中通过MPUIO接口最多可支持88的矩阵键盘,系统中采用这个接口扩展了一个45的矩阵键盘。其硬件连接示意图如图1所示,其中按键行阵列必须提供上拉信号,列阵列加二极管,防止瞬间电流过大对MPUIO口造成冲击。按照键盘的构造方式人们把键盘划分为线性键盘和矩阵键盘。其中,线性键盘是指每个按键都占用嵌入式处理器的1个IO端口,并通过这个IO端口实现人机交互,各个按键之间互不影响。使用这种方案的优点是简单、可靠,但是线性键盘对IO端口的占用量很大。因此,嵌入式系统中很少采用这种方法。另外一种矩阵键盘是指当按键数量过多时,采用矩阵的排列方法,将按键设计成n行m列的矩阵形式。其中,每个按键占用行和列的1个交叉点,并且以行和列为单位引出信号线。这样只需要占用n+m个IO端口,却可以驱动nm个按键,大大节省了对嵌入式处理器IO端口的占用,节省了宝贵的资源。矩阵键盘在减少嵌入式处理器IO端口占用的问题上做出了很大的贡献,但随之而来的问题是如何确定矩阵中按键的位置,这里采用列扫描法,其思路如下:在键盘初始化阶段,所有的列信号(KBC)都被设置输出为低电平。如果矩阵键盘中的1个按键按下,则相应的行信号和列信号线短路,行信号线(KBR)输入由高电平变为低电平,产生1个中断,然后在驱动的中断服务程序中按照表1中的序列逐列扫描列信号,读取行信号的状态,根据读回来的行信号状态就可以判断有那些按键按下。另外,键盘驱动必须解决的一个问题是键盘的抖动。在按键按下和抬起的过程中,电压信号会出现很多毛刺,这主要是由于机械按键的弹性作用引起的。尽管触点看起来非常稳定,而且快速地闭合,但相对于嵌入式处理器的运行速度来说,这种动作是比较慢的。这种脉冲在某些按键功能设计时,如果处理不当可能会带来灾难性的后果。所以必须对按键信号进行防抖检测。按键防抖检测的核心思想是在嵌入式处理器的几个时钟周期内,通过对按键信号进行多次访问,查看电平状态是否保存一致。如果保持一致,则说明按键状态已经稳定;否则,说明之前检测到的按键信号是抖动信号或外界信号干扰,系统将不会对其进行任何处理。2 嵌入式Linux设备驱动程序在Linux内核源代码中,各种驱动程序的代码量占据了整个Linux代码的85。可见,Linux设备驱动在整个操作系统中起着举足轻重的作用。设备驱动是操作系统内核和机器硬件之间的接口,它们控制着设备的操作动作,并且提供了一组API接口给应用程序,使得应用程序能够与这个设备互动。而且,设备驱动为应用程序屏蔽了硬件的细节,在应用程序看来,硬件设备只是1个设备文件,应用程序就可以像操作普通文件一样对硬件设备进行操作。在Linux操作系统中,通常将外围设备分为3种类型:字符设备、块设备和网络设备。而在Linux操作系统中,还有一类设备被定义为“平台设备”,通常So(System on Chip)系统中集成的独立的外设单元都被当作平台设备来处理,这里把45的矩阵键盘也定义为平台设备。所谓的“平台设备”并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,例如,键盘驱动,它本身是字符设备,但也将其归纳为平台设备。另外,键盘又属于输入设备,Linux内核提供了输入子系统,如键盘、触摸屏、鼠标等输入设备都可以利用输入子系统的接口函数来实现设备驱动。输入子系统由核心层(Input Core)、驱动层和事件处理层(EventHandler)三部分组成。在Linux内核中,使用输入子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键、触摸屏、鼠标等输入事件。而不再需要关心文件操作接口,因为输入子系统已经完成了文件操作接口。通过输入子系统,实现输入设备驱动时只需要完成以下工作: (1)在模块加载函数中告知输入子系统输入设备可以报告的事件。例如,可通过_set_bit(EV_KEY,input_dex,一evbit)来告知输入子系统该设备可报告按键事件。(2)在模块加载函数中注册输入设备。注册函数为:int input_register_device(struct input_dev*dev);(3)当有输入事件发生时,如按键按下抬起、触摸屏被触摸抬起移动时,通过input_report_xxx()报告发生的事件及对应的键值、坐标等状态。主要的事件类型包括EV_KEY(按键事件)、EV_REL(相对值,如鼠标移动,报告相对于最后一次位置的偏移)和EV_ABS(绝对值,如触摸屏)。用于报告EV_KEY事件的函数为:void input_report_key(struct input_dev*dev,unsigned int code,int value);(4)在模块卸载函数中注销输入设备。注销输入设备的函数为:void input_unregister_device(struct input_dev*dev);3 矩阵键盘驱动中的数据结构首先,定义一个整型数组osk_keymap用来定义按键映射表,把20个按键返回的码值映射成内核中标准的键码,这样有利于与上层应用程序的交互。通过KEY(col,row,code)宏定义来实现映射关系,如要把第2行第4列的按键映射为回车键,则通过KEY(3,1,KEY_ENTER)便可实现。其中KEY_ENTER是内核中定义的标准的键码。其次,定义矩阵键盘的设备结构体omap_kp,其定义如下:4 矩阵键盘驱动程序设计及测试首先,实现矩阵键盘驱动的加载和卸载函数,分别通过调用platform_drivet_register()和platform_driVer_unregister()实现矩阵键盘作为一个平台设备的注册和注销。其次,实现矩阵键盘驱动的探测和移除函数。在探测函数中,初始化行数、列数、中断号以及按键映射表。然后分配内存空间和输入设备,初始化omap_kp这个设备结构体和输入设备结构体input_dev,初始化定时器,设置输入设备可以报告的事件类型,并注册输入设备。最后申请中断,申请中断成功后,使能中断。移除函数则完成相反的工作。最后,实现矩阵键盘驱动的核心部分,也就是中断部分。众所周知,在Linux的中断处理中分为2部分,分别是顶半部(top half)和底半部(bottom half)。顶半部完成尽可能少的比较紧急的功能,它只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样。顶半部执行的速度就会很快,可以服务更多的中断请求。底半部,是实现中断处理的真正部分,它来完成一些延缓的耗时任务,首先通过列扫描法检测各个按键状态有没有变化,若有变化再判断是哪一列哪一行发生变化,按键的行和列确定以后,通过键值映射表来查找其有没有对应的键值;若有则通过input_report_key()向内核报告按键的键值;否则,对应的按键没有定义键值,向内核报告为假按键(Spurious Key)。然后,延时(120)Hz再判断按键是否抬起。驱动开发完成后,以模块方式加入到内核,并进行了测试,证明矩阵键盘驱动工作正常。 Linux中的大多数驱动程序都采用了层次型的体系结构,键盘驱动程序也不例外。在Linux中,键盘驱动被划分成两层来实现。其中,上层是一个通用的键盘抽象层,完成键盘驱动中不依赖于底层具体硬件的一些功能,并且负责为底层提供服务;下层则是硬件处理层,与具体硬件密切相关,主要负责对硬件进行直接操作。键盘驱动程序的上层公共部分都在driver/keyboard.c中。该文件中最重要的就是内核用EXPORT_SYMBOL这个宏导出的handle_scancode函数。handle_scancode完成的功能是:首先将扫描码转换成键码,接着根据shift, alt等扩展键的按下情况将键码转换成目标码,一般情况下是ASCII码,最后将该ASCII码放到终端设备的缓冲区中,并且调度一个tasklet负责将其在显示器上回显出来。可以看出,这个函数完成的是键盘驱动程序中最核心的一些工作,而这些核心的逻辑功能是不依赖于底层硬件的,所以可以将其独立出来,并且导出给底层的硬件处理函数调用。在这个文件中还定义了其它几个回调函数,它们由键盘驱动程序中的上层公共部分调用,并由底层硬件处理函数实现。比如kbd_init_hw, kbd_translate, kbd_unexpected_up等等。其中kbd_translate由handle_scancode调用,负责将扫描码转换成键码;键盘驱动程序的底层硬件处理部分则根据不同的硬件有不同的实现。例如PC平台上标准键盘的底层硬件处理函数都集中在driver/Pc_keyb.c中。这个文件包括了键盘中断处理函数keyboard_interrupt,扫描码到键码转换函数pckbd_translate等其他一些与底层硬件密切相关的函数。 在这种体系结构下,要添加一块特殊键盘到系统中就显得格外清晰。开发者只需为其编写驱动程序中的底层硬件处理函数,就可以将该键盘驱动起来。一般说来,底层硬件处理函数中最重要的工作就是在键盘中断处理中获取被按下键的扫描码,并且以它为参数调用handle_scancode,该扫描码可以自己定义,但它必须唯一地标识出被按下键在键盘上的位置。此外,开发者还需要提供对应的从自定义扫描码到键码的转换函数kbd_translate。具体的键码转换,将目标码放到终端的输入缓冲区,以及回显等工作都由handle_scancode负责完成。在此我们也可以看出,内核导出函数handle_scancode在整个键盘驱动程序中,起着将上层通用抽象层和底层硬件处理层粘和起来的关键作用。 一.内核模块的注册和撤销在加载模块的时候,首先运行的是内核模块的注册函数。它的功能包括内核注册设备以及变量的初始化。staticinthead,tail;int_initKeypad_init(void)intresult;result=register_chrdev(KEY_LED_MAJOR,KEY_LED_NAME,&Keypad_fops);Keypad_clear();init_waitqueue_head(&queue);prink(%s%sinitialized.n,KEY_LED_NAME,KEY_LED_VERSION);/不能用prinfreturn0;module_init(Keypad_init);/加载模块void_exitKeypad_cleanup(void)del_timer(&timer);unregister_chrdev(KEY_LED_MAJOR,KEY_LED_NAME);prink(Keypaddriverremovedn);module_exit(Keypad_cleanup);/卸载该模块二.虚拟文件系统与硬件驱动的接口staticstructfile_operationsKeypad_fops=open:Keypad_open,read:Keypad_read,poll:Keypad_poll,fasync:Keypad_fasync,release:Keypad_release,;该接口定义完之后一些便是对这几个具体函数的实现了!现在我们一起进入下一步吧,是不是觉得其实没什么难度的呢?别那么早开心着呢?这几个函数的实现时候,涉及到很多技术,包括内核定时器,等待队列的具体实现(阻塞方式),异步方式的具体实现技巧,循环队列。看到这么多技术你是否感到很兴奋呢?以下本人将以通俗的方式为你讲解,希望你能理解。三.设备的打开操作接口函数具体实现(Keypad_open)设备打开一般包括两大操作,一是完成设备的初始化,二是设备引用计数器加1staticintKeypad_open(structinode*inode,structfile*filp)read_xy();try_module_get(THIS_MODULE);/此函数为linux2.6内核增加的,不同于2.4内核,功能是计数器的值加1return0;staticvoidread_xy(void)new_data();/获取键值函数keypad_starttimer();/开启内核定时器,在固定周期时间内获取键盘新的变化以下实现键盘键值获取函数read_xy()主要是从KEY_CS(对应的读入地址,之前可以根据具体的硬件设备定义,比如definekEY_CS(*(volatileunsignedshort*)(0xf820000)此处应该根据具体的不同而不同!将读入的键值存入buf缓存中,环形缓冲的写指针是head,读指针是tail,前面已经定义过了/键盘事件的数据结构定义/typedefstructulongstatus;/按键的值ulongclick;/是否有按键按下,1表示有,0表示没有KEY_EVENTstaticKEY_EVENTcur_data,bufBUFSIZE;/BUFSIZE为宏定义,用于定义环形缓冲的大小staticvoidnew_data(void)if(KEY_CS&0xff)!=0xff)/从KEY_CS地址读入数据,若有一个为0则表示有一个按键被按下了(此处硬件电路为低电平有效)switch(KEY_CS&0xff)caseKEY0&0xff:cur_data.status=1;/1被按下break;caseKEY1&0xff:cur_data.status=2;/2被按下break;/其他一样添加,懂吗?cur_data.click=1;elseif(KEY_CS&0xff=0xff)cur_data.click=0;cur_data.status=0;if(head!=tail)/循环队列缓冲区的应用在此开始了_intlast=head-;if(lastf_flags&O_NONBLOCK)/假如用户采用的是非堵塞方式读取return_EAGAIN;add_wait_queue(&queue,&wait);/将当前进程加入等待队列current-state=TASK_INTERRUPTIBLE;/设置当前进程的状态while(head=tail)&!signal_pending(current)/假若还没有数据到循环队列并且当前进程没有受到信号shedule();/进程调度current-state=TASK_INTERRUPTIBLE;current-state=TASK_RUNNING;remove_wait_queue(&queue,&wait);if(head=tail)returncount;t=get_data();/调用get_data()函数,得到缓冲区中的数据,下面将给予详细的介绍out_buf0=t.status;out_buf1=t.click;copy_to_user(buf,&out_buf,sizeof(out_buf);/将得到的键值拷贝到用户数据区returncount;很自然我们就应该要介绍get_data()函数的实现了,该函数的功能就是从我们定义的循环队列缓冲区中读出我们要的键值,所以其实很简单的如果理解循环队列的原理,在此不多加解释,大家应该具备一般的数据结构相关的知识吧staticKEY_EVENTget_data(void)intlast=tailif(+tail=BUFSIZE)tail=0;returnbuflast;上面如果你看得懂得话,那么可以进入下面的学习了,主要介绍的是内核定时器的使用,利用等待队列实现阻塞型I/O,poll系统调用,异步通知方式,介绍完之后,我将给出一个应用实例,对于有使用过文件操作系统调用的来说,对我们所写的键盘驱动来说,他们基本上是一样的。废话少说,我们马上开始我们精彩的驱动开发!六.内核定时器的使用在该驱动中,我们假设对键盘的获取是以0.2s为周期执行。源代码如下staticstructtimer_listtimer;/我们定义的定时器,也许你会问timer_list是什么来的,其实一看名称就应该就知道了,而为什么要用到list那么多定时器呢?其实在linux中还有很多相同的定义,比如说信号,我们定义的也是信号集,你可以定义该list是一个元素的,也可以是多个的。所以对于timer_list就可以这样描述:在未来某一个特定时刻执行某一系列特定任务的功能。下面我们还会给出内核中timer_list的具体描述,staticintKeypad_starttimer(void)init_timer(&timer);/初始化定时器结构timer.function=Keypad_timer;/超时服务程序timer.expires=jiffies+20;/当前时刻加0.2sadd_timer(&timer);return0;/超时服务程序staticvoidKeypad_timer(unsignedlongdata)read_xy();/接下来说下timer-list这个数据结构,如果你不感兴趣的话可以跳过,该结构在includelinuxtimer.h中定义structtimer_liststructlist_headentry;unsignedlongexpries;spinlock_tlock;unsignedlongmagic;void(*function)(unsignedlong);unsignerlongdata;structtvec_t_base_s*base;七.利用等待队列实现阻塞型IO在用户程序执行读操作的时候有可能尚且没有数据可以读取,为此需要让read操作等待,直到有数据可以读取,这就是阻塞型io,阻塞型io可以通过使用进程休眠方法实现。在无数据可以读取的时候,采用等待队列让进程休眠,直到有数据到达的时候才唤醒进程完成数据的读操作。在本驱动中的read,若循环队列缓冲区中没有数据,则进程进入休眠态,定时器函数每隔0.2s读取键值一次,将按键状态放入缓冲并且适时唤醒进程读取数据。等待队列的使用流程如下:1.声明一个等待队列2.把当前进程加入到等待队列中3.把进程的状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;4.调用schedule,以让出cpu5.检测所需要的资源是否可用,若是,把当前进程从等待队列中删除,否则转3循环接下来我们在对read中有关等待队列阻塞实现做具体的解释staticssize_tKeypad_read(structfile*filp,char*buf,ssize_tcount,loff_t*l)DECLEARE_WAITQUEUE(wait,current);/声明等待队列,将当前进程加入到等待队列中KEY_EVENTt;ulongout_buf2;if(head=tail)/当前循环队列中没有数据可以读取if(filp-f_flags&O_NONBLOCK)/假如用户采用的是非堵塞方式读取return_EAGAIN;add_wait_queue(&queue,&wait);/将当前进程加入等待队列current-state=TASK_INTERRUPTIBLE;/设置当前进程的状态while(head=tail)&!signal_pending(current)/假若还没有数据到循环队列并且当前进程没有受到信号(该类信号具体来说是未决的休眠)shedule();/进程调度current-state=TASK_INTERRUPTIBLE;current-state=TASK_RUNNING;/该进程恢复执行remove_wait_queue(&queue,&wait);/移出等待队列if(head=tail)returncount;t=get_data();/调用get_data()函数,得到缓冲区中的数据,下面将给予详细的介绍out_buf0=t.status;out_buf1=t.click;copy_to_user(buf,&out_buf,sizeof(out_buf);/将得到的键值拷贝到用户数据区returncount;八.poll系统调用操作接口函数当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,这影响了其他文件的读写,为了避免读写阻塞,一般可以在应用程序中使用poll或者select函数。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标志读写相应的文件,实现非阻塞的读写,poll()函数通过poll系统调用,调用对应设备驱动的poll()接口函数,poll返回不同的标志,告诉主进程文件是否可以读写,这些返回标志存放在includeasmpoll.h中标志含义POLLIN如果设备无阻塞的读,就返回该值POLLRDNORM通常的数据已经准备好,可以读了,就返回该值。通常的做法是会返回(POLLLIN|POLLRDNORA)POLLRDBAND如果可以从设备读出带外数据,就返回该值,它只可在linux内核的某些网络代码中使用,通常不用在设备驱动程序中POLLPRI如果可以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select报告文件发生异常,以为select八带外数据当作异常处理POLLHUP当读设备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。POLLERR如果设备发生错误,就返回该值。POLLOUT如果设备可以无阻塞地些,就返回该值POLLWRNORM设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM)POLLWRBAND于POLLRDBAND类似在本章地驱动程序中,Keypad_poll()函数在缓冲区有新数据时(当head!tail),返回一个POLLIN|POLLRDNORM,告诉主进程有新的九.在设备驱动中实现异步通知虽然大多数时候阻塞型和非阻塞型操作的组合及poll方法可以有效查询设备是否可以读写,但是如果驱动程序能避免主动的查询,改主动为被动的信号通知触发,则可以提高程序的效率,这也就是异步通知的目的。异步通知向进程发送SIGIO信号,通知访问设备的进程,表示该设备已经准备好IO读写了。之后就是如何实现异步通知的问题了,要启动异步通知,必须执行两个步骤:首先,须要制定某个作为文件的“属主”。文件属主的进程ID保存在filp-f_owner中
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 六一玩具活动方案
- 六一糖果画活动方案
- 六一艺术派对活动方案
- 六一节小学活动方案
- 六一赠图书活动方案
- 六一音乐沙龙活动方案
- 六中垃圾分类活动方案
- 六公司招待所团购活动策划方案
- 六年级数学小组活动方案
- 理财试题及答案
- 新能源货车租赁战略合作协议书(2篇)
- 数学教师个人述职报告总结
- 森林防灭火应急处置课件
- 贡菜的栽培技术
- (高清版)DB51∕T 1292-2011 牧草种质资源田间鉴定与评价技术规程
- 2024年11月-矿山隐蔽致灾因素普查
- 刷单合同范本
- CNAS-CL02-A001:2023 医学实验室质量和能力认可准则的应用要求
- 《造血干细胞移植护理》课件
- 2025年非法集资课件:制作与投资者教育新思路
- 北京昌平小升初数学试卷
评论
0/150
提交评论