深入 Mach3 内核(中文)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Mach3Internals
A Plug-In Wr iter s Bible.
by
Art Fener ty
January28th,2008
译者说明
MACH3是一款运行于Windows平台的优秀机器控制软件,它的授权价格十分低兼,使用者可以用它来组建自己的低成本数控机床,使得CNC机床不再是可望而不可得的高贵设备。

对数控机床爱好者来说是一个很好的选择。

MACH3还具有很强大的可扩展性能,使得MACH3几乎可控制任何形式的机器,而插件是多种可扩展特性中最强大的一种。

本文就是阐述如何编写MACH3的插件。

本文的英文原版是软件作者公开发布的免费文档,可在MACH3官方支持网站http://www.machsuppor /获得本文的英文版本。

为了在中文群体中能更好地进行MACH3技术交流,本人谨对英文版本进行翻译,版权仍归属原文作者。

由于时间仓促,经多次补充后仍有部分没有翻译,但主要的内容基本上都已完成。

另外基于本人的水平有限,其中不可避免的存在翻译上的错误,不足之处万望谅解。

译者:Picasot
在本文中我们将Mach3叫作程序(Application),将脉冲驱动程序(Dr iver)叫作引擎或驱动,这样可以使概念更清晰。

序言
关于我
有些人认为我非常了解CNC,是CNC软件领域的先驱。

让我切实地告诉你,事实上并没到这个程度。

我只是阅读了一份叫“十年成为项目大师”的学术论文,里面说到任何人只要花上十年的时间在一个项目上,努力学习和用心钻研,他必定能成为这类项目的大师。

很可能文中所指的项目适用于某些领域,但我当时理解为这个理论适用于任何方面,或者说可能适用于CNC领域。

其实它并没有说到有谁花了十年时间成为了CNC方面的专家。

事实上有些人在一个领域花了一年时间,然后他们又重复了十次相同的体验,最后他们并没有成为该领域的专家。

我进入CNC领域是六年前。

直到现在我都未曾告诉过你CNC究竟代表什么意思,是“永远、烦恼、复杂”的意思吗?(Constantly Nagging Complexities)。

自从那时起,我作出了一些努力,也学到了一些知识。

但我的所学只不过是一些理论,和一些人们通过帖子或Email发布的问题报告,这些东西并没有使我成为专家。

人们利用Mach3来控制各种各样的机器运转,反而经常使我对自己所创作的这些代码感到惊讶。

要真正掌握CNC必定要花费相应长的时间,并而要作出坚定不移的努力。

其实我还没有做到这一点,我只是做到了使Mach3能够正确完成它应该做的事情,所以我能完整的了解它,并且我尝试尽量帮助你深入了解它。

这份文档不是一本手册,它没有目录索引和分类主题,最多只是对Mach3如何工作的一些讨论。

插件编程人员会希望参详其中的一些章节。

而使用者可能只是无聊才看上一看,又或者他想找到一些头绪来完成他被迫要做而又不熟悉的事情。

在我的经验来看,要了解一个程序如何工作最好的方法是使用好它。

Mach3不是一个典型的程序,它的大部分部件由事件驱动,而其它的一些部件由定时器驱动。

有人问我为什么不把它移植到Linux,理由很简单,EMC是一个很好的程序,它是基于Linux的,习惯于使用这个操作系统的人已经有了一个解决方案。

我不喜欢在Unix上写程序,驱使我完成Mach3是我更愿意提供一个解决方案,而不是销售软件。

在我停止继续开发Mach3之前,已经有超过12000个用家了。

他们对CNC有不同的认识水平,和对CNC有不同的背景需求。

而我要做的就是说明那些是已经做到的,从而引导出在确定的局限条件内使程序实现更多的工作的结果。

如果你想通过这份文档获得答案,你有必要知道这些局限条件,和为什么会存在这些局限。

如果你是插件编程人员,你需要了解程序的流程,和怎样接入到它内部。

关于这份文档
要完全了解Mach3在特定情形下会怎么做,你需要大概了解一整套系统是怎样最佳地配合在一起的。

最好能懂得CNC系统的所有方面,和它们是怎样最佳地完成工作的。

这份文档意图说明Mach3内的各个子系统是怎样相互作用的,包括Gcode、ModBus、一般输入、运算控制、和连接到软件的一切。

这是我的期望,当读完这份文档后,使用者可以对各种棘手的复杂问题有更深的了解,并且清楚设置一台特别用途的机器时需要考虑些什么。

不要期待不同章节之间的内容能前后对照和交叉参考,我只是假设你我坐在同一房间内随意地交谈有关Mach3的话题。

Mach3的发展
Mach3是一个正在发展中的产品。

最初的设计只是用于我自己使用的台式龙门,它在人们数以千计的要求中进步,几乎涉及了所有人在CNC操作中会遇到的问题。

这个过程就像永远处于完成然后继续处理更多更多的需求,每个月都在问还有什么要添加的。

在Master5改进为CNC的创作期间(Master5是Mach3的早期版本)支出庞大,在我看来成本远远高于需求,的确超出了我愿意付出的数额,而Master5只是为了我的台式龙门能够运行。

事实上,这个程序早期叫做EZCNC,但很快就改成了Master5(继Master1、2、3、4而来)。

Master5在Windows95为改进电机控制而重新编程了主板定时器到8KHz,提供了稳定的步进脉冲流,从而不需太过担心Windows中断脉冲流。

对于Windows用户来说,CNC过于昂贵的原因之一就是Windows根本没能力实时产生平滑脉冲。

Master5重编程了系统定时器,并运行于中断8的Ring0级来获得稳定流。

这允许电机控制能有效达到最高8KHz的速度。

当我们从Win95更新到Windows XP后,就需要一个新一代的定时器。

它花费了几个月的时间来研究理论,怎样强行使Windows来产生可调整的稳定脉冲。

在写这份文章的时候,最高速度已经可以去到100KHz了。

但事实上在通常的操作时,65KHz是大多数CPU 的极限,这也是有原因的。

了解定时器就是了解为什么Mach3所完成的行为与对它的操作可以一致。

所有方法都有内部的局限,而Mach3则有它的几个设计特点。

还记得花了很长时间来考虑这些情况:什么是Mach3不可能实现的、为了完成工作而依赖的一些不正当技巧、和Mach与操作系统共存的复杂策略,从而使我认为需要建立一些限制。

有些人想知道为什么使用外部设备(如Galil,G100等)这些限制仍然存在呢,这样做表面上看来是无意义的,但当你考虑到Mach3只是为打印端口而设计,而它的机制却违背了Windows 的内核模式。

这样就可以更容易理解,为什么原理机制的设计会发展缓慢,必须考虑到原始打印端口的操作的一些内部限制。

当然,工作还在进展,提供一个独立的核心组件隔离层来消除脉冲输出的外部限制是未来的方向。

Mach3的工作原理
定时器(Timer)
Mach3的定时器是比较特殊的。

它唯一的真实用途,就是决定怎样实现比Windows可以提供的速度更快。

它是怎样完成的呢,这包括几个处理步骤。

当你启动Mach3的时候,程序对主板做一系列的分析,看它是怎样利用不同的定时器。

然后重编程主板以利用几乎所有CPU未使用的本地APIC定时器。

如果没有APIC芯片可用,或者正被使用,Mach3就使用另一种方法来获取CPU。

它采取重定向系统的中断来获取INT0,这是软件环境中可以获取的最高级中断,此中断不会被其它的软件系统所中断。

Mach3以此来停止Windows并完成软件自己的工作,同时不需要Windows知道它在运行。

定时器作为一个完全独立运行的脉冲代理,通过共享内存块来与Mach3协同工作。

它跟踪发送到共享内存块的命令,并处理块中需要输出的数据。

同时它从端口获取输入数据,并把它们放到内存块中,使Mach3主程序可以发现它们。

Mach3的程序并不了解硬件相关情况,它只是简单地设置共享内存块中的内存变量,然后引擎则轻松地在它自己的Ring0级(Windows的特权空间)上高速率运行,根据那些变量执行脉冲输出、设置端口
位值、和自动执行所有指定它需要做的事情。

驱动程序会完成一些自动功能和做一些它必须做的事情,作为Windows系统不能快速响应的辅助从而完成给定的任务。

点动(Jogging)是一个很好的例子,Windows没有办法实现控制点动的复杂性,因此从程序发送来的命令必须由驱动来控制它。

复位(Homing)是另一个例子,当接触开关时Windows不能足够快地停止在精确位置,因此驱动必须介入处理。

由于技术原因,引擎不能执行浮点运算。

因为它是一个实时临界子系统,你可以想像发生以下情况时会怎样:当Windows正在计算你的支票帐户余额时,Mach3的引擎中断了它,并使用了浮点芯片,然后再返回到Windows。

这会使你的计算产生错误,因此不能使用浮点运算。

下面让我们看看系统的一些示范功能,看它们是怎样完成的。

这是领会流和了解Mach3是怎样看待世界上所有同步系统的最好途径。

点动(Jogging)
点动以纯整数的方式完成。

当程序通过内存块设置某个数值时,引擎即自动执行。

每一次中断产生,驱动都会检查是否需要执行点动。

如果不是正在执行加工程序,引擎在每次中断都会检查是否需要点动。

所谓中断,就是脉冲产生的速度,如果设置了25000速度,则每40us产生一次中断。

因此每过40us引擎就检查一次六个轴的变量设置,在变量Engine->Axis[n]中,系统查看各轴是否需要执行点动处理,如果值被设为真,引擎将检查附加变量来确定以什么速度、加速度、减速度来执行点动。

所有这些变量储存在引擎的一组结构中,叫作Axis structur es:
struct AxisInfo
{
int Index;//Current Count
int MasterIndex;//Unused at this point
char StepPin;//Pin for step pulse
char DirPin;//Pin for Direction Pulse
char StepPort;//Port#for step
char DirPort;//Port#for Direction
bool StepNegate;//low active step?
bool DirNegate;//low active Direction?
int CurVelocity;//Current Velocity
int MaxVelocity;//Current Max Velocity(jogging)
int MasterVelocity;//Master Velocity...Maximum Velocity in all circumstances.
int Acceleration;//Acceleration
bool AtSpeed;//At Speed Currently
bool Acc;//Accelerating?
bool Dec;//Decelerating?
bool Enable;//Axis Enabled?
bool Jogging;//Jogging On?
int JoggDir;//Direction of Jog;
bool Homing;//is this a homing jog?
bool DeRef;//Dereferanceing Move?
int Memory[6];
int ActiveMemory;
int Color;
int TripCount;//when to stop a probe
int DepthCount;//where a probe actually hit
bool Probing;//if we're probing or not.
bool Slave;//if this axis is a slave or not.
int SlaveAxis;//if so,who the heck are we slaved to?
};
实际上有7个轴结构,其中6个电机轴和一个主轴。

然而主轴比较特殊,并且以不同的方式进行处理。

例如处理主轴数据时会忽略点动变量。

话说使用者按下了点动键,代表你告诉了程序应获得那个电机的信息,程序将计算最大点动速度,并将结果放入相应轴结构的MaxV elocity内、同时设置加速度Acc、设置Dec 为false指示目前并非减速、最后设置Jogging为true,另外程序还需要设置Dir来指示点动的方向。

之后就是自动的魔法,驱动会视当前速率为零,将加速度值相加到当前速率,然后使用当前速率来维持步进脉冲流输出。

请记住,这个步骤每秒发生25000次,因此每步的加速度只是在很紧密的范围变动,使得加速能精确跟随加速坡道圆滑变化。

Acc值和MaxVelocity值是经过换算的数值,并非“步/秒”(steps/second)或其它单位。

事实上,它们是“步/秒”除以内核频率再乘以2048。

引擎使用这些数值来计算每次中断时的当前速度,并以这个速度来判断这个中断期间需要输出一个脉冲还是无需输出。

如果当前速度等于内核速度乘以2048则每次中断都输出一个脉冲,从而达到系统的最高速度。

MaxVelocity设置值维持速度限制在“motor tuning”设置的最大值范围内,在程序的Jog%设置比例值以同样的方式产生作用。

要停止一个轴的点动,程序只需设置Dec为tr ue,引擎会自动逐个中断地对相应轴进行减速直到为零,然后驱动会将相应的轴结构中的Jogging设置为false。

而程序只需简单地通过检查轴结构就可知道一个轴是否正在点动、什么方向、任意时刻的速度是多少。

通常Mach3本身不需要做太多工作,但它却成为了可能,因为它的引擎很能干,而且运行得非常快。

还有几个点动过程的变种。

探测功能(Probing)只是点动的一个实例。

只需简单地将步进计数值设置到Tr ipCount,引擎就会自动设置相应的Dec为true,再将probing设置为true,其它点动参数按常规设置。

这样引擎会在探测输入信号有效时自动停止点动,或当轴的Index值达到设定的Tr ipCount值时停止。

Index是一个非常重要的值,它记录走过的步数,在引擎每次输出脉冲时增加或减少,这可以使Mach3任何时候都知道自己的精确位置。

除非发生了越程(going out),否则不要设置index的值。

只有复位操作(Homing)会将Index设置为零,其余时候不会被任何代码改变,从而保持精确度。

如果DRO与电机位置不一致,或者电机失步,这都不会成为问题,因为它被设计成系统诊断的一个手段。

对于复位,程序只需简单地将轴结构的Homing值设为true,即会向在“hor ming config”内设置的方向开始点动,引擎会在触发开关时停止并反向移出(move off),然后清零相应的Index值。

程序只需要简单地等待这一切发生和完成,而事实上绝大多数时候,程序的主要工作也就是等待事件的发生和等待它们完成。

当引擎完成了一个改变了机器位置的操作,并且它知道没有更多的命令会改变位置时,它通过设置一个叫Sync的块变量向程序发出信号。

如果引擎设置了Engine->Sync为true时,程序将会以轴结构中的Index值同步到自己的世界坐标(英寸或毫米单位)。

这意味着如果发起了一次点动,并且停止在X为10.5英寸的位置,在驱动发现点动停止后,将发出一个同步命令,因而程序即会触发一次更新使X位置为10.5,从而维持了与引擎的同步。

任何进程都可以将Engine->Sync设置为真而发起同步命令,当程序完成同步后会自动将变量复位为假,通常在1/10秒内完成。

计划运动(Planned Motion)
另一类型的移动是计划运动(预先计划好的按程序方式执行的运动)。

这个工作与点动十分不同,是Mach3的主要限制的根源,并且是Mach3可以工作在Windows上的前提,也是程序内部只能采用另类机制的主要原因。

在引擎的上下文内发起计划运动,需要先告知引擎可以接受移动。

如早前所说的引擎不能执行浮点运算,而在接受一些运动事件时,必须尽快完成将要移动的计划。

计划运行是一个复杂的任务,需要消耗相当多的CPU时间。

首先让我们从引擎的角度,考虑计划移动到一个点,仅仅只是需要输出一些步进脉冲。

引擎使用一个可容纳4096个命令的缓冲环,每一个环内的命令均构成Index位置,其它的一些信息则用于帮助平滑步进脉冲流。

你可能还记得,主系统是通过各个轴的Index变量来跟踪电机移动的,它就是真实步进输出的计数。

在缓冲环内,我们将内核中断时长乘以5来划分时间。

例如内核速度为25K,则中断时间为40us,乘以5之后为200us。

因此缓冲环的entr ies实际表达了4096个200us的间隔。

因而,每个缓冲环的入口均表示各个轴的200us的位置改变(即相对之前一个入口)。

按照这个原理,可以确定缓冲环内的任意两个相邻入口所定义的距离不会超过5个步进脉冲。

这些是否已经让你抓头了呢,让我们再看看更深入的东西吧。

首先,缓冲环是一个闭合环路,就是一系列位置首尾相接。

那么先看看一个只有10个位置的小环,但请记住Mach3真正缓冲环有4096个位置。

有两个指针使用在这上面:TrajHead和TrajIndex,程序员可以以Engine->TrajHead和Engine->TrajIndex.来使用它们。

先说说将它们两个都设置为零,引擎看到它们相等则还会输出脉冲。

在程序填充缓冲时,以如下的结构填充缓冲环的每个入口:
struct TrajPoint
{
int Points[6];
int Command;
int ID;
char shifter[6];
};
你会发现这个结构足够小,但使用了4096个这样的结构。

Points[6]变量对应6个轴,数值简单地指明了移动到目标机器坐标各个轴需要输出的步进脉冲数。

Command变量比较特殊,一般不使用,通常只是告诉驱动触发一个事件,比如特殊输出类型或当处理入口时应该产生一个特定行为。

当它处理入口时,它会先执行指定行为然后再移动到指定的位置。

ID简单地保存了GCode程序代码的行号。

如G0X10这么简单的代码,已经可能在环内产生数以万计的入口,而只有GCode代码行改变了ID才会改变。

Shifter是一个特殊位值,它告诉引擎如何实时产生步进脉冲,尽可能使脉冲间距保持最好,这可以实现更圆滑的结果。

“Shifter”变量值在程序的参数设置中的“Enhanced Pulsing”项选择。

现在让我们考虑一下在10个位置的缓冲环内产生一个移动。

开始时TrajHead和TrajIndex都为零,Mach3在开始时设置它们。

因此驱动在Loc0找到入口数据。

Loc012345678910
Index01123445550
开始时Tr ajIndex和Tr ajHead都为零,引擎不移动,当前各个轴的Indexe也都为零。

当主程序在计划清单中发现一个移动需求时,开始向缓冲环填入如上数据。

在这个例子里程序共填入10个位置并使Tr ajHead指针值增加到10。

它先是基于加速度和速率在TrajHead指向的Loc内填入Index数值,然后使TrajHead增加1。

如此重复一步步填入数据直到到达移动的终点或缓冲环被填满(在这个例子里,填入到缓冲的末尾时刚好到达移动终点)。

目前引擎正在空转,当它发现Tr ajIndex不等于Tr ajHead时,它开始获取Tr ajIndex (当前为0)指向的值,并和当前轴的index进行比较,如果它们不相等(它们的差最大为5)就创建一个结构,使接住的5次中断输出相应的步进脉冲数,从而使相应轴的MasterIndex值匹配缓冲环内的index值。

在这个例子里,接下来的5次中断没有脉冲被输出。

(注:按照点动章节中struct AxisInfo的说明来看,应该使轴的index与缓冲环的index匹配才合理,不应该是Master Index。


在引擎完成了这个200us周期后,就会增加Tr ajIndex的值(使它指向1)然后发现有1步的差值,那么它在接下来的200us输出一步脉冲。

(shifter是一个8位字节,只使用了其中5位,每个位用于指示5次中断是否输出脉冲。

)当TrajIndex增加到10,它就和TrajHead的值相等,整个移动过程完成。

如果在这个虚构的10段缓冲环例子中,程序极快地填充数据使Tr ajHead达到10,而TrajIndex还是零,程序只需简单地等待,直到Tr ajHead和Tr ajIndex开始接近后,再继续向Tr ajHead填入数据。

在这个方式中,缓冲的填入和清空是自动完成的,当TrajHead或TrajIndex等于10时,继续增加将会恢复到零位置。

引擎每次只知道200us的数据,显然它不能考虑到加速曲线,这方面由程序来加以考虑并以适当的步进数值填充到缓冲环。

Ring Index's01123445550
在我们的例子里,你会看到从0到1移动得比较慢,然后2、3、4移动得比较快,再从4到5移动得比较慢,并停止在5的位置。

这是按加速曲线移动的一个缩影。

由此你会发现缓冲环表达的并非速度、也不是加速度,它只是与端口输出有关的时序信息。

这就是为什么立即停止会有问题,你不能简单地叫它减速,你需要增加一系列的减速数据。

我们有4096个入口,每个维持200us时间,所以我们可以保存4096*0.0002=819ms的运动数据。

如在100KHz这样快的内核速度时,只能保存40ms的运动数据。

这是为什么
你需要一个非常快的CPU来达到100kHz,如果填入数据的速度不够快,缓冲环将在运动中途就被清空。

如果程序填充得不够快,在一个长距离移动中你会在检测屏幕的“Buffer Load%”内看到100%的负载率。

这时你就需要降低内核速度了。

在TrajHead和Tr ajIndex不相等时,点动和其它的移动都不能执行。

流程(Flow)
如按下键盘来执行点动、或点击界面内的命令按钮执行一些简单的引擎响应。

程序只是简单地不断填充数据来保持缓冲环维持填充状态,或简单地向环填入命令流,然后再等待新事件。

让我们看看当你装入一份程序并运行它会发生什么。

这可能不是很重要,但我认为对于理解真实的程序流程还是有用的。

当你启动Mach3时,它装入所有你的设置,并将数百个标志填入变量内,使Mach3知道各种情形下该怎样做。

当启动完成后,Mach3并不会做些什么实际事件,但事实上Mach3永远不会睡眠。

它永远在不停地执行数以百计的子任务。

首先我们考虑一下在没有移动,程序好似处于“空闲”时,有些什么正在执行。

Mach将会以1/10秒的间隔运行。

这就是说每过1/10秒,系统定时器就会启动一次更新处理来完成以下流程:
1)首先,它会查看程序窗口内的所有控件,如DRO、按钮、LED等等,并逐个更新它们。

这个过程完成得非常快,因为为了节省时间,只有可见的控件被更新,因此那些不在当前窗口页内的控件不会被更新。

这个过程要求每个LED或DRO检查自己的值,如果值不等于之前显示的值,就需要更新显示。

2)更新GCode窗口和工具路径。

它们知道内部位置是否改变,并会重画屏幕来反映出来。

只要环缓冲ID改变而反映了“trajhead”不等于“tr ajindex”指针,GCode窗口将立即更新,这就是为什么GCode显示总可以自动显示当前行的原因。

3)检查Indexes和输入是否改变。

例如一个宏可能调用了SystemW aitFor(INPUT1),因此INPUT1会被检查,看看刚过去的1/10秒内被等待的对象是否被驱动程序取消。

事实上在PP模式,引擎停止了等待条件的40us内等待已经被取消。

4)如果还有更多的数据在等待填充则检查缓冲环。

在这个情况下,运动正在进行,而系统则“空闲”地等待引擎消耗掉缓冲环中的一些数据。

在25K内核速度时,引擎每1/10秒清空500个环入口,所以我们等待它最少有500个空位才再次填充。

5)如果有命令需要处理则处理它。

如有些命令需要等待缓冲环清空后才执行;或正在等待之前触发的等待事件;主轴开/关命令;等待宏调用等等。

6)轨迹计划器(Tr ajectory planner)会检查是否有剩余的移动。

如果有则计算超过200us的移动并填充到环。

当运行一个加工程序时,处理过程将一直预处理到“Lookahead”配置值指定的程序行,并把处理结果填充到计划器。

或者预处理到具有等待性质的命令。

例如:
G1X10
G1Y10
G1Z10
M5
G1X0Y0Z0
以上程序将会填充轨迹计划器到M5命令。

当M5被解释后,计划器将主轴停止命令作为“等待事件”类型,并停止继续解释程序行。

直到全部之前的移动都停止后,再执行M5命令,然后才继续从输入文件获取剩余的程序进行处理。

如例子的前三行被读取,并综合了进级率、加速度、最高速度等因素进行计算。

计划运动的多轴运动行为会考虑各个轴的设置而产生混合的进级率,而点动不会考虑各轴之间的设置。

每经过1/10秒发生一个程序循环周期,如果计划器数据非空,它的数据就会被填充到缓冲环,使引擎自动执行运动。

在填充缓冲环的时候,为了产生更平滑的输出时序,程序会重计算脉冲的平均间距,使它在基于中断时基的前提下最接近理想。

时基计算的效果意味着使用更高的频率来处理更低的速度,事实上对使用者是有利的,但这对处理器速度有更高的要求,它填充缓冲的速度必须快于引擎清除缓冲的速度。

对于那些比较慢的计算机,我的建议永远是尽量采用比较低的内核速度。

现在的计算机都很快,我怀疑大部分人都已经在用2Ghz以上的计算机,但我并不介意能跑60Khz的使用20khz的选项。

没必要使用最好的选项,应该先考虑系统的稳定性(4096的缓冲在60Khz 只保持大约341ms,而25Hhz则可以保持819ms)。

因此最好以实际需要的因素来决定采用的设置。

选择更快的速度可以提高响应,但在你切换屏幕或打开你的Email时更容易失步。

时序(Timing)
Mach3使用了Interrupt0,这是一个非常强大的中断,但有一个最大的危害是DMA周。

相关文档
最新文档