注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

一车,一房,一个温暖家

10万左右的车,80平方的房子,这是我奋斗的暖暖的家

 
 
 

日志

 
 

引用 上位机与单片机的互动-上位机显示电机状态参数(五)   

2015-03-22 14:54:24|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

今天是4月6号,时间老是断断续续的,今天终于把上位机显示电机状态参数的功能给写出来了,看似一个简单功能但却涉及到许多细节上的阻碍。

先上个图:

上位机与单片机的互动-上位机显示电机状态参数(五) - L -  
在硬件的世界里永远只有0与1,所以2代表左电机正转,32代表右电机正转。 以后我会以中文代替,现在还有部分功能尚未完善。
 
机器人部分的图改天再上了。
下面对这个功能做一下简单的讲解
首先是协议的变化----
   由于第一次搞协议这东西没经验,让我吃尽苦头,特别是协议的灵活性,因此我改了又改,目前协议如下:
单片机:

(MSB)结束(0x50)、数据域(右电机参数(1字节),左电机参数(1字节))、命令(1字节)、地址(1字节)、校验码(2字节)LSB

例:0x50、数据(N字节)0x340x100xbd(高位)0xc0(低位)

0xbd0xc0CRC[0][1]

0x10:单片机地址[2]

0x34:显示电机状态及PWM参数命令[3]

(右电机PWM、左电机PWM、右电机转向、左电机转向)

(命令集:

0x36:显示电机速度

数据域:

左电机参数(0x02正转,0x04反转,0x00刹车,0x06惰行)[4]

右电机参数(0x20正转,0x40反转,0x00刹车,0x60惰行)[5]

0x50:告诉单片机数据已经传输完毕了[6]

 

上位机协议基本没变。

这个协议肯定还存在许多漏洞,但是漏洞在有限的时间里是补不完得,所以我还是先考虑功能的实现。

 

单片机修改程序如下:

#include<STC12C5410AD.h>
#include<CRC16.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long

/*电机状态定义*/
#define  motorDrv P2   //电机驱动IO口
//左侧电机定义
#define  ML_FORWARD 0x02  //电机正转
#define  ML_BACK  0x04  //电机反转
#define  ML_FLOAT 0x06  //电机惰行, 此时停止PWM输出
#define  ML_BRAKE 0x00  //电机刹车
#define  ML_REMOVE 0xF8  //清除左侧电机状态
//右侧电机定义
#define  MR_FORWARD 0x20  //电机正转
#define  MR_BACK  0x40  //电机反转
#define  MR_FLOAT 0x60  //电机惰行, 此时停止PWM输出
#define  MR_BRAKE 0x00  //电机刹车
#define  MR_REMOVE 0x8F  //清除右电机状态

//接收数据定义
uchar ga_ucRcvBuf[10]; //接收数据寄存器
uchar gc_uCount;  //接收计数器
uchar gc_ucNum;   //接收计数器,累积数据个数
bit g_bRcvCount;  //接收完毕标志
bit g_bCheckRcvData; //数据正确性标志
//发送数据定义
uchar ga_ucSendBuf[10];  //接收数据寄存器
ulong ga_ulcrcLowRes;
ulong ga_ulcrcHighRes;
uchar ga_uccrcLowRes;
uchar ga_uccrcHighRes;
uchar *ptr;
/********************************************/
/* 名称:init_SIO       */
/* 用途:初始化串口,       */
/* 参数: 波特率 , 模式固定为:1     */
/*   1 START 8 DATA 1 STOP     */
/********************************************/
void init_SIO()
{
 TMOD = TMOD|0x20|0x00;   //设定T1为定时器模式
 PCON = 0;
 AUXR = AUXR|0x00;   //Set 12T
 SCON = SCON|0x40|0x10;  
 TH1 = 0xFA;
 TL1 = 0xFA;
 TR1 = 1;
 EA = 1;
 ES = 1;

}

void delay_1ms(uchar a)
{
 uchar x,y;
 for(x = a;x > 0;x--)
 {
  for(y = 220;y > 0;y--);
 }
}


/********************************************************************
* 名称 : checkData()
* 功能 : 检测接收到的数据是否正确?
* 输入 : 无
* 输出 : g_bCheckRcvData(返回1则为数据正确)
*********************************************************************/
/*
bit checkData(void)
{
 ulong g_ulCrcLow;   //存储计算后的低位CRC
 ulong g_ulCrcHigh;   //存储计算后的高位CRC
 uchar g_ucRcvCrcLow;  //暂储接收到的低位CRC
 uchar g_ucRcvCrcHigh;  //暂储接收到的高位CRC
 uchar g_uCrcHigh;   //将计算后的高位CRC转换成uchar类型
 if(ga_ucRcvBuf[2] == 0x10)
 {
  g_ucRcvCrcLow = ga_ucRcvBuf[0];
  g_ucRcvCrcHigh = ga_ucRcvBuf[1];
  ga_ucRcvBuf[0] = 0;
  ga_ucRcvBuf[1] = 0;
  g_ulCrcLow = cal_crc(ga_ucRcvBuf,5);
  g_ulCrcLow = g_ulCrcLow & 0xFF;  //舍去低位CRC以外的其他位
  if(g_ucRcvCrcLow == g_ulCrcLow)
  {
   g_ulCrcHigh = cal_crc(ga_ucRcvBuf,5);
   g_ulCrcHigh = g_ulCrcHigh >> 8;
   g_uCrcHigh = g_ulCrcHigh;
   if(g_ucRcvCrcHigh == g_uCrcHigh)
   { 
   //这里可以处理校验成功后的功能
   //这里为单片机接收到正确指令后给上位机回应
    g_bCheckRcvData = 1; //表明接收数据正确
   }
  }else
  {g_bCheckRcvData = 0;}
 }
 return g_bCheckRcvData;
}
*/

/********************************************************************
* 名称 : checkData()更新版
* 功能 : 检测接收到的数据是否正确?
* 输入 : 无
* 输出 : g_bCheckRcvData(返回1则为数据正确)
*********************************************************************/
bit checkData(void)
{
 union  //定义一个联合体
 {
  uint allCrc;
  uchar b[2]; //b[0]为高位crc,b[1]为低位crc
 }crcBuf;
 ulong crcSav;    //存储计算后的CRC
 uchar g_ucRcvCrcLow;  //暂储接收到的低位CRC
 uchar g_ucRcvCrcHigh;  //暂储接收到的高位CRC
 if(ga_ucRcvBuf[2] == 0x10)
 {
  g_ucRcvCrcLow = ga_ucRcvBuf[0];
  g_ucRcvCrcHigh = ga_ucRcvBuf[1];
  ga_ucRcvBuf[0] = 0;
  ga_ucRcvBuf[1] = 0;
  crcSav = cal_crc(ga_ucRcvBuf,gc_ucNum);
  crcBuf.allCrc = (uint)crcSav;
  if(g_ucRcvCrcLow == crcBuf.b[1])  //比较低位CRC
  {
   if(g_ucRcvCrcHigh == crcBuf.b[0]) //比较高位CRC
   { 
   //这里可以处理校验成功后的功能
   //这里为单片机接收到正确指令后给上位机回应
    g_bCheckRcvData = 1; //表明接收数据正确
   }
  }else
  {
   g_bCheckRcvData = 0;
  }
 }
 gc_ucNum = 0;
 return g_bCheckRcvData;
}

/*PWM输出参数设置*/
void pwmOutput(uchar mModeAdd,uchar cuCycle) 
{
 uchar ucCexCl,ucCexCh;
 ucCexCh = 255 - (cuCycle * 2.55);  //根据占空比计算比较值
 //这里是CL>=CCAPnL时PWM输出为1,所以越低的数据PWM越高
 ucCexCl = ucCexCh;
 switch(mModeAdd)       //检测模块地址
 {
  case 0:       
  {
   CCAPM0 = 0x42;
   CCAP0L = ucCexCl;
   CCAP0H = ucCexCh;
   break;
  }
  case 1:
  {
   CCAPM1 = 0x42;
   CCAP1L = ucCexCl;
   CCAP1H = ucCexCh;
   break;
  }
  case 2:
  {
   CCAPM2 = 0x42;
   CCAP2L = ucCexCl;
   CCAP2H = ucCexCh;
   break;
  }
  case 3:
  {
   CCAPM3 = 0x42;
   CCAP3L = ucCexCl;
   CCAP3H = ucCexCh;
   break;
  }
  default: break;
 } 
}

/*驱动电机*/
void motorDrive(uchar lMotordt,uchar rMotordt)  
{
 motorDrv = 0x00;   //复位P2口为0000 0000
 motorDrv |= lMotordt;
 motorDrv |= rMotordt;
}

/********************************************************************
* 名称 : Com_Int()
* 功能 : 串口中断子函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void comInt() interrupt 4 using 3
{
 EA = 0;
 if(RI == 1)   //当硬件接收到一个数据时,RI会置位
 {
  RI = 0;
  /***************************成功
  ga_ucRcvBuf[0] = SBUF;
  Delay_1ms(1);
  ************************/
  ga_ucRcvBuf[gc_uCount] = SBUF;
  gc_uCount ++;
  if(SBUF == 0x50)
  {
   gc_ucNum = gc_uCount;
   gc_uCount = 0;
   g_bRcvCount = 1;
  }
  //gc_uNum ++;
 }
 if(TI == 1)
 {
  TI = 0;
 }
 EA = 1;
}

/*PCA中断*/
void PCATimer() interrupt 6 using 2   
{
 if(CCF0 == 1)
 {
  CCF0 = 0;
 }
 if(CCF1 == 1)
 {
  CCF1 = 0;
 }
}
/**************************发送数据**************/
void sdBufData(uchar c,uchar *ptr)
{

 uchar i;
 for(i=0;i<c;i++)
 {
   SBUF = *ptr;
  while(!TI);
  TI = 0;
  ptr ++;
  delay_1ms(1);
 }
}


void main()
{
 gc_uCount = 0;
 g_bRcvCount = 0;   //接收数据计数器
 P2M0 = 0x00;      //将P20,P24设置成强推挽输出
 P2M1 = 0x01;
 CMOD |= 0x02;      ////PCA模式寄存器,Fosc/2
 CL = 0x00;
 CH = 0x00;
 init_SIO();   //波特率为9600
 EPCA_LVD = 1;  //开PCA中断
 while(1)
 {
  if(g_bRcvCount == 1)
  {
   g_bRcvCount = 0;
   EA = 0;
   if(checkData())    //数据正确性检测
   {
    g_bCheckRcvData = 0;  //将数据判断器复位
    motorDrive(ga_ucRcvBuf[4],ga_ucRcvBuf[5]);
    pwmOutput(2,ga_ucRcvBuf[6]);
    pwmOutput(3,ga_ucRcvBuf[7]);
    //显示电机状态
    ga_ucSendBuf[0] = 0;   //CRC低位
     ga_ucSendBuf[1] = 0;   //CRC高位
    ga_ucSendBuf[2] = 0x10;   //单片机地址
     ga_ucSendBuf[3] = 0x34;   //显示电机参数命令
    ga_ucSendBuf[4] = ga_ucRcvBuf[4]; //左侧电机转向
    ga_ucSendBuf[5] = ga_ucRcvBuf[5]; //右侧电机转向
    ga_ucSendBuf[6] = ga_ucRcvBuf[6]; //左侧电机PWM
    ga_ucSendBuf[7] = ga_ucRcvBuf[7]; //右侧电机PWM
    ga_ucSendBuf[8] = 0x50;    //结束信号
    ga_ulcrcLowRes = cal_crc(ga_ucSendBuf,9);
    ga_ulcrcHighRes = cal_crc(ga_ucSendBuf,9) / 256;
    ga_uccrcLowRes = (uchar)ga_ulcrcLowRes;
    ga_uccrcHighRes = (uchar)ga_ulcrcHighRes;
    ga_ucSendBuf[0] = ga_uccrcLowRes;
    ga_ucSendBuf[1] = ga_uccrcHighRes;
    sdBufData(9,ga_ucSendBuf);
    CR = 1;
   }
   EA = 1;
  }
  delay_1ms(1);
 }
}

 

这个程序折磨了我好几天,其中用了C的精华--指针,一开始老是发送不成功,后来加了while(!TI);TI = 0;就ok了。

 

 

下面是VB上位机的程序:

 

Dim ga_byRcvBuf(0 To 15) As Byte   '接收数据存储器
Dim gi_byRcvCount As Byte    '接收计数器,也是下标
Dim g_ucRcvCrcLow As Byte
Dim g_ucRcvCrcHigh As Byte
Dim g_uCrcLow As Byte
Dim g_uCrcHigh As Byte
Public gc_BufCount As Byte
Private Sub Command1_Click(Index As Integer)
        If MSComm1.PortOpen = False Then
        On Error GoTo cntOpenCom
        If Combo1.Text = "COM1" Then
            MSComm1.CommPort = 1
        ElseIf Combo1.Text = "COM2" Then
            MSComm1.CommPort = 2
        ElseIf Combo1.Text = "COM3" Then
            MSComm1.CommPort = 3
        ElseIf Combo1.Text = "COM4" Then
            MSComm1.CommPort = 4
        ElseIf Combo1.Text = "COM5" Then
            MSComm1.CommPort = 5
        ElseIf Combo1.Text = "COM6" Then
            MSComm1.CommPort = 6
        ElseIf Combo1.Text = "COM7" Then
            MSComm1.CommPort = 7
        End If
        MSComm1.PortOpen = True
         MsgBox ("串口打开完毕!")
    Else
        MsgBox ("串口已打开!")
cntOpenCom:
        MsgBox ("错误描述:" & Err.Description)
    End If
End Sub

Private Sub Command2_Click()
    If MSComm1.PortOpen = False Then
        MsgBox ("串口未打开!")
    Else
        MSComm1.PortOpen = False
        MsgBox ("串口关闭成功!")
    End If
End Sub

'可用CRC校验码
Function bCRC(data() As Byte, le As Byte) As Long
'数组指针计数器
    Dim mc_bPtr As Byte
'unsigned char i;
    Dim i As Byte
'unsigned int crc=0;
    Dim crc As Long
    LSres = le
    crc = 0
    mc_bPtr = 0
                            '<--------CRC计算入口
    Do While (le <> 0)
    le = le - 1
'for(i=0x80; i!=0; i/=2)
        Dim c As Byte       '模拟i=0x80
        c = &H80
        For i = 0 To 7
            If ((crc And &H8000) <> 0) Then
                crc = crc And &H7FFF
                crc = CLng(crc) * 2
                crc = CLng(crc) Xor &H1021
            Else
                crc = CLng(crc) * 2
            End If
            If ((data(mc_bPtr) And c) <> 0) Then
                crc = CLng(crc) Xor &H1021
            Else
            End If
            c = c / 2
         Next
             mc_bPtr = mc_bPtr + 1
    Loop
'return(crc);
    bCRC = CLng(crc)
End Function

Private Sub Command3_Click()        '输出电机的PWM与方向命令
    Dim lSende(0 To 8) As Byte
    Dim rSende(0 To 4) As Byte
    Dim g_lcrcLow As Byte
    Dim g_lcrcHigh As Byte
    Dim lmotordp As Byte
    Dim rMotordp As Byte
    Dim lpwm As Byte
    Dim rpwm As Byte
    'left motor
    If (Combo2(0).Text = "正转") Then
        lmotordp = &H2
    ElseIf (Combo2(0).Text = "反转") Then
        lmotordp = &H4
    ElseIf (Combo2(0).Text = "刹车") Then
        lmotordp = &H0
    ElseIf (Combo2(0).Text = "惰行") Then
        lmotordp = &H6
    End If
    'right motor
       If (Combo2(1).Text = "正转") Then
        rMotordp = &H20
    ElseIf (Combo2(1).Text = "反转") Then
        rMotordp = &H40
    ElseIf (Combo2(1).Text = "刹车") Then
        rMotordp = &H0
    ElseIf (Combo2(1).Text = "惰行") Then
        rMotordp = &H60
    End If
    lSende(0) = &H0
    lSende(1) = &H0
    lSende(2) = &H10
    lSende(3) = &H90
    lSende(4) = lmotordp
    lSende(5) = rMotordp
    'left motor PWM
    lpwm = Val(Text1(0).Text)
    If (lpwm <= 0) Then
        lpwm = 0
    ElseIf (lpwm >= 100) Then
        lpwm = 100
    End If
    'right motor PWM
    rpwm = Val(Text1(1).Text)
    If (rpwm <= 0) Then
        rpwm = 0
    ElseIf (rpwm >= 100) Then
        rpwm = 100
    End If
    lSende(6) = lpwm
    lSende(7) = rpwm
    lSende(8) = &H50
    'sende(4) = 10
    g_lcrcLow = bCRC(lSende, 9) And &HFF
    g_lcrcHigh = bCRC(lSende, 9) \ 256
    lSende(1) = g_lcrcHigh
    lSende(0) = g_lcrcLow
'Text3(3).Text = g_lcrcLow
    ' Print "HIGH=" & sende(4)
     MSComm1.Output = lSende
'right motor
End Sub

Private Sub Form_Load()
  Combo1.ListIndex = 5    '设置COM口选项默认值
    Timer1.Enabled = False
   ' MSComm1.InBufferSize = 40 '初始化串口
    MSComm1.InBufferCount = 0
    MSComm1.InputMode = comInputModeBinary '二进制方式
  '  MSComm1.CommPort = 1
    MSComm1.Settings = "9600,N,8,1"
    MSComm1.RThreshold = 1  ''设置接收一个字节就产生OnComm事件
    MSComm1.InputLen = 1    'input一次从缓冲区读取1字节数据
    Combo2(0).Text = Combo2(0).List(0)
    Combo2(1).Text = Combo2(1).List(0)
    gi_byRcvCount = 0
    g_ucRcvCrcLow = 0
    g_ucRcvCrcHigh = 0
    g_uCrcLow = 0
    g_uCrcHigh = 0
    gc_BufCount = 0
End Sub

Private Sub MSComm1_OnComm()
    If MSComm1.CommEvent = comEvReceive Then
      ga_byRcvBuf(gi_byRcvCount) = AscB(MSComm1.Input)
      gi_byRcvCount = gi_byRcvCount + 1
      gc_BufCount = gc_BufCount + 1
      'Text4.Text = AscB(MSComm1.Input)
       'Text4.Text = ga_byRcvBuf(0)
      If (ga_byRcvBuf(gi_byRcvCount - 1) = &H50) Then
        MSComm1.DTREnable = False
        gi_byRcvCount = 0
'Text4.Text = ga_byRcvBuf(0)
        g_ucRcvCrcLow = ga_byRcvBuf(0)
        g_ucRcvCrcHigh = ga_byRcvBuf(1)
        ga_byRcvBuf(0) = 0
        ga_byRcvBuf(1) = 0
        g_uCrcLow = 0
        g_uCrcHigh = 0
'Text4.Text = gc_BufCount
        g_uCrcLow = bCRC(ga_byRcvBuf, 9) And &HFF
'Text4.Text = gc_BufCount
        g_uCrcHigh = bCRC(ga_byRcvBuf, 9) \ 256
'Text4.Text = g_uCrcHigh
        gc_BufCount = 0
        If (g_ucRcvCrcLow = g_uCrcLow And g_ucRcvCrcHigh = g_uCrcHigh) Then
'Text4.Text = g_uCrcLow
    '这里执行命令
            If (ga_byRcvBuf(3) = &H34) Then
                Text4.Text = "成功!"
                Text3(0).Text = ga_byRcvBuf(4)      '左电机方向
                Text3(1).Text = ga_byRcvBuf(5)      '右电机方向
                Text3(2).Text = ga_byRcvBuf(6)      '左电机PWM
                Text3(3).Text = ga_byRcvBuf(7)      '右电机PWM
            End If
        End If
        MSComm1.DTREnable = True
        MSComm1.InBufferCount = 0     '清除缓冲区数据
      End If
    End If
      '  Text4.Text = "值" & gi_byRcvCount
End Sub

一直不成功的原因,我主要是

      MSComm1.InBufferCount = 0
    MSComm1.InputMode = comInputModeBinary '二进制方式
    MSComm1.Settings = "9600,N,8,1"
    MSComm1.RThreshold = 1  ''设置接收一个字节就产生OnComm事件
    MSComm1.InputLen = 1    'input一次从缓冲区读取1字节数据

这几个MSCOMM控件初始化没设定好。

不过这程序灵活性也还不够强,不过核心功能已经实现,其他都是小儿科了。

 

哎!! 这个项目几乎让我崩溃啊,一路走来一路阻碍。 不过最后都被我一一击破了,还需加油努力!

  评论这张
 
阅读(51)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018