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.5mstruct _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 failfor(i=0;i<len;i++){temp=(*(Str1+i))-'0';if(temp>9)                         // 超上限return 0;                        //return failoutPackLen*=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;elsesaveFrameFlag=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;                       //帧长度加2if(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; //存入当前帧(起始帧) 1if(frame_index_n==frame_index_s)        //帧组出现覆盖{#ifdef DEBUG_433printf("[E] i=%d,l=%d\n",frame_index_s,frame_index_l);#endifframe_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_433printf("[E],2\n");#endifframe_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);#endifif(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_433printf("[E] i=%d,l=%d\n",frame_index_s,frame_index_l);#endifframe_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个函数:
pack433DatasaveFrameToBUFF
这是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转2INSIDE_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++;                                                //帧头丢失}elseloseHead.Count=0;                                               //清除计数}if(INSIDE_RX_LEN==2)                        //次字节判断{if(res!=(PACK_HEAD_DATA&0xff))            //低位不正确{INSIDE_RX_LEN=0;                        //清除长度loseHead.Count+=2;                                         //帧头丢失(严重)}elseloseHead.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转1OUTSIDE_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--;                              //队列长度减1USART_SendData(OUTSIDE, OUTSIDE_TX_BUFF[OUTSIDE_TX_LEN++]);//sendif(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++;                                                //帧头丢失}elseloseHead.Count=0;                                               //清除计数
}
if(INSIDE_RX_LEN==2)                        //次字节判断
{if(res!=(PACK_HEAD_DATA&0xff))            //低位不正确{INSIDE_RX_LEN=0;                        //清除长度loseHead.Count+=2;                                         //帧头丢失(严重)}elseloseHead.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_433printf("[re][e] resend len over\n");#endifINSIDE_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_433printf("[re]%2d,%3d\n",INSIDE_TX_LEN,frame_index_l);#endifpack433Data(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++;                        //发送帧组序号进1if(frame_index_s>=FRAME_CLUB_MAX)frame_index_s=0;                      //发送帧序号复位//暂时不做帧组管理和相关校验 2019.10.15if(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_433printf("[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");#endifpack433Data(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_433printf("[ERROR_433] loseHead.Mode :%d\n",loseHead.Mode);#endifmemset(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_433printf("[ERROR_433] loseHead time out\n");#endifloseHead.Mode=LOSE_HEAD_MODE_OUT;}return;                                                                //超时等待主要是为了消除残留数据default:;                                                              //无异常}switch(loseTail.Mode){case LOSE_TAIL_MODE_GET:case LOSE_TAIL_MODE_OUT:#ifdef DEBUG_433printf("[ERROR_433] loseTail.Mode :%d\n",loseTail.Mode);#endifmemset(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_433printf("[ERROR_433] loseTail time out\n");#endifloseTail.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_433printf("[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;
//                  }
//                  //overif(INSIDE_RX_LEN==len)                                                          //数据获取完全{if(INSIDE_RX_BUFF[6]==PACK_FUNCTION_RESEND)              //数据需要重新发送{#ifdef DEBUG_433printf("[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);#endifmemset(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_433printf("[DEBUG_433] another frame\n");#endif}}else{#ifdef DEBUG_433printf("[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_433printf("[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天编写代码。我希望,看了这一篇博文的程序员,不要犯我的错误,不要让中断能够修改逻辑运算使用的数据变量,( ´・ω・)ノ(._.`)摸摸头
最后祝大家:
ヾヾ( ˘ ³˘人)ヾ做的全会٩(*Ӧ)و写的全对

E70_433半双工无线模组,伪全双工方案相关推荐

  1. PCB天线无线模组如何布局摆放?

    随着物联网的高速发展,市面上涌现出越来越多的智慧产品,如智能家居,智能交通,智能城市等,这些终端设备大都靠无线收.发模块来实现信息的传递与接收.随着市场竞争的加剧,硬件设备正以集成化的方向发展,天线也 ...

  2. 无线网卡、无线模块和无线模组的区别

    2022年11月10日的理解 1. 本质是一种东西,就是提供无线上网的一种设备(可以是抽象的软件设备,也可以是抽象的设计图纸中的功能设备,也可以具体的物理设备),但是对于设备的看待角度不同,对于设备的 ...

  3. 【IoT】华为发布 HiLink WiFi 无线模组

    华为已经推出了一款 HiLink Wi-Fi 无线模组. 由华为和四川爱联共同开发,模组分为 Lite 版.标准版和 Pro 版,最低售价为 9. 98 元. 这款 Hilink Wi-Fi 无线模组 ...

  4. 无线Mesh自组网方案,CV5200无线模组应用,支持高清数据远距离传输

    在非视距.快速移动条件下,利用无中心自组网的分布式网络构架,可实现语音.数据.图像等多媒体信息的实时交互. 与传统的通信网络相比,无线自组织网络具有一些突出的特点,包括无中心.自组织.多跳路由.动态的 ...

  5. 无人机远距离MESH自组网通信技术,CV5200无线模组方案应用

    近几年无人设备的大力发展,在无线通信领域技术上得到了大力融合应用,如无人机.无人车.无人船. 无人机无线图传技术,以无人机为载体,可适用于高速移动中传输图像/语音/数据信号,以便保持信号畅通连续. 无 ...

  6. 第十五届全国大学生智能汽车竞赛室外光电组全国总决赛方案

    第十五届全国大学生智能汽车竞赛 室外光电组全国总决赛方案 1. 赛事背景 全国大学生智能汽车竞赛是以智能汽车为研究对象的创意性科技竞赛,是面向全国大学生的一种具有探索性工程的实践活动,是教育部倡导的大 ...

  7. 直线模组和直线电机的区别

    现已进入智能时代了,在精密行业中,机器人也逐渐代替人工,自动化设备行业已经蓬勃发展了.作为重要传动原件的直线模组和直线电机也逐渐走进人们的生活,被广泛使用. 在日常生活中,很多人都以为直线模组和直线电 ...

  8. 三种组播×××承载方案对比

    方案一:基于PIM-SM/mGRE的承载方案 如图所示,公网运行PIM-SM协议,且PE 1.PE2和PE 3都支持mVRF实例. 不同的mVRF站点加入到同一个组播域(MD)中,通过MD内自动建立的 ...

  9. 内网无纸化会议/智慧教室实时同屏RTSP组播技术方案思考

    内网环境下,为了满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,好多开发者希望有RTSP的技术方案,用于小并发场景,特别是在组网环境好的有线环境下,使用RTSP服务配合组播,是 ...

最新文章

  1. 倒计时 4 天!高通人工智能应用创新大赛颁奖典礼线上隆重举行
  2. MFC单文档的背景上绘制一个可以自动显示文字的长方形
  3. 砂石到芯片转变旅程:一千多道工序,数百英里
  4. 【BZOJ】3456: 城市规划 动态规划+多项式求逆
  5. 非阻塞同步机制和CAS
  6. Django(part38)--制作登录界面
  7. .NET 6 的 docker 镜像可以有多小
  8. Flask 第三方组件之 Migrate
  9. 为什么我们删除Babel的舞台预设:实验性提案的明确选择加入
  10. php sql 长字符串 查找被包含的短字符串_PHP字符串
  11. CoreOS和Docker入门
  12. 【Qt串口调试助手】1.4 - 16进制接收/发送
  13. Linux使用FlowScan
  14. 机器学习算法基础6-模型保存与加载、逻辑回归、Kmeans(聚类)
  15. MSF利用ms17_010实验
  16. 辉哥基于android S 分析过度动画以及窗口动画
  17. 【亲测可用】win7下移动硬盘无法访问,拒绝访问位置不可用时的解决方案!
  18. 血氧仪方案指夹式TFT指尖心率监测脉搏
  19. C语言——百钱百鸡问题分析(最优解法)
  20. Css_display: block inline inline-bock区别

热门文章

  1. oracle锁模式详解,oracle 锁讲解之一
  2. 奥鹏大学计算机基础与应用,北京大学36080002-计算机基础与应用-第三组2019秋(资料)...
  3. 三相电压不平衡下光伏并网逆变器控制策略Matlab仿真,实现 三相不平衡条件下逆变电流三相均衡。
  4. (翻译)面包屑导航的使用时机
  5. android wifi adb连接不上,WIFI无线adb调试android
  6. 学校怎么培养计算机竞争优势,浅谈中职计算机教学论文
  7. Ubuntu16.04挂载另外一个硬盘
  8. 孤独的人群,孤独的人
  9. table 固定表格宽度,设置列宽,超出内容省略号显示
  10. 昨天刚面完阿里巴巴—分享个人总结经验(已拿offer)