蓝桥杯单片机串口通信接收字符串指令、发送拼接字符串

今日继续学习提升蓝桥杯国赛能力水平。

有道是:卜心事、灯花无语,百感孤单,鸳被羞展......

                梦方圆,又丛钟、声声惊断。

诗人杨玉衔孤单影只,偏偏又多遭磨难,一路坎坷......

正如我近日来学习提升串口通信技能一样,遇到诸多设计上的险阻。

本篇主要解决以下串口通信方面的问题:

1.上位主机发送命令字符串,单片机接收校对后执行指令

2.单片机某个变量在改变,要求与字符串拼接后发送给主机

注意:本文不是零基础开始的,需要对串口有基础认识,会用串口收发单八位字节

目录

如零基础学习串口,请传送到这篇文章:

本文提供问题解决的工程文件:

 关于单片机接收字符串指令问题的解决:

关于拼接变量与字符串发送的问题:


如零基础学习串口,请传送到这篇文章:

蓝桥杯单片机串口通信学习提升笔记_NULL指向我的博客-优快云博客

本文提供问题解决的工程文件:

问题一过程文件:

https://download.youkuaiyun.com/download/qq_64257614/87808393?spm=1001.2014.3001.5503

问题二过程文件:

https://download.youkuaiyun.com/download/qq_64257614/87809191?spm=1001.2014.3001.5503

 关于单片机接收字符串指令问题的解决:

你是否也困扰于某些省赛国赛题目(第十届)中,有上位机发送字符串命令

单片机接受并处理的要求,比如:

“单片机接收到上位机指令“START\r\n”,上报XXX数据”

“如设备接收到错误指令,返回“ERROR\r\n” ”

其实他们本意就是考察我们对串口收发的熟悉程度。

下表是printf函数的

我们可以从以下实验来学习强化这方面的知识:

/*
	本实验练习串口通信:主要练习以下内容:		 
	数组接收判断上位机的命令
		收到	START\r\n    就  打印 OVER\r\n
		其余都打印   ERROR\r\n
*/

  我们首先在变量定义来讲解:

bit URX_Over = 0;                   
unsigned char idata URX[10] = 0;
unsigned char idata URX_Num = 0;
unsigned char idata URX_tt = 0;

1.URX_Over是标志位,当其值为1时,会进入函数处理比较字符串,PS:它将在定时器中出现,  

2.URX[10] 是字符串数组,他用于接受上位机发来的字符串命令,接收处理完后要全部清零,

                   因为有些命令比较长,而有些比较短,在接受了一个长的字符串后,不清空数组

                   的话,短命令就无法正常比较了.

                    比如第一次接收到了长命令START\r\n,第二次发来短命令ST\r\n,但因为没清空数组                    所以第二次数组内的情况是ST\r\nT\r\n,这就会被判断为是一个错误指令。

memset(URX,0,sizeof(URX));//处理完命令别忘了将其清零,以便接收下个命令

3.URX_Num 用来辅助字符串数组URX[10] 接收SBUF寄存器的内容

4.URX_tt 用来标记接收是否完成,它同时在定时器中会进行自加,

                每次RI不为0,接收数据时,它都会被清零, 直到接收完毕,

                接收完毕后它就不会再被清零了

                此时的它在定时器中不断自加,到一定值后 就会 刷新标志位URX_Over,

                从而推动 字符串比较进程的开展。

涉及以上过程的代码如下贴出:

此段代码对应串口中断的过程:

bit URX_Over = 0;
unsigned char idata URX[10] = 0;
unsigned char idata URX_Num = 0;
unsigned char idata URX_tt = 0;

//串口1中断服务函数
void Uart_1_serv() interrupt 4
{
 	if(RI)
	{
		RI=0;  URX_tt = 0;
		if(URX_Num < 10) { URX[URX_Num++] = SBUF;	}		
	}
}

此段代码是定时器中的过程:

void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0x01;		//设置定时器模式
	TL0 = 0x20;		//设置定时初始值
	TH0 = 0xD1;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	EA=1;
	ET0=1;
}
void Timer0(void) interrupt 1
{
	
	TL0 = 0x20;		//设置定时初始值
	TH0 = 0xD1;		//设置定时初始值
	if(URX_Num > 0){if(++URX_tt == 30) { URX_Over = 1;} }			
}

最后是主程序While()循环中,对字符串命令的判断:

#include "stdio.h"
#include "string.h"

u8 str1[ ]="START\r\n";

if(URX_Over == 1)
{
	URX_Over =0;
	if(strncmp(URX,str1,7)==0) Uart_Sendstring("OVER\r\n");
	else   Uart_Sendstring("ERROR\r\n");
	URX_Num = 0;
}

此处需要注意:URX_Num = 0;  这句

表示处理完了字符串命令,就清零,以便URX[10] 接收下个命令字符串

下载后进行调试,效果如下:

 证明我们的串口字符串命令收发非常顺利!!!

如果字符串命令种类不止一条:

我们在处理接收的if(URX_Over == 1)语句中就需要加上一些if语句:

 下段代码就是接收START\r\n 与 HI\r\n并做相应回复的例子

 

 

关于拼接变量与字符串发送的问题:

这个问题我习惯用的有俩种解决方式:

1.用重定向的printf()函数

2.使用senddata()函数,但需要另外定义数组和写函数进行拼接字符串与变量值

但此处建议与介绍 使用printf(),虽然占用多些资源,但不影响竞赛要求!

我们都知道printf 函数的一般调用格式为:printf("<格式化字符串>", <参量表>)

以下为在chitgpt搜索到的关于%bu格式转换字符的原理与应用:

在Keil编程中,使用printf函数的%bu格式转换字符是用于

将一个unsigned char类型的整数十进制的形式输出。

%bu是printf函数的格式控制字符串中的占位符,

用于指示需要输出一个unsigned char类型的整数。

在基础知识方面,

unsigned char类型是一种无符号字符类型,它可以存储0到255之间的整数。它通常用于表示二进制数据的字节和字符。

在C语言中,

可以使用%u格式控制字符串输出unsigned char类型的整数,但是由于printf函数默认将char类型看作有符号类型,因此使用%u格式控制字符串输出unsigned char类型的整数时可能会出现问题。因此,为了避免这种问题,可以使用%bu格式控制字符串来输出unsigned char类型的整数。

下面是一个使用%bu格式控制字符串输出unsigned char类型的整数的例子:

unsigned char value = 255;
printf("The value is %bu\n", value);

输出结果为:

The value is 255

于是,我们便知道了该如何拼接变量与字符了

以下为实践内容要求:

		定义test1与 test2,每300ms自加,每次自加后发送:
			
		the new test1 is (test1的值)\r\n	the new test2 is (test2的值)\r\n	

我们只需对照着要求,在定时器中使他们自加,然后附上printf函数格式化输出即可:

定时器中并没有直接放printf函数,因为主函数也调用该函数,为防止报错重入警告,我们将printf的操作放在主函数,定时器刷新标志给主函数决定是不是该进行printf的操作了:

bit test_flag=0;


void Timer0(void) interrupt 1
{
	u16 i;
	TL0 = 0x20;		//设置定时初始值
	TH0 = 0xD1;		//设置定时初始值
	i++;
	if(URX_Num > 0){if(++URX_tt == 30) { URX_Over = 1;} }			
	if(i==300)
	{
		i=0;
		test1++;test2++;
		if(test1==10) test1=0;
		if(test2==10) test2=0;
    test_flag=1;		           //打印标志位——传给主函数
	}	
}

 以下为主函数:

void main()
{
	u8 test=2;
	
	cls_buzz_led();				//关闭外设
	UartInit();						//初始化串口
	Timer0Init();
	
	//以下三句是开机串口功能初始化测试用:
	printf("Welcome to stc15f2k60s2\n");
	printf("%c",test+'0');
	SendData(test+'0');
	
	while(1)
	{	
		if(URX_Over == 1)
		{
			URX_Over =0;
			if(strncmp(URX,str1,7)==0) Uart_Sendstring("OVER\r\n");
			else   Uart_Sendstring("ERROR\r\n");
			URX_Num = 0;
		}
		if(test_flag==1)
		{
			test_flag=0;
		printf("the new test1 is %02bu\r\n	the new test2 is %02bu\r\n	",(unsigned char)test1,(unsigned char)test2);
		}
	}
}

 其中最重要的拼接部分就是:

		printf("the new test1 is %02bu\r\n	the new test2 is %02bu\r\n	",(unsigned char)test1,(unsigned char)test2);

 下载后进行串口调试:效果如下:

在此,我们的所有问题就这么解决了,希望各位看官多多三连支持!!

单片机串口通信接收字符串的步骤如下: 1. 初始化串口模块,确定波特率、数据位、停止位、校验位等参数。 2. 等待接收串口数据,一般使用中断方式接收数据,即设置串口接收中断函数。 3. 接收串口数据后,将数据存储到内存中。 4. 检查接收到的数据是否为完整的字符串,判断方法可以是判断是否有结束符(如'\0')或者判断接收到的字节数是否达到预期。 5. 如果接收到完整字符串,则处理数据,完成后清空缓存区,等待下一次接收。 下面是一段接收字符串的示例代码: ```c #include <reg52.h> #include <stdio.h> #define UART_BAUDRATE 9600 // 串口波特率 #define UART_BUFFERSIZE 32 // 串口缓冲区大小 unsigned char uart_buffer[UART_BUFFERSIZE]; // 串口缓冲区 unsigned char valid_data = 0; // 有效数据长度 void uart_init() { /* 初始化串口模块 */ TMOD |= 0x20; // 定时器1工作在模式2,允许自动重装载 TH1 = 0xFD; // 波特率计算公式:TH1 = 256 - (晶振频率 / (32 * 波特率)),晶振频率为11.0592 MHz TL1 = 0xFD; PCON = 0x80; // SMOD = 1,波特率加倍 SCON = 0x50; // SCON的D7和D6位分别表示串口工作模式和接收允许位 IE = 0x90; // 开启串口中断和定时器中断 TR1 = 1; // 启动定时器1 } void uart_receive() interrupt 4 { /* 串口接收中断函数 */ if (RI) { /* 接收数据 */ unsigned char data = SBUF; if (valid_data < UART_BUFFERSIZE) { uart_buffer[valid_data] = data; valid_data++; } RI = 0; } } void main() { uart_init(); while (1) { if (valid_data > 0) { /* 接收到有效数据 */ if (uart_buffer[valid_data - 1] == '\0') { /* 数据接收完成 */ printf("Received: %s\r\n", uart_buffer); // 处理数据 valid_data = 0; // 清空缓冲区,等待下一次接收 } } } } ``` 在上面的代码中,初始化了串口模块并设置了串口接收中断函数。在主函数中,不断检查串口接收缓冲区中是否有数据,如果有数据则判断是否为完整字符串,如果是则处理数据并清空缓冲区。如果缓冲区中的数据不足以组成完整的字符串,则继续等待接收
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NULL指向我

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

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

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

打赏作者

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

抵扣说明:

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

余额充值