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 全双工。
也欢迎大佬来讨论远距离双向传输方案。

发布了19 篇原创文章 · 获赞 5 · 访问量 1283

猜你喜欢

转载自blog.csdn.net/ex_xyz/article/details/103678681