找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3749|回复: 3
打印 上一主题 下一主题
收起左侧

单片机循迹小车完整资料 包括注释详细的程序和硬件设计

  [复制链接]
跳转到指定楼层
楼主
资料大家看吧!截图截得不全。


单片机源程序如下:
  1. /***************************************************/
  2. /*                                        寻迹小车 FollowMe 项目                                                                 */
  3. /*          —— 主控程序轨迹控制模块                                                                 */
  4. /*              之程序部分                                                                                            */
  5. /*                                            20060905                           */
  6. /*              By DingQi                          */
  7. /***************************************************/
  8. // 注:以下文档的 TAB 为 2 个字符!

  9. /*------------------------------------------------------------------------
  10. 此程序为"寻迹小车FollowMe"项目中单板控制模式的走轨迹控制部分,附带相关调试功能。
  11. 要实现:

  12.         1)接收各种调试命令,并解析;
  13.         2)通过串口反馈所需的调试信息;
  14.         3)获取轨迹采样部分处理后的信息,产生对策,发给电机驱动部分实施。
  15.        
  16.         根据上述要实现的功能,通讯部分归此模块管理。
  17.        
  18.         第一步先将原来的电机驱动功能整合到一个MCU中,将原来的通讯功能从电机驱动模块中分解出来。

  19.        
  20. 目前的电机控制由串口实现,通讯协议定义如下:
  21. 1、帧格式:
  22.         帧头(2字节)  帧长(1字节) 命令字(1字节) 数据区(N字节)校验和(1字节)

  23. 其中:
  24.         帧头 —— 0x55  0xAA       
  25.         帧长 ——  命令字 + 数据区的长度
  26.         命令字 ——         0x01 :电机转动控制参数,开环模式,电机的PWM值、转动持续脉冲数;
  27.                                                    0x02 :电机转动控制参数,闭环模式,电机的转速、转动持续脉冲数;
  28.                                                          0x03 :电机工作参数,PWM频率、PID参数
  29.         数据区 —— 命令01:电机1数据(2字节PWM值,2字节转动持续脉冲数)电机2数据(2字节PWM值,2字节转动持续脉冲数),共 8字节;
  30.                                                 命令02:电机1数据(2字节转速值,2字节转动持续脉冲数)电机2数据(2字节转速值,2字节转动持续脉冲数),共 8字节;
  31.                                                 命令03:2字节PWM频率,2字节比例系数,2字节积分系数,2字节微分系数,2字节PID系数的分母, 共10字节,两个电机驱动器相同;
  32.         校验和 —— 从命令字开始到数据区结束所有字节的算术和的反码,取低字节。

  33. 上述数据中,PWM值,速度值、PWM频率、PID系数等定义如下:
  34.         PWM值 ——         2字节有符号数,正对应正转,负对应反转,数值为占空比的百分数,
  35.                                                 取值范围:- 1000 ——  +1000, 对应 0.1% ~ 100%;1001为电机“惰行”,1002为“刹车”;
  36.        
  37.         转动持续脉冲数 ——  2字节无符号数,0 表示连续转动;
  38.        
  39.         转速值 —— 2字节有符号数,正对应正转,负对应反转,单位为:0.1转/每分钟;
  40.                                                 取值范围:- 10000~ +10000,10001为电机“惰行”,10002为“刹车”;
  41.        
  42.         PWM频率 —— 2字节整数,单位Hz,取值范围:200 – 2000;
  43.        
  44.         PID系数 —— 均为 2字节无符号数;
  45.         PID系数分母 ——  2字节无符号数,为避免使用浮点数而增加了此参数,实际作用的PID系数为上述值除此值;
  46.                                                                         如:比例系数为190 ,PID分母为200,实际比例系数为0.95。

  47. 以上所有2字节的数据均为先低后高。
  48. 暂时不设计应答帧,因为一帧命令包含了两个电机的驱动数据。

  49. 通讯数据格式为:19200  8  N  1。
  50. 此时,一帧数据约占 7ms。

  51. 为了调试,添加转速读取命令 0x04 ,原来的读转速命令是分开实现的:
  52.         0x55  0xAA  0x02(帧长) 0x04 (读转速命令)  电机序号(1字节)校验和(1字节)
  53.        
  54.         对应的返回帧为:
  55.         0xAA 0x55  0x04(帧长) 0x84 (转速值返回) 电机序号(1字节)转速(2字节)校验和(1字节)
  56.        

  57. 因为合并到一个MCU中了,所以对应将协议改为:
  58.         0x55  0xAA  0x01(帧长) 0x04 (读转速命令) 校验和(1字节)
  59.        
  60.         对应的返回帧为:
  61.         0xAA 0x55  0x05(帧长) 0x84 (转速值返回) 电机1转速(2字节)电机2转速(2字节)校验和(1字节)


  62. 因为集成了走轨迹控制功能,所以要添加一个控制命令,使小车启动,进入到走轨迹状态或结束走轨迹的状态。
  63.         走轨迹控制命令: 0x05 .   命令参数 ——  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. 为了调试方便,增加一个内存数据读取命令:
  69.         内存数据读取命令:0x06
  70.         命令帧为:
  71.         0x55 0xAA 0x04 0x06 读数据低地址 读数据高地址 读数据长度 CS
  72.         返回帧为:
  73.         0x55 0xAA 帧长 0x86 读数据低地址 读数据高地址 读数据长度 数据N字节 CS

  74. ------------------------------------------------------------------------
  75.         因为用双轮驱动的小车由于电机等驱动元素的差异,导致走直线成为问题,故在此尝试
  76. 用这两个简易的码盘来实现直线行走。               

  77.         因为码盘的分辨率太低,所以直接用脉冲计数方式控制似乎有些不够精确,所以考虑用
  78. 两路脉冲的触发时间差别来控制。
  79.         实质上这是一种周期测量的变换,因为不能保证每个脉冲周期是真实的(即由于干扰会
  80. 导致某个周期变大或变小),所以用累计的方式消除之。

  81.         具体的方式如下:
  82.         设立两个 4 字节的计数器,2字节纪录PCA的溢出值,2字节纪录 PCA 的计时器值,这样
  83. 构成了一个对PCA计数脉冲(Fosc/2)的长整形计数器,可纪录约 388秒(2^32/11.0592M),
  84. 这个值一般可以应付大部分比赛项目中的需要。

  85.         将这个时间计数器作为两个轮子采样脉冲(只取下降沿)的时标,根据两者的差值确定两轮
  86. 的驱动差。也就是要保证两个轮子对应脉冲到达的时间相同,这样,如果没有漏计或打滑,两
  87. 个轮子行走的距离应当是一样的,轨迹也应当是直线!

  88.         这个控制也可以考虑使用PID控制,其控制的量为两个计数器的差值,定值为“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);                                                                                        // 接收数据帧
  103. void getCommandData(void);                                                                                // 从数据帧中取出命令数据
  104. unsigned int calStopCntValue(unsigned int uiRun_Num,unsigned char No); // 根据命令中的行走脉冲数计算出停止点
  105. char setMotorStat(int iRunValue);                                                        // 根据命令中的PWM值或转速设置电机状态
  106. void followLineControl(void);                                                                        // 走轨迹控制
  107. void straightRun(void);                                                                                                // 走直线控制

  108. /******************************** 外部调用函数 *********************************/

  109. // 以下函数为公共函数,需要在调用的模块中声明。

  110. /********************************************/
  111. /* 名称:init_LineCtrl_Hardware                                                        */
  112. /* 用途:初始化串口等, 以保证相应硬件工作                */
  113. /********************************************/

  114. void init_LineCtrl_Hardware(void)
  115. {
  116.         //初始化串口
  117.         init_SIO(B_19200);
  118.        
  119.         // 初始化相关中断
  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.         // 接收数据变量初始化
  131.         gi_ucSavePtr=0;
  132.         gi_ucGetPtr=0;
  133.        
  134.         gb_NewData = FALSE;
  135.         gb_StartRcv = FALSE;
  136.         gb_DataOK = FALSE;
  137.        
  138.         // 命令数据存放单元初始化
  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;                                                                        // 上电计算一次输出,以保证电机的正常工作状态
  150.         gb_M2CalOutValue = TRUE;                                                                        // 上电计算一次输出,以保证电机的正常工作状态
  151.                        
  152.         // PID 控制初始化
  153.         g_uiKp = DEFAULT_KP;                                                                                        // 加载默认系数
  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;                                                                                        // 禁止调速 PID 功能
  162.        
  163.         gb_StartLineFollow = FALSE;
  164.         gb_StartStraightRun = FALSE;
  165.         g_ucDownSampCnt = 0;                                                                                        // 初始化时将脉冲采样计数清为“0”       

  166. }

  167. /********************************************/
  168. /* 名称:lineCtrl_proc                                                                                        */
  169. /* 用途:轨迹控制部分处理入口函数,根据带入        */
  170. /*       的消息作相应处理。                                                                        */
  171. /*入口参数:要处理的消息                                                                                */
  172. /********************************************/

  173. void lineCtrl_proc(unsigned char ucMessage)
  174. {
  175.         switch(ucMessage)
  176.         {
  177.                 case NEW_RCV_DATA:
  178.                 {
  179.                         rcvdata_proc();                                                                                        // 处理接收缓冲区数据                        

  180.                         if(gb_DataOK)
  181.                         {
  182.                                 gb_DataOK = FALSE;
  183.                                 getCommandData();                                                                        // 从数据帧中提取命令数据
  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. /***************************** 模块自用函数 *******************************/

  205. // 以下函数只由模块自身使用,别的模块不用声明。

  206. /********************************************/
  207. /* 名称:init_SIO                                                                                                                */
  208. /* 用途:初始化串口,                                                                                                 */
  209. /* 参数: 波特率 , 模式固定为: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. /*用途: 检测接收缓冲区数据,                                                     */
  227. /*说明:        如果收到正确的数据帧则建立标志                        */
  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.                                 //帧头正确,启动数据区接收
  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.                 //开始接收数据处理
  259.                 if(gi_ucGetPtr==gi_ucEndPtr)
  260.                 {
  261.                         /* 数据帧接收完 */
  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.                                 // 数据校验正确
  278.                                 gb_DataOK=TRUE;
  279.                         }
  280.                 }
  281.                 gi_ucGetPtr=(gi_ucGetPtr+1)&(MaxRcvByte_C-1);
  282.         }                       
  283. }

  284. /********************************************/
  285. /*名称:        getCommandData                                                                                        */
  286. /*用途: 从接收缓冲区中取出数据,                                                 */
  287. /*说明:        建立对应标志,通知相应的处理                          */
  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];                                // 取出数据帧中的命令字
  308.        
  309.         switch (ucCommand)
  310.         {
  311.                 case PWM_MODE:
  312.                 {
  313.                         // 处理PWM开环控制命令
  314.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C - 1);                        // 指向电机 1 数据区
  315.                        
  316.                         for(j=0;j<2;j++)                                                                                                                                // 循环 2 次完成两个电机的数据提取
  317.                         {
  318.                                 itemp.b[1] = ga_ucRcvBuf[i];                                                                                // 注意,在C51中,整形等多字节数据在内存中是先高后低存放!
  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.                                 // 行走脉冲计数数据处理
  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;                                                                                                        // 得到转动脉冲计数值
  337.                                
  338.                                 ga_uiStopCnt[j] = calStopCntValue(ga_uiRotateNum[j],j);        // 计算出停止计数值
  339.                                
  340.                                 ga_cMotorStat[j] = setMotorStat(ga_iPWM_Value[j]);                // 根据命令设置电机运转标志
  341.                                
  342.                                 i = (gi_ucStartPtr + 1+4)&(MaxRcvByte_C - 1);                                        // 指向电机 2 数据区
  343.                         }
  344.                        
  345.                         gb_EnablePID = FALSE;                                                                                                                                                // 收到PWM控制命令后,禁止 PID 控制
  346.                                                        
  347.                         gb_M1CalOutValue =TRUE;                                                                                                                                        // 建立计算电机控制输出值标志,因为PWM数据变化
  348.                         gb_M2CalOutValue =TRUE;       
  349.                        
  350.                         break;
  351.                 }
  352.                
  353.                 case SPEED_MODE:
  354.                 {
  355.                         // 处理转速闭环控制命令
  356.                         i = (gi_ucStartPtr + 1 )&(MaxRcvByte_C-1);                                                        // 指向电机 1 数据区
  357.                        
  358.                         for(j=0;j<2;j++)                                                                                                                                                                // 循环 2 次完成两个电机的数据提取
  359.                         {
  360.                                 itemp.b[1] = ga_ucRcvBuf[i];                                                                // 注意,在C51中,整形等多字节数据在内存中是先高后低存放!
  361.                                 i =(i+1)&(MaxRcvByte_C-1);
  362.                                 itemp.b[0] = ga_ucRcvBuf[i];                               

  363.                                 if(itemp.all < (-10000))                                                                                // 转速数据合法性处理
  364.                                 {
  365.                                         itemp.all = -10000;
  366.                                 }
  367.                                 if(itemp.all > 10002)
  368.                                 {
  369.                                         itemp.all = 10000;
  370.                                 }
  371.                                 ga_iRotateSpeed[j] = itemp.all;                                                                // 得到转速

  372.                                 // 行走脉冲数据处理       
  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;                                                                                                                // 得到转动脉冲计数值
  378.                                
  379.                                 ga_uiStopCnt[j] = calStopCntValue(ga_uiRotateNum[j],j);        // 计算出停止计数值
  380.        
  381.                                 ga_cMotorStat[j] = setMotorStat(ga_iRotateSpeed[j]);                // 根据命令设置电机运转标志
  382.                        
  383.                                 i = (gi_ucStartPtr + 1 + 4)&(MaxRcvByte_C-1);                                                // 指向电机 2 数据区
  384.                         }
  385.                        
  386.                         if(gb_EnablePID)
  387.                         {
  388.                                 // 已启动PID控制       
  389.                         }
  390.                         else
  391.                         {
  392.                                 // 启动 PID 控制
  393.                                 gb_EnablePID = TRUE;
  394.                                 gac_ucGetSpeedCnt[MOTOR1] = 3;                                                                // 电机 1 采集3次速度数据后才允许计算PID
  395.                                 gac_ucGetSpeedCnt[MOTOR2] = 3;                                                                // 电机 2 采集3次速度数据后才允许计算PID
  396.                                 ga_iPWM_Value[MOTOR1] = INI_PWM_VALUE;                                // 电机 1 输出PWM初值,启动电机
  397.                                 ga_iPWM_Value[MOTOR2] = INI_PWM_VALUE;                                // 电机 2 输出PWM初值,启动电机
  398.                                 gb_M1CalOutValue = TRUE;                                                                                        // 通知输出计算
  399.                                 gb_M2CalOutValue = TRUE;
  400.                         }
  401.                                                
  402.                         break;
  403.                 }
  404.                
  405.                 case SET_PARA:
  406.                 {
  407.                         // 处理参数设置命令
  408.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C-1);               
  409.                         uitemp.b[1] = ga_ucRcvBuf[i];                                                                // 注意,在C51中,整形等多字节数据在内存中是先高后低存放!
  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)                                                // 数据合法性处理
  414.                         {
  415.                                 g_uiPWM_Freq = 200;
  416.                         }
  417.                         if(g_uiPWM_Freq >2000)
  418.                         {
  419.                                 g_uiPWM_Freq = 2000;
  420.                         }
  421.                         gb_M1CalOutValue =TRUE;                                        // 建立计算电机控制输出值标志,因为PWM的频率变了。
  422.                         gb_M2CalOutValue =TRUE;
  423.                        
  424.                         // 取 PID 参数
  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.                         // 读取转速命令处理
  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++)                                                                        // 循环 2 次,返回 2 个电机的转速
  468.                         {                                                               
  469.                                 itemp.all = ga_iCurSpeed[j];
  470.                                 ga_ucTxdBuf[i] = itemp.b[1];                // 返回转速值,先低后高
  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;                                                                        // 发送字节计数
  481.                         gi_ucTxdPtr = 0;                                                                        // 发送指针
  482.                         SBUF = ga_ucTxdBuf[0];                                                // 启动发送
  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;                 // 启动电机
  507.                                         ga_iPWM_Value[MOTOR1] = g_iInit_PWM;
  508.                                         ga_cMotorStat[MOTOR1] = setMotorStat(ga_iPWM_Value[MOTOR1]);                // 设置电机运转标志
  509.                                         ga_iPWM_Value[MOTOR2] = g_iInit_PWM;
  510.                                         ga_cMotorStat[MOTOR2] = setMotorStat(ga_iPWM_Value[MOTOR2]);                // 设置电机运转标志
  511.                                        
  512.                                         m_iDiffPWM = 0;                                       

  513.                                         gb_M1CalOutValue =TRUE;                                        // 建立计算电机控制输出值标志,
  514.                                         gb_M2CalOutValue =TRUE;       
  515.                                                                                
  516.                                         m_iError_Int = 0;                                                                // 初始化PID计算数据
  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]);                // 设置电机运转标志
  526.                                         ga_iPWM_Value[MOTOR2] = BRAKE_PWM;
  527.                                         ga_cMotorStat[MOTOR2] = setMotorStat(ga_iPWM_Value[MOTOR2]);                // 设置电机运转标志

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

  531.                                         gb_M1CalOutValue =TRUE;                                        // 建立计算电机控制输出值标志,
  532.                                         gb_M2CalOutValue =TRUE;       
  533.                                         break;
  534.                                 }
  535.                                
  536.                                 default: break;
  537.                         }
  538.                        
  539.                         break;
  540.                 }
  541.                
  542.                 case READ_MEMORY:
  543.                 {
  544.                         // 读内存数据处理
  545.                         i = (gi_ucStartPtr + 1)&(MaxRcvByte_C-1);               
  546.                         uitemp.b[1] = ga_ucRcvBuf[i];                                                                // 取读数据地址
  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];                                                                                                        // 取读数据长度
  551.                         if(n>(MaxTxdByte_C - 4))
  552.                         {
  553.                                 n = (MaxTxdByte_C - 4);                                                                                // 受发送缓冲区限制,减 4 个字节对应: 命令 地址 长度
  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];                                                                // 将要读数据的地址和长度返回
  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;                                                                                                                                                        // 数据区起始指针
  565.                        
  566.                         if(uitemp.b[0] == 0)
  567.                         {
  568.                                 ucI_Ptr = uitemp.b[1];                                                                                // 如果高地址为 0 ,则读IDATA内容
  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内容,因为只有256字节的XDATA,所以只取低字节。
  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;                                                                // 发送字节计数
  589.                         gi_ucTxdPtr = 0;                                                                        // 发送指针
  590.                         SBUF = ga_ucTxdBuf[0];                                                // 启动发送
  591.                        
  592.                         break;
  593.                 }
  594.                
  595.                 default:
  596.                 {
  597.                         break;
  598.                 }
  599.         }
  600. }

  601. /********************************************/
  602. /*名称:        calStopCntValue                                                                                        */
  603. /*用途: 根据得到的行走脉冲数计算出停止点          */
  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];                                                                        // 防护处理,避免正好在PCA中断时取数
  615.                 }
  616.                
  617.                 cnt1 = cnt1 + uiRun_Num;                                                                                // 得到停止的判断点
  618.         }
  619.         else
  620.         {
  621.                 cnt1 = 65535;                                                                                                                                // g_uiRotateNum =0;设置为最大值,永不停止
  622.         }       

  623.         return(cnt1);
  624. }

  625. /*********************************************/
  626. /*名称:        setMotorStat                                                                                                 */
  627. /*用途: 根据命令中的PWM值或转速值设置电机状态*/
  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. /*用途: 根据采样输出值g_cSampleOut 控制寻迹         */
  683. /*********************************************/

  684. ……………………

  685. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

所有资料51hei提供下载:
循迹小车详细资料包括电路图、软件编程.rar (238.93 KB, 下载次数: 150)


评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏8 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:539016 发表于 2020-4-28 00:14 | 只看该作者
好东西,值得学习
回复

使用道具 举报

板凳
ID:539016 发表于 2020-4-28 00:14 | 只看该作者
好东西,值得学习
回复

使用道具 举报

地板
ID:958455 发表于 2021-9-7 20:35 | 只看该作者
最近学校也在组织循迹小车的比赛,顶
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表