初中生
最后登录1970-1-1
在线时间 小时
注册时间2018-10-17
|
本帖最后由 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] |
|