找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4485|回復(fù): 3
打印 上一主題 下一主題
收起左側(cè)

單片機(jī)循跡小車完整資料 包括注釋詳細(xì)的程序和硬件設(shè)計

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:646839 發(fā)表于 2020-4-19 18:17 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
資料大家看吧!截圖截得不全。


單片機(jī)源程序如下:
  1. /***************************************************/
  2. /*                                        尋跡小車 FollowMe 項目                                                                 */
  3. /*          —— 主控程序軌跡控制模塊                                                                 */
  4. /*              之程序部分                                                                                            */
  5. /*                                            20060905                           */
  6. /*              By DingQi                          */
  7. /***************************************************/
  8. // 注:以下文檔的 TAB 為 2 個字符!

  9. /*------------------------------------------------------------------------
  10. 此程序為"尋跡小車FollowMe"項目中單板控制模式的走軌跡控制部分,附帶相關(guān)調(diào)試功能。
  11. 要實現(xiàn):

  12.         1)接收各種調(diào)試命令,并解析;
  13.         2)通過串口反饋所需的調(diào)試信息;
  14.         3)獲取軌跡采樣部分處理后的信息,產(chǎn)生對策,發(fā)給電機(jī)驅(qū)動部分實施。
  15.        
  16.         根據(jù)上述要實現(xiàn)的功能,通訊部分歸此模塊管理。
  17.        
  18.         第一步先將原來的電機(jī)驅(qū)動功能整合到一個MCU中,將原來的通訊功能從電機(jī)驅(qū)動模塊中分解出來。

  19.        
  20. 目前的電機(jī)控制由串口實現(xiàn),通訊協(xié)議定義如下:
  21. 1、幀格式:
  22.         幀頭(2字節(jié))  幀長(1字節(jié)) 命令字(1字節(jié)) 數(shù)據(jù)區(qū)(N字節(jié))校驗和(1字節(jié))

  23. 其中:
  24.         幀頭 —— 0x55  0xAA       
  25.         幀長 ——  命令字 + 數(shù)據(jù)區(qū)的長度
  26.         命令字 ——         0x01 :電機(jī)轉(zhuǎn)動控制參數(shù),開環(huán)模式,電機(jī)的PWM值、轉(zhuǎn)動持續(xù)脈沖數(shù);
  27.                                                    0x02 :電機(jī)轉(zhuǎn)動控制參數(shù),閉環(huán)模式,電機(jī)的轉(zhuǎn)速、轉(zhuǎn)動持續(xù)脈沖數(shù);
  28.                                                          0x03 :電機(jī)工作參數(shù),PWM頻率、PID參數(shù)
  29.         數(shù)據(jù)區(qū) —— 命令01:電機(jī)1數(shù)據(jù)(2字節(jié)PWM值,2字節(jié)轉(zhuǎn)動持續(xù)脈沖數(shù))電機(jī)2數(shù)據(jù)(2字節(jié)PWM值,2字節(jié)轉(zhuǎn)動持續(xù)脈沖數(shù)),共 8字節(jié);
  30.                                                 命令02:電機(jī)1數(shù)據(jù)(2字節(jié)轉(zhuǎn)速值,2字節(jié)轉(zhuǎn)動持續(xù)脈沖數(shù))電機(jī)2數(shù)據(jù)(2字節(jié)轉(zhuǎn)速值,2字節(jié)轉(zhuǎn)動持續(xù)脈沖數(shù)),共 8字節(jié);
  31.                                                 命令03:2字節(jié)PWM頻率,2字節(jié)比例系數(shù),2字節(jié)積分系數(shù),2字節(jié)微分系數(shù),2字節(jié)PID系數(shù)的分母, 共10字節(jié),兩個電機(jī)驅(qū)動器相同;
  32.         校驗和 —— 從命令字開始到數(shù)據(jù)區(qū)結(jié)束所有字節(jié)的算術(shù)和的反碼,取低字節(jié)。

  33. 上述數(shù)據(jù)中,PWM值,速度值、PWM頻率、PID系數(shù)等定義如下:
  34.         PWM值 ——         2字節(jié)有符號數(shù),正對應(yīng)正轉(zhuǎn),負(fù)對應(yīng)反轉(zhuǎn),數(shù)值為占空比的百分?jǐn)?shù),
  35.                                                 取值范圍:- 1000 ——  +1000, 對應(yīng) 0.1% ~ 100%;1001為電機(jī)“惰行”,1002為“剎車”;
  36.        
  37.         轉(zhuǎn)動持續(xù)脈沖數(shù) ——  2字節(jié)無符號數(shù),0 表示連續(xù)轉(zhuǎn)動;
  38.        
  39.         轉(zhuǎn)速值 —— 2字節(jié)有符號數(shù),正對應(yīng)正轉(zhuǎn),負(fù)對應(yīng)反轉(zhuǎn),單位為:0.1轉(zhuǎn)/每分鐘;
  40.                                                 取值范圍:- 10000~ +10000,10001為電機(jī)“惰行”,10002為“剎車”;
  41.        
  42.         PWM頻率 —— 2字節(jié)整數(shù),單位Hz,取值范圍:200 – 2000;
  43.        
  44.         PID系數(shù) —— 均為 2字節(jié)無符號數(shù);
  45.         PID系數(shù)分母 ——  2字節(jié)無符號數(shù),為避免使用浮點數(shù)而增加了此參數(shù),實際作用的PID系數(shù)為上述值除此值;
  46.                                                                         如:比例系數(shù)為190 ,PID分母為200,實際比例系數(shù)為0.95。

  47. 以上所有2字節(jié)的數(shù)據(jù)均為先低后高。
  48. 暫時不設(shè)計應(yīng)答幀,因為一幀命令包含了兩個電機(jī)的驅(qū)動數(shù)據(jù)。

  49. 通訊數(shù)據(jù)格式為:19200  8  N  1。
  50. 此時,一幀數(shù)據(jù)約占 7ms。

  51. 為了調(diào)試,添加轉(zhuǎn)速讀取命令 0x04 ,原來的讀轉(zhuǎn)速命令是分開實現(xiàn)的:
  52.         0x55  0xAA  0x02(幀長) 0x04 (讀轉(zhuǎn)速命令)  電機(jī)序號(1字節(jié))校驗和(1字節(jié))
  53.        
  54.         對應(yīng)的返回幀為:
  55.         0xAA 0x55  0x04(幀長) 0x84 (轉(zhuǎn)速值返回) 電機(jī)序號(1字節(jié))轉(zhuǎn)速(2字節(jié))校驗和(1字節(jié))
  56.        

  57. 因為合并到一個MCU中了,所以對應(yīng)將協(xié)議改為:
  58.         0x55  0xAA  0x01(幀長) 0x04 (讀轉(zhuǎn)速命令) 校驗和(1字節(jié))
  59.        
  60.         對應(yīng)的返回幀為:
  61.         0xAA 0x55  0x05(幀長) 0x84 (轉(zhuǎn)速值返回) 電機(jī)1轉(zhuǎn)速(2字節(jié))電機(jī)2轉(zhuǎn)速(2字節(jié))校驗和(1字節(jié))


  62. 因為集成了走軌跡控制功能,所以要添加一個控制命令,使小車啟動,進(jìn)入到走軌跡狀態(tài)或結(jié)束走軌跡的狀態(tài)。
  63.         走軌跡控制命令: 0x05 .   命令參數(shù) ——  1 啟動走軌跡, 2 ——  啟動走直線 ,0 停止,
  64.         命令幀為:
  65.         0x55 0xAA 0x02 0x05 0x01 CS        ——  啟動走軌跡命令
  66.         0x55 0xAA 0x02 0x05 0x02 CS        ——  啟動直線走命令
  67.         0x55 0xAA 0x02 0x05 0x00 CS        ——  停止命令

  68. 為了調(diào)試方便,增加一個內(nèi)存數(shù)據(jù)讀取命令:
  69.         內(nèi)存數(shù)據(jù)讀取命令:0x06
  70.         命令幀為:
  71.         0x55 0xAA 0x04 0x06 讀數(shù)據(jù)低地址 讀數(shù)據(jù)高地址 讀數(shù)據(jù)長度 CS
  72.         返回幀為:
  73.         0x55 0xAA 幀長 0x86 讀數(shù)據(jù)低地址 讀數(shù)據(jù)高地址 讀數(shù)據(jù)長度 數(shù)據(jù)N字節(jié) CS

  74. ------------------------------------------------------------------------
  75.         因為用雙輪驅(qū)動的小車由于電機(jī)等驅(qū)動元素的差異,導(dǎo)致走直線成為問題,故在此嘗試
  76. 用這兩個簡易的碼盤來實現(xiàn)直線行走。               

  77.         因為碼盤的分辨率太低,所以直接用脈沖計數(shù)方式控制似乎有些不夠精確,所以考慮用
  78. 兩路脈沖的觸發(fā)時間差別來控制。
  79.         實質(zhì)上這是一種周期測量的變換,因為不能保證每個脈沖周期是真實的(即由于干擾會
  80. 導(dǎo)致某個周期變大或變。,所以用累計的方式消除之。

  81.         具體的方式如下:
  82.         設(shè)立兩個 4 字節(jié)的計數(shù)器,2字節(jié)紀(jì)錄PCA的溢出值,2字節(jié)紀(jì)錄 PCA 的計時器值,這樣
  83. 構(gòu)成了一個對PCA計數(shù)脈沖(Fosc/2)的長整形計數(shù)器,可紀(jì)錄約 388秒(2^32/11.0592M),
  84. 這個值一般可以應(yīng)付大部分比賽項目中的需要。

  85.         將這個時間計數(shù)器作為兩個輪子采樣脈沖(只取下降沿)的時標(biāo),根據(jù)兩者的差值確定兩輪
  86. 的驅(qū)動差。也就是要保證兩個輪子對應(yīng)脈沖到達(dá)的時間相同,這樣,如果沒有漏計或打滑,兩
  87. 個輪子行走的距離應(yīng)當(dāng)是一樣的,軌跡也應(yīng)當(dāng)是直線!

  88.         這個控制也可以考慮使用PID控制,其控制的量為兩個計數(shù)器的差值,定值為“0”。
  89.        
  90. ------------------------------------------------------------------------*/

  91. #pragma PR
  92. #pragma OT(5,size)

  93. #pragma Listinclude
  94. #pragma code

  95. #include <E:\dingqi\keilc51\inc\math.h>

  96. #include <STC12C5410AD.h>                                /* STC12C5410AD 的頭文件*/

  97. #include <Port_Def.H>

  98. #include <ComConst.H>

  99. #include <LC_Const.H>

  100. #include <LC_Var.H>

  101. void init_SIO(unsigned char baud);                                                // 初始化串口
  102. void rcvdata_proc(void);                                                                                        // 接收數(shù)據(jù)幀
  103. void getCommandData(void);                                                                                // 從數(shù)據(jù)幀中取出命令數(shù)據(jù)
  104. unsigned int calStopCntValue(unsigned int uiRun_Num,unsigned char No); // 根據(jù)命令中的行走脈沖數(shù)計算出停止點
  105. char setMotorStat(int iRunValue);                                                        // 根據(jù)命令中的PWM值或轉(zhuǎn)速設(shè)置電機(jī)狀態(tài)
  106. void followLineControl(void);                                                                        // 走軌跡控制
  107. void straightRun(void);                                                                                                // 走直線控制

  108. /******************************** 外部調(diào)用函數(shù) *********************************/

  109. // 以下函數(shù)為公共函數(shù),需要在調(diào)用的模塊中聲明。

  110. /********************************************/
  111. /* 名稱:init_LineCtrl_Hardware                                                        */
  112. /* 用途:初始化串口等, 以保證相應(yīng)硬件工作                */
  113. /********************************************/

  114. void init_LineCtrl_Hardware(void)
  115. {
  116.         //初始化串口
  117.         init_SIO(B_19200);
  118.        
  119.         // 初始化相關(guān)中斷
  120.         IE = IE|EnUART_C;                                                                                // 允許 UART 中斷
  121. }

  122. /********************************************/
  123. /* 名稱:init_LineCtrl_Var                                                                        */
  124. /* 用途:初始化自身工作變量                                                                        */
  125. /********************************************/

  126. void init_LineCtrl_Var(void)
  127. {
  128.         unsigned char j;
  129.        
  130.         // 接收數(shù)據(jù)變量初始化
  131.         gi_ucSavePtr=0;
  132.         gi_ucGetPtr=0;
  133.        
  134.         gb_NewData = FALSE;
  135.         gb_StartRcv = FALSE;
  136.         gb_DataOK = FALSE;
  137.        
  138.         // 命令數(shù)據(jù)存放單元初始化
  139.         for(j=0;j<2;j++)
  140.         {
  141.                 ga_iPWM_Value[j] = FLOAT_PWM;
  142.                 ga_iRotateSpeed[j] = FLOAT_SPEED;
  143.                
  144.                 ga_uiRotateNum[j] = 0;
  145.         }
  146.        
  147.         g_uiPWM_Freq = INIT_PWM_FREQ;                                                        // 初始化時,將PWM的頻率置為 200Hz
  148.        
  149.         gb_M1CalOutValue = TRUE;                                                                        // 上電計算一次輸出,以保證電機(jī)的正常工作狀態(tài)
  150.         gb_M2CalOutValue = TRUE;                                                                        // 上電計算一次輸出,以保證電機(jī)的正常工作狀態(tài)
  151.                        
  152.         // PID 控制初始化
  153.         g_uiKp = DEFAULT_KP;                                                                                        // 加載默認(rèn)系數(shù)
  154.         g_uiTi = DEFAULT_TI;
  155.         g_uiTd = DEFAULT_TD;
  156.         g_uiPID_Ratio = DEFAULT_PID_RATIO;
  157.        
  158.         g_fKp = ((float)g_uiKp)/g_uiPID_Ratio;                        // 在此處計算好,減少每次 PID 的運算量
  159.         g_fTi = ((float)g_uiTi)/g_uiPID_Ratio;
  160.         g_fTd = ((float)g_uiTd)/g_uiPID_Ratio;

  161.         gb_EnablePID = FALSE;                                                                                        // 禁止調(diào)速 PID 功能
  162.        
  163.         gb_StartLineFollow = FALSE;
  164.         gb_StartStraightRun = FALSE;
  165.         g_ucDownSampCnt = 0;                                                                                        // 初始化時將脈沖采樣計數(shù)清為“0”       

  166. }

  167. /********************************************/
  168. /* 名稱:lineCtrl_proc                                                                                        */
  169. /* 用途:軌跡控制部分處理入口函數(shù),根據(jù)帶入        */
  170. /*       的消息作相應(yīng)處理。                                                                        */
  171. /*入口參數(shù):要處理的消息                                                                                */
  172. /********************************************/

  173. void lineCtrl_proc(unsigned char ucMessage)
  174. {
  175.         switch(ucMessage)
  176.         {
  177.                 case NEW_RCV_DATA:
  178.                 {
  179.                         rcvdata_proc();                                                                                        // 處理接收緩沖區(qū)數(shù)據(jù)                        

  180.                         if(gb_DataOK)
  181.                         {
  182.                                 gb_DataOK = FALSE;
  183.                                 getCommandData();                                                                        // 從數(shù)據(jù)幀中提取命令數(shù)據(jù)
  184.                         }                               
  185.                         break;
  186.                 }
  187.                
  188.                 case NEW_SAMP_DATA:
  189.                 {
  190.                         followLineControl();
  191.                         break;
  192.                 }
  193.                
  194.                 case         SAMPLE_DOWN_PULS:
  195.                 {
  196.                         straightRun();
  197.                         break;
  198.                 }
  199.                
  200.                 default:         break;
  201.                
  202.         }
  203. }



  204. /***************************** 模塊自用函數(shù) *******************************/

  205. // 以下函數(shù)只由模塊自身使用,別的模塊不用聲明。

  206. /********************************************/
  207. /* 名稱:init_SIO                                                                                                                */
  208. /* 用途:初始化串口,                                                                                                 */
  209. /* 參數(shù): 波特率 , 模式固定為:1                                                         */
  210. /*                 1 START 8 DATA 1 STOP                                                                 */
  211. /********************************************/

  212. void init_SIO(unsigned char baud)
  213. {
  214.         // 波特率表
  215.         unsigned char        code        TH_Baud[5]={B4800_C,B9600_C,B19200_C,B38400_C,B57600_C};
  216.        
  217.         AUXR = AUXR|SET_T1X12_C;
  218.         TH1 = TH_Baud[baud];
  219.         TL1 = TH_Baud[baud];
  220.         TR1 = TRUE;
  221.        
  222.         SCON        =        UART_MODE1_C|EN_RCV_C;        // 8 位模式( MODE 1)
  223. }

  224. /********************************************/
  225. /*名稱:        rcvdata_proc                                                                                                */
  226. /*用途: 檢測接收緩沖區(qū)數(shù)據(jù),                                                     */
  227. /*說明:        如果收到正確的數(shù)據(jù)幀則建立標(biāo)志                        */
  228. /********************************************/

  229. void rcvdata_proc(void)
  230. {
  231.         unsigned char i,j,k;
  232.        
  233.        
  234.         if(gb_StartRcv == FALSE)
  235.         {
  236.                 /*  檢測幀頭 0x55 0xAA LEN */
  237.                
  238.                 i=(gi_ucGetPtr-2)&(MaxRcvByte_C-1);                // 指向0x55
  239.                 j=(gi_ucGetPtr-1)&(MaxRcvByte_C-1);                // 指向0xAA
  240.                
  241.                 if((ga_ucRcvBuf[i]==0x55)&&(ga_ucRcvBuf[j]==0xAA))
  242.                 {
  243.                         i=gi_ucGetPtr;
  244.                                                
  245.                         if(ga_ucRcvBuf[i]<= (MaxRcvByte_C-1));
  246.                         {
  247.                                 //幀頭正確,啟動數(shù)據(jù)區(qū)接收
  248.                                 gb_StartRcv=TRUE;       
  249.                                 gc_ucDataLen=ga_ucRcvBuf[i];
  250.                                 gi_ucStartPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);
  251.                                 gi_ucEndPtr= (gi_ucGetPtr + gc_ucDataLen+1)&(MaxRcvByte_C-1);
  252.                         }
  253.                 }
  254.                 gi_ucGetPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);
  255.         }
  256.         else
  257.         {
  258.                 //開始接收數(shù)據(jù)處理
  259.                 if(gi_ucGetPtr==gi_ucEndPtr)
  260.                 {
  261.                         /* 數(shù)據(jù)幀接收完 */
  262.                         gb_StartRcv=FALSE;
  263.                        
  264.                         j=gi_ucStartPtr;       
  265.                         k= 0;
  266.                         for(i=0;i<gc_ucDataLen;i++)
  267.                         {
  268.                                 // 計算CS
  269.                                 k +=ga_ucRcvBuf[j];               
  270.                                 j=(j+1)&(MaxRcvByte_C-1);
  271.                         }
  272.                        
  273.                         // 取校驗和
  274.                         k +=ga_ucRcvBuf[j];                       
  275.                         if( k == 0xFF)
  276.                         {
  277.                                 // 數(shù)據(jù)校驗正確
  278.                                 gb_DataOK=TRUE;
  279.                         }
  280.                 }
  281.                 gi_ucGetPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);
  282.         }                       
  283. }

  284. /********************************************/
  285. /*名稱:        getCommandData                                                                                        */
  286. /*用途: 從接收緩沖區(qū)中取出數(shù)據(jù),                                                 */
  287. /*說明:        建立對應(yīng)標(biāo)志,通知相應(yīng)的處理                          */
  288. /********************************************/

  289. void getCommandData(void)
  290. {
  291.         union
  292.         {
  293.                 unsigned int all;
  294.                 unsigned char b[2];
  295.         }uitemp;
  296.        
  297.         union
  298.         {
  299.                 int        all;
  300.                 unsigned char b[2];
  301.         }itemp;
  302.        
  303.         unsigned char ucCommand,i,j,sum,n;
  304.         unsigned char idata *ucI_Ptr;
  305.         unsigned char xdata *ucX_Ptr;
  306.        
  307.         ucCommand = ga_ucRcvBuf[gi_ucStartPtr];                                // 取出數(shù)據(jù)幀中的命令字
  308.        
  309.         switch (ucCommand)
  310.         {
  311.                 case PWM_MODE:
  312.                 {
  313.                         // 處理PWM開環(huán)控制命令
  314.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C - 1);                        // 指向電機(jī) 1 數(shù)據(jù)區(qū)
  315.                        
  316.                         for(j=0;j<2;j++)                                                                                                                                // 循環(huán) 2 次完成兩個電機(jī)的數(shù)據(jù)提取
  317.                         {
  318.                                 itemp.b[1] = ga_ucRcvBuf[i];                                                                                // 注意,在C51中,整形等多字節(jié)數(shù)據(jù)在內(nèi)存中是先高后低存放!
  319.                                 i =(i+1)&(MaxRcvByte_C-1);
  320.                                 itemp.b[0] = ga_ucRcvBuf[i];                               
  321.                                
  322.                                 if(itemp.all < (-1000))                                                                                                // PWM值合法性處理
  323.                                 {
  324.                                         itemp.all = -1000;
  325.                                 }
  326.                                 if(itemp.all > 1002)
  327.                                 {
  328.                                         itemp.all = 1000;
  329.                                 }                               
  330.                                 ga_iPWM_Value[j] = itemp.all;                                                                        // 得到 PWM 值

  331.                                 // 行走脈沖計數(shù)數(shù)據(jù)處理
  332.                                 i =(i+1)&(MaxRcvByte_C-1);
  333.                                 uitemp.b[1] = ga_ucRcvBuf[i];
  334.                                 i =(i+1)&(MaxRcvByte_C-1);
  335.                                 uitemp.b[0] = ga_ucRcvBuf[i];
  336.                                 ga_uiRotateNum[j] = uitemp.all;                                                                                                        // 得到轉(zhuǎn)動脈沖計數(shù)值
  337.                                
  338.                                 ga_uiStopCnt[j] = calStopCntValue(ga_uiRotateNum[j],j);        // 計算出停止計數(shù)值
  339.                                
  340.                                 ga_cMotorStat[j] = setMotorStat(ga_iPWM_Value[j]);                // 根據(jù)命令設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志
  341.                                
  342.                                 i = (gi_ucStartPtr + 1+4)&(MaxRcvByte_C - 1);                                        // 指向電機(jī) 2 數(shù)據(jù)區(qū)
  343.                         }
  344.                        
  345.                         gb_EnablePID = FALSE;                                                                                                                                                // 收到PWM控制命令后,禁止 PID 控制
  346.                                                        
  347.                         gb_M1CalOutValue =TRUE;                                                                                                                                        // 建立計算電機(jī)控制輸出值標(biāo)志,因為PWM數(shù)據(jù)變化
  348.                         gb_M2CalOutValue =TRUE;       
  349.                        
  350.                         break;
  351.                 }
  352.                
  353.                 case SPEED_MODE:
  354.                 {
  355.                         // 處理轉(zhuǎn)速閉環(huán)控制命令
  356.                         i = (gi_ucStartPtr + 1 )&(MaxRcvByte_C-1);                                                        // 指向電機(jī) 1 數(shù)據(jù)區(qū)
  357.                        
  358.                         for(j=0;j<2;j++)                                                                                                                                                                // 循環(huán) 2 次完成兩個電機(jī)的數(shù)據(jù)提取
  359.                         {
  360.                                 itemp.b[1] = ga_ucRcvBuf[i];                                                                // 注意,在C51中,整形等多字節(jié)數(shù)據(jù)在內(nèi)存中是先高后低存放!
  361.                                 i =(i+1)&(MaxRcvByte_C-1);
  362.                                 itemp.b[0] = ga_ucRcvBuf[i];                               

  363.                                 if(itemp.all < (-10000))                                                                                // 轉(zhuǎn)速數(shù)據(jù)合法性處理
  364.                                 {
  365.                                         itemp.all = -10000;
  366.                                 }
  367.                                 if(itemp.all > 10002)
  368.                                 {
  369.                                         itemp.all = 10000;
  370.                                 }
  371.                                 ga_iRotateSpeed[j] = itemp.all;                                                                // 得到轉(zhuǎn)速

  372.                                 // 行走脈沖數(shù)據(jù)處理       
  373.                                 i =(i+1)&(MaxRcvByte_C-1);
  374.                                 uitemp.b[1] = ga_ucRcvBuf[i];
  375.                                 i =(i+1)&(MaxRcvByte_C-1);
  376.                                 uitemp.b[0] = ga_ucRcvBuf[i];
  377.                                 ga_uiRotateNum[j] = uitemp.all;                                                                                                                // 得到轉(zhuǎn)動脈沖計數(shù)值
  378.                                
  379.                                 ga_uiStopCnt[j] = calStopCntValue(ga_uiRotateNum[j],j);        // 計算出停止計數(shù)值
  380.        
  381.                                 ga_cMotorStat[j] = setMotorStat(ga_iRotateSpeed[j]);                // 根據(jù)命令設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志
  382.                        
  383.                                 i = (gi_ucStartPtr + 1 + 4)&(MaxRcvByte_C-1);                                                // 指向電機(jī) 2 數(shù)據(jù)區(qū)
  384.                         }
  385.                        
  386.                         if(gb_EnablePID)
  387.                         {
  388.                                 // 已啟動PID控制       
  389.                         }
  390.                         else
  391.                         {
  392.                                 // 啟動 PID 控制
  393.                                 gb_EnablePID = TRUE;
  394.                                 gac_ucGetSpeedCnt[MOTOR1] = 3;                                                                // 電機(jī) 1 采集3次速度數(shù)據(jù)后才允許計算PID
  395.                                 gac_ucGetSpeedCnt[MOTOR2] = 3;                                                                // 電機(jī) 2 采集3次速度數(shù)據(jù)后才允許計算PID
  396.                                 ga_iPWM_Value[MOTOR1] = INI_PWM_VALUE;                                // 電機(jī) 1 輸出PWM初值,啟動電機(jī)
  397.                                 ga_iPWM_Value[MOTOR2] = INI_PWM_VALUE;                                // 電機(jī) 2 輸出PWM初值,啟動電機(jī)
  398.                                 gb_M1CalOutValue = TRUE;                                                                                        // 通知輸出計算
  399.                                 gb_M2CalOutValue = TRUE;
  400.                         }
  401.                                                
  402.                         break;
  403.                 }
  404.                
  405.                 case SET_PARA:
  406.                 {
  407.                         // 處理參數(shù)設(shè)置命令
  408.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C-1);               
  409.                         uitemp.b[1] = ga_ucRcvBuf[i];                                                                // 注意,在C51中,整形等多字節(jié)數(shù)據(jù)在內(nèi)存中是先高后低存放!
  410.                         i =(i+1)&(MaxRcvByte_C-1);
  411.                         uitemp.b[0] = ga_ucRcvBuf[i];
  412.                         g_uiPWM_Freq = uitemp.all;
  413.                         if(g_uiPWM_Freq <200)                                                // 數(shù)據(jù)合法性處理
  414.                         {
  415.                                 g_uiPWM_Freq = 200;
  416.                         }
  417.                         if(g_uiPWM_Freq >2000)
  418.                         {
  419.                                 g_uiPWM_Freq = 2000;
  420.                         }
  421.                         gb_M1CalOutValue =TRUE;                                        // 建立計算電機(jī)控制輸出值標(biāo)志,因為PWM的頻率變了。
  422.                         gb_M2CalOutValue =TRUE;
  423.                        
  424.                         // 取 PID 參數(shù)
  425.                         i =(i+1)&(MaxRcvByte_C-1);
  426.                         uitemp.b[1] = ga_ucRcvBuf[i];
  427.                         i =(i+1)&(MaxRcvByte_C-1);
  428.                         uitemp.b[0] = ga_ucRcvBuf[i];
  429.                         g_uiKp = uitemp.all;
  430.                        
  431.                         i =(i+1)&(MaxRcvByte_C-1);
  432.                         uitemp.b[1] = ga_ucRcvBuf[i];
  433.                         i =(i+1)&(MaxRcvByte_C-1);
  434.                         uitemp.b[0] = ga_ucRcvBuf[i];
  435.                         g_uiTi = uitemp.all;

  436.                         i =(i+1)&(MaxRcvByte_C-1);
  437.                         uitemp.b[1] = ga_ucRcvBuf[i];
  438.                         i =(i+1)&(MaxRcvByte_C-1);
  439.                         uitemp.b[0] = ga_ucRcvBuf[i];                       
  440.                         g_uiTd = uitemp.all;
  441.                        
  442.                         i =(i+1)&(MaxRcvByte_C-1);
  443.                         uitemp.b[1] = ga_ucRcvBuf[i];
  444.                         i =(i+1)&(MaxRcvByte_C-1);
  445.                         uitemp.b[0] = ga_ucRcvBuf[i];
  446.                         if(uitemp.all >0)
  447.                         {
  448.                                 g_uiPID_Ratio = uitemp.all;
  449.                         }
  450.                        
  451.                         g_fKp = ((float)g_uiKp)/g_uiPID_Ratio;                        // 在此處計算好,減少每次 PID 的運算量
  452.                         g_fTi = ((float)g_uiTi)/g_uiPID_Ratio;
  453.                         g_fTd = ((float)g_uiTd)/g_uiPID_Ratio;
  454.                                                
  455.                         break;
  456.                 }
  457.                
  458.                 case READ_SPEED:
  459.                 {
  460.                         // 讀取轉(zhuǎn)速命令處理
  461.                         ga_ucTxdBuf[0] = 0xAA;
  462.                         ga_ucTxdBuf[1] = 0x55;                                                // 幀頭
  463.                         ga_ucTxdBuf[2] = 0x05;                                                // 幀長
  464.                         ga_ucTxdBuf[3] = 0x80+READ_SPEED;        // 返回命令
  465.                         sum = ga_ucTxdBuf[3];
  466.                         i=4;
  467.                         for(j=0;j<2;j++)                                                                        // 循環(huán) 2 次,返回 2 個電機(jī)的轉(zhuǎn)速
  468.                         {                                                               
  469.                                 itemp.all = ga_iCurSpeed[j];
  470.                                 ga_ucTxdBuf[i] = itemp.b[1];                // 返回轉(zhuǎn)速值,先低后高
  471.                                 sum += ga_ucTxdBuf[i];
  472.                                 i++;
  473.                                 ga_ucTxdBuf[i] = itemp.b[0];
  474.                                 sum += ga_ucTxdBuf[i];
  475.                                 i++;
  476.                         }
  477.                        
  478.                         ga_ucTxdBuf[i] = ~sum;                                                // 校驗和
  479.                                
  480.                         gc_ucTxdCnt = 9;                                                                        // 發(fā)送字節(jié)計數(shù)
  481.                         gi_ucTxdPtr = 0;                                                                        // 發(fā)送指針
  482.                         SBUF = ga_ucTxdBuf[0];                                                // 啟動發(fā)送
  483.                        
  484.                         break;
  485.                 }
  486.                
  487.                 case FOLLOW_LINE_CTRL:
  488.                 {
  489.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C-1);
  490.                         switch (ga_ucRcvBuf[i])
  491.                         {
  492.                                 case FOLLOW_LINE:
  493.                                 {
  494.                                         break;
  495.                                 }
  496.                                
  497.                                 case STRAIGHT_RUN:
  498.                                 {
  499.                                         gb_StartStraightRun =  TRUE;
  500.                                        
  501.                                         gc_uiPCA_OverCnt = 0;
  502.                                         g_ucDownSampCnt = 0;
  503.                                                                                
  504.                                         //gb_EnSpeed_Hi_Low = TRUE;                                // 啟動速度上下限控制
  505.                                        
  506.                                         g_iInit_PWM = INI_PWM_VALUE;                 // 啟動電機(jī)
  507.                                         ga_iPWM_Value[MOTOR1] = g_iInit_PWM;
  508.                                         ga_cMotorStat[MOTOR1] = setMotorStat(ga_iPWM_Value[MOTOR1]);                // 設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志
  509.                                         ga_iPWM_Value[MOTOR2] = g_iInit_PWM;
  510.                                         ga_cMotorStat[MOTOR2] = setMotorStat(ga_iPWM_Value[MOTOR2]);                // 設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志
  511.                                        
  512.                                         m_iDiffPWM = 0;                                       

  513.                                         gb_M1CalOutValue =TRUE;                                        // 建立計算電機(jī)控制輸出值標(biāo)志,
  514.                                         gb_M2CalOutValue =TRUE;       
  515.                                                                                
  516.                                         m_iError_Int = 0;                                                                // 初始化PID計算數(shù)據(jù)
  517.                                         m_iErrorOld = 0;
  518.                                        
  519.                                         break;
  520.                                 }
  521.                                
  522.                                 case STOP_RUN:
  523.                                 {
  524.                                         ga_iPWM_Value[MOTOR1] = BRAKE_PWM;
  525.                                         ga_cMotorStat[MOTOR1] = setMotorStat(ga_iPWM_Value[MOTOR1]);                // 設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志
  526.                                         ga_iPWM_Value[MOTOR2] = BRAKE_PWM;
  527.                                         ga_cMotorStat[MOTOR2] = setMotorStat(ga_iPWM_Value[MOTOR2]);                // 設(shè)置電機(jī)運轉(zhuǎn)標(biāo)志

  528.                                         gb_EnSpeed_Hi_Low = FALSE;
  529.                                         gb_StartStraightRun = FALSE;
  530.                                         gb_StartLineFollow = FALSE;

  531.                                         gb_M1CalOutValue =TRUE;                                        // 建立計算電機(jī)控制輸出值標(biāo)志,
  532.                                         gb_M2CalOutValue =TRUE;       
  533.                                         break;
  534.                                 }
  535.                                
  536.                                 default: break;
  537.                         }
  538.                        
  539.                         break;
  540.                 }
  541.                
  542.                 case READ_MEMORY:
  543.                 {
  544.                         // 讀內(nèi)存數(shù)據(jù)處理
  545.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C-1);               
  546.                         uitemp.b[1] = ga_ucRcvBuf[i];                                                                // 取讀數(shù)據(jù)地址
  547.                         i =(i+1)&(MaxRcvByte_C-1);
  548.                         uitemp.b[0] = ga_ucRcvBuf[i];
  549.                         i =(i+1)&(MaxRcvByte_C-1);
  550.                         n = ga_ucRcvBuf[i];                                                                                                        // 取讀數(shù)據(jù)長度
  551.                         if(n>(MaxTxdByte_C - 4))
  552.                         {
  553.                                 n = (MaxTxdByte_C - 4);                                                                                // 受發(fā)送緩沖區(qū)限制,減 4 個字節(jié)對應(yīng): 命令 地址 長度
  554.                         }
  555.                        
  556.                         ga_ucTxdBuf[0] = 0xAA;
  557.                         ga_ucTxdBuf[1] = 0x55;                                                                                        // 幀頭
  558.                         ga_ucTxdBuf[2] = n + 4;                                                                                        // 幀長
  559.                         ga_ucTxdBuf[3] = 0x80+READ_MEMORY;                                        // 返回命令
  560.                         ga_ucTxdBuf[4] = uitemp.b[1];                                                                // 將要讀數(shù)據(jù)的地址和長度返回
  561.                         ga_ucTxdBuf[5] = uitemp.b[0];
  562.                         ga_ucTxdBuf[6] = n;
  563.                         sum = ga_ucTxdBuf[3]+ga_ucTxdBuf[4]+ga_ucTxdBuf[5]+ga_ucTxdBuf[6];

  564.                         i = 7;                                                                                                                                                        // 數(shù)據(jù)區(qū)起始指針
  565.                        
  566.                         if(uitemp.b[0] == 0)
  567.                         {
  568.                                 ucI_Ptr = uitemp.b[1];                                                                                // 如果高地址為 0 ,則讀IDATA內(nèi)容
  569.                                 for(j=0;j<n;j++)
  570.                                 {
  571.                                         ga_ucTxdBuf[i] = *ucI_Ptr;
  572.                                         i++;
  573.                                         ucI_Ptr++;
  574.                                 }
  575.                         }
  576.                         else
  577.                         {
  578.                                 ucX_Ptr = uitemp.b[1];                                                                                // 如果高地址不為“0”,則讀XDATA內(nèi)容,因為只有256字節(jié)的XDATA,所以只取低字節(jié)。
  579.                                 for(j=0;j<n;j++)
  580.                                 {
  581.                                         ga_ucTxdBuf[i] = *ucX_Ptr;
  582.                                         i++;
  583.                                         ucX_Ptr++;
  584.                                 }
  585.                         }

  586.                         ga_ucTxdBuf[i] = ~sum;                                                // 校驗和
  587.                                
  588.                         gc_ucTxdCnt = i+1;                                                                // 發(fā)送字節(jié)計數(shù)
  589.                         gi_ucTxdPtr = 0;                                                                        // 發(fā)送指針
  590.                         SBUF = ga_ucTxdBuf[0];                                                // 啟動發(fā)送
  591.                        
  592.                         break;
  593.                 }
  594.                
  595.                 default:
  596.                 {
  597.                         break;
  598.                 }
  599.         }
  600. }

  601. /********************************************/
  602. /*名稱:        calStopCntValue                                                                                        */
  603. /*用途: 根據(jù)得到的行走脈沖數(shù)計算出停止點          */
  604. /********************************************/

  605. unsigned int calStopCntValue(unsigned int uiRun_Num,unsigned char No)
  606. {
  607.         unsigned int cnt1;
  608.        
  609.         if(uiRun_Num !=0)
  610.         {
  611.                 cnt1 = gac_uiPulsCnt[No];
  612.                 while(cnt1 != gac_uiPulsCnt[No])
  613.                 {
  614.                         cnt1 = gac_uiPulsCnt[No];                                                                        // 防護(hù)處理,避免正好在PCA中斷時取數(shù)
  615.                 }
  616.                
  617.                 cnt1 = cnt1 + uiRun_Num;                                                                                // 得到停止的判斷點
  618.         }
  619.         else
  620.         {
  621.                 cnt1 = 65535;                                                                                                                                // g_uiRotateNum =0;設(shè)置為最大值,永不停止
  622.         }       

  623.         return(cnt1);
  624. }

  625. /*********************************************/
  626. /*名稱:        setMotorStat                                                                                                 */
  627. /*用途: 根據(jù)命令中的PWM值或轉(zhuǎn)速值設(shè)置電機(jī)狀態(tài)*/
  628. /*********************************************/
  629. char setMotorStat(int iRunValue)
  630. {
  631.         char stat;
  632.        
  633.         switch (iRunValue)
  634.         {
  635.                 case 0:
  636.                 {
  637.                         stat = IN_STOP;
  638.                         break;
  639.                 }
  640.                
  641.                 case FLOAT_PWM:
  642.                 {
  643.                         stat = IN_STOP;
  644.                         break;
  645.                 }
  646.                
  647.                 case BRAKE_PWM:
  648.                 {
  649.                         stat = IN_STOP;
  650.                         break;
  651.                 }
  652.                
  653.                 case FLOAT_SPEED:
  654.                 {
  655.                         stat = IN_STOP;
  656.                         break;
  657.                 }
  658.                
  659.                 case BRAKE_SPEED:
  660.                 {
  661.                         stat = IN_STOP;
  662.                         break;
  663.                 }
  664.                
  665.                 default:
  666.                 {
  667.                         if(iRunValue >0)
  668.                         {
  669.                                 stat = IN_FORWARD;
  670.                         }
  671.                         else
  672.                         {
  673.                                 stat = IN_BACKWARD;
  674.                         }
  675.                         break;
  676.                 }               
  677.         }
  678.         return(stat);
  679. }

  680. /*********************************************/
  681. /*名稱:        followLineControl                                                                                 */
  682. /*用途: 根據(jù)采樣輸出值g_cSampleOut 控制尋跡         */
  683. /*********************************************/

  684. ……………………

  685. …………限于本文篇幅 余下代碼請從51黑下載附件…………
復(fù)制代碼

所有資料51hei提供下載:
循跡小車詳細(xì)資料包括電路圖、軟件編程.rar (238.93 KB, 下載次數(shù): 150)


評分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏8 分享淘帖 頂2 踩
回復(fù)

使用道具 舉報

沙發(fā)
ID:539016 發(fā)表于 2020-4-28 00:14 | 只看該作者
好東西,值得學(xué)習(xí)
回復(fù)

使用道具 舉報

板凳
ID:539016 發(fā)表于 2020-4-28 00:14 | 只看該作者
好東西,值得學(xué)習(xí)
回復(fù)

使用道具 舉報

地板
ID:958455 發(fā)表于 2021-9-7 20:35 | 只看該作者
最近學(xué)校也在組織循跡小車的比賽,頂
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表