设备驱动程序是任何操作系统的必不可少的、最保密的一个组成部分,它们实现了计算机系统所有附属设备的一个标准接口,它包含与硬件直接相关的设备驱动。从广义上说,“驱动程序”是指一些函数的集合,这些函数都能对硬件设备进行操作。本文简单分析了μC/OS-Ⅱ下设备驱动的设计与实现。

  设备驱动程序是任何操作系统的必不可少的、最保密的一个组成部分,它们实现了计算机系统所有附属设备的一个标准接口,它包含与硬件直接相关的设备驱动。从广义上说,“驱动程序”是指一些函数的集合,这些函数都能对硬件设备进行操作。驱动程序的概念在没有固定的操作系统的时候,是一个比较模糊的定义。简单地理解就是提供了一个软件到硬件(也可以是虚拟硬件)操作的函数。通常主要应该包括:设备初始化、设备的读写(输入输出)、设备的控制等信息。在μC/OS-Ⅱ下没有统一的设备驱动接口——不像windows或者linux下通过设备文件的定义模式,所以,把一些对硬件操作是通过一般的函数来完成的,叫成“驱动程序”也不为过。

  1. 简介

  外设驱动程序是实时内核和硬件之间的接口,是连接底层硬件和内核的纽带。

  编写驱动程序模块应满足以下主要功能:

  ① 对设备初始化;

  ② 把数据从内核传送到硬件和从硬件读取数据;

  ③ 读取应用程序传送给设备的数据和回送应用程序请求的数据;

  ④ 监测和处理设备出现的异常。由于在μC/OS-Ⅱ下没有统一的设备驱动接口,在该操作系统中设备驱动的设计和实现主要是通过一些对硬件操作的函数来完成。

  2. μC/OS-Ⅱ操作系统启动过程中的硬件初始化

  基于μC/OS-II的应用系统工作时,首先把CPU初始化;接着进行操作系统初始化,主要完成任务控制块(TCB)初始化、TCB优先级表初始化、空任务的创建等;然后开始创建新任务,并可在新创建的任务中再创建其他的新任务;最后调用OSSTART()函数启动多任务调度。

  当μC/OS-Ⅱ实际移植到具体的硬件平台中时,系统初始化时还要进行硬件的初始化。主函数是系统启动首先执行的一个函数,在启动μC/OS—Ⅱ之前,要屏蔽所有中断,并对全局变量初始化,防止运行出错。硬件初始化主要包括中断初始化,串口、键盘、显示等设备初始化。μC/OS—Ⅱ的初始化通过调用OSInit()函数,为OS分配任务队列、优先级状态表和准备状态表,初始化全局变量,并且创建一个空循环任务。接下来,在启动μC/OS—Ⅱ前调用OSTaskCreate()创建所有用户任务,并置准备态,创建任务时,要指定每个任务的优先级、堆栈大小和位置、任务函数入口。调用OS2Start()启动μC/OS—Ⅱ。从就绪队列中找到优先级最高的任务,作为当前任务执行。流程如图所示。

  3. μC/OS-Ⅱ操作系统对硬件的操作和控制

  3.1 函数控制硬件

   前面已经提到过,不像其他的操作系统,在μC/OS—Ⅱ中没有统一的设备驱动接口,因此对硬件的操作和控制可以通过函数来完成。在启动过程中完成硬件初始化后,系统创建一个空循环任务,然后就可以调用OSTaskCreate()创建用户任务,在任务用户任务中选择要控制的硬件,选择最佳的控制方法,调用用户自己编写的函数来完成。

图 系统启动流程

  3.2 BSP

  BSP(板级支持包)是介于底层硬件和操作系统之间的软件层次,它完成系统上电后最初的硬件和软件初始化,并对底层硬件进行封装,使得操作系统不再面对具体的操作。
为μC/OS-Ⅱ编写一个简单的 BSP。它首先设置CPU内部寄存器和系统堆栈,并初始化堆栈指针,建立程序的运行和调用环境;然后可以方便地使用C语言设置硬件的配置环境,并编制相应的操作函数,为操作系统调用提供统一的接口;在CPU、板级和程序自身初始化完成后,就可以把CPU的控制权交给操作系统了。

  4. 实际应用举例

  既然在μC/OS-Ⅱ下没有统一的设备驱动接口,系统对硬件的控制是通过一些对硬件操作的函数来完成的。下面以在μC/OS-Ⅱ实时内核下驱动程序读取A/D的三种方法,分析在实际的工程实践中μC/OS-Ⅱ设备驱动的设计和实现,以及在设计过程中应注意的一些问题。
以一个单片机数据采集系统为例,硬件环境基于C8051F015单片机。A/D转换是单片机数据采集系统的重要组成部分,实时内核下A/D驱动程序的实现过程主要取决于A/D转换器的转换时间。我们首先比较和分析μC/OS-Ⅱ下A/D采样数据的三种方法;其次介绍C8051F015单片机A/D模数转换器的配置及特点;最后,在μC/OS-II内核移植到8位单片机C8051F015的基础上,介绍编写A/D驱动程序的一般思路和方法。

  4.1 μC/OS-II实时内核下的A/D读取方法

  实时内核下,驱动程序采用什么方法读取A/D采样数据是首先考虑的问题。许多因素将影响读取A/D,如A/D的转换时间、模拟值的转换频率、输入通道数等,但最主要的是取决于A/D的转换时间。典型的A/D转换电路由模拟多路复用器(M U X)、放大器和模数转换器(ADC)三部分组成。下面描述读取A/D的三种方法。

  图1所示的是第1种读取方法。假设A/D 转换器的转换时间较慢(5ms以上),应用程序调用图1所示的驱动程序,并传递要读取的通道。驱动程序通过M U X选择要读取的模拟通道(①)开始读。转换前,延时几μs以便使信号通过M U X传递,并使之稳定下来。接着,ADC被触发开始转换(②)。然后驱动程序延时一段时间以完成转换(③)。延时时间必须比ADC转换时间长。最后驱动程序读取ADC转换结果(④),并将转换结果返回到应用程序(⑤)。

  图2所示的是第2种读取方法。当模拟转换完成后,ADC产生的一个中断信号。若ADC转换完成,ISR给信号量发一个信号(⑤),通知驱动程序,ADC已经完成转换。如果ADC在规定的时限内没有完成转换,信号量超时(③),则驱动程序不再等待下去。驱动程序和中断服务子程序(ISR)的伪代码如下:
ADRd(ChannelNumber)
{
选择要读取的模拟输入通道;
等待A M U X 输出稳定;
启动A D C 转换;
等待来自ADC 转换结束中断产生的信号量;
if (超时){
*err=信号错误;
return;
} else {
读取ADC转换结果并将其返回到应用程序 ;
}
}

ADCoversion Complete ISR{  
保存全部CPU 寄存器; /* 将CPU的PSW、ACC、 B、
DPL、DPH及Rn入栈*/
通知内核进入ISR(调用OSIntEnter()或OSIntNesting直接加1);
发送A D C 转换完成信号; /* 利用μC/OS-II内核的
OSSemPost()*/
通知内核退出ISR(调用OSIntExit());
恢复所有CPU 寄存器; /* 将CPU 的PSW、ACC、B、DPL、DPH及Rn出栈*/
执行中断返回指令(即RETI);
}

  在这种方法里,要求ISR执行时间与调用等待信号的时间之和为A/D转换时间。

  如果A/D转换时间小于处理中断时间与等待信号所需的时间之和,则可以用第三种方法。如图3所示,前两步(①②同以上两种方法)结束后,驱动程序接着在一个软件循环中等待(③)ADC直到完成转换。在循环等待时,驱动程序检测ADC的状态(BUSY)信号。如果等待时间超过设定的定时值(软件定时),则结束等待循环(循环等待超时)。如果在循环等待中,检测到ADC发出转换结束的信号(BUSY)时,驱动程序读取ADC转换结果(④)并将结果返回到应用程序(⑤)。

  驱动程序伪代码如下:
ADRd(ChannelNumber){
选择要读取的模拟输入通道;
等待A M U X 输出稳定;
启动A D C 转换;
启动超时定时器;
while (ADC Busy & Counter??0);/* 循环检测 */
if (Counter==0){
*err=信号错误;
return;
} else {
读取ADC 转换结果并将其返回到应用程序 ;
}
}

  A/D 转换速度快,这种驱动程序的实现是最好的。

  4.2 C8051F015单片机A/D模数转换器

   再来简单介绍一下C8051F015单片机A/D模数转换器的配置及特点。

   在C8051F015 单片机中,ADC的转换时钟周期至少在400ns,转换时钟应不大于2MHz。一般在启动ADC之前都要处于跟踪方式,而ADC一次转换完成要用16个系统时钟。另外,在转换之前还要加上3个系统时钟的跟踪/保持捕获时间,所以完成一次转换需19个ADC转换时钟(9.5μs)。图1中的方法简单,转换时间在ms级以上,一般用于变化慢的模拟输入信号,不适用于C8051F015。

  图2中的方法,为了减少μC/OS-II内核调用ISR所用时间,ISR一般都用汇编语言编写。从程序1中ISR伪代码可以看出,尽管ISR用汇编语言编写,代码效率高,但μC/OSII调用ISR的时间与调用等待信号时间之和大于A/D的转换时间,所以C P U用于ISR 和循环检测的开销大。图3 所示的方法显然适合于C8051F015 单片机。其优点是:可以获得快速的转换时间;不需要增加一个复杂的ISR;转换时信号改变时间更短;C P U 的开销小;循环检测程序可被中断,为中断信号服务。

  4.3 A/D驱动程序的编写

  外设驱动程序是实时内核和硬件之间的接口,是连接底层硬件和内核的纽带。编写驱动程序模块应满足以下主要功能:① 对设备初始化;② 把数据从内核传送到硬件和从硬件读取数据;③ 读取应用程序传送给设备的数据和回送应用程序请求的数据;④ 监测和处理设备出现的异常。

  A/D转换电路作为一个模拟输入模块,μC/OS-II内核应把它作为一个独立的任务(以下称为ADTask())来调用。A/D驱动程序模块流程如图4所示。ADInit()初始化所有的模拟输入通道、硬件ADC以及应用程序调用A/D模块的参量,并且ADInit()创建任务ADTask()。ADTb1 [ ]是一个模拟输入通道信息、ADC硬件状态等参数配置以及转换结果存储表。ADUpdate()负责读取所有模拟输入通道,访问ADRd()并传递给它一个通道数。ADRd()负责通过多路复用器选择合适的模拟输入,启动并等待ADC转换,以及返回ADC转换结果到ADUpdate()。

  在μC/OS-II 实时内核下各原型函数、数据结构和常量的定义如下。
INT16S ADRd(INT8U ch);
/* 定义如何读取A/D,A/D必须通过AIRd()来驱动 */
void ADUpdate(void);
/* 一定时间内更新输入通道 */
void ADInit(void);
/* A/D 模块初始化代码,包括初始化所有内部变量(通
过ADInit()初始化ADTb[]),初始化硬件A/D(通过ADInitI())及创建任务ADTask() */
void ADTask(void data);
/* 由ADInit()创建,负责更新输入通道(调用ADUpdate()) */
void ADInitI(void);
/* 初始化硬件A/D */
AD_TaskPrio:设置任务ADTask()的优先级。
AD_TaskStkSize:设置分配给任务ADTask()的堆栈大小。
AD_MaxNummber :AMUX 的输入通道数。
AD_TaskDly:设定更新通道的间隔时间。
AD ADTb1[AD_MaxNummber]:AD 类型的数组(AD 是定义的数据结构)。
4.4 总结

  对于A/D转换器接口电路驱动程序的编写归纳出以下几点:

  ① 在决定采用具体的驱动方案之前,分析设备接口电路的特点,尤其是了解设备的配置和特点;
  ② 对于处理速度快的设备,可能出现CPU的处理速度与设备处理速度不匹配,一般的设备中不带有FIFO缓冲区,须在内存中开辟缓冲区;
  ③ 在应用程序读取设备之前,一定要初始化硬件(调用初始化函数),合理定义硬件的信息和状态变量;
  ④ 不同的外设配置、环境、转换精度等都会影响到设备驱动的设计,要对各个不同的外设进行具体分析。

  5. 结束语

  在μC/OS-Ⅱ下没有统一的设备驱动接口,对硬件的控制和管理是通过函数来实现的。设备的初始化、设备的读写和控制对应各自的函数,实现各自的功能。μC/OS-Ⅱ下开发设备的驱动程序,应该考虑到具体硬件的特征和配置情况,对不同的外设情况选择最佳的硬件控制和管理方法。

  参考文献:
  [1]  Jean J.Labroase 著,邵贝贝 等译,嵌入式实时操作系统μC/OS-Ⅱ[M].北京航空航天大学出版社,2003年5月
  [2]  王田苗,嵌入式系统设计与实例开发—基于ARM微处理器与μC/OS-Ⅱ实时操作系统[M].清华大学出版社,2002.9
  [3]  姚传安,宋寅卯.μC/OS-II 实时内核下的A/D驱动程序设计. 郑州轻工业学院学报.2003.10

本文来源:嵌入式在线    作者:上海交通大学计算机科学与工程系 孙高鑫

转载于:https://blog.51cto.com/2646002/898709

浅析μC/OS-Ⅱ设备驱动的设计与实现相关推荐

  1. 浅析uc/os_II设备驱动的设计与实现

    作者:上海交通大学计算机科学与工程系 孙高鑫 设备驱动程序是任何操作系统的必不可少的.最保密的一个组成部分,它们实现了计算机系统所有附属设备的一个标准接口,它包含与硬件直接相关的设备驱动.从广义上说, ...

  2. linux pcie驱动框架_Linux设备驱动框架设计

    引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Li ...

  3. 君君学Linux设备驱动第一天之概述及开发环境搭建

    一.设备驱动的作用:     1 计算机系统里面的软件和硬件是互相成就的,没有软件的硬件是废铁,没有硬件作为依托的软件是空中楼阁.     2 当应用软件工程师不想了解硬件底层的具体操作的时候,就需要 ...

  4. 转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)

    1.3块设备驱动关键数据结构及函数API详细剖析 经过上节四个步骤我们已经熟悉并实战了一个最基本的过滤块设备驱动的设计技巧,我们这一节先不继续实战,我们本节把上节170行代码中接触到的块设备核心数据结 ...

  5. Linux和Windows设备驱动架构比较

    毕业后一直在学操作系统, 有时候觉得什么都懂了,有时候又觉得好像什么都不懂,但总体来说自认为对操作系统实现机制的了解比周围的人还是要多一些.去年曾花了几个星期的晚上时间断断续续翻译了这篇对Linux和 ...

  6. 《Linux设备驱动开发详解(第2版)》隆重出版

    Linux设备驱动开发详解(第2版)(前一版狂销3万册,畅销书最新升级) [新品] 点击看大图     基本信息 * 作者: 宋宝华       * 出版社:人民邮电出版社     * ISBN:97 ...

  7. Linux设备驱动的分层设计思想

    1.1 设备驱动核心层和例化 在面向对象的程序设计中,可以为某一类相似的事物定义一个基类,而具体的事物可以继承这个基类中的函数.如果对于继承的这个事物而言,其某函数的实 现与基类一致,那它就可以直接继 ...

  8. Linux USB设备驱动程序设计 和 USB下载线驱动设计

    Linux USB设备驱动程序设计 和 USB下载线驱动设计 USB设备驱动模型 USB设备包括配置(configuration).接口(interface)和端点(endpoint),一个USB设备 ...

  9. 基于块的linux驱动程序,基于块的Linux驱动程序 块设备驱动 centos内核编译过程 操作系统课程设计...

    操作系统的课程设计,本人也是一头雾水地做完了课程设计,在这里贴下操作过程,放下当时参考的一篇CSDN文章链接:https://blog.csdn.net/cxy_chen/article/detail ...

最新文章

  1. 有关网页渲染,每个前端开发者都该知道的那点事
  2. springDatasolr 排序
  3. Cordova打包的Vue项目在IOS无法拉起支付宝和微信支付
  4. c++-initializer_list
  5. 7 种分布式全局 ID 生成策略,你更爱哪种?
  6. osgi框架 android,基于OSGi的Android应用模块动态加载框架设计与实现
  7. 突然!高通骁龙855 Plus公布:手机厂商们集体沸腾
  8. import java.util_importjava.util.*;classKeyMaster{publi..._考试资料网
  9. 做前端开发,30 岁前能买上房子吗?
  10. Unicode16 与 UTF-8编码之间的转换
  11. 没学过JavaScript也能看懂的闭包解释
  12. wps分析工具库如何加载_分析为周杰伦打榜的夕阳红老年团,告诉你他们真实年龄!...
  13. 【百度头条】精准微营销—本地离线92GBQQ群数据库,包含全部版本
  14. 计算机视觉教程7-3:Openpose配置与实践
  15. Protel Dxp2004 创建库
  16. 【Python实战项目】全球疫情数据采集 + 可视化展示
  17. 首开先河 | 脑机接口让这位ALS患者可读可写
  18. js等待加载转圈圈效果
  19. java压缩mp4大小_压缩的mp4视频播放时间太长(exoplayer)
  20. 干货 | 这些年,我用的绘图工具大合集

热门文章

  1. 单词长度 (c语言)
  2. 自媒体怎样做能一天赚到1000块?
  3. 跨国协作区块链,深挖产业数据潜能
  4. 买房子不要一次就谈好价格,多砍几次价能省不少钱
  5. 新浪微博和博客有什么区别?
  6. DataQL之语法-词法记号
  7. (下)苹果有开源,但又怎样呢?
  8. 第146章 SQL函数 TO_POSIXTIME
  9. 科技融合教育!重庆本土英语机构荣获雅思论坛奖项
  10. 唯创知音的WTN6系列芯片