对于串口通信部分来说,单片机给计算机发送字符串好说,有多大的数组就发送多少个字节即可,但是单片机接收数据,接收多少个才应该是一帧完整的数据呢?数据接收的起点在哪里,结束在哪里?

我们编程的思路就是这样:当需要发送一帧(多个字节)数据时,这些数据都是连续不断的发送的,即发送完一个字节后会紧接着发送下一个字节,期间没有间隔或间隔很短,而当这一帧数据都发送完毕后,就会间隔很长一段时间(相对于连续发送时的间隔来讲)不再发送数据,也就是通信总线上会空闲一段较长的时间。

于是我们就建立这样一种程序机制:设置一个软件的总线空闲定时器,这个定时器在有数据传输时(从单片机接收角度来说就是接收到数据时)清零,而在总线空闲时(也就是没有接收到数据时)时累加,当它累加到一定时间(例程里是 30ms)后,我们就可以认定一帧完整的数据已经传输完毕了,于是告诉其它程序可以来处理数据了,本次的数据处理完后就恢复到初始状态,再准备下一次的接收。

那么用于判定一帧结束的空闲时间取多少合适呢? 
它取决于多个条件,并没有一个固定值,我们这里介绍几个需要考虑的原则:第一,这个时间必须大于波特率周期,很明显我们的单片机接收中断产生是在一个字节接收完毕后,也就是一个时刻点,而其接收过程我们的程序是无从知晓的,因此在至少一个波特率周期内你绝不能认为空闲已经时间达到了。第二,要考虑发送方的系统延时,因为不是所有的发送方都能让数据严格无间隔的发送,因为软件响应、关中断、系统临界区等等操作都会引起延时,所以还得再附加几个到十几个 ms 的时间。我们选取的 30ms 是一个折中的经验值,它能适应大部分的波特率(大于1200)和大部分的系统延时(PC 机或其它单片机系统)情况。

下面我将uart.c中的程序贴出来,可以参考一下思路。

/*******************************************************************************
* 文件名:uart.c
* 描  述:UART程序
* 作  者:小默haa
* 版本号:v1.0.0
* 日  期: 2019年2月18日
* 备  注:
*
*******************************************************************************/
#include "sys.h"

uint8 cntRxd = 0;				//接收字节计数器
uint8 bufRxd[64];				//接收字节缓冲区
bit flagTxd = 0;			//单字节发送完成标志,用来替代TXD中断标志位
bit flagFrame = 0;		//帧接收完成标志,即接收道一帧新数据

/*******************************************************************************
* 函数名	:UartInit
* 输入值	:uint16 baud
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:设置波特率
* 备注		:
*******************************************************************************/
void UartInit(uint16 baud)
{
  PCON &= 0x7F; 	//波特率不倍速
  SCON = 0x50;  	//8位数据,可变波特率
  AUXR &= 0xBF; 	//定时器1时钟为FOSC/12,即12T
  AUXR &= 0xFE; 	//串口1选择定时器1为波特率发生器
  TMOD &= 0x0F; 	//清除定时器1模式位
  TMOD |= 0x20;   //设定定时器1为8位自动重装方式
  TL1 = 256 - (FOSC / 12 / 32) / baud;     //设定定时初值
  TH1 = TL1;     	//设定定时器重装值
  ET1 = 0;        //禁止定时器1中断
  TR1 = 1;        //启动定时器1
	ES = 1;					//使能串口中断
}

/*******************************************************************************
* 函数名	:UART_Interrupt
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:串口中断服务程序
* 备注		:
*******************************************************************************/
void UART_Interrupt(void) interrupt 4
{

	if(RI)													//接收道新字节
	{
		RI = 0;												//清零接收中断标志位
		if(cntRxd < sizeof(bufRxd))		//接收缓冲区尚未用完时
		{
				bufRxd[cntRxd++] = SBUF;	//保存接收字节,并递增计数器
		}
	}

	if(TI)
	{
		TI = 0;
		flagTxd = 1;
	}
}

/*******************************************************************************
* 函数名	:UartWrite
* 输入值	:uint8 *buf, uint8 len
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:串口数据写入
* 备注		:buf为待发送数据的指针,len为指定的发送长度
*******************************************************************************/
void UartWrite(uint8 *buf, uint8 len)
{
	while(len--)						//循环发送所有字节
  {
		flagTxd = 0;					//清零发送标志
		SBUF = *buf++;				//发送一个字节数据
		while(!flagTxd);			//等待该字节发送完毕
	}
}

/*******************************************************************************
* 函数名	:UartRead
* 输入值	:uint8 *buf, uint8 len
* 返回值	:len
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:串口数据读取
* 备注		:buf为待发送数据的指针,len为指定的发送长度,返回值为实际读到的长度
*******************************************************************************/
uint8 UartRead(uint8 *buf, uint8 len)
{
	uint8 i;

	if(len > cntRxd)					//指定读取长度大于实际接收到的数据长度时
		len = cntRxd;						//读取长度设置为实际接收到的数据长度
	for(i = 0; i < len; i++)	//复制接收到的数据到接收指针上
	{
		*buf++ = bufRxd[i];
	}
	cntRxd = 0;								//接收计数器清零

	return len;								//返回值为实际读取长度
}

/*******************************************************************************
* 函数名	:UartDriver
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:串口驱动函数,监测数据帧的接收,调度功能函数
* 备注		:在主循环中调用
*******************************************************************************/
void UartDriver(void)
{
	uint8 len;
	uint8 pdata buf[40];
	if(flagFrame)														//有命令到达时,读取处理该命令
	{
			flagFrame = 0;
			len = UartRead(buf, sizeof(buf));		//将接收到的命令读取到缓冲区中
			UartAction(buf, len);								//传递数据帧,调用动作执行函数
	}
}

/*******************************************************************************
* 函数名	:UartRxdMonitor
* 输入值	:uint8 ms
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月18日
* 功能描述:串口接收监控,由空闲时间判定帧结束
* 备注		:在定时器中断中调用,ms为定时间隔
*******************************************************************************/
void UartRxdMonitor(uint8 ms)
{
	static uint8 idletmr = 0;
	static uint8 cntbkp = 0;

	if(cntRxd > 0)						//接收计数器大于0时,监控总线空闲时间
	{
		if(cntRxd != cntbkp)		//接收计数器改变,即刚接收到数据时,清零空闲计时
		{
			cntbkp = cntRxd;
			idletmr = 0;
		}
		else										//接收计数器未改变,即总线空闲时,累积空闲时间
		{
			if(idletmr < 30)			//空闲计时小于30ms时,持续增加
			{
				idletmr += ms;
				if(idletmr >= 30)		//空闲时间达到30ms时,即判定为一帧接收完毕
				{
					flagFrame = 1;		//设置帧接收完成标志
				}
			}
		}
	}
	else
	{
		cntbkp = 0;
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

02-26 22:59