串口超时——串口通信

目录

串口超时

简介

代码详解

判断特定命令控制单片机

STC单片机不断电下载

最后


近期学习了单片机新的外设,为了方便通过串口调试外设,根据作者(本人)的思路,顺带写了一份串口的程序。

在单片机的开发中,串口犹如单片机的一扇窗,可通过它了解或控制单片机的运行情况,找出单片机程序中出现问题的原因。正所谓工欲善其事,必先利其器,因此,有一个在开发过程中得心应手的串口,能够提高开发效率。

串口超时

简介

在使用串口接收不定长数据时,作者常常思考如何判断串口是否接收完数据了,它不像定长数据那样,计数达到即接收完成。经过大佬们的指点,知道了有所谓“串口超时”的东西。顾名思义,就是当串口开始接收到数据后,如果有一段时间没有接收到数据,就表示串口已经接收完全部数据,单片机可以对数据进行处理了。

将上面的思路用到单片机中,即在串口程序中额外添加一个定时器,当串口开始接收到数据时,清零计数值并开始计时,此后每次在计时的阈值内,接收到数据,则清零计数值,重新开始计时。直到某一次接收到数据后,在计时的阈值内没有接收到数据,串口数据接收完成,停止计时。如图:

代码详解

以STC8H8K64U单片机为例,先对串口和定时器进行初始化:

#define FOSC    11059200UL    //IRC频率
#define BAUDRATE	9600UL    //串口波特率

//串口1,使用定时器2作为波特率发生器(详情查看官方数据手册)
void TIMER2_UartInit()
{
	AUXR |= (0x01 << 0);
	AUXR |= (0x01 << 2);	//1T
	AUXR &= ~(0x01 << 3);
	TM2PS = 0;
	T2H = (65536 - FOSC/BAUDRATE/(TM2PS + 1)/4) >> 8;
	T2L = (65536 - FOSC/BAUDRATE/(TM2PS + 1)/4) & 0xff;
	AUXR |= (0x01 << 4);
}

//串口初始化函数(详情查看官方数据手册)
void UART1_Init()
{
	P3M1 = ~(0x01 << 0);
	P3M0 = ~(0x01 << 0);	//准双向
	P3M1 = ~(0x01 << 1);
	P3M0 = ~(0x01 << 1);	//准双向

    SCON = 0x50;	//模式1(可变波特率8位模式)
	PCON &= ~(0x01 << 7);	//波特率不加倍
	PCON &= ~(0x01 << 6);	//无帧错误检测
	AUXR |= (0x01 << 0);	//定时器2作为波特率发生器

	ES = 1;		//开启串口中断
	EA = 1;		//开启总中断
}

串口中断函数:

unsigned char xdata uart1_dataLength = 0;
unsigned char xdata uart1_buffer[256] = 0;

bit uart1_timeBegin = 0;	//计时位(0:停止计时;1:开始计时)
unsigned int xdata uart1_freeTime_us = 0;		//串口空闲时间

void UART1_Interrupt() interrupt 4
{
    if (RI == 1)	//接收到数据
    {
        RI = 0;

		uart1_freeTime_us = 0;	//计时清零
		uart1_timeBegin = 1;	//开始计时
        uart1_buffer[uart1_dataLength] = SBUF;
        uart1_dataLength++;
		uart1_buffer[uart1_dataLength] = 0;		//下一个字节清零后,缓存可当作字符串使用
    }
}

其中变量uart1_freeTime_us在定时器3中断中累加:

#define TIMER3_US	10    //定时器3中断时间

void TIMER3_Init()
{
	TM3PS = 0;
	T4T3M &= 0xf0;

	T4T3M |= 0x0a;

	if(T4T3M & 0x02)
	{
		T3H = (65536 - FOSC/1000000UL/(TM3PS + 1) *TIMER3_US) >> 8;
		T3L = (65536 - FOSC/1000000UL/(TM3PS + 1) *TIMER3_US) & 0xff;
	}
	else
	{
		T3H = (65536 - FOSC/1000000UL/(TM3PS + 1) *TIMER3_US/12) >> 8;
		T3L = (65536 - FOSC/1000000UL/(TM3PS + 1) *TIMER3_US/12) & 0xff;
	}

	IE2 |= (0x01 << 5);
	EA = 1;
}

//每TIMER3_US时间后进入中断
void TIMER3_Interrupt() interrupt 19
{
	uart1_freeTime_us += 10;
}

接下来是整个程序的关键函数:

#define UART1_FREE_US 1000000UL/BAUDRATE *10 *3		//串口空闲时间超过UART1_FREE_US后判断为数据接收完成,为3帧数据的时间,一帧10位

bit uart1_readDone = 0;		//串口数据接收完成标志(0:未完成数据接收;1:已完成数据接收)

//检测是否接收完串口的数据
void UART1_Loop()
{
	if(uart1_timeBegin == 1)	//计时开始
	{
		if(uart1_freeTime_us > UART1_FREE_US)		//串口空闲时间达到指定数值,数据接收完成
		{
			uart1_readDone = 1;		//数据接收完成
			uart1_timeBegin = 0;	//停止计时
		}
	}
	else	//未开始计时
	{
		uart1_freeTime_us = 0;
	}
}

//处理完从串口接收到的数据后,必需调用该函数
void UART1_Clear()
{
	uart1_dataLength = 0;	//接收数据长度清零
	uart1_readDone = 0;		//清除数据接收完成标志
}

UART1_FREE_US表示的意思是该串口接收数据完成前空闲的时间,这里我以串口接收/发送3帧数据所需要的时间为准,每10bit为一帧数据。

通过调用函数UART1_Loop,可判断串口是否接收完成数据。当接收完成后,将uart1_readDone置1,此时可以处理缓存中的数据,然后调用函数UART1_Clear清除相关数据。即:

void main()
{
	P_SW2 |= 0x80;

	TIMER2_UartInit();
	UART1_Init();
	TIMER3_Init();        //定时器3用以计算串口空闲时间

	while(1)
	{
	    UART1_Loop();
	    if(uart1_readDone == 1)		//数据接收完成
	    {//处理数据

		    UART1_Clear();	//数据处理完之后调用
	    }
	}
}

特别注意,因单片机中能开辟的缓存有限,如果接收完数据后不处理,或者接收的数据长度超出缓存范围,那么前面接收到的数据将被覆盖,所以缓存的大小应视实际情况而定。

判断特定命令控制单片机

在单片机开发中,作者经常通过串口发送特定的命令控制单片机,在这过程中单片机应该如何识别这些特定指令呢?下面举个例子。

STC单片机不断电下载

在STC单片机开发中,每次烧录程序都需要对单片机进行冷启动,次数多了,十分影响作者心情,况且在频繁的冷启动中,十分影响开关的寿命。好在STC公司留下了一条路——软复位。

通过数据手册可知:

不难看出,作者可以通过串口发送相关命令,控制单片机软复位到用户程序区或ISP区烧录程序,实现不断电下载:

#include<string.h>

#define UART1_CMD_DOWNLOAD	"DOWNLOAD MCU"
#define UART1_CMD_RESET		"RESET MCU"

//判断特定指令
void UART1_CmdJudge()
{
	if(strcmp(UART1_CMD_RESET,uart1_buffer) == 0)    //函数判断两组字符串是否相等
	{
		IAP_CONTR = 0x20;    //复位
	}
	else if(strcmp(UART1_CMD_DOWNLOAD,uart1_buffer) == 0)
	{
		IAP_CONTR = 0x60;    //复位并烧录程序
	}
}

整个主函数:

void main()
{
	P_SW2 |= 0x80;

	TIMER2_UartInit();
	UART1_Init();
	TIMER3_Init();        //定时器3用以计算串口空闲时间

	while(1)
	{
	    UART1_Loop();
	    if(uart1_readDone == 1)		//数据接收完成
	    {
            UART1_CmdJudge();
            //处理数据
		    UART1_Clear();	//数据处理完之后调用
	    }
	}
}

当然,关于不断电下载功能的程序,在单片机数据手册中有官方例程,作者的思路可能与官方思路有些出入,一切以官方为准!

最后

串口超时是作者在实际应用中而写出来的,奈何作者不管经验还是实力都远远不够,所以望读者指正,谢谢!有任何问题欢迎评论区留言。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值