打印

请大家讨论一下串口通讯的架构问题,以提高通讯效率和可

[复制链接]
2540|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
noend297|  楼主 | 2008-5-7 11:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

下面是我一个项目里的通讯程序,没有用环形缓冲区,可能会出现没及时处理的命令被覆盖的问题,如果我改用环形缓冲区的话,怎样存取和判断处理比较方便呢,下面是通讯协议:
通讯格式

帧起始苻

帧长度

命令字

数据项

 

68H
单字节

单字节

xxH
单字节

DATA
不定

 

 
 
因为每帧命令的字节数不一样,中断里网环形缓冲区里存的时候没什么问题,但通讯处理函数从缓冲区里取命令的时候就会有一定问题,指针不容易维护,而且一旦某次读出错,那以后的全部都错,我只是说一下我这个程序容易出现的问题,请大家在这里谈论一个通用性强的,结构清晰的通讯程序架构
/*------------------------------------------------------------------------------------------------**
****************************************************************************************************
**------------------------------------------------------------------------------------------------**
**     com.c                              通讯相关程序
**------------------------------------------------------------------------------------------------**
****************************************************************************************************
**------------------------------------------------------------------------------------------------*/

//----------------------------
//通讯处理
//----------------------------
void com_cl() 

 if(recv_end!=1)return;
 recv_end=0;
 switch(recv_buf[0])
       {

/*------------------------------------------------------------------------------------------------*/
         //调试命令区
        case 0xD0: 
                   if(recv_buf[1]==0x00)
                      {
                       getdatm();
                       send0();
                      }
                   else
                      {
                       switch(recv_buf[2]&0xc0)
                         { case 0x00:get7028(recv_buf[2],recv_buf[1]);   
                                     send0();                                      break;
                           case 0x80:write7028(recv_buf[2],recv_buf[1],recv_buf+3);break;
                           case 0xc0:                                              break;
                           default:                                                break;
                         }
                       }
                   break;
        case 0xD1:
                  switch(recv_buf[1]&0x80)
                        {
                          case 0x00:geteeprom();
                                    send0();                                             break;
                          case 0x80:writeeeprom(recv_buf[2],recv_buf+3,recv_buf[1]&0x7f);break;
                          default  :                                                  break;
                        }
                  break;
        case 0xD2:
                  dysdz=bcdtoint(recv_buf+1);
                  
                     
                  break;
        case 0xD3:
                  tiaoya();
                  break;
        case 0xD8:
                  if(recv_buf[1]==0x88)
                       reset485();
                  else
                      {
                       send1_add(recv_buf[1]);
                       Delay(1000);
                       xxzz();
                       send1(); 
                      }
                  break;
 
/*------------------------------------------------------------------------------------------------*/
         //电源控制命令区
        case  0xA0:                                         //复位电源
                   reset_dy();
                   break;
        
        case  0xA1:                                         //设置电压电流输入输出状态 
                   
                         if(recv_buf[1]==0)
                             kg_flag=0x77;
                         else
                             {
                                   if(recv_buf[1]==1)
                                            kg_flag=1;
                              else if(recv_buf[1]==2)
                                            kg_flag=2;
                              else if(recv_buf[1]==3)
                                            kg_flag=4;
                              else if(recv_buf[1]==4)
                                            kg_flag=16;
                              else if(recv_buf[1]==5)
                                            kg_flag=32;
                              else if(recv_buf[1]==6)
                                            kg_flag=64;
 
                              }
                          if(recv_buf[2]==1)
                              {
                               u_out(kg_flag,u_fd);
                               i_out();
                              }
                     else if(recv_buf[2]==0)
                              {
                               u_shut();
                               i_shut();
                              }
  
                   break;
        case  0xA2:                                         //设置相位       
                   xw[0]=recv_buf[1];
                   xw[1]=recv_buf[2];
                   xw[3]=recv_buf[3];
                   break;                 
            
        case  0xA3:                                        //设置电压档位和幅度                                
                   u_dw=recv_buf[1];
                   u_fd[0]=recv_buf[2];
                   u_fd[1]=recv_buf[3]; 
                   dydw();
                   break;
        case  0xA4:                                         //设置电流档位和幅度 
                   i_dw=recv_buf[1];
                   i_fd[0]=recv_buf[2];
                   i_fd[1]=recv_buf[3];
                   dldw();
                   break;
        
        case  0xA5:                                         //对称度调整                                                                             
                   dcd_adjust();
                   break;
        case  0xA6:                                         //电压粗调
                   break;
        
        case  0xA7:                                         //电流粗调
                   break;
        case  0xA8:                                         //电压细调
                   break;
        
        case  0xA9:                                         //电流细调
                   break;

/*------------------------------------------------------------------------------------------------*/
        //信息索取命令区
        case  0xB0:                                         //索取低压信息
                   getdatm();
                   send0();
                   break;
        case  0xB4:                                         //索取通讯校验状态
                   break;
/*------------------------------------------------------------------------------------------------*/
        //返回信息区
        case  0xB1:                                         //返回高压信息
                   
                   switch(recv_buf[2])
                         {
                          case 1:u_bz[0]=ctof(recv_buf+3);
                                 u_bz[1]=ctof(recv_buf+7);
                                 u_bz[2]=ctof(recv_buf+11);
                                 u_bz_new=1;
                                 break;
                          case 2:i_bz[0]=ctof(recv_buf+3);
                                 i_bz[1]=ctof(recv_buf+7);
                                 i_bz[2]=ctof(recv_buf+11);
                                 i_bz_new=1;
                                 break;
                          case 3:xw_bz[0]=ctof(recv_buf+3);
                                 xw_bz[1]=ctof(recv_buf+7);
                                 xw_bz[2]=ctof(recv_buf+11);
                                 xw_bz[3]=ctof(recv_buf+15);
                                 xw_bz[4]=ctof(recv_buf+19);
                                 xw_bz_new=1;
                                 break;
                          default:
                                 break; 
                         }

                   break;
/*------------------------------------------------------------------------------------------------*/
        //辅助命令区
        case  0xC4:                                         //信息重新发送命令
                   send0();
                   break;
        default :  break;
        }
}
//----------------------------
//初始化串口及定时器
//----------------------------
void init(void)
{
TMOD=0x21;
TH0=0x00;
TL0=0x00;
TH1=0xfd;
TL1=0xfd;
PCON=0x00;
TR1=1;
SCON0=0x50;
SCON1=0xd0;                     //UART1工作于方式3,用于多机通讯
ES0=1;
ES1=1;
ET0=1;
EA=1;
}
//----------------------------
//串口0发送
//----------------------------
void send0()

 uchar i;
 EA=0;
 SBUF0=0x68;
 while(TI==0);TI=0;  
 for(i=0;i<=send0_buf[0];i++)
  {
   SBUF0=send0_buf;
   while(TI==0);TI=0;
  }
 EA=1;
}
//----------------------------
//串口0中断
//----------------------------
 void int232_0(void) interrupt 4 using 0
 {
  uchar a;
  ES0=0;
  if(RI==1)
  {
   RI=0;
    a=SBUF0;
    if(recv_long==0)
         {
         if(a==0x68)
            recv_start=1;
         else
            if(recv_start==1)recv_long=a;
         }
    else
         {
         if(recv_start==1)
             {
             recv_buf[recv_zz]=a;
             recv_zz=recv_zz+1;
             recv_long=recv_long-1;
             if(recv_long==0)
                {
                 recv_end=1;
                 recv_zz=0;
                 recv_start=0;
                }
             }
         else
             recv_long=recv_long-1;
         } 
  } 
 ES0=1;
}

相关帖子

沙发
dld2| | 2008-5-7 15:21 | 只看该作者

Re

帧结构中可以考虑加一个校验码(简单和校验)。

我一般在接收中断中检查帧结构。检查正确的帧数据,才会放到环行缓冲区中。
这样,保证环行缓冲中的数据总是有效数据。但写入环行缓冲区前,要检查是否会溢出。
从环行缓冲读数据时,只要程序正确,就不会出现“一旦某次读出错,那以后的全部都错”的问题。

使用特权

评论回复
板凳
noend297|  楼主 | 2008-5-7 15:47 | 只看该作者

通讯

大家看一下这个问题怎么解决:
    我的通讯程序中断接收和通讯处理是完全分开的,中断处理函数是在主程序里循环检测的,检测到接收完标志,就取缓冲区的数据进行解析。这样的话受到的命令是依次处理的,就是一次命令处理完后,退出通讯处理函数,再重新检测是否受到数据。这样会出现一个问题:

mcu已经接收到一条命令,通讯处理函数判断后进入一函数执行(此函数执行时间较长),这时如果再接收一条命令,接收中断结束后还会回到尚未执行完的函数中,而不会去执行通讯处理函数,从而不会及时的执行新命令,怎样才能终止尚未处理完的函数来执行新命令啊

使用特权

评论回复
地板
super057| | 2008-5-8 21:38 | 只看该作者

是不是可以考虑开辟新任务来处理哦

是不是可以考虑开辟新任务来处理哦

使用特权

评论回复
5
walnutcy| | 2008-5-8 22:06 | 只看该作者

你的问题应该这样描述:

从串口上接收命令,
但每个命令的优先级不同,

需要接到高优先级命令时终止未执行完的低优先级任务;

给你一个参考:将低优先级的任务A分段,每个段作一个状态,

遇到高优先级的命令时直接将A的状态设为结束,从而在下一循环结束掉该任务;

有一个缺点:不能够及时响应,但可以做到接收命令的xx ms内结束任务

使用特权

评论回复
6
yxwsz| | 2008-5-8 23:33 | 只看该作者

硬件流控

从可靠性来讲,最好加上硬件流量控制,如RTS/CTS;
因为使用大的缓冲区,也有溢出的可能。

使用特权

评论回复
7
noend297|  楼主 | 2008-5-9 09:30 | 只看该作者

walnutcy的意思是不是这样

在中断里设一个标志,如果高优先级的中断来了指标值,然后在处理函数里检测这个标志,检测到直接退出?这样只要函数循环时间不要太长就可以了。

使用特权

评论回复
8
noend297|  楼主 | 2008-5-9 09:30 | 只看该作者

我用的是51,没用操作系统

我用的是51,没用操作系统

使用特权

评论回复
9
Ice_River| | 2008-5-9 16:00 | 只看该作者

51就老老实实一帧一帧处理好了!

环的话,那点地方还不够,除非帧小。

使用特权

评论回复
10
noend297|  楼主 | 2008-5-9 16:38 | 只看该作者

51跟用环形缓冲区有什么必然联系么

用环只是为了不让命令丢失,因为有些处理函数需要时间比较长,而我的片外存储器也足够用

使用特权

评论回复
11
mohanwei| | 2008-5-9 17:08 | 只看该作者

环形队列比较好用。两个函数一个写一个读轻松搞定

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

29

帖子

0

粉丝