欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > PPT文档下载  

    嵌入式操作系统驱动程序编写基础.ppt

    • 资源ID:6158776       资源大小:4.08MB        全文页数:123页
    • 资源格式: PPT        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    嵌入式操作系统驱动程序编写基础.ppt

    嵌入式操作系统-驱动程序编写基础,李春杰,主要内容,设备驱动硬件基础内核模块中断服务例程驱动程序组成,设备驱动的作用,驱动是应用软件和硬件的桥梁它使得应用软件只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作。驱动程序沟通着硬件和应用软件,而驱动工程师沟通着硬件工程师和软件工程师。,无操作系统的程序架构,在这样的系统中,虽然不存在操作系统,但设备驱动程序一般存在。一般情况下,驱动程序都会定义为一个软件模块,包含.h文件和.C文件,前者定义该设备驱动程序的数据结构并声明外部函数,后者进行驱动的具体实现。,无操作系统时驱动和应用程序的关系,在没有操作系统时,设备的接口被直接提交给用用软件。应用软件直接调用、访问设备驱动的接口设备驱动的接口函数与硬件的功能直接吻合,没有任何附加功能。两种不合理设计驱动中存在部分应用,或应用中包含驱动都会导致程序移植性和复用性变差,有操作系统的设备驱动,有操作系统的设备驱动的变化无操作系统时设备驱动的硬件工作仍然必不可少,没有它们驱动不可能与硬件打交道需要把驱动融入内核。为了实现这种融合,需要在驱动程序中设计面向系统内核的接口。这样的接口有操作系统规定,对一类设备而言结构一致,独立于具体的设备。设备驱动程序成为硬件和内核的桥梁-要写一部分内核接口,多出来两层结构,有利有弊,屏蔽了底层的差异,Linux的设备驱动,Linux设备字符设备块设备网络设备,无操作系统的LED驱动,Linux的led驱动,Linux设备驱动的重点、难点,Linux设备驱动设计的硬件基础,计算机系统的硬件主要由CPU、存储器和外设组成。驱动针对的对象是存储器和外设(包括CPU内部集成的存储器和外设)。注意不是针对CPU核,Linux设备驱动设计的硬件基础,1、处理器,Linux设备驱动设计的硬件基础,2、存储器,Linux设备驱动设计的硬件基础,3、总线与接口串口 电器特性、时序特性(掌握)、信号分类I2C有时序,无协议SPICAN有协议,有时序USBPCI网口,Linux设备驱动设计的硬件基础,Linux设备驱动设计的硬件基础,原理图分析:确定设备使用的相关资源时序分析:芯片手册芯片结构、整体性能、管件个数、电气性能寄存器编程外设控制器的编程流程,Linux设备驱动设计的硬件基础,仪器仪表使用万用表示波器等等,内核模块概述,Linux的内核模块机制给内核提供了很强的灵活性,用户通过加载内核模块可以方便的给内核添加功能;同样用户也可以将内核不需要用的功能卸载。驱动程序可以静态编译进内核,但由于外设众多,内核不可能包含所有设备的驱动。用户通过内核模块机制可以把需要用到的驱动程序动态地加入内核。,内核模块的概述,内核模块在内核空间运行,内核模块编程是在内核空间编程。内核模块可以引用内核空间导出的全局符号,因此内核模块编程与内核的版本密切相关。内核模块只能调用和使用内核提供的函数,不能使用相关的应用程序库函数。,模块编程举例-一个简单的hello 模块,为何不用printf函数语句?,上面例子是一个完整的内核模块,该模块被载入内核时会向系统的日志中写入Hello,world;当模块被卸载时,该模块会向系统日志写入一条Goodbye,cruel world的信息。,内核模块的基本结构1,一个典型的内核模块包含以下几部分:头文件声明:头文件module.h和init.h是必须的。module.h包含了加载模块需要的函数和符号的定义;init.h包含了模块初始化和清理函数的定义;如果模块在加载时允许用户传递参数,模块还应该包含moduleparam.h头文件。,内核模块的基本结构2,模块许可声明:MODULE_LICENSE宏声明此模块的许可证,否则在模块被加载时,内核会显示警告。初始化、清理函数声明:在2.6内核模块必须调用宏module_init和module_exit去注册初始化与清理函数,需要注意的是初始化清理函数必须在宏module_init和module_exit使用前定义满足以上基本结构,模块就可以正常工作了,在驱动程序中除包含模块的三个基本部分以外,还会包含文件操作及其它内容。可以在模块中包含的其他描述性定义有 MODULE_AUTHOR(声明谁编写了模块),MODULE_DESCRIPION(一个人可读的关于模块做什么的声明),等其它MODULE_宏;各种 MODULE_ 声明可以出现在你的源码文件的任何函数之外的地方.但是,一个内核代码中相对近期的惯例是把这些声明放在文件末尾.,模块的初始化与关闭,模块初始化函数注册模块提供的相关功能.这些功能,我们指的是新功能,可以由应用程序存取的或者一整个驱动或者一个新软件抽象.实际的初始化函数定义常常如:static int _init initialization_function(void)/*Initialization code here*/module_init(initialization_function);每个非试验性的模块也要求有一个清理函数,它注销接口,在模块被去除之前返回所有资源给系统.这个函数定义为:static void _exit cleanup_function(void)/*Cleanup code here*/module_exit(cleanup_function);如果你的模块没有定义一个清理函数,内核不会允许它被卸载.,初始化中的错误处理 如果在你注册工具时发生任何错误,首先第一的事情是决定模块是否能够无论如何继续初始化它自己.常常,在一个注册失败后模块可以继续操作,如果需要可以功能降级.在任何可能的时候,你的模块应当尽力向前,并提供事情失败后具备的能力.如果证实你的模块在一个特别类型的失败后完全不能加载,你必须取消任何在失败前注册的动作.,清理注册,另一种编程如下:,模块的编译,在linux2.6内核中,模块的编译需要配置过的内核源代码,没有源代码无法进行内核模块编译工作。编译、链接后生成的内核模块后缀为.ko。编译过程中首先会到内核源码目录下,读取顶层的makefile文件,然后返回模块源代码所在的目录编译。,模块的编译,模块的makefile二,模块的加载,编译好模块后用户可以利用超级用户的身份可以将内核模块加载到内核中。常见的实用程序insmod、rmmod、lsmod、modprobe,直接在内核源码树下完成模块的构建,直接在内核源码树下完成模块的构建,对于上面的示例建立模块的步骤,接下来在内核的配置文件中增加新的选项:在./driver/char/kconfig文件中增加针对example的配置选项当设置完kconfig文件并且保存在./driver/char/子目录下后,上述的代码片段最终在当前的操作系统内核配置选项中增加了新的一项config_examples相应的配置程序需要通过下面的命令进行调用,然后进行相关配置完成相关配置选项的配置工作,接下来修改./driver/char/子目录的makefile,添加example选项,指导内核配置选项config_example完成example的构建工作。,驱动模块的生成,完成上述工作以后,构建example设备驱动程序的基础设施就准备好了,利用上述步骤完成构建的好处是,不管何时调用内核构建工作,相应的驱动程序都会自动被构建。只要在相应配置选项中进行相应选择,就可以完成设备驱动程序的构建相应的命令行举例:,把驱动程序模块安装在文件系统相应的目录中。命令举例(把模块装在coyote-target目录),加载设备驱动模块,对外设的操作-1.轮询,2.中断,外设的操作时间一般较长,并且具有时间的不确定性,驱动程序向外设发出操作指令后,驱动程序采用两种方式等待操作完成。轮询模式(polling mode)CPU重复检查(轮询)设备的状态寄存器,直到寄存器的值表明I/O操作已经完成为止中断模式(interrupt mode)如果I/O控制器能够通过IRQ线发出I/O操作结束的信号,就可以使用中断模式,轮询模式的简单例子不好,可以用来粗略的判断超时,若时间比较长,比如ms级,可以在每次轮询操作之后调用schedule主动放弃CPU,直到下次被调度再次轮询,中断模式好处,外部设备通过中断线将产生的中断通知处理器,在驱动程序采用中断模式工作前必须要申请注册一个中断号。要注册给外设注册一个外部中断号,必须要确定外设使用的那个中断号,确定中断号可以通过:指定外设中断号通过检测函数检测中断号,从而确定要注册的外部中断号编写相关的中断服务例程ISR,确定外部中断号,自动检测IRQ:内核在中声明了两个用于自动检测IRQ的函数,两个函数使用结构如下:,检测IRQ函数举例,注册外部中断,中断程序的注册一个模块被希望来请求一个中断通道(或者 IRQ,对于中断请求),在使用它之前要注册它,并且当结束时释放它.函数声明在,实现中断注册接口:,中断处理程序:一个中断处理的角色是给它的设备关于中断接收的回应并且读或写数据,根据被服务的中断的含义.第一步常常包括清除接口板上的一位;让大部分硬件设备不产生别的中断直到它们的“中断挂起”位被清除.即action.中断处理程序与普通c代码没有太大不同,不同的是中断程序在中断期间运行,有如下限制:不能向用户空间发送或接收数据-发生中断的上下文是当前的用户空间,如果发数据,会污染用户空间不能执行有睡眠操作的函数不能调用调度函数-中断优先级很高,内核不支持中断中调度,每一个中断服务例程都应该尽快地释放处理器,把能够推迟的工作尽量后推。中断处理程序的上半部和下半部上半部分会立即被内核执行下半部分会被推迟执行:下半部的执行并不需要指明一个确切时间,只要把这些任务推迟,让它们在系统不太繁忙并且中断恢复后执行就可以了。,中断处理程序的上半部和下半部的划分:如果一个任务对时间非常敏感,将其放在上半部执行如果一个任务和硬件相关,将其放在上半部中执行如果一个任务要保证不被其他中断(特别是相同的中断)打断,将其放在上半部中执行其他所有任务,考虑放置在下半部执行,中断处理程序下半部的实现机制:软中断Tasklet:基于软中断来实现,但比软中断接口简单,同步要求较低;软中断保留给执行频率及时间要求高的下半部使用。工作队列,1、软中断(32g)的主要数据结构,在softirq_vec中定义在softirq_vec数组中每一项对应一个软中断,系统中最多可以有32个软中断。,优先级对应于softirq_vec的下标,软中断函数及其参数,kernel/softirq.c,include/linux/interrupt.h,内核只预定义了6个中断,优先级0:处理高优先级的tasklet和下半部分,优先级2:把数据包传送到网卡,优先级3:从网卡接受数据包,优先级5:处理tasklet,优先级1:与时钟中断相关的tasklet,优先级4:块设备相关,优先级6:调度SMP相关,include/linux/interrupt.h,软中断的初始化,初始化软中断函数软中断初始注册函数 open_softirq接受三个参数,软中断的序号(如nr置为0)、软中断处理函数以及传递给软中断处理函数的参数,软中断的触发与执行,软中断处理程序被注册后,触发软中断的函数为raise_softirq;该函数接受要被激活的软中断序号最为参数。例如:raise_softirq(NET_TX_SOFTIRQ)使该软中断的处理函数net_tx_action处于可运行状态该软中断处理函数net_tx_action会在内核下次执行软中断do_softirq()时被执行 自学linux内核的软中断机制?,2、Tasklet机制,Tasklet是I/O驱动程序中实现可延迟函数的首选方法-比软中断好建立在HI_SOFTIRQ和TASKLET_SOFTIRQ等软中断之上Tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中数组的每一项针对一个CPU,代表这个CPU上的tasklet列表分别由tasklet_action和tasklet_hi_action处理找到CPU对应的那个项,遍历执行,0:enable0:disable,include/linux/interrupt.h,next指针指向下一个tasklet,它用于将多个tasklet连接成一个单向循环链表。,func是一个函数指针,指向一个可执行的tasklet代码段,data是func函数的参数。自己写的,Tasklet的静态定义,Tasklet的使用,当需要使用tasklet时,可以按照如下方法进行1、分配一个tasklet的数据结构,并初始化=相当于声明(定义)一个tasklet2、可以禁止/允许这个tasklet=相当于定义了一个是否允许使用tasklet的窗口3、可以激活这个tasklet=这个tasklet被插入task_vec或者task_hi_vec的相应CPU的链表上,将在合适的时机得到处理调度运行Tasklet_scheduleTasklet_hi_schedule,tasklet使用,如下:/定义一个处理函数void my_tasklet_function(unsigned long);/定义了一个名叫my_tasklet的tasklet并将其与处理函数绑定,而传入参数为dataDECLARE_TASKLET(my_tasklet,my_tasklet_function,data);在需要调度tasklet的时候引用一个tasklet_schedule()函数就能使系统在适当的时候进行调度运行:tasklet_schedule(,Tasklet使用模板,3、工作队列workqueue,工作队列和tasklet这两种下半部机制的主要区别在于:Tasklet在软中断的上下文中运行,所有的代码必须是原子的,不能睡眠、不能使用信号量或其它产生阻塞的函数;工作队列在一个内核线程上下文运行,并且可以在延迟一段确定的时间后才执行;有更多的灵活性,它可以使用信号量等能够睡眠的函数。,工作队列的使用(使用内核默认队列),工作队列:使用方法和tasklet相似,如下:struct work _struct my_wq;/创建一个工作队列my_wqvoid my_wq_func(unsigned long);/定义一个处理函数通过INIT_WORK()可以初始化这个工作队列并将工作队列与处理函数绑定,如下:INIT_WORK(/初始化工作队列并将其与处理函数绑定同样,使用schedule_work(&my_irq);来在系统在适当的时候需要调度时使用运行。,中断模式的简单例子,假定实现一个简单的输入字符设备的驱动程序当在对应的设备文件上发出read()系统调用时,一条输入命令被发往设备的控制寄存器在一个不可预知的长时间后,设备把一个字节的数据放在输入寄存器驱动程序然后将这个字节作为read()系统调用的结果返回一般我们在第一次打开设备时使用request_irq注册中断,在最后一次关闭时使用free_irq释放中断。,这个驱动程序包含两个函数:实现文件对象read方法的foo_read()函数处理中断的foo_interrupt()函数,只要用户读设备文件,foo_read()函数就会被触发,对I/O设备发出读命令,等待读操作的结束,由中断处理程序唤醒,将获得的数据送到用户空间中,从设备上读入数据,唤醒read的剩余部分,再看foo_read的输入参数Struct file*filp,在这个数据的私有数据项中,VFS已经将其转换成设备驱动程序的私有的信息foo_dev_t,被定义为包含如下信息:一个信号量,互斥使用intr作为标志0:没有发生/处理中断1:处理了中断一个等待队列,用来给foo_read睡眠一个数据区,长度为1,用来存放读到的数据,char*buf,用户提供的存放数据的空间Count和ppos都没有用到再看看foo_interrupt()中,这是通过foo一个全局变量获得设备的私有数据结构的,这个数据结构与foo_read()中通过filp中获得的私有数据一致foo_interrupt的输入参数没有得到使用,这是一种很普遍的情况,驱动程序分析,设备驱动程序是操作系统最基本的组成部分,是linux内核的一部分,一般情况下可以将驱动程序设计为一个模块,在需要时动态加载到内核中。Linux将硬件设备看作一个特殊的文件来操作,该文件被称为设备文件;系统通过对设备文件的读写等操作,实现对外设的读写等操作驱动程序是设备文件与直接外设间的桥梁,Linux驱动的层次结构,用户通过操作设备文件对硬件设备进行访问。驱动程序包括设备服务子程序和中断处理程序两部分:设备服务程序包含设备操作相关的程序代码;中断处理程序负责处理设备通过中断方式向驱动程序发出的输入/输出请求。,用户进程,文件系统(设备文件),设备驱动程序,设备服务程序,中断处理程序,硬件设备,设备文件,通过设备号来寻找驱动程序,设备号,Linux系统使用设备号来标识设备文件。设备号分为主设备号和从设备号。主设备号是同一类设备的标识,对应着一个驱动程序-如优盘从设备号是在驱动程序中来指示某个物理设备的实例,从设备号使得不同的物理实例可以使用同一个驱动程序-如每个盘都编号不一样,设备文件,设备文件这是存放在文件系统中的实际文件索引节点不对磁盘上的数据块编址,而是包含硬件设备的一个标识每个设备文件包括:名字、类型(字符/块)设备号(主设备号:次设备号),同一设备驱动程序,不同设备,mknod()系统调用用来创建老式的设备文件,设备文件名。在/dev目录下产生一个文件名,操作权限和设备类型。其中设备类型指定:S_IFCHR或S_IFBLK。inod,设备号16位,主设备号:次设备号可以指定设备号,注册设备驱动程序,注册一个设备驱动程序意味着把它与对应的设备文件连接起来使得对设备文件发出的系统调用可以由内核转化为相应的设备驱动程序对应的函数按照设备驱动程序模型,分配一个新的device_driver描述符,对应到设备文件上访问一个没有注册设备驱动程序的设备文件将会返回错误码-ENODEV,注册时机如果设备驱动程序被静态编译进内核,则注册发生在内核初始化阶段如果作为一个内核模块来编译,则在装入模块的时候注册(并在卸载模块时注销),字符设备驱动程序的注册,内核中每个字符设备都对应一个 cdev 结构的变量:struct cdev struct kobject kobj;/每个 cdev 都是一个 kobject struct module*owner;/指向实现驱动的模块 const struct file_operations*ops;/操纵这个字符设备文件的方法 struct list_head list;/与 cdev 对应的字符设备文件的 inode-i_devices 的链表头 dev_t dev;/起始设备编号 unsigned int count;/设备范围号大小;,字符设备初始化,字符设备的注册函数字符设备的撤销函数,字符设备的注册,字符设备驱动程序注册,在Linux内核中存在一个数组chrdev来保存所有字符设备驱动程序信息,该数组定义在fs/char_dev.c文件中。,字符设备有关的数据结构,大部分的基础性的驱动操作包括 3 个重要的内核数据结构,称为 file_operations,file,和 inode.file_operations文件操作File打开的文件inode由内核在内部用来表示文件,file_operations文件操作(include/linux/fs.h),File数据结构,dev_t i_rdev;对于代表设备文件的节点,这个成员包含实际的设备编号.struct cdev*i_cdev;struct cdev 是内核的内部结构,代表字符设备;这个成员包含一个指针,指向这个结构,当节点指的是一个字符设备文件时.,字符驱动程序的结构,在linux系统中,设备的操作演变成对文件的操作,因此设备驱动程序最重要的工作就是编写fops指针对应的文件操作函数。每个打开的设备文件均与一组函数关联。字符驱动程序一般提供以下几个入口:以及相应的file_operations类型的文件操作接口定义,给驱动模块添加相关文件操作驱动程序的文件操作主要包括打开、关闭、读、写以及release等上述hello事例添加后程序代码如下:,示例说明:增加了一个新的内核头文件,用于获取文件系统操作的定义在hello_init()函数中增加了一个新的函数调用register_chrdev的函数调用,该函数调用把设备驱动程序注册到内核;它将类型为struct file_opretions结构,将设备函数与来自文件系统的请求绑定在一起当用户应用程序打开某个设备驱动程序表示的设备时,并进行读取数据操作(使用read()函数),文件系统就会把read()函数和模块的hello_read()对应起来,建立相应设备文件设备文件是应用程序和驱动程序(模块)绑定的桥梁命令举例建立了一个名称是hello1的设备文件,由于是以字符变量为基础的设备驱动程序,因此用C表示设备节点的类型,主版本号是234,辅助版本号是0(操作系统一般不用),通过设备文件操作设备驱动模块运用设备驱动程序,运行结果,块设备编程基础,块设备的注册,主设备名,设备名,被激活后的磁盘就可以进行相关操作了,块设备的程序结构,驱动程序的使用,

    注意事项

    本文(嵌入式操作系统驱动程序编写基础.ppt)为本站会员(牧羊曲112)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开