野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 17177|回复: 3

一种自定义通信协议分享,带数据帧缓存

[复制链接]
发表于 2019-6-21 10:54:36 | 显示全部楼层 |阅读模式
本帖最后由 wx_NdfXKX44 于 2019-6-22 09:40 编辑

[mw_shl_code=c,true]带数据帧缓冲区,无需判断标志,就算晚点处理也不会丢帧,每次get到的是缓冲区队列最先到达的一帧数据,如果数据帧缓冲区满了会覆盖最先到达的数据帧,所以缓冲区的大小可以适当的设置---
直接贴上代码................
/***************************************头文件****************************************************************************/
#ifndef  _SPCP_H_
#define  _SPCP_H_
#include <rtdevice.h>
/***********************************************************************************************************
通信格式                                         最大255               
字节     1Byte       1Byte        1Byte         1Byte        1Byte          N Byte      N+1 1Byte   N+2 1Byte
            0:起始  1:接收地址  2:发送地址  3:操作码   4:数据长度   5:数据      6:校验            7:结束
***************************************************************************************************************************/
/*移植步骤
1.spcp_Init中初始化串口硬件
2.实现.c文件中 Spcp_WriteData函数内部向串口缓冲区写数据
3.在数据接收中断调用 Spcp_ISR_InputData函数,把接收字节传入
4.如果不是用rt_thread系统,把rt_malloc ,rt_ferr,rt_memcpy等函数替换掉
*/
/**********配置********************************/
#define  SPCP_ADDR             0x00      //本机地址
#define  SPCP_BUFF_SIZE     20          //数据帧接收缓冲区大小
#define  SPCP_FHR                0xBF      //帧头
#define  SPCP_MFR               0xCF       //帧尾
/*******是否采用OS的信号量通知有新数据帧*****************/
#define  SPCP_SIG_ENABLE      0   // 使能信号通知 1:使能  0:失能
//得到信号量后用获取缓冲区函数得到数据
//(裸机是直接采用获取缓冲区函数查看是否不是NULL来判断是否有新的数据帧
// 就算晚点也没关系,因为有缓冲区,晚点可以每次获取->处理->获取->处理->获取->NULL,再处理别的事 )  
#if  SPCP_SIG_ENABLE
/********释放信号量函数放到这个宏的后边***************************/
#define  SPCP_SIG_SENDE_FUNC               rt_sem_release(Sig)                       //释放信号量放到这个宏的后面
#endif


/***********硬件的配置(自定义)*************************/
#define  SPCP_DEVICE   "uart2"   //
#define  SPCP_SPEED   BAUD_RATE_115200  



/******数据帧结构体 获取数据缓冲区数据帧时使用**********/
typedef struct
{
  uint8_t   Addr;      //发送方的地址
        uint8_t   Cmd;       //发送方的操作码
  uint8_t   dataLen;   //数据的长度
  uint8_t   *Buff;      //接收到的数据        
}Spcp_frame,*FrameType;



/******数据帧接收缓冲区专用结构体(很少使用)***********/
typedef struct
{
        uint8_t head;
  FrameType  *frame;
        uint8_t tial;
}SPCP_Buff;

/***********函数声明******************************/
void spcp_Init(void);
void Spcp_Send(
                      uint8_t addr     //地址
                     ,uint8_t cmd      //操作符
               ,uint8_t *buff    //数据
               ,uint8_t dataLen  //数据长度
              );
FrameType  Spcp_GetBuffFrame(void);
void Spcp_ISR_InputData(uint8_t data);

#endif




/**********************************************C文件****************************************************************************
#include "spcp.h"

static   rt_device_t  UART_OBJ;
static  SPCP_Buff   Buff; //数据帧接收缓冲区
void spcp_Init(void)
{
                        /***硬件初始化******/
                 struct serial_configure  Config = RT_SERIAL_CONFIG_DEFAULT;
                                                Config.baud_rate =  SPCP_SPEED; //配置波特率
                 UART_OBJ =  rt_device_find(SPCP_DEVICE);
                        if(UART_OBJ == RT_NULL)
                        {
                //          rt_kprintf("查找UART1失败\n");
                                return ;
                        }
                        
                         if(rt_device_open(UART_OBJ,RT_DEVICE_FLAG_RDWR|RT_DEVICE_FLAG_DMA_RX)==RT_EOK)
                         {  
                //                  rt_kprintf("设备打开成功\n");
                                        if(rt_device_control(UART_OBJ,RT_DEVICE_CTRL_CONFIG,(void*)(&Config)) == RT_EOK)
                                        {
                //                          rt_kprintf("设备配置完成");
                                
                                        }

                         }
         

/************初始化接收缓冲区(不可修改,只可以改rt_malloc)*************************/
        Buff.frame =(FrameType*)rt_malloc(sizeof(FrameType)*SPCP_BUFF_SIZE);
        for(int i=0;i<SPCP_BUFF_SIZE;i++)
         {
           Buff.frame = (FrameType)rt_malloc(sizeof(Spcp_frame));
                 Buff.frame->Buff = RT_NULL;
         }
        Buff.head=0;
        Buff.tial=0;
}

/*
@写函数,里边的函数调用实际的发送函数比如串口发送,【移植必改】
*/
static void Spcp_WriteData(uint8_t *data,uint8_t len)
{
    rt_device_write(UART_OBJ,0,data,len);
}



/***************以下核心部分移植需要修改malloc和memcpy函数即可******************************/

/*
校验码生成函数
@传入一帧数据
*/
static uint8_t Spcp_CRC(uint8_t *data,uint8_t len)
{
        uint8_t res = 0;
        
do{
    res = res^data[len-3];
          len--;
  }while(len!=3);
    return res;
}





/*
发送函数
@向指定的目标设备发送一帧数据
*/
void Spcp_Send(
                      uint8_t GoalAddr  //目标地址
                     ,uint8_t cmd       //操作码
               ,uint8_t *buff     //数据
               ,uint8_t dataLen   //数据长度
              )
{
        uint8_t len = dataLen+7;      
  uint8_t *send_data = (uint8_t*)rt_malloc(len);
         if(dataLen!=0)
    rt_memcpy(send_data+5,buff,dataLen); //数据拷贝
         
    send_data[0]=SPCP_FHR;  //帧头
         send_data[1]=GoalAddr;  //接收方地址
          send_data[2]=SPCP_ADDR; //发送方地址
          send_data[3]=cmd;       //操作码
          send_data[4]=dataLen;   //数据长度
          send_data[4+dataLen+2] = SPCP_MFR; //帧位
          send_data[4+dataLen+1] = Spcp_CRC(send_data,len); //计算CRC
          Spcp_WriteData(send_data,len);
          rt_free(send_data);
}


/*
判断缓冲区是否为满
@BUFF : 缓冲区结构体指针
*/
static uint8_t Sbuf_full(SPCP_Buff *Buff)
{
  if((Buff->tial+1)%SPCP_BUFF_SIZE == Buff->head)
            return 1;
        else
            return 0;
}
/*
判断缓冲区是否为空
@Buff:缓冲区结构体指针
*/
static uint8_t Sbuf_Null(SPCP_Buff *Buff)
{
if(Buff->head == Buff->tial)
  return  1;
else
  return  0;
}

/*
获取缓冲区数据帧
@ 调用本函数可得到数据帧队列中排在最前面的一帧数据,也就是最先到达的一帧数据
@返回:1获取成功:返回数据帧类型指针  
       2获取失败:返回RT_NULL  (缓冲区空会导致返回NULL)
调用get函数返回的数据帧结构体,在调用前无需另外给数据帧类型指针另外申请空间
使用例程:
   FrameType  fra =  Spcp_GetBuffFrame();
   if(fra == NULL)
     printf("失败,缓冲区空")
    //访问数据帧数据
xxx=fra->Addr //得到发送方地址
xxx=fra->cmd  //得到操作码
xxx=fra->dataLen //得到数据长度
xxx=fra->dataLen[0]; //得到数据的第0个数据
*/
FrameType  Spcp_GetBuffFrame(void)
{
        static FrameType outframe = RT_NULL;
        if(Sbuf_Null(&Buff))
        {
          return  RT_NULL; //缓冲区空
        }
        if(outframe == RT_NULL)
        {
         outframe = (FrameType)rt_malloc(sizeof(Spcp_frame));
         outframe->Buff = RT_NULL;
        }
        if(outframe->Buff != RT_NULL)
        {
          rt_free(outframe->Buff);//释放
        }
  outframe->Buff = ( uint8_t*)rt_malloc( Buff.frame[Buff.head]->dataLen);
        
  outframe->Addr = Buff.frame[Buff.head]->Addr;
        outframe->Cmd = Buff.frame[Buff.head]->Cmd;
        outframe->dataLen = Buff.frame[Buff.head]->dataLen;
        rt_memcpy(outframe->Buff,Buff.frame[Buff.head]->Buff,Buff.frame[Buff.head]->dataLen);
  Buff.head = (Buff.head+1)%SPCP_BUFF_SIZE;//移动到下一个
        return  outframe;
}


/*
接收和解析数据 并把数据放到缓存区【本函数放到中断函数中,把接收到的字节传入本函数】
@data: 中断接收到的字节数据
*/
void Spcp_ISR_InputData(uint8_t data)
{  
        static  uint8_t  count=0;
        static  uint8_t  Fixed[5]={0,0,0,0,0};  //固定的接收字节长度 起始 到 数据长度
        static  uint8_t  *frameTemp = RT_NULL;    //临时变量
        
        if(count == 0)
        {
           if(data != SPCP_FHR){        count = 0; return;} //帧头不对则返回
                  Fixed[count] = data; //保存帧头
                  count ++;
                  return;
        }
         if(count == 1)
        {
          if(data != SPCP_ADDR){count = 0; return;} //不是本机地址
     Fixed[count] = data; //保存本机地址
                 count ++;
                 return;                        
        }
        
        if(count < 5) //小于5采用固定接收数值保存,目的是为了获取第4字节的数据长度
        {
           Fixed[count] = data; //保存数据
                 count ++;
                 return;
        }
        
  if(count == 5) //如果是等于5 ,说明要保存数据了
        {

      if(frameTemp != RT_NULL)  //判断上次是否使用过,使用过要释放
     {
             rt_free(frameTemp); //释放
                 }
                         frameTemp = (uint8_t*)rt_malloc(5+Fixed[4]+2);  //申请内存 固定长度+数据长度+校验与帧尾
                   rt_memcpy(frameTemp ,Fixed,5);//把固定长度拷贝到新申请的缓冲区
                   frameTemp[count] = data; //保存第一个数据
       count ++;
       return;                 
        }        
        if(count == (5+frameTemp[4])) //判断是校验字节
        {
              frameTemp[count] = data; //保存校验
        count ++;         
     return;        
        }
        if(count == (5+frameTemp[4]+1)) //判断是否是帧尾
        {         //判断帧尾或校验不对 扔掉本次数据包   
     if( (data != SPCP_MFR) || (  Spcp_CRC(frameTemp,7+frameTemp[4]) !=  (frameTemp[5+frameTemp[4]]) ) )
     {
                            count = 0;
                    return;
                 }
                 
                                   frameTemp[count] = data; //保存帧尾
                      count = 0;        
               
                 /*****************验证全部通过***************/
           if(Sbuf_full(&Buff))
                 {
                         //缓冲区满了
                          Buff.head = (Buff.head+1)%(SPCP_BUFF_SIZE);//移动到下一个
                        
                 }

                 
                                                Buff.frame[Buff.tial]->Addr = frameTemp[2];    //保存发送方地址
                                                Buff.frame[Buff.tial]->Cmd  = frameTemp[3];    //保存操作码
                                                Buff.frame[Buff.tial]->dataLen = frameTemp[4]; //保存数据长度
                                if(Buff.frame[Buff.tial]->Buff != RT_NULL)
                                         rt_free(Buff.frame[Buff.tial]->Buff); //释放数据空间
                                
                                                Buff.frame[Buff.tial]->Buff = (uint8_t*)rt_malloc(Buff.frame[Buff.tial]->dataLen); //申请数据空间
                                                rt_memcpy(Buff.frame[Buff.tial]->Buff,frameTemp+5,Buff.frame[Buff.tial]->dataLen); //把临时数据帧中的数据拷贝到数据空间
                                                Buff.tial = (Buff.tial+1)%(SPCP_BUFF_SIZE); //缓冲区指向下一个
                        count = 0;
#if  SPCP_SIG_ENABLE
                                  SPCP_SIG_SENDE_FUNC;
#endif
                        return;
        }
   frameTemp[count] = data; //保存数据
         count ++;         
}[/mw_shl_code]
回复

使用道具 举报

发表于 2019-6-21 14:06:27 | 显示全部楼层
谢谢分享,你可以用高级模式的贴代码功能弄更整齐些 I8G{$(I$W(}JA31FVO}7W.png
回复 支持 反对

使用道具 举报

发表于 2019-6-21 14:08:33 | 显示全部楼层
好帖子 很棒 谢谢楼主
回复 支持 反对

使用道具 举报

发表于 2021-7-30 10:34:25 | 显示全部楼层
楼主能分享下源码吗
回复 支持 反对

使用道具 举报

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

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-5-3 13:27 , Processed in 0.033059 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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