刘士勋,刘满国,唐同斌,喻 戈,岳 超
(西安现代控制技术研究所, 西安 710065)
RTX系统凭借其兼容了Windows良好的人机交互特性的原因,逐渐走入各大高校、研究所的实验室内。然而,板卡出厂时大多不提供RTX驱动,即使有些板卡提供RTX驱动,这些驱动往往没有源代码,同时使用起来也未必方便,出现问题也不好定位[3]。对于大量使用RTX系统的研究生产单位而言,了解、掌握和开发板卡RTX驱动程序是相当必要的。当前对RTX驱动的研究工作还不是很多,已有的成果大多也是基于Rfm2g反射内存卡的。因此,文中以Moxa CP-118U板卡为例,对RTX驱动程序开发问题进行讨论,以求该技术能被更多探索。
Windows操作系统的架构相对复杂,用户使用板卡API访问底层硬件,需从顶层开始,层层穿越,跨越用户模式和内核模式,才能访问硬件,驱动程序可以理解为Windows内核模式下的一块“补丁”,所有驱动程序都需要按照该模式开发。
相比Windows驱动程序模型,RTX程序就相对灵活,RTX应用程序可绕过Windows内核直接通过RTX内核访问RTX硬件扩展层,直接通往硬件进行访问[5,8]。RTX系统构架示意如图1所示。
图1 RTX框架简图
当板卡安装好Windows驱动后,在RTX管理器对该板卡增加RTX支持,然后在设备管理器转换驱动为RTX下的驱动后,即可进行RTX驱动程序的开发工作。
PCI板卡驱动的开发实质是操纵板卡上芯片的寄存器。每个寄存器有其特定的作用,只要了解每个寄存器的作用及其地址,通过寄存器地址对板卡上的寄存器置数取数,即可完成数据交互等功能。
PCI板卡的各类寄存器的访问地址均存放于一个称作“PCI配置空间”的256 Byte的内存中[10]。这个空间的前64 Byte是PCI协议标准预定义的,所有PCI板卡都会使用该标准。查询板卡硬件手册,即可得到板卡寄存器地址。有些板卡如果使用了其他芯片,其硬件手册会告诉用户查找对应芯片的手册以寻找寄存器的作用及地址。
板卡驱动程序的第一步便是验证该板卡是否存在于当前计算机系统中。
为了获得指定板卡的PCI配置空间内存,RTX系统为编程人员提供了RtGetBusDataByOffset函数。该函数的参数有总线类型、PCI总线编号、插槽编号,传入正确参数即可获得PCI配置空间的参数,比较DeviceID和VendorID即可判断该板卡是否为指定板卡。如果遍历所有总线以及所有总线插槽仍无结果,则搜寻该板卡失败,程序退出或返回失败。
Moxa CP-118U板卡给用户提供I/O映射的方式来访问寄存器。寄存器的基地址存储在PCI配置空间的基址寄存器2中,保存该值方便后续对各个串口通道的相关寄存器进行访问。
操作板卡的核心便是读写板卡上的各个寄存器。深刻理解寄存器的意义和用法对操纵板卡有相当重要的帮助。
Moxa CP-118U板卡的配置包括各个串口通道的波特率的设置、数据位长度、奇偶校验位的设置等。
板卡一般都提供了中断标识寄存器。中断服务函数中就是对中断标识寄存器中的各个中断二进制位进行判断,如果某个二进制位为1则对该中断进行响应。
Moxa CP-118U板卡有8路RS 422/485串口,每路串口都配备了一块TL16C550C异步串并行收发器转换芯片,通过操作对应串口的芯片,可以顺利的完成数据的传输。其中,每路串口的寄存器共占有8 Byte,8路串口总共占用64 Byte,如图2所示。
图2 板卡寄存器组分配示意
对于每路串口,其8 Byte寄存器分配如图3所示。
图3 每路串口寄存器分配示意
欲操作一路串口,需对该路串口对应芯片的寄存器有如下了解:
1)配置链路控制寄存器LCR:完成数据位长度设置(5~8 bit),停止位长度设置(1,1/2,2),奇偶校验位设置;
2)中断使能寄存器IER:完成各类中断的使能与关闭;
3)中断标识寄存器IIR:对当前是否发生中断与当前中断类型进行标识;
4)发送保持寄存器THR与接收缓冲区寄存器RBR:串口发数和接数会分别频繁使用这两个寄存器;
5)波特率分辨率寄存器DLL、DLM:配置这两位寄存器完成串口波特率设置。
有了以上了解,即可开始驱动程序的开发。
在打开板卡函数的实现中,主要操作为根据DeviceID和VendorID查找板卡,如果搜寻到板卡,保存板卡的I/O映射地址,方便后续操作寄存器使用。
示例代码如下:
for ( bus=0; bFlag; bus++ )
for(deviceNumber=0;deviceNumber for(functionNumber=0;functionNumber { bytesWritten = RtGetBusDataByOffset(…) if((PciData->VendorID==vendorID) && (PciData->DeviceID==deviceID)) …;//找到板卡 } 其中,PciData是通过RTX系统提供的RtGetBusDataByOffset接口所获得的PCI配置空间的内存指针,将该内存中的DeviceID和VendorID成员与118U板卡的进行对比,即可验证当前所遍历板卡是否为118U板卡。对于Moxa CP-118U而言,DeviceID为0x1180,VendorID为0x1393。 在打开串口函数中,主要操作就是对欲使用的串口进行参数设置,其核心就是操纵对应串口的LCR寄存器。 1)波特率设置 Moxa CP-118U板卡上有参考输入脉冲,其频率为XIN=14.745 6 MHz,对每路串口的波特率设置就是对该脉冲进行分频,分频因子计算方法如公式(1)所示: (1) 例如,欲设置波特率为115 200 bit/s,则分频因子divisor=14.745 6×106/115 200/16=8。 下面的工作就是将分频因子写入到分频寄存器DLL和DLM中即可,需要注意的是,读写DLL和DLM,需先置LCR的BIT7为1。示例代码如下: RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+LCR, (UCHAR)(c|0x80)); RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+DLL,(UCHAR)(divisor&0x00FF)); RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+DLM,(divisor>>8)&0x00FF); 2)数据位设置 配置LCR的BIT0和BIT1可以设置数据位长度,示例代码如下: setting=WordLength-5; 3)停止位 配置LCR的BIT2可以设置停止位长度,示例代码如下: setting |= 0x00;//1位停止位 4)校验位 配置LCR的BIT3-BIT5可以设置校验位使能及校验位类型。 if(parity ==PARITY_DISABLE_118){setting |=0x00;} else if(parity == PARITY_ODD_118){setting |=0x08;} else if (parity == PARITY_EVEN_118){setting |=0x18;} RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+LCR,(UCHAR)setting); 使用RtAttachInterruptVector函数挂接中断服务函数。 使用中断模式向外发数,具体实现为向发数寄存器写数,当数据发送成功后,会进入2号中断(“发送保持寄存器空”中断),在该中断发生时继续给发数寄存器写入待发送的数据。循环这个过程直至欲发送的数据全部发送结束。 a)WriteMoxaPort的核心代码示例 WriteCounter[port-1] = 0;//清理已发送数据计数器 WriteDataLength[port-1] = iLength;//记录发送数据长度 RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+THR,WriteBuffer[port-1][WriteCounter[port-1]++]);//向发送寄存器写数,当前已发送计数器+1 b)中断服务函数相关实现示例 if(WriteCounter[port-1] { RtWritePortUchar(moxa118uBaseAdd+(port-1)*8+THR,WriteBuffer[port-1][WriteCounter[port-1]++]); //继续向缓冲区写数当前已发送计数器+1 } else { WriteDataLength[port-1]=0; //结束本次发送,清空标志位 WriteCounter[port-1] =0; } 收数是一个被动的过程,当串口总线有数据到来并且超过中断触发深度时会自动触发收数中断,触发之前,数据会存储在板卡的硬件缓冲区内。由于板卡的硬件缓存只有128 Byte,因此需要在驱动程序层增加一层缓冲区。文中设计的ReadMoxaPort函数的参数列表中有一项为用户提供欲接收数据的长度,当数据接收量大于该值时返回,具体流程如下: 1)当数据来临时存储在驱动层缓冲区内; 2)定时器检测数据长度到达用户欲得到的数据长度; 3)当数据长度满足要求后,将数据填入用户传进的缓冲区内存,返回结束。 具体实现如下: a)中断服务函数的核心实现 ReadBuffer[port-1][ReadCounter[port-1]++]=RtReadPortUchar(moxa118uBaseAdd+RBR); b)定时器函数的核心实现 if(ReadCounter[port-1]-ReadPostion[port-1]>=iDrvTriggerLevel[port-1]) RtSetEvent(hRecWaitEvent[port-1]); c)ReadMoxaPort的核心实现 if(RtWaitForSingleObject(hRecWaitEvent[port-1],INFINITE)==WAIT_FAILED) return-3; for(counter=0;counter Buffer[counter]=ReadBuffer[port-1][ReadPostion[port-1]++]; 使用RTX Application Wizard创建RTX应用程序,加载使用上述方法创建的驱动程序静态库对驱动程序性能进行测试,测试结果如表1所示。 表1 驱动性能测试结果 由于高波特率条件下中断频繁触发,过于占用计算机资源,同时也受计算机CPU性能等因素影响,921 600 bit/s波特率的条件下最多容许4路串口稳定工作,超过4个串口会丢失数据;230 400 bit/s及以下波特率条件下8路串口可以稳定工作。该性能满足了绝大多数工作的条件,可以用于日常仿真研究工作。 文中以Moxa CP-118U板卡为例,介绍了在RTX实时系统下PCI板卡的驱动编写方法,出色的实现了板卡提供的所有功能,驱动性能可以满足绝大多数工业、生产、仿真的要求。同时对于Moxa公司其他类型的多串口卡,亦可借鉴文中列举的方法和框架进行开发驱动。3.2 打开串口——OpenMoxa118UPort
3.3 打开中断模式——OpenInterruptMode
3.4 中断发数——WriteMoxaPort
3.5 中断收数——ReadMoxaPort
4 RTX驱动程序测试
5 结束语