




已阅读5页,还剩21页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
控制卡应用编程技巧几招(1)声明一下,写下这些编程技巧,即不是什么祖传秘籍,也不是什么必杀招或绝招,在此只为方便同仁们在编程控制软件时,对此可以进行适当的斟酌。以下展现的编程思想及奉上的源代码都非常简易,但并不是随手写写,可都是经过实践的。若没有成功经验作后盾,我也就没有必要在此打字练五笔了。 事实上,正如一个编程大师所言(Michael Abrash),当你的软件正常而且有效率的运行起来时,好像一切都是那么显而易见。故,在此,我仍坚持那句编程口号,将事情变得越简单越好,越简单就越有效率,越稳定。 在以下的介绍中,我将尽可能的展示本人的编程思想,最大可能的给出知其然也知其所然的解释。若你有更好的见解,希望能得到你的指正。人长大了明显标志就是变得不太负责,而且不敢承认自己还需要努力,害怕面对自己的错误。若是这样,放心,我还没长大。因为我无法保证我能面面俱到。 关于源代码的阅读,需要读者有一定的C+编程基础,至少对以下表示形式不会产生误解: const char *pString; /指定pString邦定的数据不能被修改 char * const pString; /指定pString的地址不能被修改 const char * const pString; /含上面两种指定功能 当然,随便提醒一下,这些源代码若需要加入你的软件工程当中,还需要作一些调整和修改,因此,这些源代码实质上称为伪代码也可以,之所以展现它们,是让程序员们有个可视化的快感,特别是那些认为源代码就是一切的程序员。 同时,为了提高针对性,大部分控制卡调用的函数会明确指出是邦定哪些卡的,实际应用时,程序员可自行选择,以体现一下自己的智商是可以写写软件的。 一、 控制卡类的单一实例实现 把控制卡类作一个类来处理,几乎所有C+程序员都为举双手表示赞同,故第一个什么都没有的伪代码就此产生,如下表现: class CCtrlCard public: Function public: attrib 于是,用这个CctrlCard可以产生n多个控制卡实例,只要内存足够。然而,针对现实世界,情况并不那么美好。通常情况下,PC机内只插同种类型的控制卡1到2张,在通过调用d1000_board_init或d3000_board_init函数时,它们会负责返回有效卡数nCards,然后从0-nCards*4 - 1自行按排好轴数。初始化函数就是C+的new或malloc的操作,取得系统的资源,但是控制卡的资源与内存不一样,取得资源后必需要释放才可以再次获取,即控制卡资源是唯一的。 既然控制卡资源是唯一的,那么最好Cctrlcard产生的实例也是唯一的,这样,我们可以方便的需要定义一个全局变量即可: CctrlCard g_Dmcard; 在其它需要调用的地方,进行外部呼叫: extern CctrlCard g_DmcCard; 以上方法实在太简单了,很多人都会开心起来。实质上,方法还有很多,即然可以产生n多对实例,我们的核心是只要保证调用board_init函数一次即可,故也可以单独定义一个InitBoard函数: class CctrlCard public: static int InitBoard(); /定义一个静态函数,以表警示 int CctrlCard:InitBoard() return d1000_board_init(); 还有一种方法,情况稍加复杂,但表达的功能也要强一些,以下展现可以稍微安慰一下代码狂。 Class CctrlCard public: CctrlCard(); /请注意这个构造函数的定义 CctrlCard:CctrlCard() /呵呵,也很明了 static int n(0); /注意,是个静态变量 n+; /每次调用CctrlCard生成实例时,都会计数一次 assert( n = 1 ); /在DEBUG版本下,只有n=1的情况下可以通过 /否则,会出现致命错误,还好,它会告诉你错在哪个文件, /哪一行,呵呵,是个好东东啊。 通过强行报警处理,当你有g_DmcCard这个实例时,其它的所有控制卡的定义都只能是以引用或指针的方式进行了,不会再产生新有效的实例了,对于由小组编程的项目软件,而你又恰好负责编程控制卡这一块的话,以上的显性报警,会让其它人心领神会。当然,你也可以将上面的方法加入到InitBoard当中去,可以避你的无意识的多次调用了。 附:无意识的多次调用经常发生,特别是那些对MFC机制不明确的程序员,在多文档框架下,不知道这个CctrlCard:InitBoard函数到底是应该放在CmainFrame的OnCreate里面,还是应该放在CchildFrame的OnCreate,或者是Cview的OnInitUpdate里面进行调用。 在一言难尽MFC的情况下,我建议两个小方法: No.1 将CctrlCard的函数置于Cmainframe的OnCreate或者Capp:Initstance内调用 No.2 将InitBoard函数稍加改造成这样: Int CctrlCard:InitBoard() static int n(-1000);/注意,-1000是控制卡函数不可能返回的值 if( n = -1000 ) n = d1000_board_init(); return n;/这样,即使多次调用也不样怕了,呵呵,雕虫小技也可以除虫啊 必须额外声明一下,我们不是不重视资源的释放,而是作为一个C+程序员写下这些代码是基本的义务(这也是我为什么要交待读者必须要有一定的C+基础): class CctrlCard public: CctrlCard() /定义析构函数,在此释放资源,对此,我不想再转到读者的眼球了 d1000_board_close(); 二、 数据结构及数据类型的定义,部分相关声明 调用控制卡驱动函数时,经常会有如下形式: 单轴相对运动 d1000_start_t_move( axis, pulse, start, speed, accel ); 单轴绝对运动 d1000_start_ta_move( axis, pulse, start, speed, accel ); 两轴相对插补 d1000_start_t_line( axisArray, distArray, start, speed, accel ); 两轴绝对插补 d1000_start_ta_line( axisArray, distArray, start, speed, accel ); 圆弧相对插补 d3000_start_t_arc( axisArray, C1, C2, E1,E2, dir, start, speed, accel ); 圆弧绝对插补 d3000_start_ta_arc( axisArray, C1, C2, E1,E2, dir, start, speed, accel ); 以上的调用,很多重复枯燥,又不直观,难于理解,并且在面向客户时,常常是指每分多少米,或者每秒多少毫米,很少有人问每秒多少脉冲,移动多少脉冲作距离,故需要单位之间的换算。显然,对于这些问题,我想,程序员应该找到用武之地了,所以我们一步一步来,慢慢统一各个问题。实质上,在以下的几个技巧,也需要在此澄清一些概念。 我们先来几个宏定义提高一下情绪: # define MAX_AXIS 4 /最多轴数 # define XCH 0 /定义轴的值 # define YCH 1 # define ZCH 2 # define UCH 3 .(其它以次类推) # define M_ABS 0x01 /定义一个绝对标志位 # define M_INP 0x02 /定义一个插补位 接下来深入一点点,再来几个结构定义: typedef struct tag_ARC tag_ARC( double ox=0.0, double oy=0.0, double ex=0.0, double ey=0.0, int dir=0 ): ox(ox), oy(oy), ex(ex), ey(ey), dir(dir)/定义这样一个构造函数需要勇气,看似不合理,但是好用麻 double ox,oy; double ex,ey; int dir; ARC; typedef struct tag_SPEED tag_SPEED( double start=0.0, double speed=0.0, double accel=0.0, double decel=0.0, double scc=0.0 ) : start(start), speed(speed), accel(accel), decel(decel), scc(scc) double start; double speed; double accel; double decel; double scc; SPEED; 以上两个ARC和SPEED的结构定义,把几个参数变成一个参数。比如要实现的单轴驱动函数,就变得非常明了: void Move( int nAxis, double fMM, const SPEED &speed, int nFlag = M_ABS );/往后我们再具体完善其实现。 以上的结构具有类的特性,但是由于其每个成员都可以给外部直接使用,故就不需要什么类的public及其析构函数的定义了。之所以全都采用double的数据类型,是面向客户习惯及单位计算方便的。 接下来是对控制卡常用的单位计算及部分常用变量的声明: class Cctrlcard public: (其它略去) public: /属性 mutable int ORGIN; /指定原点状态位 mutable int LIMIT_A, LIMIT_B; /指定左右限位状态位 private: /以下的属性不给外部访问的 struct tag_AXIS/单轴属性 double fUnitPM; /脉冲当量 long nRP; /每转脉冲数 double fJourey; /行程 ; tag_AXIS m_axisMAX_AXIS; ; 定义ORGIN,LIMIT_A, LIMIT_B为变量,是有两个意义: No.1 当你访问它们的状态时,不需要每次调用d1000_get_axis_status函数,你可以这样: Int nStatus = d1000_get_axis( XCH ); If( nStatus & g_DmcCard.ORGIN = g_dmcCard.ORGIN ) If( nStatus & g_DmcCard.LIMIT_A = g_DmcCard.LIMIT_A ) If( nStatus & g_DmcCard.LIMIT_B = g_DmcCard.LIMIT_B ); No.2 你可以扩展不同的卡,当外部调用的程序逻辑已被确定时,当你需要从DMC1000控制卡升级到DMC3000控制卡时,只需要给ORGIN等状态位指定不同的值即可。指定状态位的值也有一个小小的技巧,以ORGIN为例,在DMC1000控制卡,其位值在2位,则可以这样: ORGIN = 12; 在DMC3000控制卡,其值在第9位,则这样: ORGIN = 19; 方法都很简单,关键是要想得到。 对于tag_AXIS定义,引出几个函数的声明,专门为其服务: void SetUP( nit nAxis, double fMM, double nPulse, double fMax );/设定当量 double P2M ( int nAxis, long nPulse ); /脉冲转成毫米 pulse to metric long M2P( int nAxis, double fMM ); /毫米转成脉冲 mitric to pulse 现在,我们再回过头来完成Move函数的实现,以便获得一点点成就感,同时也展示一下以上的大堆表述是有其意义的。 void Move( int nAxis, double fMM, const SPEED &speed, int nFlag = M_ABS ) ( nFlag & M_ABS = M_ABS ) ? d1000_start_ta_move( nAxis, /绝对 M2P( nAxis, fMM), M2P( nAxis, speed.start ), M2P( nAxis, speed.speed), Speed.accel ): /注意是冒号,?:是一个表达式 d1000_start_t_move( nAxis, /相对 M2P( nAxis, fMM), M2P( nAxis, speed.start ), M2P( nAxis, speed.speed), Speed.accel ); 是不是很简单呢,当外部调用时,客户的观念就直接面对Metric即可,如: Move( XCH, 10.0, SPEED(5,10,0.1), M_ABS );/达到绝对位置10.0毫米处。 以上罗嗦了一大堆,对于刚开始C+编程的程序员来说应该是收益不小,对于高手,则希望能够体会一下我的良苦用心。在以下的技巧介绍当中,我将变得很简易。一般来讲,程序员的基础不是太差的话,至少能够在1分钟内明白是什么道理。控制卡应用编程技巧几招(2)发布日期:2009-3-9 0:42:58浏览次数:717三、 插补和联动函数 当程序员决定需要几轴进行插补时,尽量选择最大插补轴数,如在雕铣系统时,有时会用到两轴插补,有时会进行三轴插补,在这个基础上,为简化编程,我的理论只使用三轴插补,当需要进行两轴插补或联动时,根据相对或绝对的坐标关系,将不运动轴填入0偏移或绝对位置即可。 以下为XYZ三轴联动和插补的函数,由nFlag的M_INP位决定是否进行插补: void MoveXYZ( double fX, double fY, double fZ, const tag_SPEED &speed, int nFlag = M_ABS ) short axisArray= XCH, YCH, ZCH ; if( nFlag & M_INP = M_INP ) /插补 long distArray= M2P(XCH, fX), M2P(YCH,fY), M2P(ZCH,fZ) ; long nStart, nSpeed;/计算新的矢量速度,参见DMC1000矢量速度的计算 (矢量速度计算在此略去) ( nFlag & M_ABS = M_ABS ) ? d1000_start_ta_line( 3, axisArray, nStart, nSpeed, speed,accel ):/绝对 d1000_start_t_line(3, axisArray, nStart, nSpeed, accel );/相对 else /联动 double fpos= fX, fY, fZ; for( int I(0); ISelectObject( &newPen );时,或者除了复位之外,你真正需要调用这个SetPosition函数时,你会发现这个设计,真是人情味实足。 五、 复位,相对与绝对, 在如今PC机开发控制卡软件时代,设备上电不复位的几乎没有,在此谈到复位这个问题确实有必要,实现上,复位动作因不同设备的工艺要求而定,故一般而言,控制卡提供的那个复位函数太过简单,有点力不从心,所以,本人自己写了个复位函数,但是代码写起来将会占用很大的面版,故有此需要者,可以来电或E_mail索取。 其基本思路是采用两次找原点,第一次高速找,停止后退出,再次以较低的速度找原点。并且在执行第二次复位时,会在离原点5毫米处减速(第一次执行做不到)。 提供相对和绝对位置的概念是很有必要的,众所周知,现在控制卡能作到最小单位为1个脉冲,当然,作为数字脉冲,到此已不能再小了,故为了提高精度,通常情况下要提高计算当量,即增加每转脉冲数,或减少每转毫米数。 不论怎么,我们将问题放大并明朗化,可以看看以下片段: for( int I(0); I10000; I+)/ 走10000次 move( 0.5 );/走相对0.5个脉冲的距离 结果是:1个脉冲也发不出,造成很大的累积误差。 若换成绝对方式: for( int I(0); I10000; I+) goto( I*0.5 ); 最后的误差,最大也就是1个脉冲以内。虽然还是有误差,但总算达到可容忍的程序,再加上适当的复位操作,让客户至少不必再担心这个巨大的累积误差了。 实质上,在整个软件设计时最好采用绝对坐标系,即使要处理加工原点或工面起点等这些参数,也要把它换算成绝对位置,唯手动移动设备可以例外。另外,在CNC系统中,除了有循环用到相对坐标系,其余都是用绝对坐标系为上策,实际上,在实现编程算法上,为统一起见,最好将相对的坐标关系全部转成绝对的坐标关系,这样也便于外部进行暂停或继续的处理。 相信,到此为止,若你的设备在加工时有一定的误差漂移,你会意识到自己应该是不是要检查一下采用了什么坐标系了吧。 六、 输出输入及软限位 对于通用的I/O操作,没有什么特别要说明的,只有两点需要注意的,先给出两个小函数,以作参考: int ReadBit( int nIO ); /读指定通用输入口的电平状态,返回1 或 0 int WriteBit( int nIO, int nStatus ); / 输出电平到指定输出端口 两点注意: No.1 对于ReadBit若需要加入抗干扰处理,则写一个函数: Int RealInput( int nIO, int nStatus, int di=50 ) if( ReadBit( nIO ) != nStatus ) return 0; while( di - );/耗上几个CPU的周期时间,再读一次 return ReadBit( nIO ) = nStatus; No.2 增加一个变量及函数扩展一下输出功能: Long m_nOutStatus= 0x00000000; 再次改造一下WirteBit void WriteBit( int nIO, int nStatus ) if( nStatus ) m_nOutStatus |= (1 else m_nOutStatus &= (1 d1000_out_bit( nIO, nStatus ); 添加的访问输出状态函数: int ReadOutbit( int nIO ) static int a; a = 1(nIO-1); a &= m_nOutStatus; return a!=0; 软限位的思想原本是用于为客户节省正负限位的光电开关成本而产生的,致使使用软件限位正常的话,设备每个驱动轴只需要一个原点开关即可。当然,软限位能正确运作是非常重要的,否则很容易撞坏设备。而其正确运行,就必须依赖正确的复位动作,以找到可靠的机械原点位置。 软件限位的基本算法非常简单,特别是在一个绝对坐标系当中。其原理如下: if( pos maxPos ) pos = maxPos; 实在没有必要再详说下去了。 编程技巧介绍至此算是一个了断,若在未来的日子里,有更好的想法,我会拿出来给大家参考,请大家一起来支持这件事情,拿出自己的宝贵经验,算是给数控行业添加强有力的润滑剂吧。 谢谢。运动控制系统常用的图形图像文件格式简述随着运动控制系统的蓬脖发展,由于可以结合PC平台进行二次开发,故其数据来源逐渐由手工输入或教导,转向兼容大量绘图软件所生成的图形图像文件。如:CorlDraw,AutoCAD,MasterCAM,Pro/E等工具。由于这些绘图软件得到普遍大众的使用,并且具备强大的功能,一般而言,作为小型项目开发,只需要把它们所生成的文件读入,并作一些简单的数学处理(如:平移,镜像,旋转,错切)即可。这样也获得较高的开发效率,可以有效地节省开发者及客户使用者的成本。 以下我们简介几种常用于数控运动的文件格式,以帮助开发人员对此有个大概的理解,更好的把握项目的开发。 一、 CNC文件 CNC由国外流进,早期大量的应用于数控车铣床,故CNC包含的大量指令代码都可以通过机械加工之类的杂志获取,CNC以一种一统天下的海量囊括了所有运动控制动作。有很多指令,可以由用户自行定义(这是威力所在),在国内有很多数控设备厂商,基本上都支持标准CNC代码。如:成都广泰。 CNC文件格式一般分为非压缩格式及压缩格式两种,非压缩格式其数制形式基本上直接以小数出现。 压缩格式,数制有前省零,后省零两种,实际数值还需要由整数位和小数位决定。 如非压缩格式: X123.456 即表示当前X数值为123.456 生成此格式的,常用工具有 MasterCAM、Pro/E等 压缩格式: X123456 (没有小数点) 若以前零省,4位整数,3位小数,则X=0123.456 = 123.456 若以后零省,4位整数,3位小数,则X=1234.560 = 1234.56 生成此格式的,常见于PCB板绘图工具,如:CAM350,V2000,V2001 Protel。 支持CNC标准的文件后缀名一般常见有: .NC, .CNC, .TXT, .ROT, .GBR, .GBX, .ROU, .DRL等 以下列出CNC常用G代码指令的含义: G00 快速定位到有效位置 G01 直线插补 G02 顺圆弧插补 G03 逆圆弧插补 G04 延时暂停 G05 钻孔操作 G32 顺铣整圆 G33 逆铣整圆 G40 取消让刀(让刀一般也称作补偿) G41 开始左让刀 G42 开始右让刀 G84 钻圆操作 G85 钻槽操作 G90 绝对坐标系 G91 相对坐标系 G93 设定工作原点 以下列出CNC常用M代码指令的含义: M30 程序结束 M48 程序开始 M71 公制 M72 英制 M15 开始铣削 M16 结束铣削 模块复制操作指令: M25 块头 M01 块尾 M02 块操作开始 M08 块操作结束 M70 XY交换 M80 X镜像 M90 Y镜像 R 重复排列指令 以上指令,有些有特定的格式,若有需要进一步深入分析,可来电话与我们联系。 二、 DXF文件DXF文件格式,常见的由AutoCAD, CorlDraw绘图软件生成。DXF具有不同的版本,开发人员在分析DXF文件时,需要注意这一点,DXF文件格式的详细说明,可以到网站上,输入“ DXF文件格式”,即能找到相关的资料。 其操作码的说明,非常之丰富,本人手头的相关电子文档有十几页之多,故在此简单介绍几种常元素。 1、 直线 LINE 2、 圆弧 ARC 3、 折线 LWPOLYLINE 4、 圆形 CIRCLE 5、 圆弧 ARC 6、 椭圆 ELLISPE 7、 BZ曲线 ELLIPSE 8、 文字(一般会自选SHX字体文件) MTEXT DXF文件操作码和数值在文件中各占一行,如: 10 100000 20 100000 30 0 000 11 200000 21 100000 31 0000 以上展示的是一条直线段,起点坐标为(100.000, 100.000, 0.000),终点坐标为(200.000,100.000,0.000) 三、 PLT文件 PLT文件源自于HP绘图仪,在AutoCAD/R14版及CorlDraw软件中可以见到(需要导出),其实基于此类设备的还有EPS的一种矢量格式,即EPS文件,在此不多描述。PLT的操作指令非常丰富,不过常用以下几种指令就差不多够了。 PA 到达指定的绝对位置 PU 抬笔动作 PD 下笔动作 SP 换笔动作 AA 圆弧动作 LB 文本路径 PLT的操作码格式有许多形式,如抬笔动作: PU10023 23402; 达到指令位置后抬笔 或 PU10023,23402; 或 PA10023,23402; PU; 还有就是PA指令: PA1234 4567;(注意,两个数值之间有空格或者,分隔符) PA8324 2345; 或 PA1234,4567,8324,2345;(多坐标放在一起) 一般而言,从CorlDraw或AutoCAD/R14版生成的PLT文件,其路径大部分只包含PU,PA,PD,SP有效指令,且路径已经为最优化的运动轨迹了(在此省去开发人员对最短路径的优化处理,而DXF文件则需要根据工艺,需要再次优化,因为它的路径与绘图顺序有关,这样有时频繁编辑或修改出来的图形路径会非常纹乱,不适合直接加工处理,所以还需要开发人员再次优化路径,以达到最好的加工轨迹)。 四、 BMP文件 以上介绍的图形都属于矢量文件,BMP作为图像文件,几乎在Window的图像编程当中,是路人皆知,其简单的文件结构非常易理解,加上Microsoft的强大MSDN帮助,实在没有赘述的必要。 在此,考虑到运动控制的计算必须与图像尺寸进行匹配,故还需要提示一下如何设定这种对应关系的方法。实质上,在激光位图扫描或位图雕刻时,常需要这种关系设定,即多少像素对应多少毫米。取得这个当量关系很简单。 以Win98系统为例,点击“开始”菜单,选择“程序”项,进入“附件”,选择“画图”工具。 然后打开你所需要的目标文件,选择菜单“图像”的“属性”项,可以看到“单位”信息框中的几个单选项,我想,到此为止,稍微聪明一点的人,也许不用再指点什么东西了吧。 由于BMP文件实在是太普遍了,所以,几乎所有的图像处理软件,看图软件都支持将其它图像文件格式转换成BMP文件格式,如大名鼎鼎的Photoshop及最好用的看图软件ACDSee。 故其它的PCX,JPG,GIF,TGA,PIC图像就不是什么问题了。 五、 其它字体文件简介 Windows系统的字体文件结构非常复杂,虽然只有两种基本元素,即直线和BZ曲线,若想完成分析明白,还是要下一番苦功,好歹有一些伟大的作家将此事基本了解,可以参见一个华裔作家的Window图形图像编程,此书中对Window的字体作了全面的分析,且带光盘,对拿来主义者将是最大的礼物了。 若没办法购到此书,也不是就绝人之路了,在CDC的对象内,有一套方法,可以取得所有绘图的路径,文字路径就更不在话下了。 举例: 1 开始路径捕捉 CDC *pDC = GetDC();/自定义 pDC-BeginPath(); pDC-TextOut(0,0,”Hello, path!?”);/输出想要的文字 pDC-EndPath(); 是不是很简单呢 2 还原路径 在还原路径之前,用户还可以任意旋转,镜像,错切路径。 Int nCount = pDC-GetPath( NULL, NULL, 0 ); CPoint *pPoint = new CPointnCount; char *pCtrl = new charnCount; for( int i (0); i switch( pCtrli )/识别控制字, case . delete pCtrl; delete pPoint; 对于AutoCAD的SHX字体,分析起来完全靠代码处理,因此非常困难,若有这方面需求的用户可直接与我们联系。还有早期DOS系统下,TC2.0有BC3.1编译环境还带有一些CHR矢量字库,用户若有需求请联系我们即可。过程控制编程之简要探讨抽像一点讲,过程控制渗透在生活的各个角落。特别是在数控行业,尤为明显。早期在PLC时代,几乎是一统天下。当大小不一的控制卡公司如雨后春笋般成长起来时,控制卡在过程控制逐步体现出强劲优势。 其编程手段往往邦定非常成熟的编程工具,如:CB,VC,Dephi,CB。故其推广非常迅速. 很快拥有大量客户群。 但是,作为编程者,碰到同一程序需要控制多个同样设备时(动作不一定同步),往往在Window多任务系统的影响下,很快想到多线程技术,其实多线程编程对系统依赖性较强,不同级别的处理,将导致程序的维护成本增加,并且还需要考虑线程之间的通讯,使编程起来较为麻烦。事实上,依靠过程编程的思想,会使程序获得更简单的做法。 纯粹从编程角度来看,过程控制编程应包含几个要素,下面以C+伪码方式来表示一个类,较易说明问题: /* 一个动作封装类,包含一个 设备的所有执行动作 */ class CAction public: CAction():m_nWorkStep(-1000) CAction() public: enum None, Inital, Start, Stop, Pause, Continue ;/部分控制标记 public: int Control( int nStatus, void *pParam ); /要素2: 一定可以接受外部控制 int Run( void *pParam ); /要素3: 绝对有一个不断推动的执行过程(这是整个过程编程的心脏所在) public: int m_nWorkStep; /要素1: 必须有一个工作执行步 private: void * m_pMemberData; /要素4: 可选,邦定被操作的数据 / .其它辅助变量 ; 要素5:可选,为了能与外界用户进行沟通,Control函数与Run函数,须选择一个参数,满足用户的输入输出。但由于用户可选项太多,故古人告诉我们,万事皆空即是成功,对于C+而言,没有比void *类型更有前途了(若想体会,需搭配类型转换更有潜力)。早在一年前,本人写过一个过程控制器编程,并有成功实例,但今次版本,更有精装之特点。 住下再参见一下Control函数,及Run函数的一个小样例,这将是程序员发挥强劲功能的场地所在。如下: int CAction:Control( int nStatus, void *pParam ) /此处的设计需要一点技巧,当然完全取决程序员的经验,和对控制的理解 switch( nStatus ) case Inital: m_pMemberData = pParam;/初始化邦定的用户数据 break; case Start: m_nWorkStep = 0; /一般令第0步作为开始较易理解 break; case Stop: m_nWorkStep = -1000;/停止 break; case /其它略去 default: break; return m_nWorkStep;/返回值可自定 int CAction:Run( void *pParam ) /以下为一个动作不断返回,直至接受到停止指令 switch( m_nWorkStep ) case 0: if( IsMove() ) break; /正在忙,不执行 Move(1600); /正向移动1600单位 m_nWorkStep+; /跳至下一步 break; case 1: if( IsMove() ) break; /正在忙,不执行 Move(-1600); /反向移动1600单位 m_nWorkStep=0; /再回至第0步 break; case 1000: break;/停止 default: break;/此句最好有,以便于理解 return m_nWorkStep; /返回值可自定 对于Run函数须谨记一点:不要有长时间的循环操作,更不要有死循环,否则另外设备无法正确动作,因为另外的设备Run函数无法被激活执行,心脏无法跳动,只好等死吧! 当然,还须担心一点:多个设备同时执行时,其实时性会下降一点,若没有特别高的要求,在现今CPU狂奔的时代,以上程序框架足已满足90%的需求,还是有学习和理解的必要吧! 以上的框架出来了,那么如何操作使设备驱动起来呢,不管在DOS亦或在Window系统环境里,以下的思路都一样,参见伪代码如下: const int nSize=10; /定义10台设备对象 CAction exeArraynSize; BOOL bRunning=FALSE;/控制整个驱动的标志 Void OnRunAction()/启动函数 /初始化一下 for( int i(0); inSize; i+) exeArrayi.Control( CAction:Inital, ( void *)userData );/userData由用户选择 bRunning = true;/外部控制变量 for( i=0; bRunning; i=(i+1)%nSize)/给予运行时间,即是驱动了 exeArrayi.Run( void *)userData); /不断驱动Action设备(即激活心脏) DoEvents();/做其它事情,如系统消息检测,以防止死机 DoEvents函数功能的详解可参见本人的DMC1000控制卡不能响应系统消息,上面有不同编程工具下的实现源代码。 void OnControlAction() for( int i(0); i nSize; i+) exeArrayi.Control( Caction:(/*用户想要的动作*/), ( void *)userData); void OnOver() bRunning = FALSE;/结束驱动 注意:有很多程序员在不同编译器下,需要不同的OnRunAction函数版本,以上实现最为简单,但有一些较特殊。比如:在程序启动时,就驱动所有设备,这点本人在VC6.0环境有过经验,可以分享一下,其它环境下,还需同胞们一起去努力发现。 还有一点声明,多线程技术也不是一无是处,在花费时间较频繁的通讯编程方面,它的优势,还是有相当的诱惑力。DMC1000控制卡不能响应系统消息一、问题现象及分析 问题经常通过以下编程表现出来: /X发出脉冲后,等待脉冲发完后再做其它事情 d1000_start_t_move( 0, 6400, 3200, 6400, 0.1 ); while( d1000_check_done(0) = 0 ); 老版本的库函数如下: d1000_start_tr_move( 0, 6400, 3200, 6400, 0.1 ); while( d1000_check_done(0) = 0 ); 或者: d1000_wait_done( 0 ); 此函数内部实际上包含类似于while( d1000_check_done(0) = 0 );的语句 以上可以看出,在检测脉冲是否发完时,此段代码的执行完全独占了CPU分配给当前进程的所有时间, 因而也排挤了其它消息的响应,当然也就不能响应定时器读取位置,或其它停止操作等. 二、解决方法 解决此问题关键是让while循环时能检测系统消息,以下分别介绍在VB,VC,CB下的解决措施: 注:以下编程全以DMC1000最新的驱动库函为参考 1.VB编程 d1000_start_t_move 0, 6400, 3200, 6400, 0.1 DO DoEvents LOOP WHILE (d1000_check_done(0) = 0) 2.VC编程 在VC下编程关键是要解决类似于VB的DoEvnets函数 vo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 智慧社区:两人合伙智慧社区建设合作协议
- 商铺租赁合同中租赁物损坏赔偿及修复合同
- 借读生校园社会实践协议及实践成果转化合同
- 2025公务员求职面试题目及答案
- 激光冲击成形技术-洞察及研究
- 主管专业知识试题及答案
- 基于离线的智能配电网控制-洞察及研究
- 军史知识竞赛题及答案2025年
- 神经炎症基因调控-洞察及研究
- ICU脑梗死的护理查房
- T-YNX 002-2025 葡萄组培脱毒快繁技术规程
- 衣服投标供货方案(3篇)
- 2025至2030年中国丁酮肟市场现状分析及前景预测报告
- 公司电脑补贴管理办法
- 中石化对供应商管理办法
- Unit 2 Home Sweet Home 语法与阅读专项练习 (含答案) 人教版(2024)八年级上册
- 2025年少先队应知应会知识竞赛考试题库及答案
- 【课件】第14章+全等三角形+数学活动++式+课件2025-2026学年人教版数学八年级上册
- 2025版安全生产法全文
- 高中英语高考词汇200句-教师版(简单句80)二
- 《山居秋暝》(王维)测试题带答案
评论
0/150
提交评论