随着我国机车控制水平的不断提高,微机控制已经成为我国机车控制的重要控制方式,而逻辑控制单元LCU(Logic Control Unit)作为主要完成机车运行逻辑控制、电路故障记录控制等功能的控制模块,是关系机车安全运行的重要部件。与传统触点控制电路相比,LCU具有可靠性高、体积小、通用性好、维护简单方便等优点。
传统的LCU检测包括人力检测、单片机检测和PC机通过串口控制开发板检测[1]。但是随着LCU生产制造水平的提高,传统的检测方法已经不能很好地完成测试任务。
ARM具有设计开发周期短、功耗低、性能优越等特点,能够满足LCU检测的要求。基于Linux操作系统开发的通用LCU检测软件,可以提供多层次、多场合的复用,可节省设备成本,具有巨大的市场前景和经济效益。
1 系统设计原理
LCU检测就是对LCU内部运行逻辑做出测试并进行故障诊断。测试系统通过向被测试的LCU发出初始命令,并查看其输出结果是否与参考输出相同,从而判断被测试的LCU逻辑是否正确。
本文沿用传统继电器控制中的部分概念进行软件分析。传统硬件继电器触点分为“常开触点”和“常闭触点”两类。一条线路的运行结果会反映到另外一个线圈上,因此,继电器控制的输出可以用线圈表示。本文沿用硬件继电器中的名称进行软件逻辑分析。在程序中以BOOL变量TRUE表示触点开状态,FALSE表示触点闭状态。
线路上的信号都可以采用二进制表达,所以测试的范围会根据线路上触点个数呈现指数增长。对于要求不太严格的控制信号,只要求在适当的条件下,得到开通或闭合的结果,其动作的条件为充分不必要条件。对于要求严格的控制信号,不仅要求在确定的条件下得到应有的结果,而且要保证这个条件是充分必要条件。因此,很大程度上测试可能会遍历触点的所有组合,测试的总体消耗时间就会扩充到很大的时间范围。假设一条线路的信号流动经过时间值为ns, 则计算一条包含20个触点的线路的全部测试时间为220ns。
可见,测试情况的选择十分重要。测试复杂性的主要矛盾在于:需要证明充分必要条件的线路的多少以及该线路的触点的多少。工业标准反映在程序中的情况就是保留测试的选择性,便于在后续工作中进行选择和修改。此外,控制程序的算法,进行复合运算,提高测试的效率也是关键所在。本系统的硬件框图如图1所示。
2 嵌入式Linux操作系统移植
交叉编译器的设置是嵌入式系统开发的第一步。所谓交叉编译就是在一种体系结构的机器上编译出能够运行于另一种体系机器上的代码。若要开发在ARM目标板上运行的程序,无论是操作系统还是应用程序,都必须是基于ARM体系指令的二进制代码[2]。但是直接在ARM目标板上开发程序,无论是程序运行速度还是调试手段,都严重制约了开发效率,有些程序的编写更需要在PC机上才能完成。交叉编译的作用,就在于在PC机上开发程序,交叉编译后,再放置到ARM目标板上去执行。编译顺序如图2所示。
搭建ARM交叉编译器的步骤:(1)编译安装binutils;(2)安装linux 的头文件;(3)编译安装gcc的c 编译器;(4)编译安装 glibc;(5)编译安装gcc的c、c++编译器。
Boot-loader是RAM芯片的引导程序,它的作用是加载操作系统。两个著名的开源Boot-loader,其中一个是U-boot,它的形式就是一个代码包,包中按照Linux一般的编程习惯和不同的目标板,将相应的汇编和C语言代码罗列在对应的文件夹下,让开发人员自行配置。
修改U-boot的过程:(1)基本的硬件初始化;(2)跳转到 Stage2 的 C 入口点;(3)定义函数Nand_init;(4)跳转到Kernel。
Linux内核就是一个可执行的Linux操作系统的套件集合,简称内核[3]。套件的选择,就是内核的配置。内核的配置表明了内核的可调整性。根据Linux软件的开发规则,可到Linux内核源码包的路径下,在终端使用配置命令进行内核配置。在配置内核的时候,需要定义内核启动的命令行。
3 LCU检测系统应用程序
3.1 软件设计及其要素
软件的应用设计,就是确定软件的各个部分及各部分之间的相互关系,信息在其中发送、传递和接收,以及部分运动发展的走向和趋势,最终得到可以预见的结果[4]。它应该遵循下面四个基本要素:名称、问题、解决方案效果。
面向对象的软件开发,从不同的对待问题的层次出发能得到不同问题的解决方案。其基本工作流程如图3所示。
3.2 定义基本的类和对象及人机界面的设计
在需求分析的说明中,最常出现的三个名词分别是触点、测试和线圈。每个测试包含了不同的线圈,每个线圈又分别由触点组成。所以,触点、线圈和测试能够分别抽象出来做成单独的类。触点必须有ID、常开/常闭的类型和运行中处于的值都应该在属性中出现。测试的ID应该能够区分是哪一次测试,并且记录下所包含触点的初始值以及标准情况下结果的预计值。线圈则以触点和测试作为成员变量。
首先设计用二维数组来模拟测试与触点的关系。假设数组的横向和纵向表示分别为测试和触点,则某个线圈的设计如图4所示。
对照实际情况,可以使用二维数组进行描述。触点的加入和删除就是列的插入和删除运算,测试的加入和删除就是行的插入和删除运算。对某线圈的实验就是把每行遍历一遍,结果对照预计值进行逻辑判断。但这仅仅是表达了单个线圈的情况,多个线圈则呈现如图5所示的书页式的结构。
直观上看,重叠配置的线圈就像是检查手册的一封封页面,测试整个系统的方法就是翻弄这本“书”,从头至尾把所有的线圈遍历一遍。
3.3 序列化存储类serial和链表类
如果延续这样的思路,则关于数组的操作会产生新的问题:C语言中定义数组必须先确定数组的大小才能分配合适的内存。例如,要得到一个m×n二维数组,则必须在编译时确定m和n的大小,但实际上要求m和n是可变的。
由于文件是测试系统内部调用的接口,应该先沿着采用文件来登记测试项目的结论进行分析。采用序列化技术能够简单而方便地实现文件存储。序列化是指将对象实例的状态存储到可持久保持信息的物理设备的过程,其特点就是线性存储。序列化技术实现了文件的存储,同时它也给出了一种思路:把所有一切都统一对待,变成一根主线。应用到“二维结构”上就是将二维表变成一维的大表,同时用指针来保持逻辑的连接。如图6所示。
但序列化只提供链型的存储形式,只能用一维的链表模拟二维数组,因为不知道二维链表的指针应该如何安排。
决定采用链式联系的方法:用指针来实现连接。但是指针的传递跨越了类,因此有必要把链式结构直接抽象成类,产生定义触点链表类、测试链表类和线圈链表类,并将它们抽象出一个基本的链表类,实现链表基本的操作,再从它派生出上述三个链表类。
3.4 计算预计值
测试随意地更改其所包含的触点,随之变动的是测试的预计值。这可以采取人工输入来实现,但是如果用表达式字符串来表达测试的逻辑计算,可以通过字符替换的方法让机器来实现。
表达式字符串中间必须有触点的ID名字,常闭触点的名字前加“~”,测试线路的“与”逻辑用字符“*”来表示,或逻辑用字符“|”来表示(也可以选择其他的计算字符),用括号表示优先的线路逻辑,空格是人性化的适应性定义。这样即可以表达出线路的逻辑。
计算预计值的算法如下:
(1) 输入目标字符串。
(2) 首先去空格。
(3) 用触点的状态值替换触点ID,如果是FALSE,替换成F,如果是TRUE,替换成T。
(4) 开始循环: F*F 替换成F
F*T 替换成F
T*F 替换成F
T*T 替换成T
F|F 替换成F
F|T 替换成T
T|F 替换成T
T|T 替换成T
(T) 替换成T
(F) 替换成F
测量字符串长度,如果和上次相比变小,则重新开始循环;如果没有变化,则跳出循环进行下一步。
(5) 判断:如果字符串==T,则说明预计值是TRUE;如果字符串==F,则说明预计值是FALSE;如果字符串是其他值,说明输入的表达式有误,提示重新确定表达式。