细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的方法

目录

一、工程目的

[1、 目标](#1、 目标)

2、通讯协议及应对错误指令的处理目标

二、工程设置

三、程序改进

四、下载与调试

1、合规的指令

2、 proBuffer[0]不是#

3、proBuffer[4]不是;

4、指令长度小于5

5、指令长度大于5

6、proBuffer[2]或proBuffer[3]不是数字

7、';'位于proBuffer[2]或proBuffer[3]位置


在本文作者的文章(参考文章):细说STM32单片机USART中断实现收发控制的方法_stm32 uart 中断接收-CSDN博客 https://wenchm.blog.csdn.net/article/details/143189217 中作者曾经建立了一个Cube IDE工程,利用STM32单片机的USART2串口中断程序,实现单片机接收由串口助手发送来的指令,根据指令更新RTC时间,再通过USART2串口向串口助手发送RTC时间。

参考文章的缺点是,程序简单,没有考虑到干扰指令的影响。程序的鲁棒性很差,除了必须严格按照表格中的协议输入指令外,程序对于不知情、不甚了解协议得错误输入没有鉴别和应急处理的措施。

本文中,作者对不同情况下输入的错误指令,在程序中提供了对应的应急处理方法和错误信息提示。提高了程序的鲁棒性和可用性。

一、工程目的

1、 目标

除参考文章中提到的设计目的之外,重点解决程序设计的鲁棒性:对不同的指令输入的应急处理能力、消息提示。

2、通讯协议及应对错误指令的处理目标

通讯协议(同参考文件)及不同情景下的对错误指令的应急处理方案,对照表如下:

|---------------------|-----------------------------------|------------------------------------------------------------------------------------------|
| 上位机发送的指令字符串 | 指令功能 | 操作说明 |
| #H13; | 设置小时,将RTC时间的小时修改为13 | |
| #M32; | 设置分钟,将RTC时间的分钟修改为32 | |
| #S05; | 设置秒,将RTC时间的秒修改为5 | |
| #U01; | 恢复上传时间数据 | |
| #U00; | 停止上传时间数据 | |
| &H13; | proBuffer[0]不是# | |
| #H13! | proBuffer[4]不是; | |
| #HER;#H6R;#HE6; | proBuffer[2]或proBuffer[3]不是数字 | |
| #Y13; | proBuffer[1]不是H、M、S、U | |
| #H9; | 指令长度小于5 | 第一次输入,串口助手没有相应,因为, 串口接收中断没有接收到足够的5个 字节数据,没有产生中断。第二次输入, 助手显示#H9;#,执行相应的应急处理 程序,清空缓存,重启串口。 |
| #H192; | 指令长度大于5 | 第一次输入。串口助手显示#H192,因 为proBuffer[4]不是;执行相应的处理程序 |
| #H19;2 | 指令长度大于5 | 第一次输入。串口助手显示#H19;程 序正常处理,下一中断,助手显示2**** ,因为proBuffer[0]不是#,执行相应 的应急处理程序,清空缓存,重启串口。 |
| #H30; | 小时的数值范围不属于[0,24] | |
| #M70; | 分钟的数值范围不属于[0,60] | |
| #S70; | 秒的数值范围不属于[0,60] | |

二、工程设置

与参考文章相同。

三、程序改进

受影响的程序只有usart.c,且只需修改其中的函数void updateRTCTime()。

cpp 复制代码
/*根据串口接受来的指令字符串,更新修改RTC时间*/
void updateRTCTime()
{
	unsigned char hello1[]="Invalid command\n";
	unsigned char hello2[]="Invalid data\n";

	/* -0x30 操作用于将ASCII码表示的字符(假设是数字'0'~'9')转换为其对应的整数 */
	uint8_t timeSection = proBuffer[1]; //类型字符
	uint8_t tmp10 = proBuffer[2]-0x30; 	//十位
	uint8_t tmp1 = proBuffer[3]-0x30; 	//个位
	uint8_t val= 10*tmp10+tmp1;


//  Identify the start_bit is '#',the end_bit is ';'or not,
// Regardless of whether the input characters are less than 5 or more than 5, they will all be processed by this program in the end.
// No matter how you clear the buffer, the data received next time will not be complete.
// This situation will never improve until the serial port is restarted.
	if (rxBuffer[0] != '#' ||  rxBuffer[4] != ';')
	{
		HAL_UART_Transmit(&huart2,hello1,sizeof(hello1),200);

		memset(rxBuffer, '\0', sizeof(rxBuffer));
		memset(proBuffer, '\0', sizeof(proBuffer));

		HAL_UART_Init(&huart2);	//重启串口

		rxCompleted = RESET;
		HAL_UART_Receive_IT(&huart2, rxBuffer, RX_CMD_LEN);
		return;
	}

// Identify the data_bit is digits or not
	if (isalpha(proBuffer[2])  || isalpha(proBuffer[3]))
	{
		HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);
		memset(proBuffer, '\0', sizeof(proBuffer));
		return;
	}

	//update RTCtime
	RTC_TimeTypeDef sTime;
	RTC_DateTypeDef sDate;
	if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
	{
		//调用HAL_RTC_GetTime()之后必须调用HAL_RTC_GetDate()以解锁数据,才能连续更新Date and Time
		HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);

		switch (timeSection)
		{
			case 'H': // 修改小时
				{
					if(val <= 24)
						sTime.Hours = val;
					else
						{
							HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);
							memset(proBuffer, '\0', sizeof(proBuffer));
							return;
						}
				}
				break;
			case 'M': // 修改分钟
				{
					if(val <= 60)
						sTime.Minutes = val;
					else
					{
						HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);
						memset(proBuffer, '\0', sizeof(proBuffer));
						return;
					}
				}
				break;
			case 'S': // 修改秒
				{
					if(val <= 60)
						sTime.Seconds = val;
					else
					{
						HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);

						memset(proBuffer, '\0', sizeof(proBuffer));
						return;
					}
				}
				break;
			case 'U':
				{
					if( tmp1 == 0)
					{
						isUploadTime = 0;//pause
						return;
					}
					else
						isUploadTime = 1; //resume
					}
				break;
			default: // 不是 'H', 'M' , 'S','U'则返回
				{
					HAL_UART_Transmit(&huart2,hello1,sizeof(hello1),200);
					memset(proBuffer, '\0', sizeof(proBuffer));
				}
				return;
		}

		HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); //设置RTC时间影响到下一次唤醒
	}
}

四、下载与调试

本文的重点在于调试。测试各种情况下新设计的程序的鲁棒性:对各种错误的输入的应急处理能力和信息提示。

1、合规的指令

2、 proBuffer[0]不是#

指令不符合格式,显示指令错误。

3、proBuffer[4]不是;

指令不符合格式,显示指令错误。

4、指令长度小于5

指令长度小于5时,第一次输入后,看不到响应,再次输入后,显示错误的指令字符(只显示前5个字节),执行应急处理程序,清除缓存,重置串口初始化。

如果不重置串口初始化,无论怎么输入,程序接收到的数据总是陷入混乱的状态。

5、指令长度大于5

指令长度小于5时,显示指令错误。当proBuffer[4]=;时,如果前面的数据符合格式,那么执行数据更新;再次 输入指令后,显示指令错误,并执行相应的应急处理程序,清空缓存,重置串口。

6、proBuffer[2]或proBuffer[3]不是数字

显示数据错误。

7、';'位于proBuffer[2]或proBuffer[3]位置

显示指令错误。

其它没有测试到的错误指令输入的情形,感兴趣的网友亲测吧。

本方法,只限于大写字母,没有提供区分大小写字母的算法,感兴趣的网友可以自己去完善。

本例的程序还存在一个小BUG,就是当修改其他指令时,小时会无规律地被修改。感兴趣的网友自己去纠正吧,哈!

相关推荐
IT B业生2 小时前
51单片机教程(六)- LED流水灯
单片机·嵌入式硬件·51单片机
一枝小雨2 小时前
51单片机学习心得2(基于STC89C52):串口通信(UART)
单片机·嵌入式硬件·51单片机
IT B业生3 小时前
51单片机教程(一)- 开发环境搭建
单片机·嵌入式硬件·51单片机
u0101526584 小时前
STM32F103C8T6学习笔记2--LED流水灯与蜂鸣器
笔记·stm32·学习
海绵波波1075 小时前
Webserver(4.8)UDP、广播、组播
单片机·网络协议·udp
好想有猫猫5 小时前
【51单片机】串口通信原理 + 使用
c语言·单片机·嵌入式硬件·51单片机·1024程序员节
stm 学习ing6 小时前
C语言 循环高级
c语言·开发语言·单片机·嵌入式硬件·算法·嵌入式实时数据库
w微信150135078127 小时前
小华一级 代理商 HC32F005C6PA-TSSOP20 HC32F005系列
c语言·arm开发·单片机·嵌入式硬件
北京迅为8 小时前
【北京迅为】《STM32MP157开发板嵌入式开发指南》-第七十八章 Qt控制硬件
linux·stm32·单片机·嵌入式硬件
田三番11 小时前
使用 vscode 简单配置 ESP32 连接 Wi-Fi 每日定时发送 HTTP 和 HTTPS 请求
单片机·物联网·http·https·嵌入式·esp32·sntp