RF4463F30半双工模组,伪全双工透传方案(STM32平台)(第二章,业务逻辑)
前言
环境已经搭建完毕,接下来逐步移植模块
核心代码编写
宏定义和变量声明
相关宏定义
//通用移植
#define OUTSIDE USART3
#define O_2_I_BUFF USART3_RX_BUFF //外部数据导入缓存
#define O_2_I_LEN USART3_RX_LEN //外部数据当前缓存指针
#define I_2_O_BUFF USART3_TX_BUFF //内部数据对外发出缓存
#define I_2_O_LEN USART3_TX_LEN //内部数据当前缓存指针
#define I_2_O_QUEUE_LEN USART3_TX_QUEUE_LEN
#define I_2_O_END_LEN USART3_TX_END_LEN
上面这部分是用于对接stm32平台
#define MasterFlag //主从机标志位,有这个标志位表示是主机(如果是从机则需要注释)
#define FRAME_CLUB_MAX 20 //最大帧组队列长度
#define BUFF_LEN 100
#define BUFF_LEN_I 2000 //输入缓存区域
#define BUFF_LEN_O 3000 //输出缓存区域
#define BUFF_LEN_B BUFF_LEN_I
上面这部分是用于划分存储空间
#define PACK_MAX_DATA_LEN 35 //设定433模组间发送最大数据长度
#define PACK_LEN_lEN 1 //设定包的长度字节长度
#define PACK_NUMBER_lEN 2 //设定包的序号字节长度
#define PACK_FUNCTION_lEN 1 //设定包的类别字节长度
#define PACK_CHECK_lEN 1 //设定包的校验字节长度
#define MAX_PACK_NUMBER 0xfe //设定包序号的最大值为0xfe
#define PACK_OUTDATA_LEN (PACK_LEN_lEN+\
PACK_NUMBER_lEN+\
PACK_FUNCTION_lEN+\
PACK_CHECK_lEN)
//设定433模组间除数据外的长度
#define PACK_MAX_LEN (PACK_OUTDATA_LEN + PACK_MAX_DATA_LEN)
//设定433模组最大的数据长度
上面这部分是用于分配协议的字段
这部分可以自定义,我暂时是设计了4个字段,核心字段是长度和序号,其他都可有可无
但是我建议也基本按我这一套的设计走,至少包含:
长度,序号,功能码,校验
#define payload_length PACK_MAX_LEN+4 //44
#define freq_channel 0
struct FlagType{
unsigned char reach_1s ;
unsigned char is_tx ;
unsigned char rf_reach_timeout ;
unsigned char run ;
unsigned char is_rx ;
};
上面这部分是用于声明通用的标志位的
相关变量定义
#define tdata {'s','w','w','x',\
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 \
,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 \
,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 }
static unsigned char tx__data[220] = tdata ;
extern U8 rx_buf[100];
上面这部分是用于分配发送和接受缓存区域
长度和内容不重要,但是至少要大于等于发送长度,内存够用的话就暂时不必要优化一般可以把 220 替换成 payload_length 。
extern uint16_t frame_club[FRAME_CLUB_MAX+1][2]; //最大帧组 (新增的最后一位表示上一帧发送的数据)
extern __IO uint16_t frame_index_n; //当前的帧序号
extern __IO uint16_t frame_index_s; //准备发送的帧序号
extern __IO uint16_t frame_index_l; //奇数表示还在接受,偶数表示等待接受
extern uint8_t O_2_I_BUFF[BUFF_LEN_I];
extern uint8_t I_2_O_BUFF[BUFF_LEN_O];
extern uint16_t O_2_I_LEN;
extern uint16_t I_2_O_LEN;
extern uint16_t I_2_O_QUEUE_LEN;
extern uint16_t I_2_O_END_LEN;
extern __IO u32 g_sysUsartTime;
extern __IO u32 g_sysUsartTime_start;
extern __IO u32 g_buff_len;
extern __IO uint8_t saveFrameFlag;
extern uint8_t INSIDE_TX_BUFF[PACK_MAX_LEN]; //对内发送所需的临时缓存区域
extern uint16_t INSIDE_TX_LEN; //对内发送所需数据长度
extern __IO uint8_t outPutFlag; //对外输出标志位
extern __IO uint8_t setDataFlag; //编辑串口数据标志位
extern struct FlagType Flag;
上面这部分是用于分配数据队列缓存区域
主要有两个队列:
帧队列 frame_club
对外输出队列 I_2_O_BUFF
其中帧队列 存储的是指针 数据还是放在这里 O_2_I_BUFF
到此为止,主要的全局变量和宏定义已经声明了,接下来就是实现功能的代码了
工具函数
两个主要的工具函数
//***********************************************************************
//将发送区数数据重新按着433私有协议个格式重新打包
//***********************************************************************
void pack433Data (u8 *pData,u16 len,u8 mode)
{
u8 temporary[PACK_MAX_LEN] ={0};
//-存入帧长度
temporary[0]=len; // 存入帧长度低字节
//-存入帧序号(——)还未启用
temporary[1]=0; // 存入帧序号高字节
temporary[2]=0; // 存入帧序号低字节
//-存入帧功能
temporary[3]=mode; // 存入帧功能
//-存入校验
temporary[4]=0; // 存入校验(——)还未启用
memcpy(&temporary[5],pData,len); //-存入帧数据
// //-存入校验
// temporary[7+len]=0-packSumCheck(temporary,9+len);//存入校验
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_l>>1)>=FRAME_CLUB_MAX-2||
(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;
}
第一个函数是帧队列里的数据或者其他特殊数据,按着协议格式进行封装
第二个函数是将 O_2_I_BUFF 的缓存队列指针根据情况封入帧队列中。在这个函数里有一个关键的标志位: saveFrameFlag 用于防止被中断干扰队列。
功能函数
发送数据函数
void test_tx_data2(u8 *pdata)
{
//delay_ms(1);
Flag.is_tx = 1;
Flag.run=0;
fifo_reset();
m_spi_write_fifo(pdata);
enable_tx_interrupt();
clr_interrupt();
tx_start();
rf_timeout = 0;
Flag.rf_reach_timeout = 0;
while(Flag.run!=2) //等待发送成功
{
if(Flag.rf_reach_timeout)
{
printf("send data timeout\n");
sdn_all_reset(); //
}
}
Flag.run=0;
//clr_interrupt();
Flag.is_tx=0; //转换为接收模式
rx_init(); //接收模式初始化
Flag.run=0;
}
发送数据,并且发送完成后自动转为接受模式,如果发送动作超时,则完全复位模块。
时间管理函数
//**************************************************
//函数名 :时间戳管理函数
//功能 :判断超时,管理标志位
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void TIME_Process(void)
{
u32 nowSysTime=0;
nowSysTime= GetTickCount(); //获取当前时间戳
if(CompareTimeBase(m_time.recevie_Time,nowSysTime)>RECEVICE_TIME_OUT)//接收间隔超时
{
Flag.is_tx=1; //(简单操作)转换为发送模式
m_test.Count++; //记录错误次数
m_time.recevie_Time =nowSysTime;
printf("time out,%d,%d,%d\n",m_time.recevie_Time,m_test.Count,m_test.Count2);
}
}
主机用于判断是否出现接收超时,如果有则则转换为接受模式并且准备进行重连接
模块工作状态函数
//**************************************************
//函数名 :数据管理函数
//功能 :进行状态位切换
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void SI4463_Process(void)
{
//--------------------数据收发--------------------
if(Flag.is_tx)
{
test_tx_data2(tx__data); //发送
}
else
{
//test_Receive2(); //接受
receiveFramefrom433(); //数据接收
}
}
根据标志位选择数据的收发情况
将数据帧提取放入发送区的函数
//**************************************************
//函数名 :对内发送数据打包函数
//功能 :将缓存区域数据转移一部分进行封包,存入数据发送缓存区域中
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void pack_O_2_I_Frame(void)
{
uint16_t frame_head = 0;
uint16_t frame_end = 0;
uint16_t frame_rhead = 0; //考虑到循环存储,因此把发送数据做个分段提取
uint16_t frame_rend = 0;
uint16_t i = 0; //测试用序号
do{
saveFrameToBUFF(O_2_I_LEN,TRUE); //强制封一帧
}
while(frame_index_l==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);
#endif
INSIDE_TX_LEN=PACK_MAX_DATA_LEN+20; //临时处理长度错误问题
memset(INSIDE_TX_BUFF,0,INSIDE_TX_LEN); //清除发送区数据
}
else
{
memcpy(&INSIDE_TX_BUFF[0],
&O_2_I_BUFF[frame_head],
frame_rhead-frame_head); //复制第一段数据
memcpy(&INSIDE_TX_BUFF[frame_rhead-frame_head],
&O_2_I_BUFF[frame_rend],
frame_end-frame_rend); //复制第二段数据
}
#ifdef DEBUG_433
printf("%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; //打包数据的长度
//test
memcpy(&tx__data[4],INSIDE_TX_BUFF,PACK_MAX_LEN); //转移数据
memset(INSIDE_TX_BUFF,0,PACK_MAX_LEN); //清除发送缓存区数据
INSIDE_TX_LEN=0;
}
i = i; //去除编译的warning,无意义
}
这部分移植了E70 半双工方案里的读取帧的部分。
主业务流程为:
确保帧队列里有帧,然后把最老的帧提取出来,根据头和尾的指针位置,复制这一段数据到发送缓存区域
接收数据函数
//**************************************************
//函数名 :获取来自于433模块的帧数据
//功能 :将获取的帧数据进行解包转发操作
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void receiveFramefrom433 (void) //获取433模组的数据
{
if(Flag.run==2)
{
Flag.run=0;
clr_interrupt(); // clear interrupt
if((spi_read_buf[4] &0x08) == 0) // crc error check
{
spi_read_fifo();
fifo_reset();
m_test.Count2++;
Flag.is_rx=1; //接收动作喂狗
unpackFrame(); //解包函数
m_time.recevie_Time=GetTickCount();
//printf(",%d,%d,%d\n",m_time.recevie_Time,m_test.Count,m_test.Count2);
Flag.is_tx=1; //转换为发送模式
}
else
{
printf("---------------crc error-------------\n");
packSpecialFrame(PACK_FUNCTION_RESEND); //封包:请求重发
Flag.is_tx=1; //转换为发送模式
}
}
i=i;
}
接受数据,如果正确接受则对数据包进行解包,如果不正确接受,则一帧特殊数据包到发送区域。
解包函数
//**************************************************
//函数名 :对于帧进行解包
//功能 :对于帧数据进行解包处理
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void unpackFrame (void) //解包函数
{
u16 len =0;
u16 number =0;
u16 function =0;
u16 check =0;
u8 dataBuff[PACK_MAX_LEN]={0}; //划分缓存区域
u16 add_queue_len=0; //数据队列新增
memcpy(dataBuff,&rx_buf[4],PACK_MAX_LEN); //读取数据
len = dataBuff[0]; //获取帧长度
number = (dataBuff[1]<<8)+dataBuff[2]; //获取帧序号 //暂时未启用
function = dataBuff[3]; //获取帧功能
check = dataBuff[4]; //获取帧校验
number = number;
check = check;
switch(function) //根据帧功能进行分拣
{
case PACK_FUNCTION_RESEND: //数据需要重新发送
//空操作
//发送组里的数据不会因为发送而改变
//因此重新只需要重新进入发送姿态即可
break;
case PACK_FUNCTION_DATA:
add_queue_len=len ;
setDataFlag=1; //正在载入串口数据
I_2_O_QUEUE_LEN+=add_queue_len; //对外输出队列加长 (公共数据,线程锁包含)
setDataFlag=0; //串口数据载入完成
if(I_2_O_QUEUE_LEN>=BUFF_LEN_O)
{
I_2_O_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++) //中断发送数据
{
I_2_O_BUFF[I_2_O_END_LEN++]=dataBuff[i+PACK_OUTDATA_LEN];
if(I_2_O_END_LEN>=BUFF_LEN_O)
I_2_O_END_LEN=0;
}
}
#ifdef DEBUG_433
printf("T,%d,%d,%d\n",I_2_O_LEN,I_2_O_QUEUE_LEN,I_2_O_END_LEN);
#endif
if(outPutFlag==0) //如果没在对外输出则开启中断
USART_ITConfig(OUTSIDE,USART_IT_TXE, ENABLE); //获取数据完毕,开启中断进行发送
pack_O_2_I_Frame(); //向4463模块发送区封入数据帧
break;
case PACK_FUNCTION_START: //如果功能码字段为空表示此包是启动包(临时处理)
#ifdef DEBUG_433
printf("[ERROR_433] pack function == 0\n");
#endif
packSpecialFrame (PACK_FUNCTION_DATA); //临时处理
break;
default:;
}
}
业务逻辑如下
获取数据包的功能字节段,
根据长度字节段搬运数据。
根据功能字节段选择合适的执行动作
其中如果是重新回包请求,则无视,如果是正常数据包,则把数据转移到对外发送队列里,修改相关参数,这里有一个关键点:setDataFlag 同样是为了不让中断影响队列。
特殊功能封包函数
//**************************************************
//函数名 :打包特殊功能的数据帧
//功能 :打包特殊功能的空数据帧
//输入 :null
//输出 :null
//作者 :Bao
//**************************************************
void packSpecialFrame (u8 function)
{
memset(INSIDE_TX_BUFF,0,PACK_MAX_LEN); //清除缓存区域数据
INSIDE_TX_LEN=0;
pack433Data(INSIDE_TX_BUFF,INSIDE_TX_LEN,function); //重新打包数据
INSIDE_TX_LEN += PACK_OUTDATA_LEN; //打包数据的长度
//test
memcpy(&tx__data[4],INSIDE_TX_BUFF,INSIDE_TX_LEN); //转移数据
}
用于特殊情况下不进行数据通讯时,封印专用的功能包,并放入发送缓存区域。
外侧数据处理函数(串口中断)
void USART3_IRQHandler(void) //串口3中断
{
u8 res;
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(I_2_O_QUEUE_LEN>0) //队列里有数据
{
I_2_O_QUEUE_LEN--; //队列长度减1
USART_SendData(OUTSIDE, I_2_O_BUFF[I_2_O_LEN++]);//send
if(I_2_O_LEN>=BUFF_LEN_O)
I_2_O_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(OUTSIDE,USART_IT_RXNE)) //收到一个字就立刻发送
{
res= USART_ReceiveData(OUTSIDE);
//USART_SendData(USART1,res); //2转1
// USART_SendData(USART3,res); //转3
O_2_I_BUFF[O_2_I_LEN++]=res; //存入收区
if(O_2_I_LEN>=BUFF_LEN_I)
O_2_I_LEN=0;
saveFrameToBUFF(O_2_I_LEN,FALSE); //封帧
}
}
主函数基础操作
//read_Version(); //读版本号测试
TIME_Process(); //超时判别
SI4463_Process(); //业务逻辑
代码写的好就是主页面就是这么简洁┗( ▔, ▔ )┛
结语
这个方案,我经过测试,发现稳定性很好,但是速度不是很满意。我调出单向最快空速9KB的时候,双向空速近似是2KB 所以后续代码和结构以及一些优化我就放弃制作了。如果有这方面的开发的问题可以问我。我目前研究了E70伪全双工,E62全双工,双E70全双工,4463伪全双工,下一步是双4463 全双工。
也欢迎大佬来讨论远距离双向传输方案。