【AT89C52】关于串口接收定长数据

网络上写51单片机串口接收定长数据的例子有很多,但是没有一个写的通用性好。恰好最近做了一个利用51单片机的串口进行接收和发送数据的项目。通过学习,掌握了很多编程的细节,在此做一个总结分享。相信通过本文章的学习,你会对串口接收数据处理有一个更深刻的学习,以及对C编程有一个更加深刻的认识。向优秀的人学习可以提升自己的不足,自己去总结才能将别人的知识转变为自己的理解。

1. 实验目标

目标是接收一个长度为23字节的字符串,包括有头,帧尾。同时还要具备字符串定长发送的功能。

2.实验思路

串口初始化都不必说了,这里配置115200波特率,主要这里讲串口是采用接收中断的方法,通过串口接收中断将接收到的数据存入一个事先预定好的数组。然后再将里面的数组值放到While循环中进行处理。

3.传统串口接收框架

首先是串口初始化:

void Uart0Init(void)
{
	PCON |= 0x80;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xBF;		//定时器1时钟为Fosc/12,即12T  --单片机编程写为6T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFF;		//设定定时初值
	TH1 = 0xFF;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
		
	ES = 1;  //开串口中断,ES =1;
	TI = 0;
	EA = 0;
	RI = 0;
}

开启了串口中断已有数据就会进入中断。

中断处理函数模板可以这么写,先定义一个数组存放接收数据。

uint8  idata Line[26] = {0};  
#define BufLen  23

这里有一个很好的方法来定义变量类型。

#define recvbuf Line 

通过这样的方法,可以将定义的数组以recvbuf替代Line,这样的好处就是,通过这里的修改可以将接收的数据存放到任意指定的数组,例如Line1,Line2..... 

void Uart0_ISR(void)  interrupt 4
{
    if (RI)
    {
		RI=0;	
        if(i>=recvLen) recvLen=0;
        recvbuf[i++] = res;
	}
	if(TI)
	{
	   TI = 0;
	}
	return;
}

我们再定义一个数组,将接收到的数据进行预处理,处理的目的主要是进行帧头校验,校验和,以及跳转到后续的数据处理函数。

static uint8 state  = 0;		   
static uint8 count = 0;
uint8 i,res,checksum;  
void Uart0RecHandle(uint8 idata *line)
{
	for(i = 0; i < BufLen; i++)
	{	
		res = SerialRecBUF[i];
		res = res;
		switch(state)
		{
	 		case 0:
	 			if(res == 0x07)
				{
				 	count = 0;
					line[count] = res;
					count++;
					state = 1;
				}
				else
				{
				 	count = 0;
					state = 0;
				}
				break;
			case 1:
				if(res == 0x17)
				{
				 	line[count] = res;
					count++;
					state = 2;
				}
				else
				{
				 	count = 0;
					state = 0;
				}
				break;
			case 2:
				line[count] = res;
				count++;
				if(count >= (BufLen-1))state = 3;
				break;
			case 3:
				line[count] = res;
				checksum = 0;
				for(i=0;i<count;i++)
				{
				 	checksum+=line[i];
				}
				if(checksum == res)
				{
					Uart0FrameHandle(line);
				}
				state = 0;
				count = 0;
				break;
				default:break;
	 	}	  	
	}
}

接收完毕校验和通过,就可以进行数据处理。

然后再主函数中定义一个数组RecBuf[]

#define MAX_LENGTH 75
uint8 idata RecBuf[MAX_LENGTH] = {0};

在主循环中调用上述函数:

while(1)
{
   Uart0RecHandle(RecBuf);
}

传统数据处理方式,有一个弊端就是需要定义两个长数组。对于idata比较小的单片机,额外的数组空间对于后续编程存在隐患。同时这种接收方式不能对中断接收的数组进行判断,这就导致,后续判断无法确定数据是否接收完毕,好容易导致数据错乱。因此这种方式并不推荐。

4. 优化后的接收处理框架

针对上述问题,这个接收框架主要针对上述缺点进行优化,首先节约接收数组空间,因此我们只用一个数组用于接收和数据处理。其次,在串口接收中断中就开始进行数据判断,判断完校验和之后利用一个标志位进行判断是否接收完毕全部数组。

#define BufLen    23                                                             
uint8  idata Line[80] = {0}; 

我们就用这一个80个字节空间的数组。

#define recvbuf Line 

记得用上刚才说的好方法。

static uint8 Recflag  = 0;
static uint8 recvLen  = 0;
static uint8 state  = 0;		   
static uint8 count = 0;
static uint8 i = 0;
static uint8 res = 0;
static uint8 checksum = 0; 
void Uart0_ISR(void)  interrupt 4
{  
    if (RI)
    {
		RI=0;	 
		res = SBUF; 
		recvbuf[recvLen++] = res;
		switch(state)
		{
	 	case 0:
	 			if(res == 0x07)
				{
					state = 1;
										 
				}
				else
				{
					state = 0;
					recvLen = 0;
				}
				break;
		case 1:
				if(res == 0x17)
				{
					state = 2;			    		    
				}
				else
				{
					state = 0;
					recvLen = 0;
				}
				break;
	    case 2:
				if(recvLen >= (BufLen-1))state = 3;
				break;
		case 3: 
				checksum = 0;
				for(i=0;i<(BufLen-1);i++)
				{
				   checksum += recvbuf[i];
				}
				if(checksum == res)
				{
					Recflag = 1;  
				}
				state = 0;
				recvLen = 0; 
				break;
		default:break;
		}				
	}
	if(TI)
	{
	   TI = 0;
	}
	return;	   
}

校验和函数如下:

uint8 CheckSum(uint8 idata *pbuf, uint8 len)
{
	uint8 i;
	uint8 sum = 0;

	for(i=0; i<len; i++)
	{
		sum += pbuf[i];
	}

	return sum;
}

而在接收完毕之后我们直接拿来这个数组进行取数组,进行数据处理。后面的内容更加精彩不要错过!

对于STM32系列的微控制器,可以通过中断的方式来实现串口接收定长数据。下面是一个基本的示例代码: 首先,需要初始化串口和中断相关的配置。例如,假设我们要使用USART1作为串口接收数据,固定数据长度为10个字节: ```c #include "stm32f4xx.h" // 定义接收缓冲区大小 #define RX_BUFFER_SIZE 10 // 接收缓冲区 volatile uint8_t rxBuffer[RX_BUFFER_SIZE]; // 接收完成标志 volatile uint8_t rxComplete = 0; void USART1_Init(void) { // 使能USART1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); // 使能USART1接收中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能USART1 USART_Cmd(USART1, ENABLE); // 配置串口中断优先级 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } ``` 接下来,在中断处理函数USART1_IRQHandler()中,实现串口接收定长数据的逻辑: ```c void USART1_IRQHandler(void) { // 接收数据 if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { static uint8_t rxIndex = 0; // 读取接收到的数据 rxBuffer[rxIndex++] = USART_ReceiveData(USART1); // 数据接收完成 if (rxIndex >= RX_BUFFER_SIZE) { // 设置接收完成标志 rxComplete = 1; // 重置接收缓冲区索引 rxIndex = 0; } } } ``` 在主函数中,可以使用rxComplete标志来判断接收是否完成,并处理接收到的数据: ```c int main(void) { // 初始化串口 USART1_Init(); while (1) { // 接收完成标志被设置,表示接收到了定长数据 if (rxComplete) { // 处理接收到的数据 // ... // 清除接收完成标志 rxComplete = 0; } } } ``` 以上是一个简单的示例代码,你可以根据自己的需求进行修改和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米杰的声音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值