一乐电子

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 4877|回复: 13

[其他综合] 关于带协议的串口通信(已搞掂)

  [复制链接]
发表于 2012-6-9 19:39 | 显示全部楼层 |阅读模式
本帖最后由 LBQ691477940 于 2012-6-9 20:26 编辑

写了一个串口接收小程序带简单的协议(02,03, XX,XX  共4个字节 (其中02,03为所谓的协议头,XX代表00~FF的值)   )但存在问题。
如果上位机正确发送十六进制02 03 XX XX 的话完全没问题可以工作,但是如果上位机只发送1个十六进制02或只发送2个十六进制0203
则下次发送正确的十六进制02 03 XX XX 也会不正常工作,要发第二次正确的十六进制02 03 XX XX 才正常工作。

请问大家有无好的简单的方法处理呢?多谢!
/******************************************************************************************************/
uchar uartDataSize;  //串口已读取数据长度
uchar GLengthLoh[4]; //接收缓冲区共4个
bit UART_RI = 0;  //成接收标志位置
/******************************************************************************************************/
void UART_ISR(void) interrupt 4 //串口中断服务函数
{
uchar temp;        
if(RI)      //判断是否中断接收
{
  RI = 0;           // 清接收标志
  temp = SBUF;
  GLengthLoh[uartDataSize++ & 0x07] = temp; //与上7相当于不会大过7哈哈!
  if(uartDataSize >= 4)
  {
   uartDataSize = 0;
   if ((GLengthLoh[0] == 0x02) && (GLengthLoh[1] == 0x03))////检测命令头 0X02,0X03
   {
    UART_RI = 1;  //如果已接收到4个字节数据且协议头相同将完成接收标志位置1
   }
   else
   {
    UART_RI = 0;  //接收到数据标志位置1
   }
  }
  else if(GLengthLoh[0] != 0x02) //检测命令头 0X02,0X03
  {
   uartDataSize = 0;
  }
}
else if(TI)  //如果是发送标志位,清零
        TI = 0;
}
 楼主| 发表于 2012-6-9 20:27 | 显示全部楼层
本帖最后由 LBQ691477940 于 2012-6-9 21:18 编辑

/*************************************************************************/
uchar uartDataSize;  //串口已读取数据长度
uchar GLengthLoh[4]; //接收缓冲区共4个
bit UART_RI = 0;  //成接收标志位置
/******************************************************************************/
void UART_ISR(void) interrupt 4 //串口中断服务函数
{      
if(RI)      //判断是否中断接收
{
  RI = 0;           // 清接收标志
  if((uartDataSize == 1) && (SBUF == 0x02))
  {
   uartDataSize = 0;
  }
  if((uartDataSize == 2) && (SBUF == 0x02))
  {
   uartDataSize = 0;
  }

  GLengthLoh[uartDataSize++ & 0x07] = SBUF; //与上7相当于不会大过7哈哈!
  if(uartDataSize >= 4)
  {
   uartDataSize = 0;
   if ((GLengthLoh[0] == 0x02) && (GLengthLoh[1] == 0x03))//检测命令头 0203
   {
    UART_RI = 1;  //如果已接收到4个字节数据且协议头相同将完成接收标志位置1
   }
   else
   {
    UART_RI = 0;  //接收到数据标志位置1
   }
  }
  else if(GLengthLoh[0] != 0x02) //检测命令头 0203
  {
   uartDataSize = 0;
  }
}
else if(TI)  //如果是发送标志位,清零
        TI = 0;
}
/***********************************************************************************/
回复

使用道具 举报

发表于 2012-6-9 20:56 | 显示全部楼层
关注 关注
回复

使用道具 举报

发表于 2012-6-9 21:08 | 显示全部楼层
贴完整程序看看吧

我看过个串口接收的程序是这样子判断的:
/******************************************/
void serial_serve(void) interrupt 4
{
uchar temp;
RI=0;
EA=0;
temp=SBUF;
switch(cnt)
{
case 0:if(temp=='#')cnt=1;else outflag=0;break;
case 1:if((temp>0x30)&&(temp<0x39)){a[0]=temp-0x30;cnt=2;}else outflag=0;break;
case 2:if(temp=='(')cnt=3;else outflag=0;break;
case 3:if((temp>=0x30)&&(temp<=0x38)){a[1]=temp-0x30;cnt=4;}else outflag=0;break;
case 4:if(temp==')'){cnt=0;outflag=1;}else outflag=0;break;
default:break;
}
EA=1;
}
8路继电器的开关.rar (11.96 KB, 下载次数: 1240)
回复

使用道具 举报

发表于 2012-6-9 21:11 | 显示全部楼层
你光加针头和数据帧 你的效验帧和命令帧还有真伪哪去了?
回复

使用道具 举报

 楼主| 发表于 2012-6-9 21:17 | 显示全部楼层
你光加针头和数据帧 你的和命令帧还有真伪哪去了?
w627255898 发表于 2012-6-9 21:11 https://www.yleee.com.cn/images/common/back.gif



    效验帧常见的也就是全字节的值相加的方式多见些,但像我这样校验核要二个字节才能表示完了因 0203 后面xx/xx都可以表示 00~ff,搞起来有点复杂所以就没加上 长度帧验证帧结束帧了
回复

使用道具 举报

发表于 2012-6-9 21:29 | 显示全部楼层
帧尾呢?
(02,03, XX,XX  共4个字节 (其中02,03为所谓的协议头,XX代表00~FF的值)
你可以这样写
(02,03, XX,XX  30,20)共4个字节 (其中02,03为所谓的协议头,XX代表00~FF的值,30,20是真伪)

这样是六个字节 单片机接收先判断前两个字节对不对 如果不对就回应接收错误 让电脑重新发送 如果对 在吧XX,XX缓存到寄存器 然后在判断真伪对不对 如果不对同样 回应接收错误 让电脑重新发送 如果对就将寄存器中的数据保存到存储器等正常运作
回复

使用道具 举报

 楼主| 发表于 2012-6-9 21:59 | 显示全部楼层
贴完整程序看看吧

我看过个串口接收的程序是这样子判断的:
/*************************************** ...
阳光999 发表于 2012-6-9 21:08 https://www.yleee.com.cn/images/common/back.gif



    好像你这个程序跟我修改前的一样呀!按你的意思应该是发字符#x(y)其中x取值为1~8对应p1.0~p1.7 而y取值0~1对应为on/off
你试只发字符#x(其中x取值为1~8)试试。一样到下一次的正确控制码会错过一次的。
回复

使用道具 举报

发表于 2012-6-10 10:29 | 显示全部楼层
刚试过了 用串口助手发不完整命令  不会错过
回复

使用道具 举报

 楼主| 发表于 2012-6-11 12:45 | 显示全部楼层
本帖最后由 LBQ691477940 于 2012-6-11 12:48 编辑

经过1翻修改后好像好多少具体如下:
/*****************************************************************/
void UART_init() //串口及定时器初始化
{
TMOD = 0x21; //设置定时器1为模式2;定时器0为模式1   
SCON = 0x50; //SM0-SM1-SM2-REN-TB8-RB8-TI-RI
TH1 = 0xff;  //11.0592M 0xff = 57600  0xfd = 19200 0xfa = 9600
TL1 = TH1;  //22.1184M 0xfe = 57600  0xfa = 19200 0xf4 = 9600
PCON = 0x80; //波特率加倍
RI = 0;   //清接收标志
TI = 0;   //清发送标志
PS = 1;   //串口中断先优
ES = 1;   //开串中断
TR1 = 1;  //启动定时器
EA = 1;   //开总中断
}
/****************************************************************/
void Timer0(void) interrupt 1 //定时器中断用于处理丢包
{
TH0 = (65536 - 5000)/256;  //约5MS定时器初值//设定时值,收到数据时设定时值
TL0 = (65536 - 5000)%256;  //约5MS定时器初值  
if (TN >= 2)     // 帧中的两字符间隔TN*10ms则为出错
{
  ET0 = TR0 = TN = 0;  //关定时器0中断/定时器0/清超时变量
  uartDataSize = 0;   //指针清零
  SEND_NG();    //向串口NG字符串并转行
}
else
  TN++;  
}
/***************************************************************/
uchar uartDataSize;  //串口已读取数据长度
uchar GLengthLoh[6]; //接收缓冲区共6个
uchar TN = 0;    //接收超时变量清零
bit UART_RI = 0;  //成接收标志位置
/*******************************************************************/
union Check   //定义地址共用体把CRC校验码高位和低位分开
{     // CRC.Chdata[0]为高位 CRC.Chdata[1]为低位
unsigned int CRCdata;
unsigned char Chdata[2];   // [0] 为高位 [1] 为低位
} CRC;
/************************************************************************/
uint cal_crc(uchar *ptr, uint len)  //CRC校验计算函数
{   
        uint crc = 0xffff;   
        uchar i;   
        while(len-- != 0)
        {   
            crc ^= *ptr;
            for(i = 0;i < 8;i++)
            {   
                if((crc & 0x0001) == 0)
     crc = crc >> 1;
                else  
                {
     crc = crc >> 1;
     crc ^= 0xa001;  //40961
                }                     
            }  
            ptr++;  
        }   
        return crc;
}
/*******************************************************************************/
void UART_ISR(void) interrupt 4 //串口中断服务函数
{      
if(RI)    //判断是否中断接收
{
  RI = ET0 = TR0 = TN = 0; //接收中断标位、定时器0禁止中断、停止定时器0/接收超时变量清零
  GLengthLoh[uartDataSize++ & 0x07] = SBUF; //与上7相当于不会大过7哈哈!
  if(uartDataSize >= 6)
  {
   uartDataSize = 0;
   CRC.CRCdata = cal_crc(&GLengthLoh,4);   //求取CRC16结果
   if((GLengthLoh[4] == CRC.Chdata[0]) && (GLengthLoh[5] == CRC.Chdata[1]))
   {
    if ((GLengthLoh[0] == 0x02) && (GLengthLoh[1] == 0x03))//检测命令头 0203
    {
     UART_RI = 1;  //如果已接收到4个字节数据且协议头相同将完成接收标志位置1
    }
   }  
   else
   {
    UART_RI = 0;  //接收到数据标志位置1
    SEND_NG();   //向串口NG字符串并转行
   }
  }
  else
  {
   TH0 = (65536 - 5000)/256;  //约5MS定时器初值//设定时值,收到数据时设定时值
   TL0 = (65536 - 5000)%256;  //约5MS定时器初值
   ET0 = TR0 = 1; //开定时器0中断、启动定时器0定/时器用于在一帧数据接收时如有丢包则返回错误信息
  }
}
else if(TI)  //如果是发送标志位,清零
        TI = 0;
}
/*************************************************************/
回复

使用道具 举报

本版积分规则

QQ|一淘宝店|手机版|商店|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2025-11-4 02:03 , Processed in 0.032732 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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