E70_433半双工无线模组,伪全双工方案
前言
要搞一个远程数据传输,性价比高的模块是433模块类型,一开始选用的是E70_433模组但是后来发现需要能够全双工,E62模块大概要贵50块钱,因此就继续尝试用E70模组,看看能不能降一点成本,因此就有了以下这个方案_φ_(..) 写作业
如果是全双工模块外侧输入数据可以直接接入串口:
A-(串口)-E62_433——(空中信道)
但是因为是半双工因此我需要加入一个mcu(stm32)控制串口
A-(串口)-stm32-(串口)-E70_433——(空中信道)
本文的设计内容为stm32里的转发程序
架构设计
全双工意味着输入的时候可以输出,半双工意味着只能选择输入或者输入,因此如果半双工切换状态的速度够快,在长的时间尺度上就可以实现全双工的效果。
外侧输入模块
首先,外侧流入数据的速度必须低于433模块数据吞吐的速度因为433模组切换状态和发送都需要时间。
其次,通讯可能会锻炼,比如突然有强信号干扰,因此必须有一个合适大小的数据缓存区域,为了方便存储和读取,必须采用队列存储方式
最后,注意中断
外侧输出模块
首先,由于433速度大于外侧数据吞吐速度,因此必须有一个合适大小的数据缓存区域(需要略大于输入缓存区)
其次,为了加快通讯速度,必须采用中断发送而不是阻塞发送,因此需要使用队列存贮方式。
最后,注意中断
内侧发帧模块
两组433间的通讯认为是内侧通讯,这里主要执行的功能是读取外侧输入模块封存的数据帧和内侧接受模块封存的数据帧,拼接完成后组包对外发送。
内侧接受模块
两组433间的通讯认为是内侧通讯,这里主要执行的功能是接受另一侧433模块传来的数据包。当数据包正常时,将数据解包封存进入外侧发送模块,并且将外侧数据封存一帧以实现连续传输;当数据包不正常时(缺头,缺尾,缺身子,长度错误…等等)丢弃这一帧数据,立即回传一个重发信号包,以确保数据的稳定性
实现代码
通用宏定义
#define BUFF_LEN_B 5000
#define BUFF_LEN_O 6000
#define BUFF_LEN 200
#define FRAME_CLUB_MAX 49 //帧组最大数量
#define M_TRUE 0x5a //未启用
#define M_FALSE 0xa5 //未启用
#define TRUE 1
#define FALSE 0
#define PPP_FLAG 0x7e //ppp协议的帧头和帧尾部
#define PPP_START 0xFF //ppp协议的帧的开始
#define E70_STATE_LISTEN 0x34 //433模块状态 接受数据包完成
#define E70_STATE_SEND 0x43 //433模块状态 发送数据包完成
#define E70_STATE_START 0x00 //433模块状态 准备建立通路
#define E70_STATE_OVERTIME 0x0E //433模块状态 接受数据超时请求重发 新增:数据错误请求重发
#define E70_STATE_LISTEN_E 0x3E //433模块状态 接受丢包反馈完成
#define E70_STATE_RESEND 0xE3 //433模块状态 重新发送数据包完成
#define PACK_MAX_DATA_LEN 100 //设定433模组间发送最大数据长度
#define PACK_HEAD_lEN 2 //设定包的头字节长度
#define PACK_LEN_lEN 2 //设定包的长度字节长度
#define PACK_NUMBER_lEN 2 //设定包的序号字节长度
#define PACK_FUNCTION_lEN 1 //设定包的类别字节长度
#define PACK_END_lEN 2 //设定包的结尾长度
#define PACK_OUTDATA_LEN (PACK_HEAD_lEN +\
PACK_LEN_lEN+\
PACK_NUMBER_lEN+\
PACK_FUNCTION_lEN+\
PACK_END_lEN)
//设定433模组间除数据外的长度
#define PACK_MAX_LEN (PACK_OUTDATA_LEN + PACK_MAX_DATA_LEN)
//设定433模组最大的数据长度
#define PACK_HEAD_DATA 0x1234 //设定433模组包头
#define PACK_END_DATA 0x4321 //设定433模组包头
#define PACK_FUNCTION_WIFI 0x01 //设定433模组包功能:wifi方面启动发包 wifi start (未启用)
#define PACK_FUNCTION_DATA 0x02 //设定433模组包功能:数值 data
#define PACK_FUNCTION_NULL 0x03 //设定433模组包功能:空 null (未启用)
#define PACK_FUNCTION_4G 0x04 //设定433模组包功能:4g方面启动发包 4g start (未启用)
#define PACK_FUNCTION_RESEND 0x05 //设定433模组包功能:接受方重新发送上一包数据
#define LOSE_HEAD_COUNT 0x02 //设定包头丢失最大计数
#define LOSE_HEAD_MODE_FINE 0x00 //设定包头丢失模式:无异常
#define LOSE_HEAD_MODE_WAIT 0x01 //设定包头丢失模式:等待判断
#define LOSE_HEAD_MODE_GET 0x02 //设定包头丢失模式:获取包尾
#define LOSE_HEAD_MODE_OUT 0x03 //设定包头丢失模式:获取超时
#define LOSE_TAIL_MODE_FINE 0x00 //设定包尾丢失模式:无异常
#define LOSE_TAIL_MODE_WAIT 0x01 //设定包尾丢失模式:等待判断
#define LOSE_TAIL_MODE_GET 0x02 //设定包尾丢失模式:获取包尾
#define LOSE_TAIL_MODE_OUT 0x03 //设定包尾丢失模式:获取超时
#define E70_TIME_OUT 500 // (单位是ms)
#define EC20_TIME_OUT 200 // (单位是ms)
#define LOSE_HEAD_TIME_OUT 100 // (单位是ms)
#define LOSE_TAIL_TIME_OUT 100 // (单位是ms)
#define LOSE_PACK_TIME_OUT 200 // (单位是ms) 19200 帧间隔应该近似62.5m
struct _LOSE_HEAD_
{
__IO u32 Time; //包头丢失超时时间
__IO uint8_t Count; //帧头丢失计数
__IO uint8_t EndCount; //帧尾获取计数
__IO uint8_t Mode; //帧头丢失种类 //0.正常,1.等待判断,2.获取包尾,3.超时判别
};
struct _LOSE_TAIL_
{
__IO u32 Time; //帧尾丢失超时时间
__IO uint8_t Count; //帧头丢失计数
__IO uint8_t EndCount; //帧尾获取计数
__IO uint8_t Mode; //帧尾丢失种类 //0.正常,1.等待判断,2.获取包尾,3.超时判别
};
#define LOSE_HEAD _LOSE_HEAD_
#define LOSE_TAIL _LOSE_TAIL_
#define OUTSIDE USART2
#define INSIDE USART1
#define OUTSIDE_TX_BUFF USART2_TX_BUFF
#define OUTSIDE_RX_BUFF USART2_RX_BUFF
#define INSIDE_TX_BUFF USART1_TX_BUFF
#define INSIDE_RX_BUFF USART1_RX_BUFF
#define OUTSIDE_TX_LEN USART2_TX_LEN
#define OUTSIDE_RX_LEN USART2_RX_LEN
#define INSIDE_TX_LEN USART1_TX_LEN
#define INSIDE_RX_LEN USART1_RX_LEN
#define OUTSIDE_TX_QUEUE_LEN USART2_TX_QUEUE_LEN
#define OUTSIDE_TX_END_LEN USART2_TX_END_LEN
#define g_sysOutTime g_sysMTime
// 433 模块数据包接受完成后就是等待数据发送,数据发送完成后就是等待接受数据包,来回通讯
// 433 (send) ->- 433 (start) to 433 (send) --- 433 (listen) start mode
// 433 (send) -<- 433 (send) to 433 (listen) --- 433 (send)
// 433 (send) ->- 433 (send) to 433 (send) --- 433 (listen)
// or
// 433 (send) ->- 433 (send) to 433 (send) --- 433 (overtime)
// 433 (send) -<- 433 (send) to 433 (lis_e) --- 433 (send)
// 433 (re_s) ->- 433 (send) to 433 (re_s) --- 433 (listen) resend lost pack
// or
// 433 (re_s) ->- 433 (send) to 433 (re_s) --- 433 (overtime)
// 433 (re_s) -<- 433 (send) to 433 (listen) --- 433 (send) jump lost pack
代码解析
以上建一个.h文件丢入,然后#include
就可以了
其中OUTSIDE
是对外侧连接的串口
其中INSIDE
是对内侧连接的串口
其余有问题请留言_(´ཀ`」∠)_加班
通用工具函数
//在字符串1里找字符串2,返回字符串2结束在字符串1的位置,找不到时反馈0
uint16_t CompareString(uint8_t* Str1 ,uint8_t * Str2 ,uint16_t Str1_len,uint16_t Str2_len)
{
uint16_t outLen = 0;
uint16_t startLen = 0;
uint16_t i = 0;
if(Str1_len<Str2_len||Str2_len==0||Str1_len==0) //校验异常
return 0;
startLen=Str1_len-Str2_len;
for(i=0;i<=startLen;i++)
{
if(*(Str1+i)==*Str2) //找到起始字符
{
for(outLen=0;outLen<Str2_len;outLen++)
{
if(*(Str1+i+outLen)!=*(Str2+outLen))
{
// printf("[test] i=%d, outLen=%d,*(Str1+i+outLen)=%x,*(Str2+outLen)=%x\r\n",
// i,outLen,*(Str1+i+outLen),*(Str2+outLen));
break;
}
}
if(outLen==Str2_len)
return outLen+i; //待验证
}
}
return 0;
}
//在字符串1里找字符串2,返回字符串2结束在字符串1的位置,找不到时反馈0
uint16_t GetPackLen(uint8_t* Str1,uint16_t len)
{
uint16_t outPackLen = 0;
uint16_t i = 0;
uint8_t temp = 0;
if(len==0)
return 0; //return fail
for(i=0;i<len;i++)
{
temp=(*(Str1+i))-'0';
if(temp>9) // 超上限
return 0; //return fail
outPackLen*=10; //升一位
outPackLen+=temp;
}
return outPackLen;
}
uint32_t CompareTimeBase(uint32_t early,uint32_t late)
{
if(early>late)
{
if(early-late<10) //消除bug引起的浮动误差,不影响正常判别
{
return 0;
}
return (0xFFFFFFFF - early + late);
}
else
{
return (late - early);
}
}
uint32_t GetTickCount(void)
{
return sysTime;
}
//***********************************************************************
//将发送区数数据重新按着433私有协议个格式重新打包
//***********************************************************************
void pack433Data (u8 *pData,u16 len,u8 mode)
{
u8 temporary[PACK_MAX_LEN] ={0};
//-存入包头
temporary[0]=PACK_HEAD_DATA>>8; // 存入包头高字节
temporary[1]=PACK_HEAD_DATA&0xff; // 存入包头低字节
//-存入帧长度
temporary[2]=(PACK_OUTDATA_LEN+len)>>8; // 存入帧长度高字节
temporary[3]=(PACK_OUTDATA_LEN+len)&0xff;// 存入帧长度低字节
//-存入帧序号(——)还未启用
temporary[4]=0; // 存入帧序号高字节
temporary[5]=0; // 存入帧序号低字节
//-存入帧功能
temporary[6]=mode; // 存入帧功能
memcpy(&temporary[7],pData,len); //-存入帧数据
//-存入尾头
temporary[7+len]=PACK_END_DATA>>8; // 存入包尾高字节
temporary[8+len]=PACK_END_DATA&0xff; // 存入包尾低字节
memcpy(pData,temporary,PACK_OUTDATA_LEN+len);//打包完成
}
//***********************************************************************
//将接收区数据重新按着帧格式封存
//***********************************************************************
//static u16 len=0;
void saveFrameToBUFF(u16 pointnNumber,u8 timeoutFlag)
{
if(saveFrameFlag) //存帧标志位
return;
else
saveFrameFlag=1;
if(frame_club[frame_index_n][0]>pointnNumber) //获取帧长度
pointnNumber+=BUFF_LEN_B;
if(timeoutFlag==TRUE)
{ //2是,帧组没有帧且要求立刻生成一帧
if(frame_index_l>0) //清理非逻辑异常
{
saveFrameFlag=0;
return;
}
frame_index_l=frame_index_l+2; //帧长度加2
if(pointnNumber>=BUFF_LEN_B)
{
pointnNumber-=BUFF_LEN_B;
}
frame_club[frame_index_n][1]=pointnNumber; //存入当前帧的结束位(后一位)
frame_index_n=frame_index_n+1;
if(frame_index_n>=FRAME_CLUB_MAX)
frame_index_n=0; //帧组循环存储
frame_club[frame_index_n][0]=pointnNumber; //存入当前帧(起始帧) 1
if(frame_index_n==frame_index_s) //帧组出现覆盖
{
#ifdef DEBUG_433
printf("[E] i=%d,l=%d\n",frame_index_s,frame_index_l);
#endif
frame_index_n=frame_index_n==0?FRAME_CLUB_MAX-1:frame_index_n-1;
//取消当前帧
frame_club[frame_index_n][0]=pointnNumber;//跳过帧
saveFrameFlag=0;
frame_index_l-=2; //取消队列增长
return; //阻止封帧
}
if(frame_index_l>2) //清理非逻辑异常
{
#ifdef DEBUG_433
printf("[E],2\n");
#endif
frame_index_n=frame_index_n==0?FRAME_CLUB_MAX-1:frame_index_n-1;
frame_index_l-=2; //取消队列增长
saveFrameFlag=0;
return;
}
frame_club[frame_index_n][0]=pointnNumber; //存入当前帧(起始帧)2
saveFrameFlag=0;
return;
}
if(pointnNumber-frame_club[frame_index_n][0]>=PACK_MAX_DATA_LEN-5) //收到封帧请求时在2种情况下可以封帧
//1是,当前帧长度满足要求
{
//len=pointnNumber-frame_club[frame_index_n][0];
frame_index_l=frame_index_l+2; //帧长度加2
#ifdef DEBUG_433
//printf("[T] add pack len:%d,%d,%d,%d,%d\n",len,pointnNumber,frame_club[frame_index_n][0],frame_club[frame_index_n-1][0],frame_index_n);
#endif
if(pointnNumber>=BUFF_LEN_B)
{
pointnNumber-=BUFF_LEN_B;
}
frame_club[frame_index_n][1]=pointnNumber; //存入当前帧的结束位(后一位)
frame_index_n=frame_index_n+1;
if(frame_index_n>=FRAME_CLUB_MAX)
frame_index_n=0; //帧组循环存储
if(frame_index_n==frame_index_s) //帧组出现覆盖
{
#ifdef DEBUG_433
printf("[E] i=%d,l=%d\n",frame_index_s,frame_index_l);
#endif
frame_index_n=frame_index_n==0?FRAME_CLUB_MAX-1:frame_index_n-1;
//取消当前帧
frame_index_l-=2; //取消队列增长
frame_club[frame_index_n][0]=pointnNumber;//跳过帧
saveFrameFlag=0;
return; //阻止封帧
}
frame_club[frame_index_n][0]=pointnNumber; //存入当前帧(起始帧)
saveFrameFlag=0;
return;
}
saveFrameFlag=0;
}
代码解析
以上主要关注2个函数:
pack433Data
和saveFrameToBUFF
这是2个主要的功能函数
pack433Data
这个函数用于把需要发送向433模块的数据按着帧包格式封存
saveFrameToBUFF
这个函数用于将外测数据封存入帧队列(一共2个队列,一个帧队列,一个是输出队列)
第一个输入参数是缓存数据队列的指针号,第二个参数表示是否强制封帧,
如果强制封帧当帧队列长度为0时可以封入,如果不强制封帧当缓存数据队列长度到达帧上限则封一帧。
另外在这个函数里有一个很重要的设计:saveFrameFlag。
这是这是用于防止中断侵入逻辑的防护手段。
串口输入输出函数
void USART1_IRQHandler(void) //串口1中断 433
{
u8 res;
if(USART_GetITStatus(INSIDE,USART_IT_RXNE)) //收到一个字就立刻发送
{
res= USART_ReceiveData(INSIDE);
//USART_SendData(USART2,res); //1转2
INSIDE_RX_BUFF[INSIDE_RX_LEN++]=res;
if(INSIDE_RX_LEN>=BUFF_LEN)
{
INSIDE_RX_LEN=0;
}
if(INSIDE_RX_LEN==1) //首字节判断
{
if(res!=(PACK_HEAD_DATA>>8)) //高位不正确
{
INSIDE_RX_LEN=0; //清除长度
loseHead.Count++; //帧头丢失
}
else
loseHead.Count=0; //清除计数
}
if(INSIDE_RX_LEN==2) //次字节判断
{
if(res!=(PACK_HEAD_DATA&0xff)) //低位不正确
{
INSIDE_RX_LEN=0; //清除长度
loseHead.Count+=2; //帧头丢失(严重)
}
else
loseHead.Count=0; //清除计数
}
if(INSIDE_RX_LEN>2&&loseHead.Count<LOSE_HEAD_COUNT)
loseHead.Count=0; //清除计数
if(loseHead.Mode==LOSE_HEAD_MODE_WAIT) //当前为帧头丢失判别
{
if(res==(PACK_END_DATA&0xff)&&loseHead.EndCount==1)//获取包尾低位
{
loseHead.EndCount=2; //包尾长度记录
}
else if(res==(PACK_HEAD_DATA>>8)&&loseHead.EndCount==0)//获取包尾高位
{
loseHead.EndCount=1; //包尾长度记录
}
else
{
loseHead.EndCount=0; //无包尾字段
}
if(loseHead.EndCount>=2) //获取包尾
loseHead.Mode=LOSE_HEAD_MODE_GET; //修改标志为包尾取得
loseHead.Time=GetTickCount(); //包头丢失超时计时
}
if(loseTail.Mode==LOSE_TAIL_MODE_WAIT) //当前为帧尾丢失判别
{
if(res==(PACK_END_DATA&0xff)&&loseTail.EndCount==1)//获取包尾低位
{
loseTail.EndCount=2; //包尾长度记录
}
else if(res==(PACK_HEAD_DATA>>8)&&loseTail.EndCount==0)//获取包尾高位
{
loseTail.EndCount=1; //包尾长度记录
}
else
{
loseTail.EndCount=0; //无包尾字段
}
if(loseTail.EndCount>=2) //获取包尾
loseTail.Mode=LOSE_TAIL_MODE_GET; //修改标志为包尾取得
loseTail.Time=GetTickCount(); //包头丢失超时计时
}
if(loseHead.Count>=LOSE_HEAD_COUNT) //帧头丢失超过容忍
{
loseHead.Count=LOSE_HEAD_COUNT; //防止溢出
loseHead.Time=GetTickCount(); //包头丢失超时计时
loseHead.Mode=LOSE_HEAD_MODE_WAIT; //准备判别丢包种类
}
g_sys433Time=GetTickCount(); //载入当前系统时间
}
}
void USART2_IRQHandler(void) //串口2中断 外测
{
u8 res;
if(USART_GetITStatus(OUTSIDE,USART_IT_RXNE)) //收到一个字就立刻发送
{
res= USART_ReceiveData(OUTSIDE);
//USART_SendData(USART1,res); //2转1
OUTSIDE_RX_BUFF[OUTSIDE_RX_LEN++]=res; //存入收区
if(OUTSIDE_RX_LEN>=BUFF_LEN_B)
OUTSIDE_RX_LEN=0;
saveFrameToBUFF(OUTSIDE_RX_LEN,FALSE); //存入数据到帧组里
g_sys4GTime=GetTickCount(); //载入当前系统时间
}
if(USART_GetITStatus(OUTSIDE,USART_IT_TXE)!=RESET)//发送完成标志
{
if(setDataFlag) //如果正在载入数据,先关闭中断
{
USART_ClearITPendingBit(OUTSIDE,USART_IT_TXE);//先清除中断标志位
USART_ITConfig(OUTSIDE,USART_IT_TXE, DISABLE);//无数据则关闭中断
outPutFlag=0;
}
else if(OUTSIDE_TX_QUEUE_LEN>0) //队列里有数据
{
OUTSIDE_TX_QUEUE_LEN--; //队列长度减1
USART_SendData(OUTSIDE, OUTSIDE_TX_BUFF[OUTSIDE_TX_LEN++]);//send
if(OUTSIDE_TX_LEN>=BUFF_LEN_O)
OUTSIDE_TX_LEN=0;
USART_ClearITPendingBit(OUTSIDE,USART_IT_TXE);//先清除中断标志位
outPutFlag=1;
}
else
{
USART_ClearITPendingBit(OUTSIDE,USART_IT_TXE);//先清除中断标志位
USART_ITConfig(OUTSIDE,USART_IT_TXE, DISABLE);//无数据则关闭中断
outPutFlag=0;
}
}
//*************************************************************
// if(USART_GetITStatus(USART2, USART_IT_TXE) == RESET)
// {
// USART_ClearFlag(USART2,USART_IT_TXE);
// //USART_SendData(USART2, USART2_TX_BUFF[USART2_RX_LEN--]);//test
// }
//USART_ClearITPendingBit(USART2,USART_IT_TXE);
}
说个题外化,我很努力的让keil里排版好看:
但是复制过来就不整齐了,逼死了我这个强迫症!!!
(*▼ー(。-_-。)画风不对,如何相爱
咋办呢??
代码解析
以上一个是外测数据流通的串口中断,另一个是433数据流通时的串口中断。
433流向的串口中断主要执行3个功能
1,接受数据。
2,未进入包接受状态时抛弃非包头数据,并记录警报
3,在异常状态下抓取包尾数据
接受数据功能代表字段是这一段:
INSIDE_RX_BUFF[INSIDE_RX_LEN++]=res;
载入数据,并且移动坐标指针。
包头判别功能代表字段是这一段:
if(INSIDE_RX_LEN==1) //首字节判断
{
if(res!=(PACK_HEAD_DATA>>8)) //高位不正确
{
INSIDE_RX_LEN=0; //清除长度
loseHead.Count++; //帧头丢失
}
else
loseHead.Count=0; //清除计数
}
if(INSIDE_RX_LEN==2) //次字节判断
{
if(res!=(PACK_HEAD_DATA&0xff)) //低位不正确
{
INSIDE_RX_LEN=0; //清除长度
loseHead.Count+=2; //帧头丢失(严重)
}
else
loseHead.Count=0; //清除计数
}
通过检查存入的帧头是否正确来决定是否放弃这一帧数据
,包尾抓取的设计方法和这个类似就阐述了。
外侧数据流向的串口中断主要执行这2个功能:
1,获取数据封帧处理
2,读取发送队列发送数据
封帧功能主要通过调用这一个函数实现
saveFrameToBUFF(OUTSIDE_RX_LEN,FALSE); //存入数据到帧组里
发送数据功能主要通过这一段来实现
USART_SendData(OUTSIDE,OUTSIDE_TX_BUFF[OUTSIDE_TX_LEN++]);//send
这里也有一处不能忽视的设计:
setDataFlag
这同样是为了不让中断侵入逻辑而做的防御措施。(忘记对中断做防御,后果很严重)。
outPutFlag
这一处是我做着玩的,同样是为了防止中断侵入,但是后来发现没有必要,主要防御功能是用上一处实现的。
核心业务代码
发送模块
//*******************************************************************
// 433私有协议帧包发送模式 resend
//*******************************************************************
//
//
void resendFrameTo433 (void) //向433模组发送一帧数据
{
uint16_t frame_head = 0;
uint16_t frame_end = 0;
uint16_t frame_rhead = 0; //考虑到循环存储,因此把发送数据做个分段提取
uint16_t frame_rend = 0;
uint16_t i = 0; //测试用序号
frame_head=frame_club[FRAME_CLUB_MAX][0]; //取出帧头
frame_end =frame_club[FRAME_CLUB_MAX][1]; //取出帧尾
if(frame_head>frame_end) //帧头序号大于帧尾序号
{
frame_rhead= BUFF_LEN_B ; //头的结尾
frame_rend = 0; //尾的开头
}
else
{
frame_rhead=frame_end; //头的结尾是尾
frame_rend =frame_end ; //尾是尾
}
INSIDE_TX_LEN=(frame_rhead-frame_head)+
(frame_end-frame_rend); //获取发送长度
if(INSIDE_TX_LEN>PACK_MAX_DATA_LEN)
{
#ifdef DEBUG_433
printf("[re][e] resend len over\n");
#endif
INSIDE_TX_LEN=999; //临时处理长度错误问题
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
}
else
{
memcpy(&INSIDE_TX_BUFF[0],
&OUTSIDE_RX_BUFF[frame_head],
frame_rhead-frame_head); //复制第一段数据
memcpy(&INSIDE_TX_BUFF[frame_rhead-frame_head],
&OUTSIDE_RX_BUFF[frame_rend],
frame_end-frame_rend); //复制第二段数据
}
#ifdef DEBUG_433
printf("[re]%2d,%3d\n",INSIDE_TX_LEN,frame_index_l);
#endif
pack433Data(INSIDE_TX_BUFF,INSIDE_TX_LEN,PACK_FUNCTION_DATA);//重新打包数据
INSIDE_TX_LEN += PACK_OUTDATA_LEN; //打包数据的长度
uart_sendData(INSIDE, INSIDE_TX_BUFF,INSIDE_TX_LEN); //发送data的数据
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
e70_433_state=E70_STATE_SEND; //发送完成置433状态为发送
i = i; //去除编译的warning,无意义
}
//*******************************************************************
// 433私有协议帧包发送模式 send
//*******************************************************************
//
//
void sendFrameTo433 (void) //向433模组重新发送一帧数据
{
uint16_t frame_head = 0;
uint16_t frame_end = 0;
uint16_t frame_rhead = 0; //考虑到循环存储,因此把发送数据做个分段提取
uint16_t frame_rend = 0;
uint16_t i = 0; //测试用序号
if(frame_index_l>>1) //也就是已经存储了若干帧
{
frame_index_l-=2; //取出1帧
frame_head=frame_club[frame_index_s][0];//取出帧头
frame_end =frame_club[frame_index_s][1];//取出帧尾
frame_club[FRAME_CLUB_MAX][0]=
frame_club[frame_index_s][0];//保存帧头
frame_club[FRAME_CLUB_MAX][1]=
frame_club[frame_index_s][1];//保存帧尾
frame_club[frame_index_s][0]=0; //清除旧数据
frame_club[frame_index_s][1]=0; //清除旧数据
frame_index_s++; //发送帧组序号进1
if(frame_index_s>=FRAME_CLUB_MAX)
frame_index_s=0; //发送帧序号复位
//暂时不做帧组管理和相关校验 2019.10.15
if(frame_head>frame_end) //帧头序号大于帧尾序号
{
frame_rhead= BUFF_LEN_B ; //头的结尾
frame_rend = 0; //尾的开头
}
else
{
frame_rhead=frame_end; //头的结尾是尾
frame_rend =frame_end ; //尾是尾
}
INSIDE_TX_LEN=(frame_rhead-frame_head)+
(frame_end-frame_rend); //获取发送长度
if(INSIDE_TX_LEN>PACK_MAX_DATA_LEN)
{
#ifdef DEBUG_433
printf("[e] frame_head=%d\n",frame_head);
printf("[e] frame_end =%d\n",frame_end);
printf("[e] frame_index_s =%d\n",frame_index_s);
printf("[e] frame_index_n =%d\n",frame_index_n);
printf("[e] frame_head_n =%d\n",frame_club[frame_index_s-2][0]);
printf("[e] frame_end_n =%d\n",frame_club[frame_index_s-2][1]);
printf("[e] frame_head_n =%d\n",frame_club[frame_index_s-3][0]);
printf("[e] frame_end_n =%d\n",frame_club[frame_index_s-3][1]);
#endif
//INSIDE_TX_LEN=PACK_MAX_DATA_LEN; //临时处理长度错误问题
INSIDE_TX_LEN=999; //临时处理长度错误问题
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
}
else
{
memcpy(&INSIDE_TX_BUFF[0],
&OUTSIDE_RX_BUFF[frame_head],
frame_rhead-frame_head); //复制第一段数据
memcpy(&INSIDE_TX_BUFF[frame_rhead-frame_head],
&OUTSIDE_RX_BUFF[frame_rend],
frame_end-frame_rend); //复制第二段数据
}
#ifdef DEBUG_433
//printf("%2d,%3d,%5d,%5d\n",USART1_TX_LEN,frame_index_l,frame_head,frame_end);
//printf("%d,%3d,%3d\n",frame_index_l,frame_index_s,frame_index_n);
printf("%2d,%3d\n",INSIDE_TX_LEN,frame_index_l);
//printf("1\n");
#endif
pack433Data(INSIDE_TX_BUFF,INSIDE_TX_LEN,PACK_FUNCTION_DATA);//重新打包数据
INSIDE_TX_LEN += PACK_OUTDATA_LEN; //打包数据的长度
uart_sendData(INSIDE, INSIDE_TX_BUFF,INSIDE_TX_LEN); //发送data的数据
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
e70_433_state=E70_STATE_SEND; //发送完成置433状态为发送
}
i = i; //去除编译的warning,无意义
}
代码解析
这里是2个函数,一个是正常的send函数另一个是重新发送数据的resend函数
两个函数业务逻辑是一样的,唯一不同的是数据来源。在send函数里,数据来源是帧组队列,在resend函数里数据来源是备份帧。
(隐患消除,如果没有备份帧呢?比如一侧死机了重启后,另一侧要求resend,不用担心,此时会发送空帧重建连接不会出现故障。至于丢失的数据,丢了就丢了呗)
( ̄▽ ̄)~*
接收模块
//*******************************************************************
// 433私有协议帧包接受模式 receive
//*******************************************************************
//
//
void receiveFramefrom433 (void) //获取433模组的数据
{
u16 len=0;
u16 add_queue_len=0;
u16 i;
//u8 frame_ok =0; //全解包标志
while(INSIDE_RX_LEN||loseHead.Mode||loseTail.Mode)
{ //缓存区有数据就必须完成一次解包
switch(loseHead.Mode)
{
case LOSE_HEAD_MODE_GET:
case LOSE_HEAD_MODE_OUT:
#ifdef DEBUG_433
printf("[ERROR_433] loseHead.Mode :%d\n",loseHead.Mode);
#endif
memset(INSIDE_TX_BUFF,0,BUFF_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
pack433Data(INSIDE_TX_BUFF,INSIDE_TX_LEN,PACK_FUNCTION_RESEND);//重新打包数据
INSIDE_TX_LEN += PACK_OUTDATA_LEN; //打包数据的长度
uart_sendData(INSIDE, INSIDE_TX_BUFF,INSIDE_TX_LEN); //发送data的数据
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
memset(&loseHead,0,sizeof(loseHead)); //清除故障标志
return;
case LOSE_HEAD_MODE_WAIT: //已经判断为头数据丢失
if(CompareTimeBase(loseHead.Time,GetTickCount())>LOSE_HEAD_TIME_OUT)
{
#ifdef DEBUG_433
printf("[ERROR_433] loseHead time out\n");
#endif
loseHead.Mode=LOSE_HEAD_MODE_OUT;
}
return; //超时等待主要是为了消除残留数据
default:; //无异常
}
switch(loseTail.Mode)
{
case LOSE_TAIL_MODE_GET:
case LOSE_TAIL_MODE_OUT:
#ifdef DEBUG_433
printf("[ERROR_433] loseTail.Mode :%d\n",loseTail.Mode);
#endif
memset(INSIDE_TX_BUFF,0,BUFF_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
pack433Data(INSIDE_TX_BUFF,INSIDE_TX_LEN,PACK_FUNCTION_RESEND);//重新打包数据
INSIDE_TX_LEN += PACK_OUTDATA_LEN; //打包数据的长度
uart_sendData(INSIDE, INSIDE_TX_BUFF,INSIDE_TX_LEN); //发送data的数据
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
INSIDE_TX_LEN=0;
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
memset(&loseTail,0,sizeof(loseTail)); //清除故障标志
return;
case LOSE_TAIL_MODE_WAIT: //已经判断为头数据丢失
if(CompareTimeBase(loseTail.Time,GetTickCount())>LOSE_TAIL_TIME_OUT)
{
#ifdef DEBUG_433
printf("[ERROR_433] loseTail time out\n");
#endif
loseTail.Mode=LOSE_TAIL_MODE_OUT;
}
return; //超时等待主要是为了消除残留数据
default:; //无异常
}
if(INSIDE_RX_LEN>=4) //接受长度包括了长度字节
{
len=0;
len+=(INSIDE_RX_BUFF[2]<<8);
len+=(INSIDE_RX_BUFF[3]); //获取应该接受的包长度
if(INSIDE_RX_LEN>=len) //理论上完全接受了
{
if(INSIDE_RX_BUFF[len-2]==(PACK_END_DATA>>8)&&
INSIDE_RX_BUFF[len-1]==(PACK_END_DATA&0xff)
) //包尾获取成功
{
add_queue_len=len-PACK_OUTDATA_LEN; //获取data长度
setDataFlag=1; //正在载入串口数据
OUTSIDE_TX_QUEUE_LEN+=add_queue_len;
if(OUTSIDE_TX_QUEUE_LEN>=BUFF_LEN_O)
{
//OUTSIDE_TX_QUEUE_LEN=BUFF_LEN;
OUTSIDE_TX_QUEUE_LEN-=add_queue_len;
#ifdef DEBUG_433
printf("[ERROR_433] Too late to outgoing,give up\n");
#endif
}
else
{
for(i=0;i<add_queue_len;i++) //中断发送数据
{
OUTSIDE_TX_BUFF[OUTSIDE_TX_END_LEN++]=INSIDE_RX_BUFF[PACK_OUTDATA_LEN-PACK_END_lEN+i];
if(OUTSIDE_TX_END_LEN>=BUFF_LEN_O)
OUTSIDE_TX_END_LEN=0;
}
}
setDataFlag=0; //串口数据载入完成
if(outPutFlag==0) //如果没在对外输出则开启中断
USART_ITConfig(OUTSIDE,USART_IT_TXE, ENABLE); //获取数据完毕,开启中断进行发送
// // 测试模式封存数据入缓存区域,原地返回
//
// for(i=0;i<add_queue_len;i++)
// {
// OUTSIDE_RX_BUFF[OUTSIDE_RX_LEN++]=INSIDE_RX_BUFF[PACK_OUTDATA_LEN-PACK_END_lEN+i];
// if(OUTSIDE_RX_LEN>=BUFF_LEN_B)
// OUTSIDE_RX_LEN=0;
// }
// //over
if(INSIDE_RX_LEN==len) //数据获取完全
{
if(INSIDE_RX_BUFF[6]==PACK_FUNCTION_RESEND) //数据需要重新发送
{
#ifdef DEBUG_433
printf("[DEBUG_433] pack fail :[");
for(i=0;i<len;i++)
printf("%x ",INSIDE_RX_BUFF[i]);
printf("] [end] \n");
printf("[DEBUG_433] len :%x\n",len);
#endif
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
e70_433_state=E70_STATE_LISTEN_E; //解包反馈数据重发
g_sysOutTime=GetTickCount(); //更新存储的时间戳
return;
}
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
e70_433_state=E70_STATE_LISTEN; //解包有效更新状态
g_sysOutTime=GetTickCount(); //更新存储的时间戳
do{
saveFrameToBUFF(OUTSIDE_RX_LEN,TRUE); //强制封一帧
}
while(frame_index_l==0); //防止中断引起封帧失败
return;
}
else //还有第二帧数据
{
for(i=0;i<INSIDE_RX_LEN-len;i++)
INSIDE_RX_BUFF[i]=INSIDE_RX_BUFF[i+len]; //搬运数据
INSIDE_RX_LEN=USART1_RX_LEN-len;
#ifdef DEBUG_433
printf("[DEBUG_433] another frame\n");
#endif
}
}
else
{
#ifdef DEBUG_433
printf("[DEBUG_433] pack fail :[");
for(i=0;i<len;i++)
printf("%x ",INSIDE_RX_BUFF[i]);
printf("] [end] \n");
printf("[DEBUG_433] len :%x\n",len);
#endif
//长度达标,但是包尾数据判断异常
loseTail.Mode=LOSE_TAIL_MODE_WAIT; //置入数据丢失标志位
loseTail.Time=GetTickCount(); //载入当前系统时间
for(i=PACK_OUTDATA_LEN;i<INSIDE_RX_LEN;i++) //审核包尾异常情况
{
if(INSIDE_RX_BUFF[i-2]==(PACK_END_DATA>>8)&&
INSIDE_RX_BUFF[i-1]==(PACK_END_DATA&0xff)) //发现包尾
{
loseTail.Mode=LOSE_TAIL_MODE_GET; //置入包尾获取标志位
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
return ;
}
}
}
}
else if(INSIDE_RX_BUFF[len-2]==(PACK_END_DATA>>8)&&
INSIDE_RX_BUFF[len-1]==(PACK_END_DATA&0xff)&&
INSIDE_RX_LEN<len
)
{
#ifdef DEBUG_433
printf("[DEBUG_433] pack lose :[");
for(i=0;i<INSIDE_RX_LEN;i++)
printf("%x ",INSIDE_RX_BUFF[i]);
printf("] [end] \n");
printf("[DEBUG_433] len :%x,%x\n",len,INSIDE_RX_LEN);
#endif
//提前获取包数据,包长字段错误或者包中字段丢失
loseTail.Mode=LOSE_TAIL_MODE_GET; //置入包尾获取标志位
memset(INSIDE_RX_BUFF,0,BUFF_LEN); //清除接收区数据
INSIDE_RX_LEN=0;
return;
}
}
}
i=i; //去除warn
}
代码解析
这一段可难了我写了1天多,希望大家能看懂
这一段代码主要实现3个功能
1,将接受缓存区域的数据按着包格式解包,
2,验证解包后数据的正确行,正确存入发送队列,错误则生成警报
3,当存在警报时,如果满足条件就立即封重发请求
数据正确的时候的注意事项
当数据正确后必然能够将数据封入对外发送队列,但是一定不能被中断影响了逻辑。并且发送缓存区域必须比接受缓存区域稍大一点(我就多给了1k)因为这样才能确保数据阻塞时不会溢出。(从代码调试注释里,应该能看出我吃的苦)
数据不正确时候的注意事项
如果能够进入这里进行数据判断,必然已经通过了包头检验,因此基本只有以下3种包错误情况:
包中段错误,包尾部错误,包长错误。
其中包长因为是一个判断依据因此,最终只会生成包中段或者包尾部错误。
这两种错误最终会有2种表现形式:
缺少尾部或者尾部提前出现
缺少尾部的错误我用超时判断作为为处理方案,生成警报
尾部提前出现我认为这一包应该是发送完成了,立即发出重发警报。
另外由于头部缺少和尾部缺少都是在接受时段产生的错误,因此我都在上述这个函数里进行实现。这样上述这个函数就会有3种主要运行形态:
头缺少
尾缺少
正在接受(或者刚好接受完成)
其中
头缺少状态由接受中断逻辑判断产生;
尾缺少状态由接受完成逻辑判断产生。
在我的测试和研究之下我发现433模组的绝大部分的传输失效都是出现字段丢失,比如12345变成1345,因此我没有再进一步设计包序号和包校验,如果后续这个项目还能进行下去我会补上这一段程序设计。
代码流程
为了方便读者理解这一段代码,我专门用语言描述一下这个函数模块的运行流程:
首先进入函数,
当接受区域有数据(头正常)或者警报有状态时进入运行区间:
{
当警报为丢头时,进入警报状态分拣,
{
如果是超时或者有尾
{
立即发送回包请求重发;
清除警报缓存
}
如果是等待判别
{
看一下是否超时
}
return;
}
当警报是丢尾时
{
//流程相同略
}
如果收区字段长度达到4(也可以是9,因为9是空包长度)
{
获取包长字段
如果收区字段长度达到包长字段
{
检验包尾,如果包尾正常
{
取出数据
封入对外发送队列
{
清除多余数据
强制封一个发送帧用于保证通讯不断裂
}
{
如果有帧重叠,重新再解一次(无用的设计)
}
}
如果包尾错误
{
查看是否有包尾在前面的字段
有则警报获取包尾
无则警报超时
}
}
{如果提前获取包尾
认为包错误,警报获取包尾
}
}
}
结语
本次程序设计还有2处没有设计:
1是通讯完全断裂的完善解决方案
2队列异常守护
当前对于通讯异常只要收到了2个以上的字节就能够重新建立连接,但是如果完全断开通讯就只有一个还未设计完善的重发机制,这部分我没有写在博客里因为还不够完善。
另外队列存贮通常有3个要素:1队列长度,2当前位置,3使用位置。我用队列长度做队列控制,但是在某种异常下,队列长度和位置会不协调导致数据残留。(例如中断,因此我重新设计了中断防御机制)为了运行稳定,应定时处理一下帧队列状态用于保护帧组稳定。
综上,如果有什么意见和建议欢迎留言指正,如果觉得还OK点个赞再走吧
附带
以上代码我从11月2号到11月14号编写完成,基本用时7天,其中主要的问题是在解决bug,这个bug是帧组异常导致的数据残留。我用了4天去解决bug,1天解决设计失误,1天设计方案,1天编写代码。我希望,看了这一篇博文的程序员,不要犯我的错误,不要让中断能够修改逻辑运算使用的数据变量,( ´・ω・)ノ(._.`)摸摸头
最后祝大家:
ヾヾ( ˘ ³˘人)ヾ做的全会٩(*Ӧ)و写的全对