引论:我们为您整理了13篇驱动程序设计范文,供您借鉴以丰富您的创作。它们是您写作时的宝贵资源,期望它们能够激发您的创作灵感,让您的文章更具深度。
篇1
一、引言
嵌入式系统的硬件组成除了包括中心控制部件嵌入式系统处理器外,还有输入输出装置以及一些扩充装置开关、按键、传感器、模数转换器、LCD显示器、触摸屏及LED(发光二极管)等嵌入式硬件设备。嵌入式硬件环境是整个嵌入式操作系统和应用程序运行的硬件平台,不同的应用通常有不同的硬件环境[1]。硬件平台的多样性是嵌入式系统的一个主要特点。如何让这些设备工作起来,是嵌入式驱动程序的任务。由于I/O设备种类繁多,工作模式各不相同,驱动程序是整个嵌入式软件设计中最复杂、代码量最大、最繁琐的部分。
二、嵌入式驱动程序概述
外设驱动程序可以对系统提供访问设备的接口,把操作系统(软件)和设备(硬件)分离开来。当设备改变的时候,只需要更换相应的驱动程序,不必修改操作系统的内核以及运行在操作系统中的软件。系统的驱动程序要受控于相应的操作系统的多任务之间的同步机制。在操作系统中使用信号量、邮箱等机制进行协调。操作系统只和特定的驱动抽象层通讯,无论在抽象层下面对应的是什么类型的设备,对操作系统和用户的应用程序来说都是统一的接口[2]。驱动抽象层位置图如图1所示。
图1:驱动抽象层位置图
该部分包括基于ARM架构的串行口通讯、键盘驱动原理、I/O接口电路、A/D接口电路、LCD的驱动控制、触摸屏程序设计的内容。
三、嵌入式驱动程序设计研究
1.串行口通讯
通过监视串行口,把我们在程序中插入的想要反馈程序运行情况的串行口语句输出到显示器中的超级终端中。这样便可以实时监控程序的运行情况,方便调试程序。串行口组件设计是将接收到的字符再发送给串口(计算机与开发板是通过超级终端通讯的),即按PC键盘通过超级终端发送数据,开发板将接收到的数据再返送给PC,在超级终端上显示[3]。
要想设计好串行口驱动程序,需要做如下几步:
(1)熟悉串口通讯原理;
(2)查阅ARM串口寄存器文档,包括S3C2410控制、状态和数据寄存器;
(3)查阅电平转换芯片资料(max3232);
(4)设计硬件电路图;
(5)设计串口驱动(包括串口寄存器初始化,发送接收函数等)。
异步通信必须遵循的3项规定为:
(1)字符的格式
(2)波特率
(3)校验位
初始化时需要设置波特率、停止位、奇偶校验、数据位等参数。异步串行方式是将传输数据的每个字符一位接一位(例如先低位、后高位)地传送。数据的各不同位可以分时使用同一传输通道,因此串行I/O可以减少信号连线。如图2给出了异步串行通信中一个字符的传送格式。
图2:串行通信字符格式
开始前,线路处于空闲状态,送出连续“1”。传送开始时首先发一个“0”作为起始位,然后出现在通信线上的是字符的二进制编码数据。每个字符的数据位长可以约定为5位、6位、7位或8位,一般采用ASCII编码。后面是奇偶校验位,根据约定,用奇偶校验位将所传字符中为“1”的位数凑成奇数个或偶数个。也可以约定不要奇偶校验,这样就取消奇偶校验位。最后是表示停止位的“1”信号,这个停止位可以约定持续1位、1.5位或2位的时间宽度。至此一个字符传送完毕,线路又进入空闲,持续为“1”。经过一段随机的时间后,下一个字符开始传送才又发出起始位。
2.键盘驱动原理
键盘的设计是将一个瞬时接触开关放置在每一行与每一列的交叉点。矩阵所需的键的数目显然根据应用程序而不同。每一行由一个输出端口的一位驱动,而每一列由一个电阻器上拉且供给输入端口一位。键盘扫描过程就是让微处理器按有规律的时间间隔查看键盘矩阵,以确定是否有键被按下。一旦处理器判定有一个键按下,键盘扫描软件将过滤掉抖动并且判定哪个键被按下。每个键被分配一个称为扫描码的唯一标识符。应用程序利用该扫描码,根据按下的键来判定应该采取什么行动。如图3所示为逐行扫描法工作原理:逐根行线输出0电平,而其他行线保持高电平;同时检测列,列全1就没有键按下,0有键按下。行线和列线状态组合在一起就确定了是哪个键按下,如110 1110是0键按下,1010111是7键按下。
图3:逐行扫描法原理图
3.I/O接口电路
I/O系统的目标是对RTOS和应用程序员隐藏设备特定的信息,并且对系统的I/O设备提供一个统一的访问方法。下面是从不同角度看I/O系统:
(1)从系统软件开发者角度看,I/O操作意味着与设备的通信、对设备编程初始化和请示执行设备与系统之间的实际数据传输以及操作完成后通知请求者。系统软件工程师必须理解设备的物理特性,如寄存器的定义和设备的访问方法。
(2)从RTOS的角度看,I/O操作意味着对I/O请求定位正确的设备,对设备定位正确的设备驱动程序,并解决对设备驱动程序的请求。有时要求RTOS保证对设备的同步访问。RTOS必须进行抽象,对应用程序员隐含设备的特性。
(3)从应用程序员角度看,目标是找到一个简单、统一和精练的方法与系统中出现的所有类型的设备。
I/O接口的编址方式分为两种:
(1)I/O接口独立编址――端口映射方式
这种编址方式是将存储器地址空间和I/O接口地址空间分开设置,互不影响。设有专门的输入指令(IN)和输出指令(OUT)来完成I/O操作。
(2)I/O接口与存储器统一编址方式――内存映射这种编址方式不区分存储器地址空间和I/O接口地址空间,把所有的I/O接口的端口都当作是存储器的一个单元对待,每个接口芯片都安排一个或几个与存储器统一编号的地址号。也不设专门的输入/输出指令,所有传送和访问存储器的指令都可用来对I/O接口操作。
4.A/D接口电路
A/D转换器能将模拟量转换为数字量的电路;D/A转换器能将数字量转换为模拟量的电路。A/D转换器和D/A转换器是沟通模拟电路和数字电路的桥梁,也可称之为两者之间的接口。A/D 转换器是模拟信号源和CPU 之间联系的接口,它的任务是将连续变化的模拟信号转换为数字信号,以便计算机和数字系统进行处理、存储、控制和显示。一般A/D转换过程要经过采样、保持、量化和编码四个步骤。前两步在取样―保持电路中完成,后两步则在A/D转换器中完成。D/A转换器的基本原理是将输入的每一位二进制代码按其权的大小转换成相应的模拟量,然后将代表各位的模拟量相加,所得的总模拟量就与数字量成正比,这样便实现了从数字量到模拟量的转换。
A/D转换器较常用的是逐次逼近型,如图4所示。转换前,先将SAR寄存器各位清零。转换开始时,控制逻辑电路先设定SAR寄存器的最高位为“1”,其余位为“0”,此试探值经D/A转换成电压Vc,然后将Vc与模拟输入电压Vx比较。如果Vx≥Vc,说明SAR最高位的“1”应予保留;如果Vx
图4:逐次逼近式A/D转换原理图
5.LCD的驱动控制
LCD显示器的基本原理就是通过给不同的液晶单元供电,控制其光线的通过与否,从而达到显示的目的。因此,LCD的驱动控制归于对每个液晶单元的通断电的控制,每个液晶单元都对应着一个电极,对其通电,便可使光线通过。
液晶模块有两种工作模式:图形方式和文本方式。在图形方式下,模块上的缓冲区映射的是液晶屏上显示的图形点阵;在文本方式下,模块上的缓冲区对应的是液晶屏上显示的文本字符,包括英文字符和英文标点符号。因为汉字字库没有包含在液晶模块之中,所以液晶屏在文本方式下只能显示英文,不能显示汉字。液晶屏的操作主要包括:初始化、设置液晶屏的工作模式(文本或者图形)、更新显示、开启(或者关闭)背光等。
6.触摸屏程序设计
触摸屏是专门处理是否有笔或手指等物体按下触摸屏,平常相互绝缘的两层导电层就在触摸点位置有了一个接触,并在按下时分别给两组电极通电。因其中一面导电层(顶层)接通X轴方向的5V均匀电压场,使得检测层(底层)的电压由零变为非零,控制器侦测到这个接通后,其对应位置的模拟电压信号经过A/D转换送回处理器。经过坐标转换后,得到触摸点的x/y坐标。
触摸屏接口专用芯片ADS7843是Burr-Brown公司生产的,能够完成电极电压的切换及采集接触点处的电压值,并进行A/D转换。在完成一次x/y坐标采样的过程中需要一次模式转换即在点击触摸屏之前是等待中断模式,当有触摸动作产生触摸屏中断以后,在x/y的坐标采集驱动中设置成自动的x/y位置转换模式,在完成采集以后再转换回等待中断模式,准备下一次的触摸采样。
四、总结
嵌入式驱动程序设计渗透在掌上电脑、笔记本电脑和手机等各个领域,这些设备要想正确工作,必须借助相应硬件及其驱动程序。随着嵌入式系统在工业控制领域、智能机器人、移动通讯以及智能家电、网络家电的应用,驱动程序设计方面的开发人员需求量也会越来越多。
基金项目:北京信息科技大学《嵌入式系统安全》课程建设项目
[参考文献]
[1]王小妮、魏桂英、杨根兴. 嵌入式组件设计[M].北京航空航天大学出版社,2012
篇2
Abstract:This paper introduces the design of Device Drivers of PCI synchronous clock card based on WDF model. Briefly introduces the system architecture and works on our own PCI synchronous clock card, and Analysis the framework of the WDF model and the design process. Focused on the research and development of the WDF Device Drivers based on the PCI synchronous clock card, including hardware access, Interrupt notification. The driver has passed the test for stability and reliability.
Key words: WDF; PCI; interrupt; driver; synchronous clock card.
时间是科学实验、科学研究和工程技术等领域中的一个基本物理参量。为了保证系统各部分时间的一致性和正确性,系统内各设备的同步时钟从卡从时钟源获取高精度的标准时间,提供给相应设备。这样系统内各设备的时间与时间源相同而保持一致。同步时钟卡一般采用PCI总线方式。PCI总线能够实现设备间的快速访问,它以突出的性能受到计算机和通信界工程师们的青睐。
因此如何开发出稳定、可靠、高效的PCI设备驱动程序成为驱动工程师们面临的一个棘手的问题[5]。过去对于PCI设备驱动程序的开发大多采用WDM(Windows Driver Model)框架,但是它编程比较复杂,快速掌握其开发要领对于初学者来说比较困难[5]。本文所述的PCI同步时钟卡的驱动程序的开发采用微软最新推出的WDF(Windows Driver Foundation)驱动模型。WDF驱动模型提供事件驱动和面向对象的驱动程序开发框架,大大降低了设备驱动程序的开发难度[5]。
1 同步时钟卡系统架构
本文所述的驱动程序是基于自行研发的PCI同步时钟卡,其原理框图如图1所示。本同步时钟卡选择PCI9052芯片做为PCI总线的接口芯片。该电路除了用到PCI9052外,还用到了单片机、EEPROM、双口RAM、CPLD。单片机是系统的控制单元;串行EEPROM存储了PCI9052芯片所需要的配置信息;双口RAM用于PC机与时钟卡之间交换数据;CPLD用于200us时标的产生和中断的控制。
同步时钟卡的工作流程如下:同步时钟从卡接收时钟源输出的时间信号,单片机将其解析成高精度的同步时间信息,控制逻辑(CPLD)通过1PPS脉冲信号产生200us的高精度时间刻度,于是产生高精度的同步绝对时标,连续存储于双口RAM中,最后计算机通过PCI总线接口获取高精度的绝对时间。本系统中计算机获取双口RAM中的时间数据的方式有两种:(1)PC机主动读取双口RAM中的数据。(2)外部事件通过中断通知PC机事件发生,PC机收到通知后读取双口RAM中的时间信息,可获得外部事件发生的精确时刻。两种方式分别涉及驱动程序的硬件访问和中断通知。于是涉及到本文介绍的重点:基于WDF模型的PCI总线驱动程序的开发。
2 WDF驱动程序设计
微软对过去的WDM(Windows Driver Model)驱动程序的架构做了改进,形成了全新的WDF(Windows Driver Foundation)框架结构。它将原来普通软件开发中面向对象的技术应用到了驱动程序的开发中。WDF改变了驱动程序与操作系统内核之间的关系,在传统的WDM驱动程序中,不仅要处理硬件,还要处理驱动程序与操作系统内核之间的交互[4]。现在WDF则使驱动程序与操作系统内核独立开来,驱动程序与操作系统交互工作将由框架内封装的方法(函数)去完成,这样驱动开发工程师只需专注处理目标硬件的行为即可,避免了两面不周顾此失彼的弊端。不仅大大降低了驱动程序的代码量,还使整个系统更加稳定、可靠。
WDF驱动程序包括两个类型,一个是内核级的,称为KMDF(Kernel-Mode Driver Framework);另一个是用户级的,称为UMDF(User-Mode Driver Framework)。本文所述的驱动程序采用KMDF模式。
2.1 WDF驱动程序开发流程
本文所用开发环境为Microsoft Windows Driver Kit(WDK) 8.1和Microsoft Visual Studio 2013,操作系统为Windows7。先安装VS2013,再安装WDK8.1,便可在VS2013中直接创建KMDF工程。根据同步时钟卡所需功能编写好驱动程序即可进行编译。
WDF驱动程序框图如图2所示。
2.2 基于WDF模型的PCI设备驱动程序的实现
WDF模型的设备驱动程序从功能上可分为三个部分:初始化设备、控制设置与交换数据[3]。初始化设备主要实现设备的识别、驱动对象与设备对象的建立与硬件资源的分配;控制设置负责应用程序与驱动程序的连接和设备的打开;交换数据处理的是设备功能的具体应用,即PCI总线与同步时钟卡之间的数据传输。
从本质上来说,WDF模型的设备驱动程序是由入口函数DriverEntry和事件例程及其子函数组成的[3]。操作系统在第一次加载驱动程序时会通过调用DriverEntry例程来完成设备驱动程序和框架的初始化[3]。所有的驱动程序都必须包含一个DriverEntry例程。对于不同类型的驱动程序其入口函数DriverEntry也不同,可分为:设备驱动、纯软件驱动与过滤驱动。本文所述的PCI总线驱动程序属于设备驱动,在入口函数DriverEntry中,主要完成两件事:注册EvtDriverDeviceAdd回调例程、创建和初始化WDFDRIVER对象。
WDF_DRIVER_CONFIG_INIT(&config,PCIdriverEvtDeviceAdd);
//注册EvtDriverDeviceAdd回调例程
status = WdfDriverCreate(DriverObject, RegistryPath,...);
//创建驱动对象
2.2.1初始化设备
在驱动程序被成功初始化完成之后,操作系统会顺序调用EvtDriverDeviceAdd、EvtDevicePrepareHardware等回调例程以实现所控制的设备的初始化。
当首次枚举设备时,EvtDriverDeviceAdd例程在系统初始化时被PnP管理器调用。在系统运行过程中,任何时候一个新的相同设备被枚举,系统都将调用此例程。EvtDriverDeviceAdd例程是设备初始化过程中最新被调用的回调例程,它需要完成:设备对象的创建,创建符号链接或设备对象GUID接口,创建一个或多个I/O队列,各种事件的回调函数的注册,如即插即用、电源管理、I/O处理例程等[1]。
EvtDriverDeviceAdd例程的主要代码如下所示:
注册即插即用基本例程:
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = PCIDriverEvtDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = PCIDriverEvtDeviceReleaseHardware;
..........
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
创建设备对象:
WDF_FILEOBJECT_CONFIG_INIT(&f_config,...);
WdfDeviceInitSetFileObjectConfig(DeviceInit, &f_config,WDF_NO_OBJECT_ATTRIBUTES);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, ..);
status = WdfDeviceCreate(&DeviceInit, &attributes, &control_device);
创建队列对象并注册回调例程:
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,....);
ioQueueConfig.EvtIoDeviceControl = PCIdriverEvtIoDeviceControl;
ioQueueConfig.EvtIoStop = PCIdriverEvtIoStop;
status = WdfIoQueueCreate(control_device,&ioQueueConfig,...);
创建符号链接:
status = WdfDeviceCreateSymbolicLink(control_device, &ustring);
创建中断对象:
deviceContext = GetDeviceContext(control_device);
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,PCIDriverEvtInterruptIsr,
PCIDriverEvtInterruptDpc);//设置中断服务例程和延迟过程调用WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&interruptAttributes,..);
status = WdfInterruptCreate(control_device,&interruptConfig,
&interruptAttributes,&deviceContext->Interrupt);
EvtDriverDeviceAdd例程调用完成之后,系统将调用EvtDevicePrepareHardware例程初始化地址指针,将设备所占用的I/O地址和内存地址映射为虚拟地址,驱动程序将通过这些虚拟地址完成与设备的数据传输。由于串行EEPROM存储了PCI9052芯片所需要的配置信息,系统将自动为本文所述的PCI同步时钟卡分配资源,它们包括双口RAM的内存地址、PCI9052的I/O地址空间,所以EvtDevicePrepareHardware例程必须将这些资源映射为虚拟地址。对于I/O端口,只需将首地址与地址数目值保存在设备上下文;对于存储器芯片,调用MmMapIoSpace函数将物理地址映射为系统内核虚拟地址,然后保存于设备上下文。相对应的,当设备被卸载时,系统会自动调用EvtDeviceReleaseHardware回调例程释放之前申请的硬件资源。
for (i = 0; i < WdfCmResourceListGetCount(ResourceListTranslated); i++) {//WdfCmResourceListGetDescriptor函数获取该资源的描述符
descri = WdfCmResourceListGetDescriptor(ResourceListTranslated, i);
switch (descri->Type)
{case CmResourceTypeMemory:
Mem_Count++;
if (Mem_Count == 2)//将双口RAM地址映射为虚拟地址
{pDevice_context->MemBaseAddress = MmMapIoSpace(
descri->u.Memory.Start,
descri->u.Memory.Length,
MmNonCached);
pDevice_context->MemLength = descri->u.Memory.Length;}
break;
case CmResourceTypePort://将PCI9052的I/O地址映射为虚拟地址
pDevice_context->Io_baseAddress = descri->u.Port.Start.LowPart;
pDevice_context->Io_length = descri->u.Port.Length;
default:
break;}}
2.2.2控制设置与数据交换
应用程序实现和驱动程序通信的过程是:应用程序首先调用CreateFile函数打开设备,然后可以使用DeviceIoControl和驱动程序通信,包括写数据给驱动程序和从驱动程序读数据两种情况,也可以用WriteFile写数据给驱动程序或用ReadFile从驱动程序读数据,当应用程序退出时,调用用CloseHandle关闭设备。本文所述的系统是用DeviceIoControl和驱动程序通信。CreateFile打开设备的方式有两种:符号链接名与GUID接口,本文所述驱动程序采用的是符号链接名的方式。
m_hDevice=CreateFile(sLinkName,...);//以符号链接名的方式打开设备
上述代码中sLinkName为符号链接名,它与驱动程序中设置的符号链接名相同。m_hDevice为返回的设备的有效句柄,应用程序就可以应用它调用DeviceIoControl函数与驱动程序交换数据。应用程序的请求会被放入请求队列中,并在EvtIoDeviceControl函数之中被处理。
本文中应用程序获取时钟卡上的时间信息的方式有两种:(1)直接读取。(2)中断方式。
对于第一种方式,应用程序直接调用DeviceIoControl函数与驱动程序交换数据。由于系统的双口RAM被映射到虚拟内存,驱动程序可以使用下面两条指令对双口RAM进行读写: READ_REGISTER_XXX;//读双口RAM,WRITE_REGISTER_XXX;//写双口RAM。
对于中断方式,当被捕获的外部事件发生时,驱动程序会进入中断服务例程EvtInterruptIsr,然后进入延时过程调用EvtInterruptDpc,首先清中断源,然后将双口RAM中的时间数据读取到设备上下文中缓存,该数据即为外部事件发生的时间,最后通知应用程序读取该数据。应用程序将调用DeviceIoControl函数获取设备上下文中的时间信息。驱动程序与应用程序通信的方法有两种:DeviceIoControl异步完成和WIN32事件通知。本文所述系统采用WIN32事件通知的方法。对于此种方法,应用程序初始化时首先生成一个通知事件,并通过DeviceIoControl函数的输入缓冲区发送给驱动程序,驱动程序创建相应的内核事件,同时使能PCI9052的LINT1中断,当该事件发生时,驱动程序会通知应用程序,应用程序的一个子线程不停的循环等待驱动程序发来的事件发生通知。当设备被卸载时需要撤销该内核事件。具体主要代码如下:
应用程序生成通知事件:
mhEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
应用程序子线程中等待事件发生:
while (WaitForSingleObject(mhEvent, 0) != WAIT_OBJECT_0)
{...}
驱动程序创建相应的内核事件:
ObReferenceObjectByHandle(....);
允许PCI中断,使能PCI9052的本地LINT1中断,pREG为PCI9052映射的I/O空间的基 地址:
inter = READ_PORT_USHORT(pREG + 0x4c);
inter |=0x43;
WRITE_PORT_USHORT(pREG + 0x4c, inter);
清中断源,设置PCI9052的CS3引脚有效,通知CPLD清掉LINT1信号:
inter = READ_PORT_USHORT(pREG + 0x50);
inter |= 0x800;
WRITE_PORT_USHORT(pREG + 0x50, inter);
驱动程序给应用程序发送事件,通知应用程序读取数据:
KeSetEvent(pDevice_context->Event, 0, FALSE);
驱动程序内撤销内核事件:
ObDereferenceObject(...);
3 结语
驱动程序是硬件与应用程序通信的桥梁,它对系统性能提升的作用举足轻重。高效、稳定、可靠的驱动程序可以使系统性能得到很好的提升。
本文简要介绍了PCI同步时钟从卡的工作原理,并重点讨论了基于WDF模型的PCI设备驱动程序设计方法。本文所述的PCI同步时钟卡驱动程序,在WDK8.1中成功编译,自动生成SYS文件(驱动程序代码)和INF文件(设备安装信息),成功安装并且能够稳定可靠地运行。经测试,捕获的时间精度达到误差小于200us,满足系统设计要求。涉及本驱动程序的系统已应用于三峡大坝左岸发电厂发变机组的故障录波系统中,运行稳定可靠。总而言之,WDF驱动模型优化并简化了设备驱动程序的开发,比传统的WDM驱动模型更加稳定。
参考文献
[1]武安河.Windows设备驱动程序WDF开发[M].北京:电子工业出版社,2009.
[2][美]Ronald D. Reeves 著,张猛等 译.Windows设备驱动程序开发[M].北京:人民邮电出版社,2012.
篇3
0 引 言
设备互联(PCI)总线是一种先进的高性能局部总线,可同时支持多组设备[1]。CPCI总线应用于工业和嵌入式领域,其规范改进自PCI规范,CPCI规范在电气方面兼容PCI规范,只是在封装结构上进行了加强,CPCI板的封装结构基于IEC 60297?3,IEC 60297?4以及IEEE 1101.10定义的欧式板卡外形[2]。既然电气特性上兼容PCI规范,因此CPCI驱动程序的设计本质就是PCI驱动程序设计。
当前Windows环境下用于PCI设备驱动开发的工具主要是DDK,DriverStudio以及WinDriver。前两者功能强大,但是开发者需要熟知操作系统的体系结构、汇编语言和设备驱动程序结构体系方法,还需要具备丰富的驱动程序开发经验,否则可能造成软件不稳定甚至系统崩溃,另外前两者开发周期长。而Jungo公司开发的WinDriver改变了传统的驱动程序开发方法,其整个驱动程序中的所有函数都是工作在用户态下,使开发者不需要掌握前两者所需的预备知识就可以开发出与之相媲美的程序[3]。
为了实现在主控计算机和信号处理板之间快速通信,采用了CPCI并行总线技术,信号处理板采用内嵌PCI模块的DSP6416芯片。软件开发基于Windows平台和VC++6.0编程环境,为了便于应用程序调用驱动程序,按照模块化的软件设计思想,驱动程序以DLL动态链接库的形式封装。为提高工作效率、缩短开发周期,开发工具选用WinDriver。
1 WinDriver简介
WinDriver是一套设备驱动程序开发组件,它的目的就是方便程序员快速开发出PCI,ISA,CPCI,PCIE等设备的Windows驱动程序[4]。
1.1 WinDriver原理
WinDriver的体系架构分为两种模式:用户模式和内核模式。对硬件进行操作时,开发者应用程序调用WinDriver用户模式的库函数,用户模式的库函数再调用WinDriver内核,WinDriver内核再调用操作系统底层函数实现对硬件的最终访问。其与硬件模板、用户驱动程序、用户应用程序之间的关系即体系架构见图1[5?6]。对于某些在用户模式下不能实现的高性能硬件驱动程序,可通过WinDriver的内核插件功能实现:在用户模式下完成编程和调试,不做任何修改,直接将该高性能要求的程序模块植入内核插件,WinDriver即从内核模式下调用该程序模块。
1.2 WinDriver特点
作为一款实用的驱动程序开发工具包,WinDriver的主要优点和特征如下:
(1)通过内核插件功能(Kernel PlugIn)能够实现用户模式的易用和内核模式的高性能;
(2)友好的驱动向导允许不写一行代码即可实现硬件诊断;
(3)支持所有PCI/PCMCIA/CardBus/ISA/EISA/CompactPCI/PCIExpress设备,与制造商无关;
(4)可以利用常见的软件开发平台包括MSDEV/VisualC/C++,Borland Delphi,Visual Basic6.0等;
(5)开发者不需要知道DDK,ETK,DDI及任何其他系统层面的编程知识;
(6)支持I/O、DMA中断处理和直接访问板卡映射的存储器;
(7)支持多CPU及多PCI总线平台。
1.3 用WinDriver开发驱动程序
利用WinDriver开发驱动程序有2种方式:通过驱动程序向导生成驱动程序框架,再对框架程序进行修改和调试;直接编写驱动程序。
通过驱动程序向导开发步骤:板卡检测、诊断;生成驱动程序框架;调试、编译驱动程序。
直接编写代码方式步骤:
(1)包含WinDriver相关的头文件;
(2)WinDriver库函数调用,WinDriver库函数典型调用流程见图2[7]。
2 CPCI信号处理板卡驱动程序设计
2.1 硬件环境
实现PCI总线协议一般有2种方法:一是用FPGA设计实现,由于PCI协议比较复杂,实现较困难;二是采用专业PCI总线控制芯片,如AMCC公司的S5933、PLX公司的PCI 9080等通用PCI接口芯片[8]。本信号处理板采用第二种方法,选用自带PCI接口模块的DSP6416,主控计算机上的应用程序通过驱动程序将控制命令字主动写入DSP内存,实现主控计算机对信号处理板的控制;信号处理板结果数据处理完毕后,向主控计算机发中断,驱动程序响应该中断,并主动读取指定DSP内存获取结果数据。
结果数据和模块状态信息存入L2缓存单元,主控计算机下发的命令字也写入L2缓存单元。结果数据缓存划分为大小各为28 KB的Block1和Block2两块区域;模块状态信息缓存大小为24字节;控制命令缓存大小为1 B。DSP中与PCI操作有关的缓存定义见表1。
表1 DSP中与PCI操作有关的缓存定义
当Block1缓存填满后,新的结果数据存入Block2缓存,同时DSP给主控计算机发PCI中断,主控计算机通过PCI接口读取Block1;同理当Block2填满后,新的结果数据存入Block1缓存,主控计算机通过PCI读取Block2。Block1和Block2缓存交替接收结果数据。
2.2 CPCI驱动程序DLL接口设计
为了便于应用程序访问驱动程序,按照模块化的软件设计思想,驱动程序以DLL动态链接库的形式进行封装,应用程序通过与驱动程序DLL之间的接口来访问信号处理模块的板上资源,下发控制命令、获取结果数据。主要接口及其功能描述如下:
(1)DSP6416DLL_Init(CWnd* pMainWnd):打开并注册WDC库、打开设备,初始化中断;
(2)DSP6416DLL_SendCmd(BYTE BCommand):主控计算机中的控制命令数据写入DSP中命令存储区;
(3)DSP6416DLL_ReadState(BYTE *StateData):从DSP的状态存储区读取信号处理板的状态数据;
(4)DSP6416DLL_ReadResult(BYTE *ResultData):从DSP的Block1或Block2数据缓存区读取结果数据;
(5)DSP6416DLL_Exit():关闭中断,关闭设备,关闭WDC库。
2.3 CPCI驱动程序实现
在硬件环境和接口、驱动程序封装形式及其软件接口确定后,剩下的工作就是CPCI驱动程序的实现。该工作主要内容为驱动程序DLL各接口函数的编码实现和WinDriver库函数调用。
2.3.1 文件包含
包含与WinDriver相关的头文件:windrvr.h,windrvr_int_thread.h,wdc_lib.h。
2.3.2 驱动程序初始化
驱动程序初始化主要工作和步骤包括:打开WinDriver,WinDriver授权、版本号检查、板卡检测、板卡信息获取、模块配置、板卡注册和PCI中断使能,驱动程序初始化流程见图3。
2.3.3 驱动程序向DSP内存写数据
DSP6416的PCI接口支持四种类型的数据交换[9?10]:从模式写,外部PCI主设备通过PCI接口写数据到DSP;从模式读,外部PCI主设备通过PCI接口读取DSP中的数据;主模式写,DSP主设备通过PCI接口向外部设备写数据;主模式读,DSP主设备通过PCI接口向外部设备读数据。
4 结 语
通过实际应用,发现用WinDriver开发的本驱动程序运行稳定可靠,达到了主控计算机对信号处理板实时控制,特别是信号处理板中大容量数据实时上传的目的。由于系统方案确定了CPCI并行总线作为通信手段,硬件设计时采用了自带主从式PCI接口模块的DSP6416芯片,驱动开发工具选择了快速高效的WinDriver工具包,以及对驱动程序形态进行DLL封装,本驱动程序从需求设计到完成编码和调试不到一个月的时间,在保证软件质量的同时,缩短了研制周期,提高了开发效率。
参考文献
[1] 李贵山,戚德虎.PCI局部总线开发者指南[M].西安:西安电子科技大学出版社,1997.
[2] PICMG. Compact PCI core specification PICMG 2.0 R3.0 [R]. [S.l.]: PICMG, 1999.
[3] 王磊,鲁新平,李吉成.WinDriver在开发基于PLX9056芯片的PCI设备驱动程序中的应用[J].现代电子技术,2006,29(18):77?79.
[4] 李静,赵保军.基于TMS320C6416内嵌PCI设备驱动程序开发[J].微机发展,2005,15(10):135?137.
[5] Jungo Ltd. WinDriver PCI/ISA/CardBus v8.02 user’s guide [R]. US: Jungo Ltd, 2005.
[6] 简育华.基于WinDriver的 PCI驱动程序开发[J].火控雷达技术,2011,40(1):68?70.
[7] Jungo Ltd. WinDriver PCI/PCMCIA/ISA v8.02 Low?Level API Reference[R]. US: Jungo Ltd, 2005.
篇4
Designed of PCI Device Driver Based on DDK
ZHAO bin1, TIAN Ze1, CHEN Jia2
(1.Xi'an Shiyou University, Xi'an 710065, China; 2. Xidian University, Xi'an 710072, China)
Abstract: This paper takes a PCI device as an example, describes WDM drivers and PCI bus protocols. And introducing a method of WDM driver design base on DDK(Driver Development Kit), realizing identification and testing function.
Key words: WDM; PCI; DDK; device driver
因为某项目的需求,需要在PC机上调试一PCI设备,在Windows系统下实现主机通过PLX9056桥芯片对PCI设备的访问,但前提条件是当操作系统装载驱动程序正确的情况下。如果驱动程序装载不正常,主机首先就不能够识别PCI设备;如果驱动程序运行不正常,用户就不能正确的访问PCI设备。因此,PCI驱动程序是实现主机识别板卡以及正确访问设备的关键。
目前常用的驱动开发工具有:微软提供的DDK(Device Driver Kits),以及第三方厂商提供的DriverStudio和WinDriver,现在大多数驱动开发人员都是使用DriverStudio和WinDriver,其中DriverStudio将DDK函数按照逻辑功能组织,把很多常用功能封装成类,建立了一个基于C++语言的面向对象的编程环境,大大降低了开发难度和开发周期;WinDriver将一些基本的操作如存储读写、I/O端口读写、中断服务、DMA操作等进行了封装,开发者只需编写一个外壳来调用这个驱动程序,就可以完成对硬件设备的访问。前两种开发工具使用简单,开发速度快,但开发的驱动程序执行效率受到限制。DDK虽然编程难度较大,对编程人员的要求也较高,但是功能强大,编程灵活,使用范围广,可应用于各类硬件驱动程序的编写,而且有助开发人员深刻理解驱动程序和WDM(Windows Driver Mode),所以DDK是更理想的开发工具。
1 WDM式设备驱动的概述
1.1 WDM概念
设备驱动程序需与操作系统最底层进行交互,不同的操作系统底层结构对应于不同的设备驱动程序模型,也影响设备驱动程序的兼容性。在Windows 2000以后,微软公司加入了新的驱动程序模型,这就是WDM。
1.2 WDM式驱动的基本结构
WDM模型中,两个驱动设备对象分别是物理设备对象(Physical Device Object,简称PDO)和功能设备对象(Function Device Object,简称FDO)。其关系是“附加”与“被附加”的关系。
当某个设备插入PC机时,PDO会自动创建。确切的说,PDO是由总线驱动创建的。PDO需要配合FDO一起使用,系统会提示检测到新设备,要求安装驱动程序。需要安装的驱动程序就是WDM程序,此驱动程序就是创建FDO并附加到PDO之上。当一个FDO附加在PDO上的时候,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO靠近物理设备,被称作底层驱动,而FDO接近发出I/O请求,被称作高层驱动。并且从WDM驱动的设计思路可以看出,WDM驱动是支持即插即用的。
在FDO和PDO之间还会存在过滤驱动,在FDO上面的过滤驱动被称作上层过滤驱动,在FDO下层的驱动,被称作下层过滤驱动。在WDM模型中,过滤驱动不是必须存在的,PDO和FDO是必须的。
2 PCI总线协议
PCI总线协议是PC上最基本的总线,一般显卡、网卡都设计成PCI总线设备。其他总线都是挂在PCI总线上的。例如,ISA总线是通过PCI-ISA桥设备挂在PCI总线上,而USB总线是通过USB HOST设备挂在PCI总线上的。
2.1 PCI总线简介
PCI(Pheripheral Component Interconnect)总线是当前最流行的总线之一,是由Intel公司首先推出的一种局部总线。它定义了32位数据地址总线,并且可以扩展为64位,其支持突发读写操作,也同时可以支持多组设备。
在当前的PC体系结构内,几乎所有外部设备采用的各种各样的接口总线,均是通过桥接电路挂接在PCI系统内的。在这种PCI系统中,Host/PCI桥称为北桥,连接主处理器总线到基础PCI局部总线。PCI-ISA桥称为南桥,连接基础PCI总线到ISA总线。其中南桥通常还含有中断控制器、IDE控制器、USB控制器和DMA控制器等设备。
2.2 PCI配置空间简介
PCI有三个相互独立的物理地址空间:设备存储器地址空间、I/O地址空间和配置空间。配置空间是PCI所特有的一个物理空间。由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。
系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置,实现真正的即插即用。
3 开发实例
Windows从总体上分为内核模式(Kernel Mode)和用户模式(User Mode)。驱动程序运行在内核模式下,拥有操作系统的最高权限。编写驱动程序主要是为了操作硬件设备,这些硬件设备的操作主要包括访问物理映射内存、设备端口等。而应用程序是运行在用户模式下,应用程序无法直接与硬件设备通信,必须借助于驱动程序。
所开发的PCI设备应用软件和驱动软件驻留在PC机中,其中应用软件与特定的子系统有关,应用软件通过调用PCI驱动软件实现子系统功能要求。应用软件和驱动软件在Windows下的关系如图2所示。
3.1 PCI设备驱动程序
PCI模块驱动软件是实现PCI板卡与宿主机(PC机)应用软件间的接口控制与数据传递的专用软件,它可提供PCI板卡与PC机各类消息数据的读、写支持,以及对PCI板卡内部程序的调度。
PCI驱动程序开发软件是DDK,用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为IRP(I/O Request Package,即输入输出请求包)数据结构,不同IRP数据会被“派遣”到不同的派遣函数(Dispatch Function)。
驱动软件的入口程序是DriverEntry,在DriverEntry中包含了AddDevice函数的设置。PCI设备是属于被动加载的设备,操作系统必须加载PDO后调用AddDevice例程,而AddDevice例程就是负责创建FDO并附加到PDO之上。
其次,DriverEntry必须加入IRP_MJ_PNP的派遣回调函数。IRP_MJ_PNP就是即插即用IRP,是由即插即用管理器发送给PCI驱动程序的。
当启动PCI设备的时候,设备管理器将IRP_MN_START_DEVICE发送给PCI驱动。在处理完此IRP后,驱动程序会将处理结果存储在IRP的设备堆栈中。程序可从当前堆栈就可以获取PDO从PCI配置空间中的有用信息,如中断号、设备物理内存及IO端口信息等。因为实际应用中,不能提前知道这些描述符在数组中出现的位置,因此在实现中必须用循环先把资源值提取到一组局部变量中,然后在处理这些资源信息。
对于枚举PCI设备资源,主要分为四类,分别是设备端口、设备物理内存、DMA、中断等。在实现中,程序里枚举了I/O端口资源,物理内存资源和中断资源。其中,I/O端口资源包括I/O端口地址、I/O端口地址长度,物理内存资源包括基地址0和基地址2,中断资源包括中断请求级、中断向量、CPU亲缘关系、中断模式以及共享中断等。
在设备即将停止的时候,即插即用管理器将发送IRP_MN_REMOVE_DEVICE给PCI驱动,这个IRP标志着设备即将关闭,PCI驱动在此时会做一些资源回收工作。
因此开发PCI设备驱动最重要的一步就是将IRP_MN_START_DEVICE中获取的设备资源记录下来,以便以后使用。
3.2 PCI设备应用程序与驱动程序的通讯
PCI设备应用程序的开发软件是VC6.0,在应用程序中寻找设备是通过设备接口和设备号决定的,其实现过程主要是通过SetupDiXX系列函数得到设备接口。
在实际应用中,PLX9056的本地配置寄存器映射在PLX9056配置寄存器中基地址寄存器0,待测试设备的内存空间由硬件设计人员映射在PLX9056的基地址寄存器2。因此,在应用程序中需定义供用户使用的通过基地址0和基地址2的读写接口函数。例如:
基本的读函数有:
ULONG ReadFromBase0(HANDLE handle,ULONG Offset,
UCHAR *buff,ULONG length);
ULONG ReadFromBase2(HANDLE handle,ULONG Offset,
UCHAR *buff,ULONG length);
基本的写函数有:
ULONG WriteToBase0(HANDLE handle,ULONG Offset,
UCHAR *buff,ULONG length);
ULONG WriteToBase2(HANDLE handle,ULONG Offset,
UCHAR *buff,ULONG length);
应用程序可以通过Win32 API DeviceIoControl操作设备与驱动程序互相通信。DeviceIoControl内部会使操作系统创建一个IRP_MJ_DEVICE_CONTROL类型的IRP,然后操作系统会将这个IRP转发到派遣函数中。
而以上的读写接口函数的本质就是调用DeviceIoControl来实现的。例如从基地址0读函数ReadFromBase0函数原型,如图3。
其中,DDK定义的DeviceIoControl的函数原型为:
BOOL DeviceIoControl{
HANDLE hDevice,//设备句柄
DWORD dwIoControlCode, //控制码
LPVOID lpInBuffer,//输入数据缓冲区指针
DWORD nInBufferSize, //输入数据缓冲区长度
LPVOID lpOutBuffer, //输出数据缓冲区指针
DWORD nOutBufferSize,//输出数据缓冲区长度
LPDWORD lpByteReturned,//输出数据实际长度单元长度
LPOVERLAPPED lpOverlapped //重叠操作结构指针
};
DeviceIoControl的第二个参数是I/O控制码,控制码也称IOCTL值,是一个32位的无符号整型。DDK提供一个宏CTL_CODE,定义为:
CTL_CODE(DeviceType,Function,Method,Access)
其中,DeviceType为设备对象的类型;Function为驱动程序定义的IOCTL值,0x000到0x7ff为微软保留,0x800到0xfff由程序员自己定义;Method是操作模式,其中包括METHOD_BUFFERED为使用缓冲区方式操作,METHOD_IN_DIRECT为使用直接写方式操作,METHOD_OUT_DIRECT为使用直接读方式操作,METHOD_NEITHER为使用其他方式操作;Access为访问权限。
如上述例子ReadFromBase0函数,在驱动程序中用CLT_CODE宏定义定义的IOCTL码:
#define IOCTL_READ_BASE_BAR0 CTL_CODE(
FILE_DEVICE_UNKONWN,
0x800,
METHOD_BUFFERED,
FILE_ANY_ACCESS)
驱动程序中IOCTL派遣函数的实现是首先得到当前I/O堆栈,从I/O堆栈中再一次得到输入缓冲区大小,输出缓冲区大小,以及IOCTL。在实现过程中,运用C语言中的switch语句分别处理不同的IOCTL。在每个IOCTL情况下,就必须使用DDK提供的内核函数WRITE_REGISTER_XX系列函数和READ_REGISTER_XX系列函数操作物理设备内存。具体流程如图4所示。
当应用程序需要操作读写接口函数时,设备管理器就会发送相对应的IRP给设备驱动,驱动程序就会调用DispatchControl函数找到相应的IOCTL码,应用程序再调用DeviceIoControl操作设备。
4 结束语
本文以实际应用的一个PCI设备驱动开发为例着重介绍了驱动程序和应用程序的相互关系,并且还简单介绍了WDM式设备驱动和PCI总线协议。设备驱动程序是Windows操作系统重要的内核组建,在系统中起至关重要。如果驱动程序出错,很容易使Windows操作系统崩溃。开发利用DDK开发驱动程序能够使开发者加深对Windows内核和WDM规范的理解。
参考文献:
[1] 张帆,史彩成.Windows驱动开发技术详解[M].北京:电子工业出版社,2009.
[2] Walter Oney.Programming The Microsoft Windows Driver Model[M].1999.
篇5
USB 协议是1994年底由康柏、IBM、英特尔等几家公司联合提出来的外部总线接口协议。USB就是英文中Universal Serial Bus(通用串行总线)的缩写。USB总线具有其他总线所不具备的如:热插拔、数据传输可靠、扩展方便、成本低等一系列特点,因此在嵌入式系统中被广泛使用。
一个USB系统一般是由一个USB主机控制器、一个或多个USB集线器和一个或多个USB设备节点组成。USB系统的物理连接具有层次性。USB总线连接USB设备和USB主机,是一种星型拓扑结构。USB的拓扑结构如图1所示。
在一个USB系统传输数据的过程中有两个非常重要的概念,就是USB传输模式和USB描述符。USB传输模式是指USB设备传输数据的形式。USB设备支持四种传输模式:控制传输模式、同步传输模式、中断传输模式和批量传输模式。控制传输模式是用来处理USB主端口到USB从端口的数据传输,主要是设备控制指令、设备查询状态指令和确认指令。同步传输模式是指传输和时间关系密切的信息所使用的一种传输方式,是一种周期的、连续的单向传输方式。中断传输模式这类传输模式主要用于传输非周期性的、自然发生的、数据量很小的信息,这类数据传输的方向是从设备到主机,适用于键盘、鼠标、操纵杆等设备上。最后一种是批量传输模式,该模式适用于大量的、对时间没有要求的数据传输,如U盘或者移动硬盘等设备。
USB设备在逻辑上分为几个层次,分别是设备层(Device)、配置层(Config)、接口层(Interface)、端点层(Endpoint)。各个层次都有与之相对的描述符,分别是设备描述符、配置描述符、接口描述符和端点描述符。
2 Linux下的USB驱动框架
USB设备的设备描述符在Linux系统中用usb_device_descriptor结构体表示,它描述了USB设备的一般信息。配置描述符用usb_config_descriptor结构体表示,它给出了USB设备的配置信息。接口驱动程序是在一个配置内给出一个接口信息,它在Linux中由usb_interface_descriptor结构体表示。端口描述符被主机用来决定每个端口的带宽需求,它在Linux系统中由usb_endpoint_descriptor结构体表示。
编写一个USB驱动程序,是从usb_driver结构体开始的。Linux中模块加载函数调用usb_register()和usb_unregister()从而对usb_driver结构体进行加载与卸载。如果某个设备信息与该驱动中usb_device_id usb_mouse_id_table 结构体的信息相一致,则会调用usb_driver中探测成员函数probe(),将初始化USB断点信息,并对设备做一些初始化工作,分配urb结构体,准备数据传输。其urb处理大致框架结构如图2所示。
当鼠标设备在用户空间打开时,将提交 probe 函数构建的 urb 请求块,urb 将开始为传送数据而忙碌了。urb 请求块就像一个装东西的“袋子”,USB 驱动程序把“空袋子”提交给 USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过 USB core 交还给 USB 驱动程序,这样一次数据传输就完成了。
3 结束语
由于USB简单方便快捷等优点,许多外接设备会越来越青睐USB接口,这是一种发展的趋势。Linux系统具有开源、安全等特性,用户也在急剧增加。届时,会有越来越多的USB驱动加入Linux内核之中。
参考文献
[1]Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman等.LINUX设备驱动程序[M].北京:中国电力出版社,2006.
[2]Universal Serial Bus Specification Compaq,Intel,Mi―crosoft,NEC Revision 1.1.September 23,1998.
[3]温卡特斯瓦兰.精通Linux驱动程序开发[M].北京:人民邮电出版,2009.
[4]胡晓军,张爱成.USB接口卡发技术[M].西安:西安电子科技大学出社,2005:15-17.
作者简介
篇6
1.1 WDM模式(Windows Driver Model)
Windows2000对驱动程序的编写不再基于以往的Win3.x和Win9x下的VxD(虚拟设备驱动程序)结构,而是基于一种新的驱动模型——WDM(Windows Driver Model)。
WDM为Windows98/2000/XP操作系统的设备驱动程序的设计提供了统一的框架。WDM来源于Windows NT的分层32位设备驱动程序模型(layered 32-bit device driver model)。它支持更多的特性,如即插即用(PnP)、电源管理、WMI和NT事件。
1.2 设备驱动程序
设备驱动程序是操作系统的一个组成部分,它由I/O管理器(I/O Manager)管理和调动。Windows2000操作系统下的I/O管理器功能描述如图1所示。
I/O管理器每收到一个来自用户应用程序的请求就创建一个I/O请求包(IRP)的数据结构,并将其作为参数传递给驱动程序。驱动程序通过识别IRP中的物理设备对象(PDO)来区别是发送给哪一个设备。IRP结构中存放请求的类型、用户缓冲区的首地址、用户请求数据的长度等信息。驱动程序处理完这个请求后,在该结构中填入处理结果的有关信息,调用IoCompleteRequest将其返回给 I/O管理器,用户应用程序的请求随即返回。访问硬件时,驱动程序通过调用硬件抽象层的函数实现。
1.3 DriverStudio工具简介
NuMega Lab公司开发的DriverStudio是一整套开发、调试和检测Windows平台下设备驱动程序的工具软件包。它把DDK(Device Development Kit)封装成完整的C++函数库,根据具体硬件通过向导生成框架代码,并且提供了一套完整的调试和性能测试工具SoftICE、DriverMonitor等。
2 应用实例
本文利用PCI专用接口芯片PCI9052设计了一个数据传输控制卡。卡上主要的芯片有PCI9052、FIFO(CY7C4221)、CPLD(MAX7064S)和A/D转换器(MAX1197)。传输卡硬件框图如图2所示。面阵CCD得到的视频信号经过调理电路,生成的视频调理信号通过A/D转换器进行数字化处理,送入FIFO中。在CPLD的控制下,数据经过PCI9052送入PCI总线,再传送到计算机内存中,并显示在监视器上。驱动程序必须实现如下几个基本功能:(1)硬件中断;(2)能支持应用程序获取数据;(3)能根据外部FIFO(CY7C4221)的状态启动或停止突发传输。
在数据输入过程中,最重要的是对数据进行实时控制,因此需要硬件中断。在中断程序中,根据外部FIFO状态完成数据的读入。
2.1 用DriverWizard生成驱动程序框架
DriverStudio中的DriverWorks软件为开发WDM程序提供了一个完整的框架。它包含一个可快速生成WDM驱动程序框架的代码生成向导工具DriverWizard,而且还带有许多类库。在用DriverWizard生成的程序框架中写入相对于设备的特定代码,编译后即可得到所需的驱动程序。
在利用DriverWorks V2.7的向导Driver Wizard完成驱动程序的框架时共有11个步骤,其中关键步骤有:
(1)在第四步中选中PCI,并在VendorID和DeviceID中分别输入厂商号和设备号,还需填入PCI Subsystem ID和PCI Revision ID。这四项可以用网上的免费软件PCITree或PCIView浏览PCI设备,用这两个软件也可以得到BAR0~BAR5的资源分配情况和中断号。
(2)第七步IRP队列排队方法,它决定了驱动程序检查设备的方式。本设计选SystemManaged,则所有的IRP排队都由系统(即I/O管理器)完成。
(3)第九步是最关键的一步。首先在Resources中添加资源,在name中输入变量名,在PCI Base Address中输入0~5的序列号。0~5和BAR0~BAR5一一对应。在设置中断对话框中,在name栏写入中断服务程序的名称,选中创建中断服务程序ISR?穴Create ISR?雪,不选创建延迟程序调用DPC(Create DPC),选中Make ISR/DPC class functions,使ISR/DPC成为设备类的成员函数。
其次选中Buffer以选取读写方式,用于描述与I/O操作相关的数据缓冲区。本设计需要快速传送大量数据,因此采用Direct I/O方式。
(4)在第十步中,需要加入与应用程序或者其他驱动程序通信的I/O控制代码参量。
2.2 驱动程序模块框图和代码分布
PCI设备驱动程序模块包括配置空间的访问模块、IO端口模块、内存读写模块和终端模块等。各模块之间是对等的。驱动程序模块框图如图3所示。
驱动程序初始化模块代码段放在#pragma code_seg(″INT″)和#pragma code_seg()之间。在系统初始化完成后,这部分代码从内存中释放,防止占用系统宝贵的内存资源。#pragma code_seg()之后是驱动程序和系统的许多模块的实现部分。这部分在驱动程序运行后不会从内存中释放。
2.3 驱动程序主要模块的实现
(1)配置空间的访问模块
DriverWorks的KPciConfiguration类封装了访问PCI设备配置空间的所有操作。首先初始化这个类的实例:
KpciConfiguration PciConfig()m_Lower.TopOfStack());
/?觹m_Lower是 KpnpLowerDevice类的对象。m_LowerTopOfStack()返回当前设备堆栈顶部的设备对象。*/
初始化完后可以直接利用成员函数 ReadHeader/ WriteHeader函数访问所有的配置寄存器。
为了确定映射空间的类型和大小,先向目标基地址寄存器写入0Xffffffffh,然后回读该寄存器的值。如果最低位为1,表示映射于I/O空间,反之为存储空间;如果映射于存储空间,从第四位开始计算0的个数可以确定内存空间的大小;如果是I/O方式,从第二位开始计算0的个数可确定I/O空间的大小,最大为256字节。如果设备的存储空间超过256字节,要实现设备的整个存储部分的访问,就必须采用内存映射。
(2)I/O操作模块
Driverworks的KIoRange类封装了I/O端口访问的操作。部分代码如下:
{……
KIORange DevIoPort () ;//创建实例
NTSTATUS status= DevIoPort ().Initialize ( pResListTranslated,pResListRaW,PciConfig.BaseAddressIndexToOrdinal(0));
/* 第一个参数为转换后的资源列表指针;第二个参数为原始资源列表指针;第三个参数中的0为 I/O口对应的基地址,用来转换成特定端口资源的序数?*/
If(NT _SUCCESS(status))
{……
DevIoPort.inb(0,LineBuf1,10);
/*成功初始化后可分别用KIoRange类的成员函数inb(/outb)从端口中读/写字节 */
}
else{Invalidate();return status;
/*未能初始化成功,错误信息在status中*/
{
……}
(3)内存读写模块
DriverWorks的 KMemoryRange类封装了端口访问的操作。
status=m_MemoryRange().Initialize(pResListTranslated,pResListRaw, PciConfig.BaseAddressIndexToOrdinal(0));
此函数的参数、意义及具体用法与I/O端口的操作基本相同。
内存对象也用来发送控制字,以控制CPLD的开始和停止等。实际上控制字是通过PCI9052发送的。该控制字地址已被映射成PCI的内存空间。所以定义一个指向内存空间的内存对象,通过该对象即可发送控制字。
(4)中断模块
在中断模块,首先要激活PCI9052中断使能位,然后判断硬件中断响应是否产生,如果有,则进行突发传输,读入FIFO中的数据。
BOOLEAN TranCard::Isr_MyIrq(void)
{ if (// 中断未产生)
{……
return FALSE;}
else
{/* 如果产生硬件中断,设置命令寄存器,进行突发数据传输 */
return TRUE;}
}
为了将硬件中断与编写的中断服务程序连接在一起,采用InitializeAndConnect方法,部分代码如下:
NTSTATUS TranCardDevice?押?押OnStartDevice(KIrp I )
{……
status=m_MyIrq. InitializeAndConnect(
pResListTranlated,
LinkTo(Isr_MyIrq),
This;)
……}
2.4 驱动程序的调用
编写驱动程序本身不是最终目的,最终目的是调用驱动程序管理资源,并为用户应用程序使用。驱动程序加载以后,它的许多进程处于Idle状态,实际上需要用户应用程序去调用激活。应用程序利用Win32 API直接调用驱动程序,实现驱动程序和应用程序的信息交互。
首先用CreateFile()打开设备,获得一个指向设备对象的句柄。使用CreateFile函数时应注意:由于驱动程序是*.sys,所以第一个参数应该是这个设备对象的标志连接(symbolic link)。该标志连接名有一个设置数据文件搜索路径的数字号,而这个数字号通常是零。如果这个连接名是″TranCard″,则传递给CreateFile的宇符串就是:″\\\\.\\ TranCard0″。例如:
HANDLE hDevice=CreateFile(″\\\\.\\TranCard0″)GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ, NULL?, OPEN_EXISTING,0,NULL);
然后用 DeviceIoControl()进行数据的传送。最后用CloseHandle( )关闭设备句柄。
下面是应用DeviceIoControl()程序片段。
{……
m_b=DeviceIoControl(hDevice,TRANCARD_IOCTL_
RECEIVE(buffer, sizeof,buffer, NULL,0,&buffersize,NULL);
……}
2.5 驱动程序的调试
篇7
Design of USB Device Driver Based on Real Time Operation System VxWorks
WANG Hao
(College of Computer, Xidian University, Xi'an 710071, China)
Abstract:The architecture of USB dirver based on VxWorks is given, general method and key technology in developing USB device dirver are analyzed.Then the device driver of LM9833 is implemented, expectant performace of target system is achieved. The general process of developing USB device dirver used in this paper can be refered by others USB device driver developing based on VxWorks.
Key words: VxWorks; USB device driver; pipe; callback
1 VxWorks下USB驱动概述
VxWorks是WindRiver公司开发的具有工业领导地位的高性能实时操作系统(Real Time Operation System, RTOS)内核。VxWorks5.5实现了USB1.1协议栈。图1提供一个VxWorks下USB主驱动栈的简单结构。
在栈的最低层是USB主控制器(USB Host Controller, HC),这是主系统中控制每一个USB设备的硬件。在主控制器上层是一个与硬件独立的主控制器驱动(USB Host Controller Driver,HCD)。USBD是在HCD之上的与硬件独立的模块,USBD管理每一个与主机相连的设备,向高层提供可与USB设备通信的路径,USBD实现了USB总线枚举、总线带宽分配、传输控制等操作。在栈的顶层是USB Client 模块,一般是特定的USB Class Driver,负责管理与USB主机连接的不同类型的设备。用户自己的USB设备驱动程序通常是在USBD这一层上完成。
2 VxWorks下USB设备驱动详解
2.1 驱动程序提供的函数
2.1.1 向应用程序提供的接口函数
设备驱动程序的主要作用是向上层应用程序屏蔽硬件,向上层应用程序提供统一的接口函数,驱动程序一般需要实现的函数如表1所示。
图1 USB主驱动栈结构
表1 驱动程序提供的接口
usbMSCDevInit();这是一个通用的初始化例程,可以在BSP中调用,也可以由应用程序来调用,但只能被调用一次,该例程初始化必须的数据结构,并向USBD注册驱动程序。USB设备与USB主控制器之间的所有操作都通过USBD来完成,因此在调用usbMSCDevInit()之前必须确定USBD已经初始化,在使用USB设备之前也要确保USB主控制器已经挂接到USBD。
usbMSCDevCreate();这是一个创建设备例程,当有设备接入时,回调函数usbMSCDevAttachCallback()调用该例程创建一个逻辑设备,当这个例程被调用时必须在系统中存在一个实际的USB物理设备。
usbMSCDevDestroy();从系统中删除设备。首先释放设备占有的资源,从设备链表中移除该设备,同时调用usbdPipeDestroy()销毁该设备与主机之间的通信管道,最后释放设备结构体。
usbMSCDevShutDown();该例程卸载已注册的设备驱动程序,并回收所有已分配资源,包括删除设备、回收信号量等。
2.1.2 两个重要的回调函数
在编写USB设备驱动程序时,除了上述接口函数外,还需要编写另外两个重要的回调函数:usbMSCDevAttachCallback()和usbMSCIrpCallback()。usbMSCDevAttachCallback()用于跟踪设备的请求实现动态接入或删除;usbMSCIrpCallback()是一个IRP callback,当每一个IRP执行完成之后调用该回调函数,实现IRP之间同步。
2.2 设备标识
在VxWorks中USB设备用USBD_NODE_ID来唯一区别,它是USBD用来跟踪一个特定设备的句柄,它与USB设备的真正USB地址无关。这表明Client并不关心设备是物理上与哪一个USB主控制器连接,应用为每个设备抽象一个Node ID,使Client可以不用考虑物理设备的连接细节以及USB地址分配。当一个Client通知有一个设备连接或断开时,USBD经常通过Node Id来定位设备。同样,当USB设备与USBD通信时,它必须向USBD传递该设备的Node Id。Node ID通常作为设备结构体的一个重要成员。
2.3 回调(Callback)
USB操作是严格遵守时序的,WindRiver USBD采用回调机制来解决时序问题。例如在USBD识别一个动态连接事件之后会激活一个动态attach callback操作,这是一个注册到USBD的回调例程,回调函数会判断当前的操作,如果是接入(USBD_DYNA_ATTACH),就会调用usbMSCDevCreate()来在当前的USB句柄上创建一个逻辑设备结构体;如果是移出(USBD_DYNA_REMOVE)就会调用usbMSCDevDestroy()来删除设备。同样,当USBD处理完成一个USB IRP之后,Client的一个IRP callback被激活,该回调例程是由Irp.userCallback域来指定。在IRP callback中释放IRP同步信号量。
2.4 数据传输
USB设备与主机之间是通过USB管道进行通信的。一旦Client配置完一个设备,就可以利用USBD提供的管道(pipe)传输功能与设备进行数据交换。管道是建立在USBD Client与设备特定的endpoint之间的单向通道。调用usbdPipeCreate()函数创建一个管道后返回一个管道句柄USBD_PIPE_HANDLE,以后所有的读写操作都分别在各自的管道句柄上进行。管道在刚创建完后是处于终止的状态,为了能有效使用它们,还必须用usbdFeatureClear()来清除该终止状态。每一个管道有以下性质:USBD_NODE_ID、端点地址,管道类型、数据流方向、包的最大有效载荷量、带宽需求等。对于块传输没有带宽限制。VxWorks的USB驱动程序的各层驱动程序间通过IRP机制进行通信,当有数据传输请求发生时,上层向下传递IRP。USBD得到请求后,调用HCD接口,将IRP转化为usb的传输。这就需要提供一个特殊的回调函数usbMSCIrpCallback(),当块传输结束时调用该回调函数。以下是读设备的具体过程。
1) 创建设备时创建out和in管道:
usbdPipeCreate(usbdHandle, NodeId, outEpAddress, configuration, interface, USB_XFRTYPE_BULK, USB_DIR_OUT,maxPacketSize,0,0, outPipeHandle);
usbdPipeCreate(usbdHandle, NodeId, inEpAddress, configuration, interface, USB_XFRTYPE_BULK, USB_DIR_IN,maxPacketSize,0,0, inoutPipeHandle);
2) 定义IRP callback:usbMSCIrpCallback(pVOID p)。
3) 构造USB_IRP outIrp请求包。
4) 提交outIrp:usbdTransfer (usbdHandle, outPipeHandle, &outIrp)。读命令在outIrp.bfrList[0].pBfr域中。
5) 等待outIrp处理结束,结束调用usbMSCIrpCallback()释放IRP同步信号量。
6) 构造USB_IRP inIrp请求包。
7) 提交inIrp:usbdTransfer (usbdHandle, inPipeHandle, &inIrp)。
8) 等待inIrp处理结束,读取的数据在inIrp.bfrList[0].pBfr域中。
2.5 多个设备管理
驱动程序用LIST_HEAD 来存储多个设备,每个接入的USB设备利用其LINK节点加入到LIST_HEAD链表当中;一个LINK节点包含三部分:linkFwd指针、linkBack指针和pStruct指针;其中pStruct指向一个待连接的设备结构体。当有一个设备创建完成时调用usbListLink()将该设备加入链表,删除设备之前调用usbListUnlink()从链表中移出该设备。由于USB设备是用Node ID来唯一标识的,在查找设备的时需要利用usbListNext()来遍历设备链表,直到某个设备的Node ID与特定设备的Node ID相匹配为止。在多个设备管理时,采用USBD_NODE_ID usbd_scan_id[DEVICE_NUM]数组来存放多个设备的Node ID,在对设备进行读写时只需提供设备的索引序号就可以直接得到设备Node ID,提高了对设备的访问速度。
3 VxWorks下LM9833驱动程序实现
3.1 LM9833 USB接口简介
LM9833扫描仪控制器在一个单独的IC上可以提供一个完整的USB图像扫描控制系统。它的USB接口提供4个USB端点(Control Endpoint,Interrupt Endpoint,Bulk In Endpoint和Bulk Out Endpoint),内部有00~7F个寄存器,其中00寄存器是存放一个8bits的图象数据,其它分别为控制或状态寄存器。通过向bulk out端点发送四字节的读命令可以从bulk in端点读取这些寄存器的值,也可以向bulk out端点发送四字节的写命令加待写数据完成写寄存器。四字节命令依次表示:读写模式(1字节)、起始地址(1字节)、读写长度(2字节)。其中读写模式bit0为0表示写,1表示读;bit1为0表示非增模式,为1表示增模式,即读写寄存器完成之后寄存器地址加1。LM9833的工作过程就是通过读写这些寄存器来完成的。
3.2 设备描述符结构
typedef struct _usbScanDev
{
USBD_NODE_ID scanDevId; /* USBD node ID of the device */
UINT16 configuration; /* Configuration value */
UINT16 interface; /* Interface number */
UINT16 altSetting; /* Alternate setting of interface */
UINT16 outEpAddress; /* Bulk out EP address */
UINT16 inEpAddress; /* Bulk in EP address */
USBD_PIPE_HANDLE outPipe; /* Pipe handle for Bulk out EP */
USBD_PIPE_HANDLE inPipe; /* Pipe handle for Bulk in EP */
USB_IRP inIrp; /* IRP used for bulk-in data */
USB_IRP outIrp; /* IRP used for bulk-out data */
USB_IRP statusIrp; /* IRP used for reading status */
UINT8 * scanInData; /* Pointer for bulk-in data */
UINT8 * scanOutData; /* Pointer for bulk-out data */
BOOL connected; /* TRUE if USB_SCAN device connected*/
LINK scanDevLink; /* Link to other SCAN devices */
UINT8 CommandByte[4]; /* Which read/write command the device */
UINT16 actBytes; /* actual bytes will be transfered */
UINT8 direction; /* data transfer direction */
} USB_SCAN_DEV, *pUSB_SCAN_DEV;
设备描述符结构中包含了设备的一些重要信息和访问该设备时的必须资源,如Node ID、IN/OUT管道、IN/OUT IRP等等。
3.3 注册设备(LM9833)驱动程序
注册驱动程序一般包含两大步:与USBD建立连接和注册attach callback。以下是注册LM9833驱动程序的源代码。
#define USB_SCAN_CLASS 0xff
#define USB_SCAN_SUB_CLASS0x00
#define USB_SCAN_DRIVE_PROTOCOL0xff
STATUS usbScanDevInit()
{……
if(usbdClientRegister ("SCAN_CLASS", &usbdHandle)!=OK||
usbdDynamicAttachRegister (usbdHandle, USB_SCAN_CLASS, USB_SCAN_SUB_CLASS,
USB_SCAN_DRIVE_PROTOCOL, usbScanDevAttachCallback) != OK)
……
}
usbScanDevInit()调用usbdClientRegister()向USBD注册一个名为SCAN_CLASS的Client,同时调用usbdDynamicAttachRegister()向USBD注册一个回调例程usbScanDevAttachCallback (),跟踪该Client请求,当设备动态地接入或移出系统时会自动地调用该回调函数。一个Client在利用usbdDynamicAttachRegister()进行注册时只对特定的class、subclass、protocol感兴趣。在成功注册Client后,USBD返回一个Client句柄(USBD_CLIENT_HANDLE),以后对USBD的调用都会用到这个句柄。Attach callback 如下。
LOCAL VOID usbScanDevAttachCallback
(
USBD_NODE_ID nodeId,
UINT16 attachAction,
UINT16 configuration,
UINT16 interface,
UINT16 deviceClass,
UINT16 deviceSubClass,
UINT16 deviceProtocol
)
该回调函数主要响应外部设备的动作,实现USB设备的动态接入和移除。
3.4 创建设备
创建设备函数如下:
LOCAL STATUS usbScanPhysDevCreate(USBD_NODE_ID nodeId, UINT16 configuration, UINT16 interface)
当接入设备时激活usbScanDevAttachCallback()操作,回调函数会根据接入(USBD_DYNA_ATTACH)动作调用usbScanPhysDevCreate()创建一个逻辑设备,在创建设备的同时,创建设备与USB主机之间通信的管道(pipe)。管道是建立在USB设备端点(endpoint)之上,是主机与设备之间数据传输的单向通道。设备与主机之间数据传输管道是建立在批量端点(bulk endpoint)之上,有BULK_IN和BULK_OUT两个端点,从而建立双向的数据通路。最后将该设备加入设备链表,进行多个设备的管理。创建设备流程如图2所示。
图2 创建设备流程
3.5 读写设备
对设备进行读写时,首先需要将读写函数转换成设备能够识别的命令,对于LM9833来说,需将读写函数转换成LM9833所能识别的四字节读写命令,读写时将这四字节的命令置于IRP包数据域的最前端,这样到数据到达设备时首先接收到的是四个字节的命令,LM9833会根据这四个字节的命令完成相应的功能。读写函数原型为:
STATUS usbScanRead/usbScanWrite
(
UINT dev, /* sequence number of the device */
UINT Addr, /* start address in register */
UINT8 *pBuffer, /* pBuffer to receive/send data from/to device*/
UINT Len, /* lenth of pBuffer */
BOOL bIncrement /* incremece of address in register or not */
)
LM9833的一个读写控制流程如图3所示,查找设备流程如图4所示。
图3 LM9833读写控制流程
图4 查找设备流程
设备命令组帧:
const unsigned int MODE_INC_READ = 0x03;
const unsigned int MODE_NOINC_READ = 0x01;
const unsigned int MODE_INC_WRITE = 0x02;
const unsigned int MODE_NOINC_WRITE = 0x00;
switch (Cmd)
{
case USB_SCAN_WRITE:/* bulk out */
pScanDev->CommandByte[0] = (bIncrement>0)? MODE_INC_WRITE : MODE_NOINC_WRITE;
pScanDev->CommandByte[1] = Addr+((bIncrement>0)? i : 0);
pScanDev->CommandByte[2] = (Len >> 8); /* length of the data to be written */
pScanDev->CommandByte[3] = (Len & 0xff);
memcpy(pScanDev->scanOutData, pScanDev->CommandByte, 4); /* 4 bytes Lm9833 command followed by write data */
memcpy(pScanDev->scanOutData + 4, pBuffer + i, Len);
pScanDev->actBytes = Len+4; /* actual length to be transfered*/
pScanDev->direction = USB_SCAN_DIR_OUT;
break;
篇8
windows nt的网络体系结构是基于国际标准化(iso)制定的标准模型──开放式系统互连(open system interconnection:osi)参考模型分层建立的,这种方式有利于随时扩展其它功能和服务。
windows nt网络模型开始于mac子层,网卡驱动程序就驻留在其中。它通过相关的网卡把windows nt与网络连接起来,图中的多个网卡表明在一台运行windows nt的计算机上能使用多种网卡。
这一网络体系结构包括两个重要接口──ndis接口与传输驱动
程序接口(tdi)。这两个接口把两个层隔离开来,办法是相邻的部件只允许按单一的标准来写,不允许多重标准。例如一个网卡驱动程序(在ndis接口的下面)就不需要特地按每个传输协议来写它的代码块,恰恰相反,该驱动程序是写给ndis接口的,它通过符合ndis的相应传输协议来请求服务。这些接口包含在windows nt的网络体系结构中,以容纳可移植、可互换的模块。
在两个接口之间,是传输协议。它在网络中起着组织者的作用。一个传输协议规定了数据以何种方式呈递给下一个接收层,以及如何对数据相应地进行打包。它通过ndis把数据传给网卡驱动程序,并通过tdi把数据传给转发程序(redirector)
tdi之上是转发程序,它把本地的网络资源申请转送给网络。
为了能和其他厂商的网络互连,windows nt允许有多个转发程序。对于每一个转发程序windows nt计算机必须也有一个相应的供应者(provider)(由网络厂商提供)。多供应者路由选择程序决定适当的供应者,然后借助于供应者,对应用请求到相应的转发程序做出选择。windows nt支持两种类型的网络驱动程序
传输驱动程序
实现数据链路层中的逻辑链路控制子层协议和传输层协议。向 下与ndis接口,向上与tdi接口。
网卡驱动程序
实现对物理层的管理和数据链路层中介质访问控制子层协议,通过ndis向下管理物理网卡,向上与传输驱动程序通信。
§1.1.3 windows nt网卡驱动程序
windows nt环境下的网卡驱动程序也分为两种:
miniport网卡驱动程序:miniport驱动程序只须实现与网络硬件相关的操作(包括发送和接收)。而所有底层网卡驱动程序的通用操作(如同步),一般由ndis接口程序来实现。
full网卡驱动程序:full网卡驱动程序必须实现所有硬件相关和同步、排队等操作。例如full网卡驱动程序为了响应数据接收,需要保持本身的捆绑信息,而miniport就可以由ndis接口库来实现。
在windows nt的早期版本中,full网卡驱动程序要求开发者实现许多底层操作,来处理多处理器的核心问题以及处理器、线程的同步,这样不同的开发者在大量重复着许多相同的工作。
而miniport网卡驱动程序允许开发者仅仅写一些与网络硬件相关的代码即可,而那些通用的函数由ndis接口库来实现,这样开发出来的驱动程序减少了不必要的工作。
第二节miniport驱动程序的结构
ndis接口规范了网卡驱动程序的实现,同时也对tdi驱动程序的实现提出了一定的要求,在nt中,ndis约束下的网卡驱动程序、tdi驱动程序和系统的关系如下图所示:
图2.0 ndis约束下的网卡驱动程序、tdi驱动程序和系统的关系
miniport驱动程序包括驱动程序对象、驱动程序源代码和ndis接口库代码。windows nt ddk提供ndis.h作为miniport驱动程序的主要头文件,定义了miniport驱动程序的入口点、ndis接口库函数和通用数据结构。
上边缘函数的作用是网卡驱动与ndis接口库进行通信,而下边缘函数是tdi协议驱动程序与ndis通信的手段。ndis用一个叫做逻辑网卡的软件对象来描述系统中的每块网卡,而逻辑网卡与windows nt设备对象的通信由i/o子系统来管理,描述网卡的设备对象包括相关的网络信息如名字、网络地址和网卡内存基地址等,它还包含与硬件相关的驱动程序状态数据(捆绑数目,捆绑句柄,包过滤数据库等)。ndis分配一个句柄到miniportinitialize这个上边缘函数的一个结构中,然后miniport网卡驱动程序将在以后提供这个句柄来给ndis调用,这个结构一直被ndis保持,并且对miniport驱动程序不透明。 当miniport网卡驱动程序初始化一块网卡时,它创立自己的内部数据结构来描述网卡,记录需要它管理的与设备相关的状态信息。当miniport网卡驱动程序调用ndismsetatttibutes或ndismsetattributesex两ndis库函数时,它传递一个句柄给这数据结构。这样,当调用miniport驱动程序入口点时,它就传递这个句柄来验证驱动程序所对应的网卡的正确性。这个数据结构为miniport网卡驱动程序所拥有并维护。miniport nic驱动程序还需要维护一组对象,这些对象是系统定义的对象标识符(object idetifier:oid)来标识,以描述驱动程序的性能和当前状态信息。为查询这些信息,上层驱动程序调用ndisrequest向ndis接口库指示oid。oid表示了调用所需的信息类型,如miniport驱动程序所支持的lookahead缓冲区大小等。ndis接到上层驱动程序的查询请求,将oid传递给上边缘函数miniportqueryinformation实现对oid的查询,如果上层驱动程序请求改变状态信息则调用miniportsetinformation实现对oid的设置。典型的miniport nic驱动程序必须有一些函数来通过ndis接口实现上层驱动程序与硬件的通信。这些函数称为上边缘服务函数。
这些上边缘服务函数由驱动程序的开发者根据驱动程序面向的特定低层网络类型和硬件以及相应环境,可以有选择地实现,但必须保证驱动程序最基本的功能,这些基本功能包括初始化、发送、中断处理、重置、参数查询与设置和报文接收。
miniportinitialize:操作系统根据系统配置信息,检测出网卡已安装时,由ndis接口在初始化时调用,主要完成低层网络类型确定,对应于物理网卡的逻辑网卡初始化,中断信息注册,网卡与主机通讯方式的确认。i/o端口的申请与注册,内存映像,mib的初始化,物理网卡的验证与初始化等。
miniportreconfigure:支持网卡参数动态变化,和miniportinitilize一样由ndis接口以初始化级别调度执行(不能屏蔽中断,必须由驱动程序承认并清除在此期间产生的中断),支持即插即用和软配置的网卡在动态改变参数时,必须提供此函数。
miniportqueryinformation:查询网卡的状态以及网卡驱动程序的操作或统计参数,如是否支持组通讯、网卡的物理速率是否支持回环、是否支持直接拷贝等,这些参数以oid方式统一管理。
miniportsetinformation:ndis接口或协议驱动程序通过调用此接口改变驱动程序维护的oid库,一些操作参数的改变也将同时改变驱动程序状态,例如组地址的设置。
miniportreset:包括网卡硬件重置和驱动程序软件重置,软件重置包括驱动程序状态重置,以及一些相关的参数重置,还需考虑有些参数的恢复,重置时不必完成所有正在活跃的外部请求,但必须释放已占用的外部资源。
miniporthalt:挂起网卡并释放该网卡驱动程序占用的所有资源,在此期间不屏蔽中断。
miniportisr:高优先级的中断处理程序,进行的工作包括初始中断处理类型,决定是否进行中断转交,对卡上中断进行处理 等,该服务类型只在以下情况被调用:
ndis接口调用miniportinitialize和miniporthalt两函数时。
.中断处理类型设为每此中断处理过程都调用时。
为使系统能及时响应所有硬件中断,高优先级的硬件中断处理程序应尽可能的减少运行时间,防止长时间的屏蔽低优先级中断,避免造程中断丢失。
miniporthandleinterrupt:由中断延时处理程序在中断延时处理时进行调用。ndis排队所有的延时处理,该服务主要处理发送完成、报文接收、描述符用尽、溢出、网卡异常等中断。
miniportsend:ndis收到上层发送请求时经过若干协议处理再向下调用此服务过程,发送的packet已含有llc和mac头,该服务过程进行边界对齐、packet约束重整、描述符映射和报文发送、以及发送资源和packet缓冲队列管理。
miniporttransferdata:多个已和网卡捆绑的协议驱动程序在接收到报文到达指示后,向网卡驱动程序发出传送请求以拷贝各自所需的报文数据部分,网卡驱动程序根据各协议驱动程序对单个packet是否进行多次拷贝,以决定是否暂存只允许单次拷贝的packet等。
miniportcheckhandle:ndis每秒调用此服务函数一次,驱动程序发现网卡异常时报告给ndis由ndis调用miniportreset进行硬件重恢复。
miniportenableintrrupt:中断使能。
miniportdisableinterrupt:中断屏蔽。
另外,每个网卡驱动程序必须有一个初始化入口点,由driver entry函数实现,它和系统相关,由操作系统在装入驱动程序时调用,主要完成初始化ndis wrapper,再由wrapper初始生成驱动程序管理块并完成相应各种初始化工作,登录网卡驱动程序所有上边缘服务入口点,同时写入ndis版本信息。ndis接口库包括在ndis.sys中,它是一个核态函数库,有一套抽象的函数,无论协议驱动程序还是nic驱动程序都连接到这个库中,以实现上下层之间的操作。
第二章fddi网卡驱动程序的加载和运行
第一节 网卡驱动程序的安装
windows nt网卡驱动程序安装的目的是实现网卡相应硬件信息和驱动程序在windows nt注册库中的注册,使windows nt能够正确识别网卡,了解所必需的软硬件信息并能在windows nt启动时加载相应驱动程序。
网卡驱动程序安装时,首先在主群组的控制面板中选择“网络”,然后添加网卡,指定相应信息文件──oemsetup.inf的路径,以完成以下两个必要的操作:
复制驱动程序到相应的系统目录(windows nt根目录system32drivers)中;
在windows nt注册库中存入相应软硬件信息。
下面主要以fddi网卡为例介绍安装驱动程序所必需的工作:
§2.1.1网卡一般硬件参数
对于fddi网卡,必须在编写其oemsetup.inf文件时确定以下硬件参数:
总线类型:pci(5)……括号中的数字5表示pci总线在ndis中的总线类型代码;
厂商代号:0x5588……系统加载时确定网卡的标记,也是编程时确定pci槽号的标识;
cfid: 0x01;
介质类型:光纤(3) ……括号中的数字表示光纤在ndis中的介质类型代码;
是否支持全双工:支持。
对于其它的硬件信息在此inf配置信息文件中可有可无,如若配置,则可在驱动程序的编写时利用这些信息,方便编程,同时有利于其它应用对其参数的确定和使用。网卡驱动程序的安装通常将创建登录表中的四个不同子键:
software registrion键,对应于驱动程序,存在于hkey_local_machinesoftwarecompany productnameversion中。我们的fddi网卡驱动程序所对应的是hkey_local_machinesoftwarenet612yhfddiyhfddi1.0;
网卡的软件登录键,存在于hkey_local_machinesoftwaremicrosoft windows ntnt3.51networkcardsyhfddi1;
驱动程序的服务登录键,存在于hkey_local_machinesystemcurrentcontrolsetservices
网卡的服务登录键,存在于hkey_local_machinesystemcurrentcontrolsetservices
对于每一个网络部件,一个名为netrules的特殊子键在邻近的驱动程序或网卡登录子键里创建,netrules标识网络部件为网络整体的一部分。
fddi网卡驱动程序对应的标准软件登录表项将出现在以下路径:
hkey_local_machinesoftwarenet612yhfddiyhfddi1.0;
驱动程序对应的标准项的值为:
description =yhfddi/pci adapter controller
install date =……
……
refcount =0x01
servicename =yhfddi
softwaretype =driver
title =yhfddi/pci adapter controller
而且在yhfddi驱动程序相关的netrules子键下,这些值项为:
bindable =yhfddi driver yhfddi adapter non exclusiver
bindform =“yhfddisys”yes no container
class = reg_multi_sz “yhfddi driver basic”
infname =oemnad1.inf
type =yhfddisys ndisdriver yhfddidriver
use =driver
yhfddi网卡在如下路径的networkcards子键里介绍:
hkey_local_machinesoftwaremicrosoft
windows ntnt3.51networkcardsyhfddi1;
网卡的标准项包括以下这些值:
description =yhfddi/pci adapter controller
install date =……
manufacturer =net612
productname =yhfddi
servicename =yhfddi01
title =[01]yhfddi/pci adapter controller
§2.1.3编写inf信息配置文件
gui inf描述语言被windows nt用以书写系统所有部件的配置文件,当然也可以用以书写网络系统各部件的配置文件,该配置文件描述了网络部件安装、配置、删除的执行过程。当网络部件进行初始安装或二次安装(通常通过ncpa进行)时,安装程序读取部件对应的配置文件,进行解释执行。gui inf描述语言由节、命令、逻辑操作、变量规范、流程控制以及一套调用dll或外部程序的机制组成,其中,节是配置文件的主体,节可分为install节(类似于函数),shell节(也类似于函数,但可调用insall和shell节),detect节(不包含命令),一个配置文件一般由若干不同类型的节组成。驱动程序的开发者根据需要可以在配置文件中编写相应代码,使得用户和系统之间能进行交互,并且由用户决定一些配置参数。
nt网卡配置文件有其一套规范,驱动程序开发者必须按规范编写配置文件,一般来说,一个配置文件至少应该提供下面三个节:
安装入口点:[identify]shell节。该节主要功能是给出安装部件的类型名,系统通过它识别该部件属于哪一大类(display,mouse,scsi,network等)中的哪一类(网络adapter,driver,transport,service,network和netprovidor),同时,还需要给出映像文件和配置文件所在的源介质及标识。
[returnoption]shell节。系统执行安装identify节后,执行该节。它主要功能是检查所需安装的部件是否支持的硬件平台和语言,并给出网卡名(有些配置文件支持多类网卡,此时必须让用户进行选择,并获得选择结果)。
[installoption]shell节。该节是配置文件得主体,也是上次安装完后再次进行配置、删除、更新的入口点。主要功能是拷贝映像文件和配置文件,生成配置的各种选项,创建该部件在注册库中对应的各种登录子树并更新重写。
第二节 驱动程序的加载过程
篇9
1WDM模式驱动程序
1.1WDM模式(WindowsDriverModel)
Windows2000对驱动程序的编写不再基于以往的Win3.x和Win9x下的VxD(虚拟设备驱动程序)结构,而是基于一种新的驱动模型——WDM(WindowsDriverModel)。
WDM为Windows98/2000/XP操作系统的设备驱动程序的设计提供了统一的框架。WDM来源于WindowsNT的分层32位设备驱动程序模型(layered32-bitdevicedrivermodel)。它支持更多的特性,如即插即用(PnP)、电源管理、WMI和NT事件。
1.2设备驱动程序
设备驱动程序是操作系统的一个组成部分,它由I/O管理器(I/OManager)管理和调动。Windows2000操作系统下的I/O管理器功能描述如图1所示。
I/O管理器每收到一个来自用户应用程序的请求就创建一个I/O请求包(IRP)的数据结构,并将其作为参数传递给驱动程序。驱动程序通过识别IRP中的物理设备对象(PDO)来区别是发送给哪一个设备。IRP结构中存放请求的类型、用户缓冲区的首地址、用户请求数据的长度等信息。驱动程序处理完这个请求后,在该结构中填入处理结果的有关信息,调用IoCompleteRequest将其返回给I/O管理器,用户应用程序的请求随即返回。访问硬件时,驱动程序通过调用硬件抽象层的函数实现。
1.3DriverStudio工具简介
NuMegaLab公司开发的DriverStudio是一整套开发、调试和检测Windows平台下设备驱动程序的工具软件包。它把DDK(DeviceDevelopmentKit)封装成完整的C++函数库,根据具体硬件通过向导生成框架代码,并且提供了一套完整的调试和性能测试工具SoftICE、DriverMonitor等。
2应用实例
本文利用PCI专用接口芯片PCI9052设计了一个数据传输控制卡。卡上主要的芯片有PCI9052、FIFO(CY7C4221)、CPLD(MAX7064S)和A/D转换器(MAX1197)。传输卡硬件框图如图2所示。面阵CCD得到的视频信号经过调理电路,生成的视频调理信号通过A/D转换器进行数字化处理,送入FIFO中。在CPLD的控制下,数据经过PCI9052送入PCI总线,再传送到计算机内存中,并显示在监视器上。驱动程序必须实现如下几个基本功能:(1)硬件中断;(2)能支持应用程序获取数据;(3)能根据外部FIFO(CY7C4221)的状态启动或停止突发传输。
在数据输入过程中,最重要的是对数据进行实时控制,因此需要硬件中断。在中断程序中,根据外部FIFO状态完成数据的读入。
2.1用DriverWizard生成驱动程序框架
DriverStudio中的DriverWorks软件为开发WDM程序提供了一个完整的框架。它包含一个可快速生成WDM驱动程序框架的代码生成向导工具DriverWizard,而且还带有许多类库。在用DriverWizard生成的程序框架中写入相对于设备的特定代码,编译后即可得到所需的驱动程序。
在利用DriverWorksV2.7的向导DriverWizard完成驱动程序的框架时共有11个步骤,其中关键步骤有:
(1)在第四步中选中PCI,并在VendorID和DeviceID中分别输入厂商号和设备号,还需填入PCISubsystemID和PCIRevisionID。这四项可以用网上的免费软件PCITree或PCIView浏览PCI设备,用这两个软件也可以得到BAR0~BAR5的资源分配情况和中断号。
(2)第七步IRP队列排队方法,它决定了驱动程序检查设备的方式。本设计选SystemManaged,则所有的IRP排队都由系统(即I/O管理器)完成。
(3)第九步是最关键的一步。首先在Resources中添加资源,在name中输入变量名,在PCIBaseAddress中输入0~5的序列号。0~5和BAR0~BAR5一一对应。在设置中断对话框中,在name栏写入中断服务程序的名称,选中创建中断服务程序ISR?穴CreateISR?雪,不选创建延迟程序调用DPC(CreateDPC),选中MakeISR/DPCclassfunctions,使ISR/DPC成为设备类的成员函数。
其次选中Buffer以选取读写方式,用于描述与I/O操作相关的数据缓冲区。本设计需要快速传送大量数据,因此采用DirectI/O方式。
(4)在第十步中,需要加入与应用程序或者其他驱动程序通信的I/O控制代码参量。
2.2驱动程序模块框图和代码分布
PCI设备驱动程序模块包括配置空间的访问模块、IO端口模块、内存读写模块和终端模块等。各模块之间是对等的。驱动程序模块框图如图3所示。
驱动程序初始化模块代码段放在#pragmacode_seg(″INT″)和#pragmacode_seg()之间。在系统初始化完成后,这部分代码从内存中释放,防止占用系统宝贵的内存资源。#pragmacode_seg()之后是驱动程序和系统的许多模块的实现部分。这部分在驱动程序运行后不会从内存中释放。
2.3驱动程序主要模块的实现
(1)配置空间的访问模块
DriverWorks的KPciConfiguration类封装了访问PCI设备配置空间的所有操作。首先初始化这个类的实例:
KpciConfigurationPciConfig()m_Lower.TopOfStack());
/?觹m_Lower是KpnpLowerDevice类的对象。m_LowerTopOfStack()返回当前设备堆栈顶部的设备对象。*/
初始化完后可以直接利用成员函数ReadHeader/WriteHeader函数访问所有的配置寄存器。
为了确定映射空间的类型和大小,先向目标基地址寄存器写入0Xffffffffh,然后回读该寄存器的值。如果最低位为1,表示映射于I/O空间,反之为存储空间;如果映射于存储空间,从第四位开始计算0的个数可以确定内存空间的大小;如果是I/O方式,从第二位开始计算0的个数可确定I/O空间的大小,最大为256字节。如果设备的存储空间超过256字节,要实现设备的整个存储部分的访问,就必须采用内存映射。
(2)I/O操作模块
Driverworks的KIoRange类封装了I/O端口访问的操作。部分代码如下:
{……
KIORangeDevIoPort();//创建实例
NTSTATUSstatus=DevIoPort().Initialize(pResListTranslated,pResListRaW,PciConfig.BaseAddressIndexToOrdinal(0));
/*第一个参数为转换后的资源列表指针;第二个参数为原始资源列表指针;第三个参数中的0为I/O口对应的基地址,用来转换成特定端口资源的序数?*/
If(NT_SUCCESS(status))
{……
DevIoPort.inb(0,LineBuf1,10);
/*成功初始化后可分别用KIoRange类的成员函数inb(/outb)从端口中读/写字节*/
}
else{Invalidate();returnstatus;
/*未能初始化成功,错误信息在status中*/
{
……}
(3)内存读写模块
DriverWorks的KMemoryRange类封装了端口访问的操作。
status=m_MemoryRange().Initialize(pResListTranslated,pResListRaw,PciConfig.BaseAddressIndexToOrdinal(0));
此函数的参数、意义及具体用法与I/O端口的操作基本相同。
内存对象也用来发送控制字,以控制CPLD的开始和停止等。实际上控制字是通过PCI9052发送的。该控制字地址已被映射成PCI的内存空间。所以定义一个指向内存空间的内存对象,通过该对象即可发送控制字。
(4)中断模块
在中断模块,首先要激活PCI9052中断使能位,然后判断硬件中断响应是否产生,如果有,则进行突发传输,读入FIFO中的数据。
BOOLEANTranCard::Isr_MyIrq(void)
{if(//中断未产生)
{……
returnFALSE;}
else
{/*如果产生硬件中断,设置命令寄存器,进行突发数据传输*/
returnTRUE;}
}
为了将硬件中断与编写的中断服务程序连接在一起,采用InitializeAndConnect方法,部分代码如下:
NTSTATUSTranCardDevice?押?押OnStartDevice(KIrpI)
{……
status=m_MyIrq.InitializeAndConnect(
pResListTranlated,
LinkTo(Isr_MyIrq),
This;)
……}
2.4驱动程序的调用
编写驱动程序本身不是最终目的,最终目的是调用驱动程序管理资源,并为用户应用程序使用。驱动程序加载以后,它的许多进程处于Idle状态,实际上需要用户应用程序去调用激活。应用程序利用Win32API直接调用驱动程序,实现驱动程序和应用程序的信息交互。
首先用CreateFile()打开设备,获得一个指向设备对象的句柄。使用CreateFile函数时应注意:由于驱动程序是*.sys,所以第一个参数应该是这个设备对象的标志连接(symboliclink)。该标志连接名有一个设置数据文件搜索路径的数字号,而这个数字号通常是零。如果这个连接名是″TranCard″,则传递给CreateFile的宇符串就是:″\\\\.\\TranCard0″。例如:
HANDLEhDevice=CreateFile(″\\\\.\\TranCard0″)GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL?,OPEN_EXISTING,0,NULL);
然后用DeviceIoControl()进行数据的传送。最后用CloseHandle()关闭设备句柄。
下面是应用DeviceIoControl()程序片段。
{……
m_b=DeviceIoControl(hDevice,TRANCARD_IOCTL_
RECEIVE(buffer,sizeof,buffer,NULL,0,&buffersize,NULL);
……}
2.5驱动程序的调试
篇10
引 言
VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是嵌入式开发环境的关键组成部分。良好的持续发展能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域占据一席之地。它以其良好的可靠性和卓越的实时性被广泛地应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中,如卫星通讯、军事演习、弹道制导、飞机导航等。
1 嵌入式系统
嵌入式系统是以嵌入式计算机为技术核心,面向用户、面向产品、面向应用,软硬件可裁减的,适用于对功能、可靠性、成本、体积、功耗等综合性严格要求的专用计算机系统。和通用计算机不同,嵌入式系统是针对具体应用的专用系统,目的就是要把一切变得更简单、更方便、更普遍、更适用;它的硬件和软件都必须高效率地设计,量体裁衣、去除冗余,力争在同样的硅片面积上实现更高的性能。
嵌入式系统主要由嵌入式处理器、外围硬件设备、嵌入式操作系统以及特定的应用程序等四部分组成,是集软硬件于一体的可独立工作的“器件”;用于实现对其它设备的控制、监视或管理等功能。
嵌入式系统应具有的特点是:要求高可靠性;在恶劣的环境或突然断电的情况下,要求系统仍然能够正常工作;许多嵌入式应用要求实时处理能力,这就要求嵌入式操作系统(EOS)具有实时处理能力;嵌入式系统中的软件代码要求高质量、高可靠性,一般都固化在只读存储器中或闪存中,也就是说软件要求固态化存储,而不是存储在磁盘等载体中。
2 设备驱动程序
Vxworks5.4中驱动程序主要分为三种:字符、块以及网络驱动程序。本文所介绍的网卡驱动程序则属于网络设备驱动程序。
2.1 网络设备驱动程序设计
网络的各功能部件图1所示,网络设备驱动程序实际上是处理硬件和上层协议之间的接口程序。网络传输协议层分发数据在应用程序接口和网络接口之间。网络化网络协议(如IP协议)发送数据在网络主机之间。连接/接口层使能主机隶属于硬件到相同物理媒质的通信。
在Vxworks5.4中,网卡驱动程序又分为END(Enhanced Network Driver)和BSD两种。它们分别处于如图2所示结构中。
2.1.1 BSD驱动程序设计
在Vxworks5.4中,网络驱动程序都是基于BSD UNIX版本4.3基础上的,这些驱动程序都定义在一个全局例程中,那就是attach子程序,xxattach( )子程序中包含5个函数指针,它们都被映射到ifnet结构中,这5个函数可见表1,它们在IP协议层任何地方被调用。
表1 网络接口处理
驱动程序入口xxattach( )调用ether_attach( )来把上述5个函数映射到ifnet结构中,ether_attach( )调用如下:
ether_attach(
(IFNET *) & pDrvCtrl->idr,
unit,
"xx",
(FUNCPTR) NULL,
(FUNCPTR) xxIoctl,
(FUNCPTR) ether_output( ), /* generic ether_output */
(FUNCPTR) xxReset
);
pDrvCtrl->idr.ac_if.if_start = (FUNCPTR)xxTxStartup;
上述参数中,需要一个接口数据记录(Interface Data Record (idr)),unit号和设备名,下面四个参数就是相关驱动程序的函数指针。第一个函数指针指的是init( )例程,这个例程可要可不要,第二个函数指针指的是ioctl( )接口,它允许上层来控制设备状态;第三个函数指针指的是把数据包送到物理层;最后一个函数指针指的是如果TCP/IP堆栈决定需要复位的话,它就复位这个设备。
接着下面那一句代码表示添加数据传输例程到IDR,ether_output( )例程被调用后,传输开始例程就被TCP/IP协议堆栈调用。
篇11
1 引言
计算机软件技术的发展日新月异,给高等院校相关专业的教学带来了很大的挑战,为了更好地适应不断变化的社会就业需求,就必须在传统的计算机专业教学模式的基础上开辟出一条新路。
在这样的背景下,乐山师范学院计算机科学学院早在2005年就开始开展校企合作办学,与企业联合培养校企合作方向的学生,至今已是第八届。相比普通班,校企合作教改班所开设的专业课程更符合于当前计算机人才市场的需求,典型的特点就是注重对学生的专业技能尤其是程序设计和软件开发能力的系统性培养,严格按照软件工程师的培养模式来开展相关的理论和实践教学环节,这在很大程度上改变了以往只注重专业理论教学的局限性。
在对近几年教改学生的就业情况进行分析以后,明确肯定了校企合作教学模式为我院本科人才培养体系的改革起到了决定性的促进作用,学生的专业技能有了明显的增强,也大大提高了毕业生的就业率。
但与此同时也认识到存在的一些问题:首先,传统的以程序设计语言语法描述为主线的教学方式,以及模式化的实验内容,使教师在教学过程中容易将重点偏向理论,降低了对学生实践能力的锻炼和考核;其次,我们的软件工程师主要是在教室和机房这样的环境下培养出来的,缺乏真刀真枪的实践锻炼机会;最后,虽然校企合作人才培养方案的整体实施效果不错,但也很难培养出高层次的计算机专业人才。
如果以上几点不能有效地解决,那么校企合作办学的成效和前景将受到限制,因此迫切地需要一种途径去驱动程序设计类专业课程的教学模式改革,经过长期、反复的思考和摸索,我们认为通过开展学科专业竞赛活动来推动课程教学改革是比较可行的。而在种类繁多的计算机学科专业竞赛中,最权威、级别最高的就是《ACM/ICPC国际大学生程序设计竞赛》。
本教改项目结合ACM竞赛来促进计算机专业教学体系特别是程序设计类课程的教学改革,教改实施对象主要为计算机科学学院软件工程专业方向的学生。首先针对程序设计类课程教学存在的问题以及问题产生的原因进行分析,然后在ACM竞赛模式和特点的基础上,尝试通过结合ACM竞赛来改革课程开设体系和课程教学模式,最后提出了解决问题的具体措施,并在实际教学应用中取得了一定的成效。
2 当前程序设计类课程教学存在的问题
2.1 人才培养模式陈旧,实践教学比例不足
在传统的被动教学模式中,学生缺乏学习主动性、创新性和行业竞争力。而计算机专业课程大多属于实践型课程,强调动手能力。为了加深对理论知识的理解,必须提高实践教学质量,理论和实践教学的学时分配要作适当调整。
2.2 实践内容模板化,缺乏创新能力的培养
首先,设计性、综合性实验偏少,很难培养学生的创造性思维;其次,实验内容严重脱离了现代软件工程过程,更谈不上对综合型应用问题的解决;最后,在实践教学过程中,教师干预太多,学生处于被动完成实验任务的角色。
2.3 缺乏互助学习能力,团队协作意识较差
当前软件项目的开发都是以团队形式实施的,团队成员之间需要合理分工和无障碍沟通。但在传统教学模式中,以项目组为单位来开展教学活动的机会非常少,更谈不上互助学习和团队协作了。
2.4 课程考核模式单一,缺乏激励机制
课程考核主要采用传统考核模式,考核内容受限于教材知识点,缺乏对学生知识结构与实践技能的综合考察,不利于学生综合实践能力和创新能力的培养,最终形成“高分低能”的现象。
3 改革措施
本教改项目主要通过以下几个方面来实施以ACM竞赛促进程序设计类课程教学改革的方案。
3.1 改革课程开设计划
全面分析了目前程序设计类专业课程教学中存在的一些问题(比如教法和学法等方面),结合ACM的竞赛大纲和竞赛模式来调整开课计划,把原计划一学期的《程序设计基础》课程的教学时间调整为一学年,第一学期是程序设计的入门教学,主要介绍高级程序设计语言编程基础;第二学期是程序设计的进阶教学,主要介绍算法设计与分析。
3.2 改革课程实践教学模式[1]
(1)实验内容分级化:
将实验内容分成知识型(单一算法)、应用型(算法和实际问题结合)和综合型(若干小算法的综合,用于解决一个较大规模的问题)。不同级别题型的权值不同,每一级别中又包含若干个相同权值的题目,学生可以根据自身情况选择不同级别的题型和题目数量,这样既考虑到了不同层次学生的学习需求,又达到了统一的实验目的。
(2)实验题目趣味化:
传统的程序设计类实验题目普遍比较枯燥,难以调动学生的学习兴趣和设计思路。参考ACM的海量题集,由任课教师将实验题目生活化和趣味化,使学生自主选择合理的数据结构和算法来解题,这样可以充分激发学生的学习主动性和积极性,将被动学习转化为主动学习,更好地达到了实践教学的目的。
(3)实验时间分散化:
考虑到实验课时非常有限,可参照ACM竞赛平台来构建“程序设计在线评测系统”,功能包括用户管理、题库管理、在线提交、在线排名、在线讨论等。学生注册后可在任何时间登陆该系统进行选题、提交、评测和讨论等自主学习环节,将有限的课内练习时间延续到课外。
3.3 开发资源网站
在全面搜集ACM竞赛相关资源的前提下,以程序员协会的学生会员为主力设计并开发了“ACM资源网站”,并挂靠在学院的Web服务器上,以该资源网为平台来开展竞赛的宣传、组织、培训等活动,同时也为相关课程的理论实践教学和学生自主学习提供了一个优质的信息化平台。
3.4 建设学生梯队
依托于乐山师范学院第二课堂课程《ACM程序设计》的开设,以乐山师范学院三星级社团“程序员协会”为活动主体,在全校范围内吸纳对计算机编程和竞赛感兴趣的学生,成立“ACM竞赛兴趣小组”,通过举办专业讲座、学生科研、协会内部竞赛、协会沙龙等活动,为本专业学生提供一个进一步增强职业技能的交流和学习平台,同时也要在兴趣小组中发现适合参加ACM竞赛的后备人才,面向各年级构建ACM竞赛梯队。
3.5 建立激励机制
增设创新学分,设置创新环节,搭建创新实践的平台,让学生有更多的机会展示自己的专业特长。将参加ACM等学科竞赛纳入学生的综合测评,通过设立竞赛奖学金制度来引导学生积极参加课外科技活动、不断提高自身的创新素质。
3.6 组织参赛
在本教改项目的实施过程中,还要积极组织学生参加各个级别的ACM赛事。对于每一次竞赛,首先成立竞赛领导小组,分析官方公布的竞赛大纲,及时、准确地改革专业教学体系目标和课程开设计划;其次根据往届参赛经验,结合本次竞赛的具体情况制定出竞赛活动方案,将竞赛的宣传、组织、选拔、培训、参赛、奖励等环节制度化;然后选拔ACM参赛队伍,指派经验丰富且取得过优异成绩的教练对参赛队员进行长期、深入、全方位的强化培训和指导;最后通过对竞赛成绩的分析再次调整专业课程开设计划和教学模式。[2]
3.7 改革考核手段
ACM模式的重要特色之一是完善而严谨的考核机制,所以我们大胆尝试将ACM的考核方式借鉴到程序设计类课程的考核环节中,采用ACM模式的黑箱测试,将学生在“程序设计在线评测系统”中获得的成绩以50%的权重加入到课程考核指标当中。这一方面减少了教师的工作量,降低了考核错误率,另一方面做到了客观、公正,更好地发掘了学生的创新能力,提高其对知识点的掌握程度。
4 要解决的关键问题
4.1 课程教学形式的改革,特别是如何处理实践教学和理论教学的比重关系,以及如何让学生能够真正地解决问题,而不是按照设定好的思路去模仿着解决问题。
4.2 课程评价体系的改革,尤其是目前的实践环节评价机制弊端明显,严重束缚了学生的创新能力,错误地引导学生把自己改造为一个受制于理论教材的傀儡。
4.3 差异化教学,考虑到ACM竞赛的难度较大,所以必须考虑到在将ACM融入到专业课程教学过程之后,如何确保整体教学质量并解决好部分学生学习能力较差的问题。
4.4 在ACM竞赛中取得更好的成绩,必须建立有效的组织、选拔、培训、参赛、总结等相关机制。
5 结语
ACM竞赛对程序设计类专业课程的教学改革起到了积极的推动作用,从教学队伍建设的角度来看,它在提高教师的教学水平、科研能力、促进专业的对外交流等方面都起到了重要的作用;从学生培养的角度来看,它在提高学生的学习兴趣、自学能力、创新能力、求真务实的科学态度上有很大的帮助。
总之,通过合理的应用ACM竞赛这个平台,可以使我们的计算机专业教学更趋科学化、规范化,可以让我们的学生开拓视野,促进实践型、创新型人才的培养,提高学生的就业竞争力。
参考文献:
[1]常子楠.基于ACM模式的程序设计类课程实践教学探索[J].计算机教育,2010(16):144-146.
[2]项炜.以学科竞赛促进计算机专业教学改革的探索[J].改革与开放,2009(12):207.
篇12
1.1 Linux操作系统
Linux是一套可以免费使用和自由传播的类UNIX操作系统,其实际上只是一个操作系统的内核,主要用于Intel x86系列CPU的计算机上[ ]。
谈及Linux的起源,其灵感源自于UNIX。UNIX操作系统于1969年由Bell实验室设计开发,之后Linus Torvalds设计了Linux,该系统在发展初期就得到了广大程序员的帮助,逐步发展成为现今这样一个拥有自己版权的完整的系统。
Linux具有很多特点,如支持多种体系结构,支持大量的设备,具有完善的网络功能,开放源代码,软件资源丰富,内核稳定等,可总结为以下几点:
(1)强大的编程能力[ ]。由于Linux源自于世界各地成千上万的程序员和黑客,使得Linux就犹如加入到了一个高手如云的编程组织中,同时,由于GPL的存在,Linux开放源码,吸引更多专业人士的加入,在这种需求的刺激下,Linux提供的开发工具功能也越来越完善,越来越强大。
(2)完善的组网能力。Linux具有强大的组网能力,它对当前的TCP/IP协议[ ]提供了完全的支持,同时也支持下一代Internet协议IPv6。在安全性方面,Linux内核中包括了IP防火墙代码、IP防伪及IP服务质量控制等特性。现阶段,Linux广泛用于服务器,其可以作为Windows客户机的打印和文件服务器,也可用做NT的文件和打印服务器,甚至可作为Macintosh客户机的文件和打印服务器。另外,Linux还包括了一个Ftp服务器、一个电子邮件传输程序以及POP和IMAP邮件服务程序。
(3)Linux是自由开放的。Linux是自由软件,允许成千上万的人检查软件,修改软件,最终可以按照用户自己的意愿来定义自由软件,可以定制自己的Linux。
(4)系统稳定。Linux提供了完全的内存保护机制,每个进程都运行在各自的虚拟地址空间中,不会损坏进程或内核使用的地址空间。一台运行Linux的机器启动一次可以运行数个月。在安全性的较量上,Linux明显比Windows98和NT占上风。据统计分析,Linux是目前最安全的操作系统。
1.2 嵌入式系统
最早期的8位/16位系统,大多都是没有操作系统的,然而在进入32位时代之后,系统软件变得越来越复杂,出现了控制能力不够,维护成本过高,系统升级困难等问题,促使了操作系统的迅猛发展。而嵌入式操作系统,则被定义为“以应用为中心、以计算机技术为基础、软硬件可裁剪、适应应用系统,对功能、可靠性、成本、功耗等方面有严格要求的专用计算机系统”。
嵌入式操作系统的特点有:提供较好的内核管理、多任务管理、资源管理、稳定性好、可裁剪和配置、满足实时性需求、针对性强等。随着计算机信息技术的不断发展,嵌入式操作系统也在不断演化升级,常见的有uC/OSⅡ操作系统,eCOS操作系统,VxWorks嵌入式实时操作系统,WinCE操作系统以及Linux操作系统等。嵌入式Linux利用嵌入式系统实时性、稳定性的特性和Linux相辅相成,很好的弥补了Linux实时性差的缺点,使得Linux在嵌入式领域发展迅速。近年来,嵌入式Linux操作系统的应用相当热门,已经广泛应用于笔记本电脑、连网装置、网络电视等各式各样的通信基础产品中。
2 系统硬件环境
2.1 Intel的XScale系统结构
XScale系统结构是对StrongARM的系统结构的扩充和改进。Intel的第一个StrongARM处理器SA-110实现了ARM系统结构的第4版,即ARMv4内核。StrongARM是ARM公司的注册商标,最初由ARM公司与当时DEC公司联合开发,由DEC生产和销售,但是后来由Intel生产。SA-110是第一个采用哈佛结构的ARM内核,因此,它有两个独立的高速缓存和两个MMU。这样取指令与读/写就不需要在时间上互相错开,从而提高了访问高速缓存的速度。
2.2 PXA27x处理器
PXA27x处理器是Intel公司在2004年4月12日正式的当前最新的嵌入式处理器。它的时钟频率从312到624MHz,并内建64MB(megabytes)的堆栈型IntelStrataFlash内存。它内置了Intel公司的无线MMX技术,能够为3D游戏与影片应用提供更高的效能,显著提升多媒体性能。312MHz的CPU(PXA27x系列中最低时钟频率的产品)将达到520MHzARMCPU的多媒体处理效能,而钟频达到624MHz则可以具备775MHzARMCPU的表现。
3 系统软件环境的建立
3.1 嵌入式操作系统的选择
嵌入式操作系统的选择主要从以下几个方面加以考虑。
①操作系统的硬件支持:首先是否支持目标平台,其次可移植性。②开发工具的支持程度:选择嵌入式操作系统时必须考虑与之相关的开发工具。在线仿真器(ICE)、编译器、汇编器、链接器、调试器以及模拟器等不同程度的影响到嵌入式软件的开发。
③能否满足应用要求:应用对操作系统的要求包括实时性能、不同语言的支持、标准兼容性、技术支持、源代码还是目标代码等等。
若找不到一个合适的操作系统或者买不起昂贵的商用操作系统,可以考虑自己建立一个操作系统。
3.2 系统结构设计
Trident PNX8471芯片平台能够接收BTSC(Broadcast Television System Committee),该平台有两个硬盘接口、一个MII 接口(Media Independent Interface,介质无关接口或称为媒体独立接口,它是IEEE-802.3定义的以太网行业标准。它包括一个数据接口,以及一个MAC和PHY之间的管理接口)、2个USB2.0接口、1个USB1.1接口、5路TS输入接口、2路I2C,支持3D图形加速。在性能上支持:视频支持1080P60输出,支持H.264、MPEG2、VC-1、DiVX、AVS等格式,同时也支持RMVB格式480P的输出,3DTV支持H.264 MVC 1080P30输出;音频支持BTSC、CH3/4,输出支持YPbPr、RGB、Y/C、CVBS;TS处理方面最大支持5路基带输入和4路DMA输入,最大支持192个Filter。
Trident PNX8471芯片平台的编译使用GNU3.81,Perl版本5.8.8,支持Android2.3,Linux内核版本2.6.34。平台在基于Android的架构上增加了数字电视相关的功能模块。实现本项目的EPG功能,同时兼顾EPG功能的扩展性,我们需要在硬件抽象层增加Demux模块,用来接收PSI/SI数据,这个部分将是Program模块、Event模块、Time模块公用的模块;在Libraries层增加Program模块、Event模块、Time模块,用来实现节目、事件、时间的功能;在应用框架层(Application Framework)增加获取EPG功能的EPG JNI接口;在应用层增加EPG的呈现。
4 结语
嵌入式系统已成为以高速CPU和嵌入式操作系统为核心的软硬件综合系统。系统兼容性好,效率高,而且具备文件和目录管理、设备支持、多任务、网络支持、图形窗口及用户界面等功能。
参考文献:
[ ]彭晓明,王强.Linux核心源代码分析[M].北京:人民邮电大学出版社,2000.
[2]李善平,刘文峰.Linux与嵌入式系统[M].北京:清华大学出版社,2006.3.
篇13
文章编号:1004-373X(2010)03-131-03
Design and Implement of New Voltage Monitoring Instrument Based on STC12C5A60S2
CHEN Weifeng,DENG Xiaoying,LV Tianwen
(College of Physics Science and Technology,Yangzhou University,Yangzhou,225002,China)
Abstract:A design based on micro-controller STC12C5A60S2 is introduced because of the demand of monitoring the real-time voltage on power grid.This system consists of the voltage data acquisition module and the display module,which can display the real time and real-time voltage.It contains the functions as below:warning when the voltage beyond or above the limit and the setting of ratio and system coefficient,and the statistic of the voltage etc.The latest and high effective 1T8051 single chip microcomputer and memory are used to fasten the speed of execution and storage.The program is edited by the high-level language,which contains the subroutines such as initialization of the system and the key scanning,handling of menu and statistic of the voltage.
Keywords:voltage monitoring instrument;STC12C5A60S2 micro-controller;real time;D/C conversion
0 引 言
电压监测系统是对电网电压质量进行监测并自动记录的智能化仪表系统,为统计电压的合格率及其他参数、反映电压质量的管理提供正确的数据[1]。电压监测系统的最小组合为一台智能电压监测仪。通过仪表的使用,即可实现对电压监测点的各种电压参数进行测量并记录,同时给出电压合格率、电压偏高不合格率、电压偏低不合格率等计算结果。电压监测系统的另外┮恢肿楹鲜怯傻缪辜嗖庖羌由系缈ā⑼ㄑ痘及上位计算机等配套设备组成。电压监测仪所记录的数据很多,通过仪表键盘来查询抄录十分耗时耗工,而直接用小打印机打印也有许多缺点和不便之处[2]。本系统可实现电网电压集中监测,并有查询、统计报表、电压越限报警、典型工作日设定、系统变比和电压值误差系数可调整等一系列功能[3]。
1 系统设计
采集模块以高性能微处理器STC12C5A60S2为核心[4],由信号变换、实时时钟、串行通信和大容量串行存储器等模块电路构成。整个系统结构组成如┩1所示。
图1 系统结构
STC12C5A60S2是宏晶科技今年新推出的一款处理器,全面兼容传统的51系列。STC12C5A60S2是┮桓鍪敝/及其周期,增强型51内核,速度比普通的8051快8~12倍。工作电压比较宽,为3.3~5.5 V。增加第二复位功能引脚,并且具有外部掉电检测电路,可在掉电时,及时将数据保存进E2PROM。内部有1 280 B的RAM数据存储器。芯片内部有E2PROM功能,擦写次数达10万次以上。具有ISP/IAP功能,8通道10位高速ADC,速度可达2.5×105次/s,2路PWM还可作2路D/A使用。内部已经集成了独立的波特率发生器,此系列单片机串行通信的速率可以不由内部定时器T1的溢出率来决定,这样可以让T1来实现定时或者计数的功能。此系列的单片机还有双串口的功能,一个串口可以被系统使用,剩下的串口可以用来作系统程序调试信息的输出,避免一个串口被占用的情况下,只能使用I/O口去模拟UART时序的麻烦。
监测系统是弱电系统,而电网电压一般是220 V左右的交流电,不能由系统直接进行测量。必须把测量的电压通过带缓冲器的降压器,降至3~4 V的交流小信号。
降压之后的电压信号为交流小信号,再通过真有效值转换芯片AD536转换成有效值相等的直流信号并输出[5]。
AD536转换输出的直流信号通过V/F变换芯片LM331输出其直流信号所对应频率的数字脉冲,供单片机进行采集,从而单片机计算出直流信号的大小。在本设计中没有采用传统的A/D转换器把模拟信号变成数字信号,而是采用LM331进行V/F变换[6]。
V/F变换的电路图如图2所示。
图2 V/F变换电路图
LM331的动态范围宽,可达100 dB;线性度好,最大非线性度失真小于0.01%,工作频率低到0.1 Hz时尚有较好的线性;变换精度高,数字分辨率可达12 b;电路简单,只需接入几个外部元件就可方便构成V/F变换电路,并且容易保证转换精度[7]。根据上面的电路和数据手册可以得出LM331输出频率的计算公式:
Fout=Vb2.09 VR2+R1R41R5C4
LM331输出的数字脉冲通过光耦TLP521进行隔离,再送入单片机的定时器T1进行计数。定时器T0用来定时,每隔1 s钟单片机在T0的定时中断服务程序中读取T1计数器的值,因为间隔是1 s,所以读出来的值就是LM331输出脉冲频率的大小。由上面的公式可以计算出AD536输出的有效值Vb,进而可以推算出此时测量电网电压的大小[1]。
根据要求,此系统需要记录历史电压。这里选择铁电存储器FM24C512,它是一款容量高达512 KB的非易失性存储器,它采用了先进的铁电处理技术[8]。原理图如图3所示。
图3 FM24C512原理图
FM24C512以总线速度进行写操作,无须延时。┫乱桓鲎芟咧芷诳梢粤⒓纯始,无需进行数据轮询,最高总线频率高达1 MHz。另外,FM24C512具有比E2PROM高得多的写操作次数。而且,因为写操作不需要在内部提升电路写电路的电源供电电压,所以,在写操作过程中,FRAM比E2PROM消耗的功率要低得多。
FM24C512使用I2C通信协议,简化了与微控制器的接口电路。它使用极少的管脚,占用极小的板空间,只使用两个管脚和处理器进行通信,一个是时钟线SCL,另一个是数据线SDA。因为所使用的主处理器没有相关的I2C通信接口,所以只能用两个I/O口去模拟I2C的时序。时序图如图4所示。
图4 FM24C512工作时序图
在记录历史电压时,实时时间芯片DS1302产生的时间作为记录电压的参考[9]。DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5~5.5 V。采用三线接口与CPU进行同步通信,并可采用突发方式┮淮未送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1302兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力[10]。它与单片机的连接如图5所示。
DS1302与CPU的连接只需要三条线,即时钟线(SCLK)、数据线(IO)、复位线(RST)。相应的时序图如图6所示。
图5 DS1302与单片机的连接
图6 DS1302工作时序图
根据图6,就可以编出相应读取DS1302时间的子程序。
uchar Rd_1302(uchar add)
{
uchar ucda;
Ds_Rst = 0;//先把复位口拉低
Ds_Clk = 0;//再把时钟口清零
Ds_Rst = 1;//把复位口至高,准备写入地址
Input_Byte(add);//设定操作寄存器的地址
ucda = OutPutByte(); //读出相应寄存器数据
Ds_Rst = 0;//把时钟线恢复至低电平
return ucda; //返回读取的数据
}
为了增强人机的交互性能,选用金鹏电子的OCMJ12232液晶显示模块作为系统的显示部分。OCMJ12232的显示方式为点阵图形,可以显示任意图形或者汉字,内带8 192个中文点阵,具有并行和串行两种接口方式。本设计采用了串行接口方式,具有占用I/O资源少,编程简单等特点。
2 流程设计
系统程序由显示模块、按键扫描模块、频率计数模块、记录统计模块、系统菜单模块等组成。在软件编写过程中,记录统计模块较为复杂,其算法为:程序判断是否到了下一秒钟,如果没有则继续采集电压再进行判断,否则进行有关电压秒记录的处理;接着判断是否到了一分钟,如果没有则退出,否则进行有关电压分钟记录的处理;下一步再判断是否到了一小时,如果没有则退出,否则进行有关电压小时记录的处理;以此类推,还
要判断是否满了一天、一个月的情况。其中,还需要判断是否是典型工作日,每个月可以设置三个典型工作日,典型工作日中每个小时的记录都被保存下来,以便用户查看。流程图如图7所示。
图7 软件流程图
3 结 语
本文详细介绍了基于STC12C5A32AD的电压监测系统,适用于0~480 V交流电压的监测。本设计已经完成了设计和调试,并通过了客户的验收,成功运行在电网公司的监控大厅。
参考文献
[1]杜秀芳,曹玉强,张静.智能型真有效值电压检测仪[J].兵工自动化,2006,25(5),84-87.
[2] 张建民,黄剑.DC-100/C电压监测系统的研制[J].华东电力,2001(4):31-32.
[3]孔,段峥辉.GSM短信电压监测仪的应用分析[J].电力设备,2005,12(6):61-63.
[4]宏晶科技.STC12C5A60AD系列单片机器件手册[EB/OL].,2009.
[5]赵鹏,李志刚.AD536的性能及其应用[J].国外电子测量技术,2004(2):17-19.
[6]刘滨.VM-I型电压监测记录仪的结构与功能[J].吉林电力技术,1997(2):50-51.
[7]漆文辉.变电站电压质量监测系统的研制[J].仪器仪表学报,2002,23(5):192-197.