串口通信(8)串口中断“边接收边解析数据“的通信程序

 本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步!

> 发布人:@日月同辉,与我共生_单片机-CSDN博客

> 欢迎你为独创博主日月同辉,与我共生点赞❤❤❤+关注👍+收藏🌹+评论☺。

系列专栏: CSDN-单片机串口通信学习系列🎁

> 我的格言是:“尽最大努力,做最好的自己!💪

要转载,请提前告知!!!

版权声明:本文为CSDN博主「日月同辉,与我共生」的原创文章,CSDN独一份。

目录

一、数据帧

1.1数据类型

1.2帧尾

1.3ASCII码

二、系统设计

2.1设计要求

2.2系统原理

2.2.1接收原理&正确性判断原理

2.2.2持续时间原理

三、硬件设计

3.1串口设计

3.2LED模块设计

3.3蜂鸣器模块设计

四、软件设计

4.1串口初始化模块

4.2接收模块

4.3定时器初始化模块

4.4LED+蜂鸣器功能模块

4.5主程序模块

4.6发送数据模块

4.7清除缓冲模块

4.8uart.h

五、结果

一、数据帧

发送到数据帧可以细分成【帧头】、【地址】、【类型】、【长度】、【数据块】、【效验】、【帧尾】7种,但并不是每次发送的数据都要有这7种。

像【地址】,一般只有多机通信会用到,单机通信不需要有。多机通信时,会给每台机编一个地址,确保数据不会发送到其他的机。还有,【长度】并不是每次都要有,如果发的数据长度是固定的,一般不需要有,发送到数据长度不固定时,才需要有。

1.1数据类型

数据类型一般决定处理某个模块。比如我与用户约定好,数据类型01,处理LED,数据类型02,处理蜂鸣器,那么我的设备发送数据01给对方,则对方去处理LED,发送数据02给对方,则对方去处理蜂鸣器。

1.2帧尾

一次通信的结束,一般用一些特殊符号(比如回车等)表示,以防止帧尾前面的数据与帧尾一样,导致通信出现故障。

1.3ASCII码

ASCII码是一种字符编码方式,共有128个字符,包括英文字母、数字、标点符号和一些控制字符等。每个字符用7个二进制位表示,这些二进制位可以转换成十进制数,也可以转换成十六进制数。

字母 A 在 ASCII 码中的二进制表示为 01000001,十进制表示为 65,十六进制表示为 0x41。

数字 0 在 ASCII 码中的二进制表示为 00110000,十进制表示为 48,十六进制表示为 0x30。

控制字符包括换行符、回车符、制表符等,它们的 ASCII 码值在 0~31 和 127 中。

二、系统设计

2.1设计要求

虚拟串口com3发送数据给51单片机com1,com1发回数据给com3,同时处理好接收到的数据。

这次数据帧有帧头+类型+数据块+帧尾组成。帧头为AA 55,数据类型分为01/02,数据块分为高8位和低8位,帧尾为0x0D(回车)。

数据类型为01时,LED灯工作,数据类型为02时,蜂鸣器工作。不论01还是02,持续时间与数据块有关(高8位、低8位)。

2.2系统原理

2.2.1接收原理&正确性判断原理

上两篇文章我们用定时器实现串口通信,本质是先接收后处理数据

回顾上两篇文章:

串口通信(6)应用定时器中断+串口中断实现接收一串数据-CSDN博客

串口通信(7)判断数据帧头来接收一串数据-CSDN博客

今天我们讲述如何一边接收数据一边处理数据

定义一个数组recv_buf,用于存储接收到数据,采用switch-case语句,每接收一个【帧头】数据,立刻用if语句判断【帧头】是否正确,若不正确,则重头判断。

若帧头AA 55数据均正确(recv_buf[0]=AA,recv_buf[1]=55),则进入下一步骤。

在分支2(case 2),接收【类型】(recv_buf[2])和【数据块】(recv_buf[3]和recv_buf[4])。

接着在分支3中,如果帧尾(recv_buf[5]=0x0D)正确,则开始处理LED灯和蜂鸣器。

2.2.2持续时间原理

分别定义两个控制持续时间的变量led_data、beep_cnt。

持续时间=高8位<<8+低8位

在定时器定时1ms的中断中,利用if语句来控制LED灯/蜂鸣器的持续时间。

至于是LED灯还是蜂鸣器工作,由recv_buf[2]决定。

三、硬件设计

3.1串口设计

com1与com3通信,com2是两者的通信媒介,所以单片机com1的发送端TXD接com2的TXD,接收端RXD则接com2的RXD即可。

3.2LED模块设计

LED灯采用共阳极接法,左端接电源(高电平1),右端通过电阻接P1^0,P1^0低电平时,LED导通,发亮;P1^0高电平时,LED不导通,不亮(熄灭)。

3.3蜂鸣器模块设计

R2右端接到P1^7,P1^7为低电平时,三极管导通,+5V电源(提供电压电流的作用)与蜂鸣器连接,蜂鸣器正极为高电平;P1^7为高电平,三极管不导通,蜂鸣器正极为低电平。如图,黄色波形为R2右端(buzzor),蓝色为蜂鸣器正极波形,蜂鸣器发声的原理就是高低电平不断切换。

蜂鸣器模块电路图:

四、软件设计

4.1串口初始化模块

波特率为9600b/s,采用中断法。

回顾中断法:UART通信-中断法

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xFD;			//设置定时初始值
	TH1 = 0xFD;			//设置定时重载值
	ET1 = 0;			  //禁止定时器中断
	ES=1;           //串口中断打开
	TR1 = 1;			  //定时器1开始计时
}

4.2接收模块

可以看看本文2.2.1讲述的原理。

void ES_timers() interrupt 4 //接收中断
{
	static unsigned char machine_step=0;//状态变量
	if(RI)
	{ 
		RI=0; 
		switch(machine_step)
		{
			      case 0:recv_buf[0]=SBUF;//接收第一个数据
			            if(recv_buf[0]==0xAA)
						 {
							 machine_step=1;//帧头第一个数据正确,状态变为1
						 }
						 else
						 {
							 machine_step=0;//帧头第一个数据错误,重新判断下一个是否正确
						 }
				         break;
			       case 1:recv_buf[1]=SBUF;//接收第二个数据
			             if(recv_buf[1]==0x55)
						 {
							 machine_step=2;//帧头第二个数据正确,状态变为2
							 recv_cnt=2;
						 }
						 else
						 {
							 machine_step=0;//帧头第一个数据错误,重新重头判断是否正确
						 }
				         break;
			       case 2:recv_buf[recv_cnt]=SBUF;//接收第3、4、5个数据
						 recv_cnt++;
			             if(recv_cnt>4)
						 {
							 machine_step=3;
						 }
						 else
						 {
							 machine_step=2;
						 }
				         break;
				   case 3:recv_buf[recv_cnt]=SBUF;//接收第6个数据
						if(recv_buf[recv_cnt]==0x0D)//帧尾 0x0D
                         {
							
							switch(recv_buf[2])//通过数据类型判断处理LED还是蜂鸣器
							{
								case 0x01:
								  led_data=recv_buf[3]<<8;
								  led_data=led_data+recv_buf[4];//高8位<<8+低8位=LED控制时 
                                                                //间
								  led_cnt=0;
								  break;
								case 0x02:
								  beep_data=recv_buf[3]<<8;
								  beep_data=beep_data+recv_buf[4];//高8位<<8+低8位=蜂鸣器控 
                                                                  //制时间
								  beep_cnt=beep_data;
								  break;
								default:break;	
							}
							machine_step=0;
							recv_cnt=0;
							recv_flag=1;//接收完成标志位,=1时表明接收完成
						}			
				    break; 
				    default:break;		 
		}
  }
}

4.3定时器初始化模块

定时器T0定时1ms:

回顾定时器:

中断&定时计数器-CSDN博客

单片机笔记(3)定时计数与中断-CSDN博客

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	ET0=1;          //定时器0中断打开
	TR0 = 1;				//定时器0开始计时
}

4.4LED+蜂鸣器功能模块

定时1ms,则每1ms中断1次:

void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
{
	TR0=0;
	if(recv_flag==1)//接收完毕
	{
		if(led_cnt<led_data)
		{
			LED=~LED;
			led_cnt++;
		}
		else
		{
			led_cnt=0;
			recv_flag=0;
		}
		if(beep_cnt!=0)
		{
			Beep=~Beep;
			beep_cnt--;
		}
		else
		{
			beep_cnt=0;
			recv_flag=0;
		}
	}
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TR0=1;
}

4.5主程序模块

void main()
{
	UartInit();     //调用串口初始化函数
	Timer0_Init();  //调用定时器T0初始化
	EA=1;           //总中断允许
	while(1)
	{
		if(recv_flag==1)//接收完数据后单片机发送回数据给com3
		{
		  sendString(recv_buf);//发送数据,数组recv_buf存储接收到的数据
		  clr_recvbuffer(recv_buf);//清除缓冲
		}
	}
}

4.6发送数据模块

不懂指针和数组的,可以试着回顾前面写的文章的3.2节:

串口通信(5)-一串固定长度数据的接收-CSDN博客

void sendByte(unsigned char dat) //发送一帧数据功能函数
{
	SBUF=dat;
	while(!TI);
	TI=0;
}

void sendString(unsigned char *dat)//发送字符串函数
{
	while(*dat != '\0')
	{
		sendByte(*dat++);
	}
}

4.7清除缓冲模块

unsigned char *buf=recv_buf,则buf[i]=0是将第i+1个数据清0。

void clr_recvbuffer(unsigned char *buf)
{
	unsigned char i;
	for(i=0;i<MAX_REX_NUM;i++)
	{
	  buf[i]=0;
	}
}

4.8uart.h

#ifndef __UART_H__
#define __UART_H__

#include <reg51.h>
#include <stdio.h>

#define MAX_REX_NUM 20
#define MAX_timer_cnt 5

extern unsigned char recv_buf[MAX_REX_NUM];
extern unsigned char recv_cnt;
extern unsigned char start_timer;
extern unsigned char recv_timer_cnt;
extern unsigned char recv_flag;
extern unsigned char led_data;
extern unsigned char beep_data;
extern unsigned char led_cnt;
extern unsigned char beep_cnt;

void UartInit(void);
void sendByte(unsigned char dat);
void sendString(unsigned char *dat);
char putchar(char c);
void clr_recvbuffer(unsigned char *buf);

#endif

本次设计完整代码,请关注不白吃并在评论区回复666!!!

@日月同辉,与我共生-不白吃CSDN博客

五、结果

LED灯:

蜂鸣器:

下一文将着重串口中断即时解析用户自定义通讯协议--接收数据字节固定情况,亲爱的读者敬请期待,下一文更精彩!!!

一日不读书,胸臆无佳想。我叫不白吃,喜欢我的,可以支持我@日月同辉,与我共生!!!

欢迎大家的阅读和交流,也可以自由查阅博主其他文章哦!!!

@日月同辉,与我共生_单片机基础,单片机串口通信-CSDN博客@日月同辉,与我共生擅长单片机基础,单片机串口通信,等方面的知识,@日月同辉,与我共生关注stm32,c语言,51单片机,proteus,单片机领域.https://blog.csdn.net/LIN___IT?spm=1000.2115.3001.5343

  • 33
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值