两种思路实现VB环境下的PCI设备底层访问

时间:2011-09-04

    Visual Basic从1991年诞生以来,现在已经20年了。BASIC是微软的起家产品,微软当然不忘了这位功臣。随着每微软技术的浪潮,Visual Basic都会随之获得新生。可以预见,将来无论微软又发明了什么技术或平台,Visual Basic一定会首先以新的姿态登上去的。如果你想紧跟微软,永远在的技术上快速地开发,你就应该选择Visual Basic。

    VB是Visual Basic的简称,是由美国微软公司于1991年开发的一种可视化的、面向对象和采用事件驱动方式的结构化程序设计语言,可用于开发 Windows 环境下的各类应用程序。它简单易学、效率高,且功能强大可以与 Windows 开发工具SDK相媲美。在Visual Basic环境下,利用事件驱动的编程机制、新颖易用的可视化设计工具,使用Windows内部的广泛应用程序接口(API)函数,动态链接库(DLL)、对象的链接与嵌入(OLE)、开放式数据连接(ODBC)等技术,可以高效、快速地开发Windows环境下功能强大、图形界面丰富的应用软件系统。

    Visual 意为可视的、可见的,指的是开发像Windows操作系统的图形用户界面(Graphic User Interface,GUI)的方法,它与其他编程软件不同的是不需要编写大量代码去描述界面元素的外观和位置,只要把预先建立好的对象拖放到屏幕上相应的位置即可。应该说,这是质的飞跃,是编程技术的革命。Basic 实际上是一个短语的缩写,这个短语就是 Beginners' All-Purpose Symbolic Instruction Code ,其中文意思为“初学者通用符号指令代码”。   

    在VB开发环境下,用户要访问诸如数据采集卡之类硬件上的PCI设备,一般来说有两种途径:一是直接访问,即用VB直接编写访问PCI设备的接口函数(这种方法要有相关软件的支持);二是间接访问,即VB调用其它编程语言(如汇编,C/C++等)写的底层驱动模块(一般封装成动态连接库DLL的形式)实现。    

    本文介绍了在VB开发环境下访问PCI设备的方法。对于其他设备,方法与此大同小异。

  1PCI总线的配置空间

  PCI规范定义了三种地址空间,除了存储器和I/O地址空间外,为支持PCI设备系统资源的自动配置,还定义了配置地址空羊。PCI总线的配置空间由256个字节组成,分为预定首区和设备关联区。预定首区包括开始64个字节,对所有的PCI设备来说,都必须支持该区的设置;设备关联区的寄存器有不同的的PCI设备厂家自己定义。配置空间的预定的首区分两个部分,前16个字节的定义对各类PCI设备而言都是相同的,后48个字节空间根据设备支持的功能有不同的分配。首区类型定义了该空间的分配情况(目前只有一种类型00H)。表1是首区的组织结构。

   

  所有的PCI设备必须支持首区的供应商ID、设备ID、指令和状态区。对于其他寄存器的使用可根据设备的楞能来选择。对于不同的PCI设备,其供应商ID由PCI SIG分配以确保性,而设备ID则由供应商自己分配。

   2PCI设备的配置过程

  PCI总线的配置空间规范保证了所有PCI设备对“即插即用”的支持。系统在上电后,“即插即用”BIOS通过隔离算法读取每一个“即插即用”设备的资源申请数据,并分配相应的系统资源,同时检查资源的冲突情况,然后引导、加载操作系统,并将控制权交给操作系统;如果加载的是“即插即用”操作系统(WINDOWS 95及以后版本),那么操作系统将接管系统的资源管理权,它首先从BIOS读取“即插即用”设备的资源配置信息,并仲载资源冲突情况,然后配置BIOS尚未配置的“即插即用”设备,将设备的配置信息写入配置管理器,激活无资源冲突的“即插即用”设备,装载相应的设备驱动程序。

  3 VB下PCI设备的访问

    VB采用了面向对象设计思想,它基本思路是把复杂的设计问题分解为多个能够完成独立功能且相对简单的对象集合。所谓“对象”就是个可操作实体如窗体、窗体中命令按钮、标签、文本框等,面向对象编程就是指程序员可根据界面设计要求直接在界面上设计出窗口、菜单、按钮等类型对象并为每个对象设置属性。驱动程序访问PCI设备的过程一般包括扫描PCI总线,相找指定的PCI设备,确定I/O等资源分配情况,进行I/O、存储器、中断以及DMA等操作。VB本身并不能实现上述对PCI设备的访问过程,下面介绍在VB下通过其他途径实现对PCI设备的访问。

  3.1 VB直接访问

    VB为编程提供了个集成开发环境在这个环境中编程者可设计界面、编写代码、调试直至把应用编译成可在Windows中运行可执行文件并为它生成安装VB集成开发环境为编程者提供了很大方便。WINDRIVER为VB只提供了非常有限的I/O访问能务(如串口通信),在VB下直接访问PCI设备时需要借助其它软件。目前WINDRIVER是KEFTech公司主推产品,是许多PCI厂家所推荐的驱动器程序开发工具。WINDRIVER为VB 4.0以上版本提供了一个类模块(WINDRIVER.CLS),利用这个类模块,用户可以手工编写自己需的接口函数来访问相应的设备。

    下面以具体例子来说明WINDRIVER.CLS的使用方法。

  3.1.1 扫描PCI总线得到指设备的数目

  利用WINDRIVER.CLS提供的应用程序接口函数(APIs),编写一个扫描PCI总线,获得指定PCI设备数目的函数下:

  Function GetCardsNum (dwVendorID As)

  Long, dwDeviceID As Long) As Integer

  Dim pciScan As WD_PCI_SCAN_CARDS

  Dim hWD As Long

  HWD = WD_Open()

  If Hwd =INVALID_HANDLE_VALUE Then

  MsgBox "设备打开出错"

  Exit Function

  End If

  PciScan.searchId.dwVendorId =

  DwVendorID

  pciScan .searchId.dwDeviceID =

  dwDeviceID

  WD_PciScanCards hWD, pciScan

  WD_Close (hWD)

  GetCardsNum = pciScan.dwCards

  End Function

  该函数可以通过输入参数:PCI设备的供应商ID和设备ID得到所需的PCI设备数目。如查找AMCC公司的PCI适配芯片S5933,则输入参数为:&H10E8和&H4750。

  下面例子用于读写S5933的PCI配置寄存器。在工程的全局模块中需要先定义下列数据结构,同时设备必须处于打开状态。

  Type AMCC_INNTERRUPT

  Int As WD_INTERRUPT

  HThread As Long

  Trans(O To 1)As WD_Transfer

  End Type

  Type AMCC_ADDR_DESC

  dwLocalBase As Long

  dwMask As Long

  dwBytes As Long

  dsAddr As Long

  dwAddrDirect As Long

  flsMemory As Boolean

  End Type

  Type AMCC_STRUCT

  HWD As Long

  CardLock As WD_CARD

  PciSlot As WD_PCI_SLOT

  CardReg As WD_CARD_REGISTER

  AddrDesc(0 To AD_PCI_BARS-1)As

  AMCC_ADDR_DESC

  fUseInt As Boolean

  int As AMCC_INTERRUPT

  End Type

  3.1.2 读写PCI配置寄存器

  完成以上数据结构的定义后,用下面的函数可写S5933的PCI配置寄存器内容。

  Function AMCC_ReadPCIReg (hAmcc As

  AMCC_SETRUCT, dwReg As Long)

  Dim pciCnf As WD_PCI_CONFIG_DUMP

  Dim dwVal As PVOID

  pciCnf.pciSlot = hAmcc.pciSlot

  pciCnf.pBuffer = dwVal

  pciCnf.dwOffer = dwReg

  pciCnf.dwBytes = 4

  pciCnf.flsRead = True

  WD_PciConfigDump hAmcc.hWD, pciCnf

  AMCC_ReadPCIReg = dwVal

  End Function `读函数

  Sub AMCC_WritePCIReg (hAmcc As

  AMCC_STRUCT, dwReg As Long, dwData As PVOID)

  Dim pciCnf As WD_PCI_CONFIG_DUMP

  pciCnf.pciSlot = hAmcc.pciSlot

  pciCnf.pBuffer = dwVal

  pciCnf.dwOffer = dwReg

  pciCnf.dwBytes = 4

  pciCnf.flsRead = False

  WD_PciConfigDump hAmcc.hWD, pciCnf

  End Sub `写过程

  参数说明:

  hAMCC 设备打开后系统分配的句柄

  dwReg 读写的PCI配置寄存器

  dwVal 读出的寄存器数据

  dwData 写入寄存器的数据

  以上例子仅仅是抛砖引玉。WINDRAR.CLS类模块提供了功能极为强大的底层驱动的API函数,用户通过编写相应的驱动模块可以方便地实现对各类硬件的I/O、存储器映射、中断以及DMA等操作,同时可以实现WIN32下物理内存空间的申请、读写等处理。另外对于实时性要求较高的设备,WINDRIVER提供的“内插”(Plug-In)特性可以让程序的相关模块运行于Ring 0内核模式(Kernel mode),以提高性能。开发完成的底层驱动模块既可直接为VB的应用程序调用,也可以在VB下封装成DLLs供其它的WIN32开发工具调用。

  3.2 自定义DLL访问

  DLL使VB的功能得到极大的增强,使得VB的应用范围不断扩大,使用更加灵活。VB通过调用自定义DLL可以实现对硬件的底层访问。下面用例了说明VB对DLL的调用及DLL的编写过程。

  3.2.1 DLL的功能和编写

  本例中的DLL通过扫描PCI总线,得到总线上S5933接口芯片的数目,打开指定设备,向S5933的输入邮箱子中写入命令字,然后从输出邮箱1中读取返回数据,关闭设备。

  extern "C" _declspec (dllexport) int _stdcall GetCardsNum()

  {

  AFX_MANAGE_STATE (AfxGetStaticModuleState());

  int cards;

  cards=AMCC_CountCards (0x10e8,0x4750);

  return cards;

  } //此函数得到S5933的数目;

  extern "C" declspec (dllexport) DWORD_stdcall Send-

  Command(int CardNum, DWORD dwCmd)

  {

  AFX_MANAGE_STATE (AfxGetStaticModuleState());

  DWORD data;

  If (AMCC_Open (&Hamcc, 0x10e8,0x4750, Card-

  Num, 0)) //打开指定设备

  {

  AMCC_WriteRegDWord(hAMCC, OMB1_ADDR);

  dwCmd); //写入命令字

  do{

  data=AMCC_ReadRegDWord(hAMCC,MBFF_ADDR);

  }while((data&0x000f0000)==0x00000000);

  //等待输入邮箱1满

  data=AMCC_ReadRegDWord(hAMCC,IMB 1_ADDR);

  //读取返回数据

  if(Hamcc) AMCC_Close(Hamcc)

  //关闭设备

  return data;

  else

  {AfxMessageBox(“打开设备失败!”);

  return 0;}

  程序中用到的函数包含在WINDRIVER的API函数库中,在VC++下编译时加上头文件:

  #include "amcclib.h"

  #include "amcclib.c"

  同时在DEF文件中列出DLL的导出函数名,生成的DLL即可为VB即可为VB所调用。读者也可用其它工具编写驱动模块,封装成DLL即可。

  2.2.2 VB调用DLL

  VB调用动态连接库(DLL)时,首先声明DLL,然后即可像调用VB的语句或函数一样使用DLL中的例程。下面介绍VB调用上例生成的DLL(假设文件名为Test.dll)。

  声明

  Public Declare Function GetCardsNum Lib "Test.dll"()

  As Integer

  Public Declare Function SendCommand Lib "Test.dll"

  (ByVal dwCmd as Long) As Long

  在声明时需要注意:DLL的路径;参数传递的方式;参数的类型。

  另外,VB遵从_stdcall的参数传递约定,而VC++默认_cdecl的传递约定,因此在DLL中的导出声明需采用_stdcall的装饰符。

  调用

  一旦声明后,在VB的应用程序中就可调用DLL中的例程。如:

  Private Sub Form_Load()

  Dim CardsNum As Integer

  CardsNum = GetCardsNum()

  MsgBox“系统中有”+ Str(CardsNum)+“块S5933插卡!”

  End Sub

  WINDRIVER包括了诸如AMCC、Altera、PLX、Galileo、V3、PLDA等公司PCI芯片的专用C/C++的 API函数库,其中包含了I/O读写,内存映射,中断处理以及DMA等底层驱动的函数,可以非常方便地用VC++,BC++以及C++Builder等工具编译成DLLs供VB调用。

    在Windows环境下是以事件驱动方式运行每个对象的都能响应多个区别事件,每个事件都能驱动段代码事件过程,该代码决定了对象功能。通常称这种机制为事件驱动的编程机制。可由用户操作触发也可以由系统或应用触发例如单击个命令按钮就触发了按钮Click(单击)事件该事件中代码就会被执行,若用户未进行任何操作(未触发事件)则就处于等待状态整个应用就是由彼此独立事件过程构成。

    功能再强大,Visual Basic 仍然有一个缺点:在没有MSVBVM**.DLL的计算机上必须安装所需的DLL文件(同样Visual C++在没有MSVCRT*.DLL情况下亦无法运行)。不过在Windows 2000之后,这个DLL就被预置到操作系统中了,一般来说,程序如果只使用了VB内部的控件和对象,基本上不会有太大的问题。

  本文提供了两种在VB的开发环境下访问PCI设备的方法。种方法需要有WINDRIVER的VB运用库支持,可以在VB环境下直接编写所需的接口函数,但对WINDRAR。CLS类模翰中定义的内核数据结构要有较深的了解;第二种方法具有一定的灵活性、普遍性,编写的DLL的工具较多,DLL除了可用于VB外,还可用于其他的WIN32开发工具,有较强的适应性。

  以上方法在北京航空航天大学测控技术研究所研制的PHD2000高速并行数据采集系统中得到实际应用,取得了良好的效果。


  
上一篇:了解PBO技术及实时嵌入式软件开发
下一篇:PCI总线设备配置空间用I/O命令访问方法

免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

相关技术资料