网站首页
IC库存
IC展台
电子资讯
技术资料
PDF文档
我的博客
IC72论坛
ic72 logo
搜索关键字: 所有资讯 行业动态 市场趋势 政策法规 新品发布 技术资讯 价格快报 展会资讯
  • 达普IC芯片交易网 > 新闻中心 > 技术信息 > 正文
  • RSS
  • 采用VXD技术实现实的通信
    http://www.ic72.com 发布时间:2007/4/28 4:29:12

    摘要:讨论Windows虚拟设备驱动(VXD)技术,并采用此项技术示范性地做出应用于PC串口实时通信的虚拟驱动程序,找到一种可以在PC中实现实时通信的途径。

    关键词:VXD 实时 串口通信

    引言

    在微软的视窗操作系统中,系统内核掌管所有的应用程序,通过独特的任务调度算法实现CPU的分时多任务处理方式。多任务处理对大多数用户可能是件好事,但是对那些想把实时通信建立在Windows操作系统上的特殊用户来说,操作界面的图形化并不比MS-DOS的单任务更具吸引力。在视窗操作系统里可以进行实时通信和控制码?答案是:VXD技术可以帮我们在获取友好的人机界面的同时还拥有很强的实时性。

    1 VXD技术解析

    VXD技术可追溯到Windows3.1,它的引入就是要让操作系统实现多工以及硬件资源的共享。为了支持多个MS-DOS任务同时执行,Windows98让每个MS-DOS应用程序在各自的虚拟机(VM)上运行,各自互不相干;而所有的Widnows应用程序却都在一个虚拟机上运行。图1所示的结构框图很好地说明了Windows98的整体架构。

    图1中,由众多的VXD组成系统级代码处于最底层。其中,处于中心地位的是一名为VMM32的VXD,它负责协调和管理所有的VXDs。其它VXDs则通过消息机制(这个消息机制由VMM32.VXD来维护)彼此联系。由所有VXDs开放出的服务接口(API)组成了一个服务网,它们彼此通过合作的方式,提供Windows98的系统底层驱动服务。

    从以上Windows98系统架构可以看出,要想在视窗平台下获取很强的实时性,仅靠提升应用程序线程优先级的方法是不够的。因为Win32应用程序代码属于Ring3级,而VXD代码则属于Ring0级;采用VXD撰写的实时通信程序可以完全不受代码限制,可以直接对硬件进行操作。VXD的这个特点正是实时通信建立所必须的。

    设计实时通信的VXD前,先解释以下几个问题:

    ①VMM32使用VPICD.VXD虚拟化每个硬件和软件中断。VMM32为每个虚拟机(VM)维护一个IDT结构,当中断发生时,CPU先保护中断现场,然后经由当前VM的IDT把这个中断引导至相应的中断处理程式。

    中断的虚拟化,使我们有机会给每个中断提供新的中断处理函数,并可以让多个硬件共享同一个中断号。VPICD.VXD为我们提供这些服务。

    ②VMM有两个调度器,用以在多个线程和VMs之间实现抢占式多工。主调度器负责选定下一个将被执行的线程。这个选择可以是一个,也可以是多个。然后,主调度器把选择结果送给所谓的时间片调度器,并由后者完成各个应用程序间的时间片分配。调度器也时应用程序经由呼叫Win32线程优先调整API(如SetThreadPriority和SetPriorityClass等)做出回应。当中断发生时,VMM32自动提升中断处理函数所在VM之优先级,保证中断处理函数能及时被执行。

    ③VXD和Win32应用程序可直接通信。Win32应用程序可通过一个系统API(DevicelOControl(…))来呼叫位于底层的VXD为其服务。在呼叫VXD前,首先必须调用CreatFile(…)这个API加载该VXD(如果该VXD是一个静态VXD,则不用加载)。所有的呼叫动作其实都通过VMM32完成。VXD也可以通过消息方式和位于上层的Win32应用程序通信。She11.VXD为所有希望以消息机制和Win32应用程序通信的VXD提供了这一服务。

    以上是编写一个串口通信驱动需要的系统层面知识。对于Windows底层的了解。

    2 用VXD实现一个实时串口通信驱动

    接下来用VXD技术实现一个实时串行通信的驱动。这个VXD是一个动态(Dynamic)VXD,当它的服务被呼叫时,VMM32会动态加载这个VXD。作者采用的工具是C+98DDK。当然也可以使用其它的工具,如MASM6.11(或更高版本)、VtoolsD。用C搭配DDK完成VXD构建的好处是,可以使用C语言完成绝大部分的程序,程序比较容易阅读和维护。

    用C来实现一个VXD驱动,需要准备如下条件:一个.ASM的汇编语言接口文件(在其中定义VXD要处理的系统消息和输出API),一个.C的函数实现文件(在其中完成自己函数实体),一个.DEF的定义文件(在其中定义VXD中各个段的别名并汇成一个DDB)和一个.MAK档(用来编译并连接生成VXD,可有可无)。在这里,仅给出用C实现的函数档。至于其它的文件,可以从本文所列的参考书目或其它文献中找到相关文档的说明。

    这个串口通信驱动程序的功能是:实时送出一个Byte的数据,实时接收一个Byte的数据。作为演示之用,并没有加入其它代码。该VXD驱动主要由如下3个系统消息(由VMM32来维护和管理)处理函数组成,其代码如下:

    (1)OnSysDynamicDeviceInit()函数

    BOOL OnSysDynamicDeviceInit()

    { //OnSysDynamicDeviceInit

    irqhandle=VPICD_Virtualize_IRQ((DWORD)(&irq4));

    if(irqhandle= =0){

    return FALSE;

    }

    return TRUE; //OnSysDynamicDeviceInit

    }

    该函数用来完成VXD初始化所做的工作。在本例中,由于实时监视串口中断的需要,要给COM1的中断安装一个自定义的断服务函数。98DDK已经提供了这个函数的C语言版,其原型是HIRQ static VPICD_Virtualize_IRQ(PVID pvid),在vpicd.h中。该函数需要一个指针作为参数(指向名为VPICD_IRQ_Descriptor的结构体),函数传回一个指向该虚拟IRQ的句柄(该句柄在后来的VPICD服务中需要提供)。VPICD_IRQ_Descriptor结构体的组成为:

    typedef struct VPICD_IRQ_Descriptor{

    USHORT VID_IRQ_Number; //IRQ号(0~15)

    USHORT VID_Options; //标志位选项

    ULONG VID_Hw_Int_Proc; //硬件中断服务程序的地址

    ULONG VID_Virt_Int_Proc; //虚拟中断服务程序

    ULONG VID_Mask_Change_Proc //Mask Change调用例程

    ULONG VID_IRET_Proc; //IRET调用例程

    ULONG VID_IRET_Time_Out; //在Vm的进程优先级提升之前的最大等待时间

    ULONG VID_Hw_Int_Ref; //硬件中断服务程序的数据存放地址

    }VID;

    其中只用到三位。在本例中需要声明一个名为irq4的全局变量为VID结构,并付给如下初值:VID irq4={4,0,hwproc,0,0,0,0,500,0},表示将要虚拟化IRQ4,改变其中断处理函数为void hwproc(void),该函数的原型如下:

    void hwproc(void){

    _asm{

    mov dx,0x3f8

    in al,dx

    mov byte ptr [readin],al

    clc

    }

    return;

    }

    在这个中断处理中,仅仅从COM1的数据寄存器(地址为3F8h)中读取接收到的数值,并把该数值存放在一个类型为BYTE、名为readin的内存中。

    (2)OnSysDynamicDeviceExit()函数

    BOOL OnSysDynamicDeviceExit()

    {

    VPICD_Force_Default_Behavior(irqhandle);

    //解除IRQ4虚拟化

    return TRUE;

    } //OnSysDynamicDeviceExit

    该数提供了用于善后处理VXD在卸载时需要完成的事件。在本例中,和VXD初始化对应,需要解除对COM1的中断IRQ4的虚拟化。作者也是用98DDK在vpicd.h中提供的外包函数void static_inline VPICD_Force_Default_Behavior(HIRQ hirp)。该函数唯一需要的参数便是使用VPICD_Virtualize_IRQ函数传回的IRQ句柄。

    (3)OnDeviceIoControl()函数

    DWORD OnDeviceIoControl(PDIOCPARAMETERS p){

    Switch (p->dwIoControlCode)

    {

    case 1: //端口写功能

    if(!p->lpvOutBufferp->cbOutBuffer<1)

    { //输出缓存的有效性检查

    return ERROR_INVALID_PARAMETER;

    }

    if(serial_out((DWORD)(p->lpvInBuffer)))

    { //数据发送

    *(BYTE*)(p->lpvOutBuffer)=*(BYTE*)(p->lpvInBuffer);

    }

    else{

    *(BYTE*)(p->lpvOutBuffer)=0;

    }

    open_int(); //打开com1中断

    return 0;

    case 2: //端口读功能

    if(*(BYTE*)reading= =0x00)

    { //数据读入

    *(BYTE*)(p->lpvOutBuffer)=0x00;

    return 0;

    }

    *(BTYE*)(p->lpvOutBuffer)=*(BYTE*)(readin);

    return 0;

    }

    return 0;

    }

    return 0;

    }

    OnDeviceIoControl函数用来处理Win32应用程序对VXD的呼叫。Win32应用程序的呼叫会让VMM32送给该VXD一个系统信息,并传递进一个DIOCPARAMETERS结构的指针。该结构里包含Win32应用程序呼叫时传递进来的各个参数。这个结构的组成如下:

    Typedef stunct DIOCParams{

    DWORD Internall; //指向客户寄存器的指针

    DWORD VMHande; //该VM的句柄

    DWORD Internal2; //指向DDB结构的指针

    DWORD dwIoConrolCode; //DeviceIoControl例程中呼叫的控制码

    DWOD lpvInBuffer; //DeviceIoControl例程呼叫所传递进来的输入缓冲区地址

    DWORD cbInBuffer; //输入缓冲区的大小

    DWORD lpvOutBuffer; //DeviceIoControl例程呼叫所传递进来的输出缓冲区地址

    DWORD cbOutBuffer; //输出缓冲区的大小

    DWORD lpcbBytesReturned; //拷贝到输出缓冲区中的字节数(可以为NULL)

    DWORD lpOverlapped; //DeviceIoControl例程呼叫所传递进来的重叠I/O块结构

    DWORD hDevice; //Ring3层呼叫应用程序句柄

    DWORD tagProcess; //例程标签

    }

    DIOPARAMETERS;

    其中,dwIoControlCode指明了Win32应用程序需要VXD提供的哪一项服务。在本例中采用一个switch-case语句作为服务入口,如下所示。其中服务1为让串口送出一个字节,服务2为读取一个已经由串口接收的字节。函数open_int()是用来初始化串口以便接收字节数据;函数BOOL serial_out(DWORD pBuffer)是让串口发出一个字节。它们的函数体分别如下:

    BOOL serial_out(DWORD pBuffer){

    if(pBuffer= =NULL){

    return FALSE;

    }

    _asm {

    pushfd

    cli

    push eax

    push edx

    mov dx,0x3fb ;设置COM1的波特率

    mov al,0x83

    out dx,al

    mov dx,0x3f8

    mov al,12

    out dx,al

    mov dx,0x3f9

    mov al,0

    out dx,al

    mov dx,0x3fb ;设置COM1的线控项

    mov al,3

    out dx,al

    mov dx,0x3f9 ;CMM1关中断

    mov al,0

    out dx,al

    mov dx,0x3fa ;关闭com1的FIFO功能

    mov al,0

    out dx,al

    mov dx,0x3f8 ;字节发送

    mov al,byte ptr [pBuffer]

    out dx,al

    pop edx

    pop eax

    popfd

    sti

    }

    return TRUE;

    }

    serial_out这个函数体的实现是用汇编语言实现的。因为涉及到很多的端口提供以及CPU的标志(flag)和压栈操作,因此考虑到用汇编语言编写会简化代码。因为此串口传输中,用到了关闭中断的指令(cli),所以,当写操作所要求完成的任务很多时,此关中断指令会让程序的实时性很好地体现出来,但cli指令有效时间过长会导致系统问题,所以还是要谨慎使用。

    Void open_int(void){

    _asm{

    mov dx,0x3f9 ;COM1开中断

    mov al,0x05

    out dx,al

    }

    return;

    }

    open_int函数用来把PC串口的中断设备按照需要设立起来。函数体很简单,仅改变了地址为3F9h的内容,意为设置Rx data ready和Line status中断位,以便让CPU可以及时在COM1的中断服务程序里读取串口接收到的字节。

    以上涉及到串口输入和输出的函数体实现代码中,用到了PC16550 UART的资料。

    至此,一个可用于实时串口通信的VXD驱动程序已经完成。由于篇幅所限,不能将其它必要的文档一同提出来讨论。

    3 Win32客户测试程序

    有了上述VXD驱动程序,还需要搭配一个Win32客户程序来进行测试。在网络补充版(http://www.dpj.com.cn)中,给出一个笔者在VC6下编制的一个控制台应用程序片断,以供参考。

    现在编制VXD驱动还没有一个集成开发环境(IDE)。本文的驱动程序是用VC6.0自带的编译器编译的。由于要编译汇编文档,所以还需要把一个MASM汇编器(要求6.0以上版本)及其相关文档拷贝到VC6.0的VC98\u30446目录下。

    4 结论

    通过以上对VXD技术的简要分析以及一个用VXD实现的通信驱动可以看出,在Windows操作系统中,采用VXD技术,可以很好地克服由多工带来的时延问题,很好地解决了在Windows平台下实时通信的问题。






    相关信息:
    www.ic72.com 达普IC芯片交易网
  • 行业动态
  • 市场趋势
  • 政策法规
  • 新品发布
  • 热点信息

    Baidu

    IC快速检索:abcdefghijklmnopqrstuvwxyz0123456789
    COPYRIGHT:(1998-2010) IC72 达普IC芯片交易网
    客户服务:service@IC72.com 库存上载:IC72@IC72.com
    (北京)联系方式: 在线QQ咨询:点击这里给我发消息 联系电话:010-82614113 传真:010-82614123
    京ICP备06008810号-21 京公网安备 11010802032910 号 企业资质