打印

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

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


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

帧起始苻 
帧长度 
命令字 
数据项 
 

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;
}
 
 

相关帖子

沙发
农民讲习所| | 2008-5-7 16:18 | 只看该作者

通用标准化的,俺都做成库文件使用。

//-------------------------------------------------------------
//协议解析
//-------------------------------------------------------------
#include "....SystemCom.h"
#include "....SystemMemory.h"
#include "....SystemMessage.h"
#include "....SystemQueue.h"
#include "....SystemCommunicateQueue.h"

enum TaskState
{
    eSTX,
    eSync,
    eDataLength,
    eData,
    eChkSum,
    eETX
};

struct InPackTask
{
    struct CommunicateQueue *pCommunicate;
    void (*CallBack_Package)( struct CommunicateQueue *pCommunicate );          //解析到正确包后给外部程序处理
    U8 mTask;
    U8 mDataLength;
};

struct InPackage
{
    struct InPackTask *pPackTask;
    U8 mTaskMax;
    U8 mTaskRegisterId;
};
struct InPackage sInPackage;

#define this sInPackage

//-------------------------------------------------------------
//检查校验和
//正确返回TRUE
//-------------------------------------------------------------
bool InPackage_ChkSumCheck( struct InPackTask *pPackTask )
{
    U8 i;
    U8 j;
    U8 mSum = 0;
    
    j = pPackTask->mDataLength + 3 - 1;
    for( i=1; i<j; i++ )
    {
        mSum += Queue_Read( pPackTask->pCommunicate->psReci, i );
    }
    
    if( mSum == Queue_Read( pPackTask->pCommunicate->psReci, i ) )
    {
        return true;
    }
    else {
        return false;
    }
}

//-------------------------------------------------------------
//注册包解析的回调处理
//-------------------------------------------------------------
void Package_Register( struct CommunicateQueue *pCommunicate, void (*CallBack_Package)( struct CommunicateQueue *pCommunicate ) )
{
    struct InPackTask *pPackTask = &this.pPackTask[ this.mTaskRegisterId++ ];
    
    pPackTask->pCommunicate = pCommunicate;
    pPackTask->CallBack_Package = CallBack_Package;
}

//-------------------------------------------------------------
//初始化
//入口:队列指针
//-------------------------------------------------------------
void Package_Init( U8 mTaskMax )
{
    Memory_Memset( (U8 *)&this, 0, sizeof(struct InPackage) );
    
    this.pPackTask = (struct InPackTask *)Memory_Malloc( sizeof(struct InPackTask) * mTaskMax );
    
    this.mTaskMax = mTaskMax;
}

//-------------------------------------------------------------
//单任务解析
//-------------------------------------------------------------
void InPackage_SingleTask( struct InPackTask *pPackTask )
{
    switch( pPackTask->mTask )
    {
L_ProtcolLoop:
        case eSTX:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) )
            {
                if( Queue_Pop( pPackTask->pCommunicate->psReci ) != STX )
                {
                    goto L_ProtcolError;
                }
                pPackTask->mTask = eSync;
            }
            else break;
            
        case eSync:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) )
            {
                if( Queue_Read( pPackTask->pCommunicate->psReci, 0 ) != SYNC )
                {
                    goto L_ProtcolError;
                }
                pPackTask->mTask = eDataLength;
            }
            else break;
            
        case eDataLength:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) >= 3 )
            {
                //已经收到数据块长度
                pPackTask->mDataLength = Queue_Read( pPackTask->pCommunicate->psReci, 1 );
                pPackTask->mTask = eData;
            }
            else break;
        
        case eData:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) >= (pPackTask->mDataLength + 2 ) )
            {
                pPackTask->mTask = eChkSum;
            }
            else break;
            
        case eChkSum:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) >= (pPackTask->mDataLength + 3 ) )
            {
                //检查校验和
                if( InPackage_ChkSumCheck( pPackTask ) ){
                    pPackTask->mTask = eETX;
                }
                else {
                    goto L_ProtcolError;
                }
            }
            else break;
            
        case eETX:
            if( Queue_Num( pPackTask->pCommunicate->psReci ) >= (pPackTask->mDataLength + 4 ) )
            {
                //检查ETX
                if( Queue_Read( pPackTask->pCommunicate->psReci, pPackTask->mDataLength + 4 - 1 ) == ETX )
                {
                    //正确包,去掉同步后给程序使用
                    Queue_Pop( pPackTask->pCommunicate->psReci );
                    
                    if( pPackTask->CallBack_Package ){
                        ( *pPackTask->CallBack_Package )( pPackTask->pCommunicate );
                    }
                    else {
                        //无处理函数,丢弃整个包
                        Queue_Pop( pPackTask->pCommunicate->psReci );   //长度字
                        
                        while( pPackTask->mDataLength-- )
                        {
                            Queue_Pop( pPackTask->pCommunicate->psReci );
                        }
                    }
                    Queue_Pop( pPackTask->pCommunicate->psReci );       //chksum
                    Queue_Pop( pPackTask->pCommunicate->psReci );       //etx
                    
                    pPackTask->mTask = eSTX;
                    goto L_ProtcolLoop;                                 //从头重新检测
                }
                else{
                    goto L_ProtcolError;
                }
            }
            break;
    }
    return;

L_ProtcolError:
    pPackTask->mTask = eSTX;
    goto L_ProtcolLoop;                                             //从头重新检测
}

//-------------------------------------------------------------
//循环
//-------------------------------------------------------------
void Package_Loop( void )
{
    U8 i;
    struct InPackTask *pPackTask = this.pPackTask;
    
    for( i=0; i<this.mTaskRegisterId; i++ )
    {
        InPackage_SingleTask( pPackTask++ );
    }
}

使用特权

评论回复
板凳
5880527| | 2008-5-7 17:50 | 只看该作者

去找本PLC的通讯协议来照着编一个就可以了

使用特权

评论回复
地板
emailli| | 2008-5-7 19:17 | 只看该作者

如果想知道思维方式

不妨到我的博客上去看看

关于通信协议的实现

我不但给了代码

还给了实现的分析

比直接看代码应该要好多了

PS:不用环形缓冲区,一样可以做到响应很快,命令不被覆盖的。

使用特权

评论回复
5
noend297|  楼主 | 2008-5-8 17:23 | 只看该作者

谢谢emailli

你的**我正在看呢,你上面提到建立通用的处理程序在网站上怎么找不到了

使用特权

评论回复
6
OneMillion| | 2008-5-8 19:06 | 只看该作者

emailli 博客里面的那个串口分析写的好啊,

 那样的技术**看起来才有意思.

使用特权

评论回复
7
emailli| | 2008-5-8 19:08 | 只看该作者

网站上没有 通用处理程序的下载链接

只是 农民讲习所 有一个帖子

他发了很多图片形成了一个系列

我曾经整理过,弄成了一个PDF文档

如果有需要,我可以发给你

使用特权

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

谢谢,发给我看一下吧

我的邮箱noend297@163.com
我上你的博客看了,很多不错的**,看了很受启发,感谢分享!

使用特权

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

农民讲习所里也找不到帖子

农民讲习所里的相关的帖子好像删掉了,只有标题,没内容

使用特权

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

本版积分规则

6

主题

29

帖子

0

粉丝