朱 旭
(中国航发西安动力控制科技有限公司,陕西 西安 710077)
CAN(Controller Area Network,控制器局域网络)总线是20 世纪80 年代提出的一种串行数据通讯总线,已在包括航空工业在内的众多领域中得到广泛应用[1]。在航空发动机测试、使用过程中,通常需要针对CAN 设备进行二次开发,用于与各设备间的数据传输。二次开发的应用软件将人机交互程序分为应用层与驱动层,见图1。驱动程序负责操控设备的相关信息,应用程序负责功能实现。
目前在市场上存在多品牌、多种类的CAN 通讯设备,尽管不同品牌的CAN 通讯设备功能一样,但每个设备的原理、使用方法却不尽完全相同,导致使用者(二次开发者)在使用过程中,针对各类设备的二次开发,需配合不同应用程序,这样会影响生产效率,甚至容易导致出错。本文提出一种利用C#工具开发驱动程序方法,在应用程序中添加后,只提供同一接口给应用层调用。因此当使用不同设备后,只需在驱动层中添加或更改相应模块,不会影响应用层功能,在降低程序开发复杂度的同时提高了二次开发程序的通用性。
本文在VS2010 工具下完成C#程序开发工作[2]。采用面向对象技术,将所有CAN 通讯设备归结为一个类,作为基类。每个CAN 设备看成一个基类的派生类,这样,派生类拥有基类的特性。再将特定的方法和属性组合,封装为一个特定功能集合(称为接口),每个实例的具体内容在其接口中进行定义实现[3-4]。
基类完成共同功能的设计,将基类设定为抽象类,每个设备作为一实例,派生于基类。这样每个实例就拥有了基类的属性和方法,各实例的不同功能在实例的接口中分别具体实现,针对不同情况,做出适应性更改。这样既满足了相同功能的一致性,又达到不同设备灵活使用的现实需求。
上位机程序设计分为驱动层和应用层两个部分。驱动层主要用于配置设备参数,使其能够正常通讯。针对使用设备进行类的实例化,继承基类的共有属性和方法,完成共同状态的配置。对于具体功能方法,需要引用不同接口,在接口中进行定制化和个性化的详细功能实现。在驱动层通过基类和接口完成不同设备的驱动,同时为应用层提供了相同的入口参数。应用层接受驱动层传递的数据,进过分析、识别判断,完成最终程序的功能,如数据物理量的转换、显示。在此只考虑与驱动接口的数据类型和格式是否一致,不需关心驱动层的设备类型。
按照上述设计思路,UML表示法如下页图2 所示。
图2 软件架构UML 图
本文通过CAN 通讯卡实现上位机和嵌入式控制器之间的数据交互,从而对控制器中相应内容进行管理、更改、维护。如下页图3 所示,上位机的人机界面发送指令到控制器,控制器接受到指令,先判断格式是否正确,然后按照通讯协议,分析出指令的具体内容,执行相应动作,完成后反馈信号或发送数据到上位机,以通知其执行结果。上位机接收到数据,同样要进行数据的甄别、判断,若正确则将结果显示于界面,提示用户执行结果。
图3 系统框图
假设整个系统中使用了2 种不同品牌的CAN 通讯卡,对于用户而言,接入任意设备,其使用方法应完全一致。
在进入程序时,如图4 所示,首先在人机界面选择设备类型。后续进入主程序时根据之前的选择自动完成设备初始化工作,并在主界面提示设备状态。初始化完成后根据指令,进行数据传输和发送等功能,并将数据解析的结果显示在程序主界面。如图5 所示。
图4 初始化界面
图5 程序主界面
设备1、2 分别提供不同的动态链接文件,完成各自的驱动和功能实现。
每种设备的提供的动态链接文件格式和使用方法都不一样,因此,在驱动设计时,将2 个设备分别定义为DevCan1、DevCan2 类,根据使用情况进行选择,然后实例化对象,在实例化时将抽象类的虚方法进行重写。
首先,定义一个抽象基类DevClass。类定义包含ID、波特率、数据格式等字段,用以配置使用环境。基类方法包括InitialCan()和CloseCan()方法,这两个方法用于设备初始化和关闭。在函数内分别各自调用原厂家提供的动态链接库实现。再次,定义2 个接口,分别为发送数据ISendMessage、接收数据IRec-Data 函数。每个接口实现函数的返回值一致(例如,0表示失败,1 表示成功)。因此,同样结果对应用层处理逻辑就是一致的。
若使用设备1,则实例化对象设备1,并继承于基类,支持发送、接收2 个接口。在发送和接收过程中,将待发送和接收的数据存放于数组中,供应用层调配。
同理,若使用设备2,则实例化设备2,并完成相应配置。
下面以初始化为例,具体实现代码如下:
应用层与驱动层的数据交互采用共享数组的方式。驱动层完成设备驱动后,将事件和所需数据整理后,传递到应用层。应用层负责接收、响应事件,并解析、处理数据。在主程序中定义委托,将方法封装在委托对象内,实现传递回调函数的作用,多个委托可以完成多种事件的订阅,事件订阅对于使用的设备可不必关心。程序中将接收和发送数据事件分别定义,具体代码(以设备1 为例)如下:
在应用层建立2 个线程,分别用于接收数据、发送指令,各线程独立工作,互不影响,从而提高程序的执行效率。线程中处理委托中的订阅事件,执行相应操作,实现数据交换和通讯功能。程序架构如图6所示。
图6 程序架构
本文设计了针对不同CAN 通信设备的通用上位机应用程序,在同一应用程序中,利用接口和类完成不同设备的驱动,为应用层提供数据和事件。应用层只负责事件和数据处理,与驱动的设备无关,应用层针对不同设备的事件处理逻辑一致。在实际使用中,新增设备只需进行驱动部分的设计,完成对应接口的功能,就能直接使用,这样能够快速开发完成程序,提高通用性,缩短了开发周期。经过试验验证,此方法能够有效、正确实现多设备同时驱动的目标。