2.28 串口收发数据包代码

接线图

 

HEX数据包

发送数据包

/*为了发送数据包,在这里先定义个缓存区的数组*/
uint8_t Serial_TXPacket[4];//发送载荷数据

void Serial_SendPacket() //调用这个函数TxPacket数组数据就会自动加上包头包尾发送出去
{
	Serial_SendByte(0xFF);//发送包头
	Serial_SendArray(Serial_TXPacket,4); //发送载荷数据
	Serial_SendByte(0xFC);//发送包尾
}

接收数据包

/*为了接收数据包,在这里先定义个缓存区的数组*/
uint8_t Serial_RXPacket[4];//接收载荷数据
uint8_t Serial_RxFlag; //让主程序判断是否接收完毕的标志位

if(USART_GetFlagStatus(USART1,USART_IT_RXNE) ==SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1); 
		if(RxState == 0)
		{
			//等待包头的程序
			if(RxData == 0xFF)
			{
				RxState = 1;
				pRxPacket = 0;
			}
			
		}
		else if(RxState == 1)
		{
			//接收数据的程序
			Serial_RXPacket[pRxPacket] = RxData;
			pRxPacket++;
			if(pRxPacket >= 4)
			{
				RxState = 2;
			}
		}
		else if(RxState == 2)
		{
			//等待包尾的程序
			if (RxData == 0XFC)
			{
				RxState = 0;
				Serial_RxFlag =1;
			}
		}
	}
}

uint8_t Serial_GetRxFlag(void)
{
	if(Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

通过上面的函数,可以写出整体的逻辑函数,让串口正常的收发HEX数据包,并在OLED屏幕上显示:

int main(void){
	OLED_Init();
	Serial_Init();
	Anjian_Init();
	
	OLED_ShowString(1,1,"TxPacket");
	OLED_ShowString(3,1,"RxPacket");
	Serial_TXPacket[0]= 0x1;
	Serial_TXPacket[1]= 0x2;
	Serial_TXPacket[2]= 0x3;
	Serial_TXPacket[3]= 0x4;
	
	
	
	while(1)
	{
		if(Anjian() == 1)
		{
			Serial_TXPacket[0]++;
			Serial_TXPacket[1]++;
			Serial_TXPacket[2]++;
			Serial_TXPacket[3]++;
									   
			OLED_ShowHexNum(2,1,Serial_TXPacket[0],2);
			OLED_ShowHexNum(2,4,Serial_TXPacket[1],2);
			OLED_ShowHexNum(2,7,Serial_TXPacket[2],2);
			OLED_ShowHexNum(2,10,Serial_TXPacket[3],2);
			
			Serial_SendPacket();
		}
		if(Serial_GetRxFlag() == 1)
		{
			OLED_ShowHexNum(4,1,Serial_RXPacket[0],2);
			OLED_ShowHexNum(4,4,Serial_RXPacket[1],2);
			OLED_ShowHexNum(4,7,Serial_RXPacket[2],2);
			OLED_ShowHexNum(4,10,Serial_RXPacket[3],2);
		}

	}
}

文本数据包

本次演示的是可变包长,含包头包尾,以@符号为包头,换行的两个符号为包尾,中间的载荷字符数量不固定。

发送数据包

发送数据直接使用 Serial_SendString(char* String)函数即可,无需其他操作。

接收数据包

/*为了发送数据包,在这里先定义个缓存区的数组*/
char Serial_RxPacket[100];
uint8_t Serial_RxFlag;

void USART1_IRQHandler()
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0; //指示接收到哪一个数据了。
	
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE) ==SET)
	{
		uint8_t RxData = USART_ReceiveData(USART1); 
		if(RxState == 0)
		{
			//等待包头的程序
			if(RxData == '@')
			{
				RxState = 1;
				pRxPacket = 0;
			}
			
		}
		else if(RxState == 1)
		{
			if(RxData == '\r')
			{
				RxState = 2;
			}else
			{
				Serial_RxPacket[pRxPacket] = RxData;
				pRxPacket++;
			}
		}
		else if(RxState == 2)
		{
			//等待包尾的程序
			if (RxData == '\n')
			{
				Serial_RxPacket[pRxPacket] = '\0';
				RxState = 0;
				Serial_RxFlag =1;
			}
		}
	}
}

uint8_t Serial_GetRxFlag(void)
{
	if(Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

有了上面的函数,可以进行逻辑代码的编写了:

int main(void){
	OLED_Init();
	Serial_Init();
	LED_Init();
	
	OLED_ShowString(1,1,"TxData"); //发送数据
	OLED_ShowString(3,1,"RxData"); //接收数据
	while(1)
	{
		if (Serial_GetRxFlag() == 1)
		{
			OLED_ShowString(4,1,"                ");
			OLED_ShowString(4,1,Serial_RxPacket);
			
			if(strcmp(Serial_RxPacket,"LED_ON") == 0)
			{
				LED1_ON();
				Serial_SendString("LED_ON_OK\r\n");
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,1,"LED_ON_OK");
			}
			else if(strcmp(Serial_RxPacket,"LED_OFF") == 0)
			{
				LED1_OFF();
				Serial_SendString("LED_OFF_OK\r\n");
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,1,"LED_OFF_OK");
			}
			else
			{
				Serial_SendString("ERROR_COMMAND\r\n");
				OLED_ShowString(2,1,"                ");
				OLED_ShowString(2,1,"ERROR_COMMAND");
			}
		}
	}
}

 

最后还有个问题需要说明,如果连续发送数据包,程序处理不及时,可能会导致数据包错位,在这里,文本数据包,每个数据包是独立的,不存在连续,这如果错位了,问题就比较大了,所以在程序里,可以改成每次处理完成之后,再开始接收下一个数据包。

具体怎么处理呢?

if(RxData == ‘@’ && Serial_RxFlag == 0),即再加一个判断条件,只有标志位等于0了,再开始接收下一个数据,这样写数据和读数据就严格分开了,不会同时进行,避免数据包错位的现象。