欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

单片机学习笔记————51单片机实现在串口接收中断里即时解析数据头的特殊程序框架

程序员文章站 2022-06-08 21:34:23
...

proteus虚拟串口的实现:https://mp.csdn.net/console/editor/html/107251649

一、使用proteus绘制简单的电路图,用于后续仿真

单片机学习笔记————51单片机实现在串口接收中断里即时解析数据头的特殊程序框架

 

二、编写程序

/********************************************************************************************************************
----	@Project:	USART
----	@File:	main.c
----	@Edit:	ZHQ
----	@Version:	V1.0
----	@CreationTime:	20200712
----	@ModifiedTime:	20200712
----	@Description:	
----	波特率是:9600 。
----	通讯协议:EB  GG XX XX XX XX ED
----	其中第1位EB就是数据头.
----	其中第2位GG就是数据类型。01代表驱动蜂鸣器,02代表驱动Led灯。
----	其中第3,4,5,6位XX就是有效数据长度。高位在左,低位在右。
----	其中第7位ED就是数据尾,在这里也起一部分校验的作用,虽然不是累加和的方式。
----	在本程序中,
----	当数据类型是01时,4个有效数据代表一个long类型数据,如果这个数据等于十进制的123456789,那么蜂鸣器就鸣叫一声表示正确。
----	当数据类型是02时,4个有效数据代表一个long类型数据,如果这个数据等于十进制的123456789,那么LED灯就会闪烁一下表示正确。
----	十进制的123456789等于十六进制的75bcd15 。
----	发送以下测试数据,将会分别控制蜂鸣器Led灯。
----	控制蜂鸣器发送:eb 01 07 5b cd 15 ed
----	控制LED灯发送:eb 02 07 5b cd 15 ed
----	单片机:AT89C52
********************************************************************************************************************/
#include "reg52.h"
/*——————宏定义——————*/
#define FOSC 11059200L
#define BAUD 9600
#define T1MS (65536-FOSC/12/500)   /*0.5ms timer calculation method in 12Tmode*/

#define const_rc_size 20	/*接收串口中断数据的缓冲区数组大小*/

#define const_receive_time 5	/*如果超过这个时间没有串口数据过来,就认为一串数据已经全部接收完,这个时间根据实际情况来调整大小*/

#define const_voice_short 80	/*蜂鸣器短叫的持续时间*/
#define const_led_short 80	/*LED灯亮的持续时间*/

/*——————变量函数定义及声明——————*/
/*蜂鸣器的驱动IO口*/
sbit BEEP = P2^7;
/*LED*/
sbit LED = P3^5;

unsigned int uiRcregTotal = 0;	/*代表当前缓冲区已经接收了多少个数据*/
unsigned char ucRcregBuf[const_rc_size];	/*接收串口中断数据的缓冲区数组*/

/*为串口计时器多增加一个原子锁,作为中断与主函数共享数据的保护*/
unsigned char ucVoiceLock = 0;	/*蜂鸣器鸣叫的原子锁*/
unsigned char ucLedLock = 0;	/*Led灯点亮时间的原子锁*/

unsigned int uiVoiceCnt = 0;	/*蜂鸣器鸣叫的持续时间计数器*/
unsigned int uiRcVoiceTime = 0;	/*蜂鸣器发出声音的持续时间*/

unsigned int uiLedCnt = 0;	/*Led灯点亮的计时器*/

unsigned long ulBeepData = 0;	/*蜂鸣器的数据*/
unsigned long ulLedData = 0;	/*LED的数据*/

unsigned char ucUsartStep = 0;	/*串口接收字节的步骤变量*/
/**
* @brief  定时器0初始化函数
* @param  无
* @retval 初始化T0
**/
void Init_T0(void)
{
	TMOD = 0x01;                    /*set timer0 as mode1 (16-bit)*/
	TL0 = T1MS;                     /*initial timer0 low byte*/
	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
}

/**
* @brief  串口初始化函数
* @param  无
* @retval 初始化T0
**/
void Init_USART(void)
{
	SCON = 0x50;
	TMOD = 0x21;                    
	TH1=TL1=-(FOSC/12/32/BAUD);
}

/**
* @brief  外围初始化函数
* @param  无
* @retval 初始化外围
* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。
* 只要更改以下对应变量的内容,就可以显示你想显示的数字。
**/
void Init_Peripheral(void)
{
	ET0 = 1;/*允许定时中断*/
	TR0 = 1;/*启动定时中断*/
	TR1 = 1;
	ES = 1;	/*允许串口中断*/
	EA = 1;/*开总中断*/  
}

/**
* @brief  初始化函数
* @param  无
* @retval 初始化单片机
**/
void Init(void)
{
	LED  = 0;
	BEEP = 1;
	Init_T0();
	Init_USART();
}
/**
* @brief  延时函数
* @param  无
* @retval 无
**/
void Delay_Long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i<uiDelayLong;i++)
   {
      for(j=0;j<500;j++)  /*内嵌循环的空指令数量*/
          {
             ; /*一个分号相当于执行一条空语句*/
          }
   }
}
///**
//* @brief  延时函数
//* @param  无
//* @retval 无
//**/
//void Delay_Short(unsigned int uiDelayShort)
//{
//   unsigned int i;
//   for(i=0;i<uiDelayShort;i++)
//   {
//		 ; /*一个分号相当于执行一条空语句*/
//   }
//}
/**

* @brief  Led灯的服务程序
* @param  无
* @retval 无
**/
void led_service(void)
{
	if(uiLedCnt < const_led_short)
	{
		LED = 1;	/*LED亮*/
	}
	else
	{
		LED = 0;
	}
	
}
/**
* @brief  定时器0中断函数
* @param  无
* @retval 无
**/
void ISR_T0(void)	interrupt 1
{
	TF0 = 0;  /*清除中断标志*/
	TR0 = 0; /*关中断*/
	/* 
	* 此处多增加一个原子锁,作为中断与主函数共享数据的保护
	*/
	if(ucVoiceLock == 0)	/*原子锁判断*/
	{
		if(uiVoiceCnt != 0)
		{
			uiVoiceCnt --;
			BEEP = 0;
		}
		else
		{
			;
			BEEP = 1;
		}		
	}

	if(ucLedLock == 0)	/*原子锁判断*/
	{
		if(uiLedCnt < const_led_short)
		{
			uiLedCnt ++;	/*Led灯点亮的时间计时器*/
		}
	}
	TL0 = T1MS;                     /*initial timer0 low byte*/
	TH0 = T1MS >> 8;                /*initial timer0 high byte*/
  	TR0 = 1; /*开中断*/	
}

/**
* @brief  串口接收数据中断
* @param  无
* @retval 以下是在串口接收中断里即时解析数据头的特殊程序框架,
* 它的特点是靠数据头来启动接受有效数据,靠数据尾来识别一串数据接受完毕,
* 这里的数据尾也起到一部分的校验作用,让数据更加可靠。这种程序结构适合应用
* 在传输的数据长度不是很长,而且要求响应速度非常高的实时场合。在这种实时要求
* 非常高的场合中,就不像之前一样做数据累加和的复杂运算校验,只用数据尾来做简单的
* 校验确认,目的是尽可能提高处理速度。
**/
void usart_receive(void)	interrupt 4
{
	if(RI == 1)
	{
		RI = 0;
		switch(ucUsartStep)	/*串口接收字节的步骤变量*/
		{
			case 0:
				ucRcregBuf[0] = SBUF;
				if(ucRcregBuf[0] == 0xeb)	/*数据头判断*/
				{
					ucRcregBuf[0] = 0;	/*数据头及时清零,为下一串数据的接受判断做准备*/
					uiRcregTotal = 1;	/*缓存数组的下标初始化*/
					ucUsartStep = 1;	/*如果数据头正确,则切换到下一步,依次把上位机来的数据存入数组缓冲区*/
				}
				break;
			case 1:
				ucRcregBuf[uiRcregTotal] = SBUF;	/*依次把上位机来的数据存入数组缓冲区*/
				uiRcregTotal ++;
				if(uiRcregTotal >= 7)	/*已经接收了7个字节*/
				{
					if(ucRcregBuf[6] == 0xed)	/*数据尾判断,也起到一部分校验的作用,让数据更加可靠,虽然没有用到累加和的检验方法*/
					{
						ucRcregBuf[6] = 0;	/*数据尾及时清零,为下一串数据的接受判断做准备*/
						switch(ucRcregBuf[1])	/*根据不同的数据类型来做不同的数据处理*/
						{
							case 0x01:	/*与蜂鸣器相关*/
								ulBeepData = ucRcregBuf[2];	/*把四个字节的数据合并成一个long型的数据*/
								ulBeepData = ulBeepData << 8;
								ulBeepData += ucRcregBuf[3];
								ulBeepData = ulBeepData << 8;
								ulBeepData += ucRcregBuf[4];
								ulBeepData = ulBeepData << 8;
								ulBeepData += ucRcregBuf[5];
								if(ulBeepData == 123456789)	/*如果此数据等于十进制的123456789,表示数据正确*/
								{
									ucVoiceLock = 1;	/*共享数据的原子锁加锁*/
									uiVoiceCnt = const_voice_short;	/*蜂鸣器发出声音*/
									ucVoiceLock = 0;	/*共享数据的原子锁解锁*/
								}
								break;
								case 0x02:	/*与Led灯相关*/
								ulLedData = ucRcregBuf[2];	/*把四个字节的数据合并成一个long型的数据*/
								ulLedData = ulLedData << 8;
								ulLedData += ucRcregBuf[3];
								ulLedData = ulLedData << 8;
								ulLedData += ucRcregBuf[4];
								ulLedData = ulLedData << 8;
								ulLedData += ucRcregBuf[5];
								if(ulLedData == 123456789)	/*如果此数据等于十进制的123456789,表示数据正确*/
								{
									ucLedLock = 1;	/*共享数据的原子锁加锁*/
									uiLedCnt = 0;	/*在本程序中,清零计数器就等于自动点亮Led灯*/
									ucLedLock = 0;	/*共享数据的原子锁解锁*/
								}
								break;						
						}
					}
					ucUsartStep = 0;	/*返回上一步数据头判断,为下一次的新数据接收做准备*/
				}
				break;
		}
	}
	else
	{
		TI = 0;
	}
}

/*————————————主函数————————————*/
/**
* @brief  主函数
* @param  无
* @retval 实现LED灯闪烁
**/
void main()
{
	/*单片机初始化*/
	Init();
	/*延时,延时时间一般是0.3秒到2秒之间,等待外围芯片和模块上电稳定*/
	Delay_Long(100);
	/*单片机外围初始化*/	
	Init_Peripheral();
	while(1)
	{
		led_service();	/*Led灯的服务程序*/
	}
}

三、仿真实现

单片机学习笔记————51单片机实现在串口接收中断里即时解析数据头的特殊程序框架

51单片机实现在串口接收中断里即时解析数据头的特殊程序框架