聲明一下,寫下這些編程技巧,即不是什么祖傳秘籍,也不是什么必殺招或絕招,在此只為方便同仁們在編程控制軟件時,對此可以進行適當的斟酌。以下展現的編程思想及奉上的源代碼都非常簡易,但并不是隨手寫寫,可都是經過實踐的。若沒有成功經驗作后盾,我也就沒有必要在此打字練五筆了。 事實上,正如一個編程大師所言( Michael Abrash ),當你的軟件正常而且有效率的運行起來時,好像一切都是那么顯而易見。故,在此,我仍堅持那句編程口號,將事情變得越簡單越好,越簡單就越有效率,越穩定。 在以下的介紹中,我將盡可能的展示本人的編程思想,最大可能的給出知其然也知其所然的解釋。若你有更好的見解,希望能得到你的指正。人長大了明顯標志就是變得不太負責,而且不敢承認自己還需要努力,害怕面對自己的錯誤。若是這樣,放心,我還沒長大。因為我無法保證我能面面俱到。 關于源代碼的閱讀,需要讀者有一定的 C++ 編程基礎,至少對以下表示形式不會產生誤解: const char *pString; // 指定 pString 邦定的數據不能被修改 char * const pString; // 指定 pString 的地址不能被修改 const char * const pString; // 含上面兩種指定功能 當然,隨便提醒一下,這些源代碼若需要加入你的軟件工程當中,還需要作一些調整和修改,因此,這些源代碼實質上稱為偽代碼也可以,之所以展現它們,是讓程序員們有個可視化的快感,特別是那些認為源代碼就是一切的程序員。 同時,為了提高針對性,大部分控制卡調用的函數會明確指出是邦定哪些卡的,實際應用時,程序員可自行選擇,以體現一下自己的智商是可以寫寫軟件的。 留個電話: 0755-26434329 E_mail: support@leisai.com 有更濃興趣的上這個 QQ 號嗎也行: 372161225 一、 控制卡類的單一實例實現 把控制卡類作一個類來處理,幾乎所有 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 ); 以上的調用,很多重復枯燥,又不直觀,難于理解,并且在面向客戶時,常常是指每分多少米,或者每秒多少毫米,很少有人問每秒多少脈沖,移動多少脈沖作距離,故需要單位之間的換算。顯然,對于這些問題,我想,C++程序員應該找到用武之地了,所以我們一步一步來,慢慢統一各個問題。實質上,在以下的幾個技巧,也需要在此澄清一些概念。 我們先來幾個宏定義提高一下情緒: # define MAX_AXIS 4 // 最多軸數 # define XCH 0 // 定義X軸的值 # 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_axis[MAX_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 = 1<<2; 在 DMC3000 控制卡,其值在第 9 位,則這樣: ORGIN = 1<<9; 方法都很簡單,關鍵是要想得到。 對于 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 分鐘內明白是什么道理。
狀 態:
離線
公司簡介
產品目錄
公司名稱:
深圳市雷賽智能控制股份有限公司
聯 系 人:
梁邦敏
電 話:
755-26401178
傳 真:
地 址:
深圳市南山區登良路天安南油工業區2棟3樓
郵 編:
518000
主 頁: