1 数控系统软件芯片的划分
合理的芯片划分,是开发软件芯片的首要步骤。数控系统软件芯片库中的各芯片以界面的方式开放,通过接口参数和界面信息的提示,用户可掌握芯片的启动、结束和运作。不同芯片的内部为黑箱封装,外部接口开放,并在此基础上实现新系统的构建。因此,如何定义出合理的数控系统软件芯片,使芯片的外部接口易于标准化、规范化,内部易于进行黑箱封装,是我们开发数控系统软件芯片库的关键步骤。
目前,尽管数控系统从系统的设计方法到系统的实现方式千差万别,但是其基本原理和软件的组成都是类似的。在对现有的数控系统[3]和用户需求进行仔细而全面分析的基础上,同时,在总结现有系统控制结构的共有特征,并对其进行适当的归类和抽象的基础上,将数控系统划分为以下几个基本的功能模块。
(1)人机交互界面模块 此模块主要完成在系统运行前和运行中系统参数的修改和设定,如设定系统工作模式(自动、手动、点动等),图形显示模式,系统初始化设定,坐标偏置设定,G代码程序的编辑等。
(2)零件代码解释模块 负责根据用户的系统配置,以及零件程序的语法规则对用户编写的零件程序进行语法检查,并进行解释译码,将源代码指令中给出的各种信息进行分离提取,变成各种状态和数据,为预处理芯片提供语法上正确的零件程序的中间代码。
(3)刀补预处理模块 负责对解释后的数据进行预处理及插补前的准备工作。
(4)轨迹插补模块 负责加减速的控制、插补、终点判别等工作,向位置控制器输出通过轨迹运算后的进给量。
(5)轴伺服控制模块 在从I/O及插补运算得到的信息的帮助下,通过精插补控制机床执行机构按NC指令指定的路径和速度运动。
(6)I/O模块 负责控制器的输入和输出(包括机床检测信号及位置和相关反馈信息的输入、控制指令的输出等)。
以上这几个模块间具有互操作性、可移植性和可扩展性,因而可作为数控软件芯片库的基本芯片的划分。
2 数控系统软件芯片的构建及工作原理
软件芯片概念的提出是软件重用发展过程中的里程碑。开发软件芯片就是采用面向对象技术把特定类中的一些通用模块做成独立的可重用的对象类。由于面向对象具有封装、分类、消息响应和继承等很有价值的特点,使得软件芯片和系统其它部分的耦合度得到尽可能的降低,这为软件芯片的开发和使用提供了可*保证。同时,由于芯片都是对较成熟的技术进行封装而实现的,在实践上是经过了验证的,也就是说一个成熟的芯片已经将错误率降到了最低点,所以可以利用数控软件芯片来构造新的数控系统能最大程度地保证系统的可*性。 软件芯片的构建就是将功能模块的本体部分进行黑箱封装,使之输入接口和输出接口尽量简单、规范。由于C++语言的面向对象特性和封装性较好[4],所以在本系统中将VC作为编程环境来进行芯片本体的构建。整个芯片是基于静态库创建的,最后生成一个Lib库文件。所有功能的实现都封装在Lib库文件中。用户使用时,不需要知道芯片内部的功能(如初始化、错误信息处理、数据分离)是怎样实现的,只需将对应的.Lib文件和.H文件加入到自己的系统中,然后依照芯片说明提出的接口要求,通过接口参数调用相应的方法即可。接口参数和方法在Lib文件中定义为,用户可以在外界通过它们和芯片进行交互。就如同用户通过硬件IC的引脚来使用芯片内部的功能一样。
下面就以零件程序解释芯片为例,简述数控系统软件芯片的构建过程。
首先,对芯片的本体功能进行分析,定义出合适的接口。一般来说,一个完整的零件数控加工程序,由若干程序段组成,一个程序段又由若干个代码字组成,最后以“;”结束。每个代码字由文字符和数字符组成,代码字之间用空格符隔开。
根据自上向下的原则,该部分又可划分为以下几个部分:
(1)词法检查 对源程序的数据进行拼写及位数检查;
(2)语法检查 对程序段中的G代码和其它功能码的格式进行检查,如G代码的相容性检查等;
(3)语义检查 对上下文相关的错误进行检查,如I、J、K和R不能出现在同一行代码中等;
(4)译码 将程序段的信息进行提取,变成相应的状态量和数据量,存储在输出缓冲区中。
在综合考虑数控系统解释器的内部逻辑关系和数控系统的运动控制的基础上,将解释芯片的输入口数据定义为以字符串形式输入的一行数控代码段(char* LineStr);输出口数据定义为一个包含各种信息量的结构。
输出数据结构:
typedef struct{
int Gp01; ∥1组G代码
……
int Gp15; ∥15组G代码
int N, ∥程序段号
G, ∥准备功能
M, ∥辅助功能
P,Q,L,D,H; ∥其他参数字符
long T; ∥刀具选择
double F; ∥进给速度
double S; ∥主轴速度
double D; ∥刀具半径
double X,Y,Z,A,B,C,I,J,K,R,U,V,W; ∥尺寸字
……
BOOL bLastCmnd; 最后一行指示标志
}NCcode
如要对一行代码段进行解释译码,则先声明一个实例:
CCode code; //CCode为Lib文件所生成的类
然后调用其成员函数,
code.InterCode(char* LineStr)
完成对LineStr中代码段的词法、语法、语义检查和数据分离。在进行解释过程中,如果发现代码段中有语法或语义错误,InterCode 会给出相应的提示,并返回到编辑状态重新编辑。
类似的,如要对结构中的标志位进行初始化,则可通过调用code.FlagINI()实现。同时,还可以通过在外部对code.ENDED变量进行赋值来结束整个芯片的运行。
解释完成后的各种状态和数据信息存放在前面定义的数据结构中。用户只需按照一般结构的读法去取相应的数据,如NCcode.G; NCcode.M ,也可以将整个数据结构作为下一个芯片的入口,实现芯片与芯片之间的数据传递。
为了防止在芯片进行译码时,外界对数据结构进行操作而产生错误。本芯片使用了临界区的方法,即在方法InterCode()被调用时,就用CcriticalSection的成员函数Lock进行加锁处理,拒绝外界访问正在更新的数据,以免出现新老数据同时被读入的错误。解释完成后,用Unlock进行解锁处理,使用户能访问更新以后的数据。
3 芯片间的同步和协调
当若干个芯片组成一个实用系统后,芯片之间的同步问题就变得格外重要,尤其是像数控系统这样对实时性要求比较高的系统。在使用软件芯片构建数控系统时,每一个有具体功能的芯片,如译码、插补等,都是一个单独的线程。线程与线程之间的通信,是利用事件对象的方式来实现的。本系统由于是在VC环境下开发的,所以可以利用MFC库中的CEvent类及其成员函数来完成。每一个事件对象可以有两种状态:信号态和非信号态。事件可以监控线程是否被置于信号态,并由此决定在适当的时候运行相应的线程。使用事件对象进行线程通信的另一个原因是事件对象的声明十分容易,就像声明一个全局变量一样简单。如CEvent InterCodedStart;事件对象创建以后,是处于非信号态的,要是事件对象处于信号态,只需调用事件对象的成员函数 SetEvent(),即InterCodeStart.SetEvent();
在执行了上面的语句后,事件对象InterCodeStart便处于信号态。线程监视事件是否处于信号态可利用下面的Windows API 函数实现。
::WaitForSingleObject(InterCodeStart.mhObject, 0);在此情况下,如果函数返回的是值为WAITOBJECT0,则事件已被置于信号态,否则,事件仍处于非信号态。通过这些消息和方法,我们就可以实现系统各线程之间的通信,也可以通过这些事件对象间的通信控制将若干芯片“粘连”成一个有机的实用系统。
4 结束语
本系统全部是在 Windows NT下的VC环境中开发的,所以利用了很多MFC中的基本类,这些都大大提高了整个系统的灵活性,增强了整个系统的功能。如,数控代码的编辑器是基于CEditView类创建的,因此在使用时,它可以利用CEditView类本身所带的一些编辑功能,如New、Open、Save、Copy、Cut、Paste等,这使数控程序的编辑及管理变得和Windows下的文件管理一样方便容易。同时,VC中各种控件的使用,不但增加了系统控制的易操作性,还使控制界面变得美观、友好。这些都是值得进一步深入探讨的地方。利用可重用构件技术来开发软件芯片,并利用其构建新的数控系统的思想已用于国家自然科学基金项目“高可*性数控系统软件芯片库及其运行环境”的研究,取得了良好的效果,不但很好地实现了数控系统的开放性设计和资源重用,而且由于是基于Windows NT和IPC等通用环境下开发的,对数控系统的升级换代和对市场的及时响应,都具有良好的开发前景。