固件的开发是移植与开发相结合。本设计参考了Philips公司提供的D12固件程序范例,对于USB协议操作的相关代码可以直接移植使用,而数据采集、传输、存储等部分则是全新的开发工作。固件程序结构如图3所示。硬件抽象层对D12的数据读、写以及各种指令的写入进行函数封装;D12命令接口层对D12的所有控制指令的函数进行封装;USB向量请求模块完成USB上电配置、向量请求等各类事件的响应处理;USB协议层包括对USB协议操作的封装以及对USB标准请求的响应;中断服务进程包括USB中断、ADC中断以及定时器0中断(记录测量时间)等。主程序及ADC中断服务程序流程图如图4所示。主程序首先完成各种初始化,然后进入主循环,等待中断的发生,并根据标志变量执行相应的函数。当打开控制电路时,脉冲峰值别电路自动启动A/D转换,转换结束信号会触发微控制器外部中断1,进入ADC中断服务程序,读取A/D转换结果并存入缓存中,然后中断返回。当D12有事件需要处理时,将触发微控制器外部中断0,微控制器读取D12的中断状态寄存器,判断中断的来源并作出相应的处理。若由数据端点触发,则相应地读取或写入数据;若由控制端点0触发,则判断请求的类型。标准请求由USB协议处理模块处理,用户自定义向量请求由USB向量请求模块处理。
2.2 USB设备驱动程序的设计在Windows环境下,USB设备驱动程序遵循WDM(Win32 Driver Mode)方式。为了简化设计,并兼顾驱动程序的运行效率,笔者选用了DriverStudio2.7工具软件中的DriverWorks组件进行USB设备驱动程序的开发。DriverWorks为WDM设备驱动程序的开发提供了完善的支持。其中包含一个非常完善的源代码生成工具DriverWizard以及相应的类库和驱动程序范例,它还支持在C 下进行设备驱动程序的开发。通过DriverWizard生成的代码只需要进行少量的修改可以使用,这使得驱动程序开发者可以将精力集中在驱动功能的实现上,而不必理会太多的WDM开发细节。本设计在DriverWizard的最后自定义了三个IOCTL接口对USB设备进行控制,如表2所示。然后在自动生成的驱动程序代码中向相应的IOCTL函数添加代码,用函数BuildVerdorRequest构建USB协议的自定义向量请求(Vendor Request)。由编译修改后的源代码即可得到驱动程序文件McaD12.SYS。
表2 自定义IOCTL接口自定义IOCTL接口功能说明Mca_IOCTL_START启动多道采集数据Mca_IOCTL_READ开始读取数据Mca_IOCTL_START停止多道数据2.3 USB应用程序的设计应用程序的设计在Visual C 6.0开发环境下进行。根据实际要求,本设计需要在软件中对采集的数据进行整理、分析并显示。其功能模块主要有数据采集、谱数据显示、ROI操作、系统刻度、谱分析等,其结构框图如图5所示。在Win32系统中,USB设备被抽象为一个文件,应用程序只需要通过几个API函数就可以实现与驱动程序中USB设备的通信。API函数如表3所示。
表3 设备文件操作API函数API函数功能说明CreateFile打开设备ReadFile从设读取数据WriteFile向设备发送数据CloseHandle关闭设备DeviceIoControlI/O控制操作本程序设计使用MFC多线程技术。单击开始按钮,程序就创建一个用户接口线程,并且通过IOCTL启动USB设备,然后在此线程每隔一定时间(10~20ms)从USB总线上读取一次数据;而程序自身的主线程则不断地依据读取的数据刷新屏幕,显示多道能谱。当单击停止按钮或是设定采集时间到时,程序则通过IOCTL停止USB设备的数据采集,终止用户接口线程,并且停止屏幕谱线的更新。当创建用户接口线程时,首先从CwinThread类派生一个CioThread类,然后调用AfxBeginThread()函数创建CioThread类的对象进行初始化,启动线程运行。根据需要可将初始化和结束代码分别放在类的InitInstance()和ExitInstance()函数中。其中,InitInstance()函数是从USB采集数据的线程的主要函数。从中实现对IOCTL的调用、对USB设备数据的读取等功能。其流程如图6所示。
