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

目录

一、工程目的

1、目标

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

二、工程设置

三、程序改进

四、下载与调试

1、合规的指令

2、不以#开头,但以;结束,长度不限

3、以#开头,不以;结束,也不包含;,长度不限

4、以#开头,以;结束,长度<5

5、以#开头,以;结束,长度>5

6、非数字位于proBuffer[2]或proBuffer[3]位置

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

8、时间数值超过范围


        在本文作者的文章(参考文章)里,细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的方法_stm32 usart中断 at指令-CSDN博客  https://wenchm.blog.csdn.net/article/details/143461698,谈到了一种方法,对不同情况下输入的错误指令,在程序中提供了对应的应急处理方法和错误信息提示,提高了程序的鲁棒性和可用性。参考文章的程序中,每次接收5个字节的指令字符,然后对接收到的5个字节数据在updateRTCTime()函数里进行判断和处理。

        本文对参考文章进行了修改,每次接收中断只接收1个字节(也就是1个字符)的数据,先在接收回调(含内部调用的自定义函数)函数里对输入指令的字符串合规性、指令字符串长度长度进行大范围的判断和处理。然后再在updateRTCTime()函数里进行细节(数据头尾格式、数值范围、是否数字、是否指令字)方面的判断和处理。

一、工程目的

1、目标

         再次提供一种新方法,每次接收中断只接收1个字符,重点解决程序设计的鲁棒性:对不同的指令输入的应急容错处理能力、消息提示。

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

         通讯协议(参照参考文章的表格)。

        需要注意的是: 

  • 对符合规则的指令字符串输入,比如“#H23;",程序显示指令字符串,并更新RTC时间;
  • 对长度<=5,其他方面符合规则输入,比如“#H3;",“#HT3;",“#T23;",程序显示指令字符串,并消息提示。
  • 对以#开头的、指令字符串长度大于5,并且以;结束的输入,比如在串口助手发送“#H236;",助手会显示;H236,并消息提示,无效的指令。无论多长的数据,其处理规则是,每隔5个字符,后面的数据从左侧开始覆盖前面的数据。
  • 对以#开头的、指令字符串长度不限,并不含有;的输入,比如在串口助手发送“#H256",无论串口助手发送多少次,助手都没有显示,直到串口助手发送包含;在内的且长度不大于5的字符串为止,显示最后接收到的5个字符。并消息提示,无效的指令。
  • 不以#开头或不含有#,但以;结尾,长度<=5的字符串输入,显示输入的字符串,并消息提示,无效的指令。
  • 不以#开头或不含有#,也不含有;,长度不限的字符串输入,无论串口助手发送多少次,助手都没有显示,直到串口助手发送包含;在内的且长度不大于5的字符串为止,显示最后接收到的5个字符。并消息提示,无效的指令。

二、工程设置

        与参考文章相同。

三、程序改进

        受影响的程序有usart.c,usart.h。

        修改 usart.h,定义RX_CMD_LEN = 1。这个常量用于控制函数HAL_UART_Receive_IT()每次接收数据的长度,修改为1则每次只接收1字节的数据,即1个字符。

/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes */
#include <ctype.h>
/* USER CODE END Includes */extern UART_HandleTypeDef huart2;/* USER CODE BEGIN Private defines */
#define	RX_CMD_LEN 1		    //每次接收到的指令长度1字节
extern uint8_t rxBuffer[];  	//5字节的输入缓冲区,如#H15;
extern uint8_t isUploadTime;	//是否上传时间数据
/* USER CODE END Private defines */void MX_USART2_UART_Init(void);/* USER CODE BEGIN Prototypes */
void on_UART_IDLE(UART_HandleTypeDef *huart);	//IDLE中断函数
void updateRTCTime();							//根据接收指令更新RTC
/* USER CODE END Prototypes */

        修改usart.c,增加一个变量定义rxBufPos和一个宏定义PRO_CMD_LEN。

        此处,切记接收缓存和发送缓存不要赋初值,不然可能引起某些情况下的指令输入产生混乱。

/* USER CODE BEGIN 0 */
#include "rtc.h"
#include <string.h>
#include <stdio.h>/* 新增的两句用于接收不定长数据 */
#define PRO_CMD_LEN 5				// String length must be 5。
uint8_t rxBufPos = 0;				// Receive buffer bit index
/* 新增的两句用于接收不定长数据 */uint8_t	proBuffer[10]; 				//为DEBUG观察方便,两个缓存数组不赋初值
uint8_t	rxBuffer[10];
uint8_t	rxCompleted = RESET;		//HAL_UART_Receive_IT()接收是否完成uint8_t	isUploadTime = 1;			//是否上传时间数据/* USER CODE END 0 */

        修改usart.c,修改函数HAL_UART_RxCpltCallback()和on_UART_IDLE()的代码,修改并删除函数updateRTCTime()冗余的判断和操作。 

/* USER CODE BEGIN 1 */
/*串口接收完毕中断回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART2){rxCompleted = SET;__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); //允许IDLE中断}
}/*IDLE事件中断的检测与处理,获得proBuffer*/
void on_UART_IDLE(UART_HandleTypeDef *huart)
{if (__HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE) == RESET)return;__HAL_UART_CLEAR_IDLEFLAG(huart); 				//清除IDLE挂起标志__HAL_UART_DISABLE_IT(huart, UART_IT_IDLE); 	//禁止IDLE事件中断if (rxCompleted){uint8_t ch = rxBuffer[0];if(ch == '#')rxBufPos = 0;	                        //收到#则指令头部位置0//最多接收5个字符if (rxBufPos < PRO_CMD_LEN){proBuffer[rxBufPos] = ch;rxBufPos++;// 输入的指令字符串必须#开头,并且包含;无论;是否在末尾,;都代表指令结束if(ch == ';' )	    //遇到;则打印指令并更新RTC{//把接收到的指令字符显示到串口助手HAL_UART_Transmit(huart,proBuffer, strlen((char*)(proBuffer)), 200);HAL_Delay(10);updateRTCTime(); //把接收到的指令更新RTC时间// 每次发送指令到串口助手后清除缓存memset(rxBuffer, '\0', sizeof(rxBuffer));memset(proBuffer, '\0', sizeof(proBuffer));// 位索引复位,这个操作尤其在正确输入之后遭遇错误输入的时候有意义rxBufPos = 0;}}// When the length of the input string is greater than 5 and does not contain';',// the index value is cleared to zero,// and the unfinished string can continue to be received until it encounters';'.if (rxBufPos == PRO_CMD_LEN){rxBufPos = 0;}/*更新完RTC时间后,要把rxCompleted复位,并再次启动串口接收,让程序处于等待串口输入的状�??*/rxCompleted = RESET;/* 再次启动串口接收 */HAL_UART_Receive_IT(huart, rxBuffer, RX_CMD_LEN);}
}/* 对接收到的指令做是否合规的判断,更新修改RTC时间 */
void updateRTCTime()
{unsigned char hello1[]="Invalid command\n";unsigned char hello2[]="Invalid data\n";// 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 (proBuffer[0] != '#' ||  proBuffer[PRO_CMD_LEN - 1] != ';' ){HAL_UART_Transmit(&huart2,hello1,sizeof(hello1),200);HAL_UART_Init(&huart2);	//重启串口return;}// Identify the data_bit is digits or notif (isalpha(proBuffer[2])  || isalpha(proBuffer[3])){HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);return;}//update RTCtimeRTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;/* -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;if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK){//调用HAL_RTC_GetTime()之后必须调用HAL_RTC_GetDate()以解锁数据,才能连续更新Date and TimeHAL_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);return;}}break;case 'M': // 修改分钟{if(val <= 60)sTime.Minutes = val;else{HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);return;}}break;case 'S': // 修改秒{if(val <= 60)sTime.Seconds = val;else{HAL_UART_Transmit(&huart2,hello2,sizeof(hello2),200);return;}}break;case 'U':{if( tmp1 == 0){isUploadTime = 0;//pausereturn;}elseisUploadTime = 1; //resume}break;default: // 不是 'H', 'M' , 'S','U'则返回{HAL_UART_Transmit(&huart2,hello1,sizeof(hello1),200);}return;}HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); //设置RTC时间影响到下一次唤醒}
}
/* USER CODE END 1 */

        新增了一个宏定义PRO_CMD_LEN,其值为5,即表示一条指令的长度5字符;

        新增了一个变量rxBufPos,用于表示缓冲区proBuffer的当前存储位置索引。

        调用HAL_UART_Receive_IT()以中断方式接收数据时,长度设置为RX_CMD_LEN=1,这样每收到一个字符,就会执行一次回调函数HAL_UART_RxCplCallback(),这个函数里开启IDLE事件中断后,就会执行on_UART_IDLE()。函数on_UART_IDLE()的功能是对接收到的一个字符ch进行判断和处理。每当起始符为#,就将rxBufPos设置为0;如果rxBufPos小于5,就将ch存入缓冲区proBuffer,并且使rxBufPos加1;如果ch是指令结束符“;”。就调用函数updateRTCTime()对指令进行解析和处理。

        on_UART_IDLE()中第一个判断语句中的函数是__HAL_UART_GET_IT_SOURCE(),不再是__HAL_UART_GET_FLAG()。因为上位机连续发送5字节,MCU串口接收到1字节后就开启了IDLE事件中断,但是因为后续还有连续的数据接收,所以IDLE事件的中断标志位并不会立刻置位,而是在接收完5字节后才置位。如果使用_HAL_UART_GET_FLAG()判断IDLE事件中断的中断标志位,中断一次后就不会再处理后续的数据了。

        相对于参考文章,其它地方的程序修改,都是为了提高程序的容错能力。这些修改,作者都DEBUG过,十分好用。 

四、下载与调试

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

1、合规的指令

         显示输入的指令字符串,并更新RTC时间。

 

2、不以#开头,但以;结束,长度不限

        只显示最后接收到的5个字符,并消息提示,无效的指令输入。

 

3、以#开头,不以;结束,也不包含;,长度不限

         不论发送多少次,串口助手没显示,直到发送包含;在内且长度<=5的指令后,才只显示最后接收到的5个字符,并消息提示,无效的指令输入。

        依次发送截图中的指令,串口助手没有响应(程序的后台是有相应的) ,直到发送“#H8”,并显示无效的指令。最终的结果的指令是否合规,要看机缘。

4、以#开头,以;结束,长度<5

        显示输入的指令字符串,并消息提示,无效的指令

5、以#开头,以;结束,长度>5

        只显示最后接收到的5个指令字符,并消息提示,无效的指令

 

6、非数字位于proBuffer[2]或proBuffer[3]位置

         显示输入的指令字符,并消息提示,无效的数据。

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

        只显示;之前的数据。并消息提示,无效的指令。

      

8、时间数值超过范围

        显示输入的指令字符串,并消息提示,无效的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/465844.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

路见不平 ! 基于tensorlfow快速迭代的户型图分类功能

前言 在工作之余&#xff0c;发现合作的同事需要手动筛选户型图&#xff0c;存在一些老旧或无家具的户型图。这启发我们通过机器学习的模型预测来辅助校验&#xff0c;进而优化筛选流程。当前本期目标为6万个,后续也会有数据需要筛选,已经筛选出一部分数据 可以进行模型训练&am…

字符串接龙 /单词接龙 (BFs C#

卡码网 110和 力扣127 和LCq 108题都是一个解法 这两道题乍一看在结果处可能不一样 力扣要求 字符串里边必须包含对应的最后一个字符 而110不需要最后一个字符 但是在实验逻辑上是一致的 只是110需要把如果在set中找不到最后一个字符就直接返回0的逻辑删去 就可以了 这就是…

STM32之看门狗

STM32有独立看门狗&#xff08;IWDG&#xff09;和窗口看门狗(WWDG)。 采用窗口看门狗&#xff08;WWDG&#xff09;&#xff0c;有一个死前中断&#xff0c;可以用来作一个报警的功能。 独立看门狗超时时间计算公式 假设LSI是32KHz,超时时间等于 预分频系数&#xff08;4&…

平安科技(外包)面试分享

前言&#xff1a; 这是成都这边的平安科技面试分享&#xff0c;上家公司是做海外的&#xff0c;好不容易逮到公司离职赔偿的机会&#xff0c;我就离职了&#xff0c;没想到过了国庆节之后&#xff0c;工作是那么的难找&#xff0c;大概投了1-2周简历&#xff08;外包和短期项目…

Python 在PDF中绘制形状(线条、矩形、椭圆形等)

在PDF中绘制图形可以增强文档的视觉效果。通过添加不同类型的形状&#xff0c;如实线、虚线、矩形、圆形等&#xff0c;可以使文档更加生动有趣&#xff0c;提高读者的阅读兴趣。这对于制作报告、演示文稿或是教材特别有用。本文将通过以下几个示例介绍如何使用Python 在PDF中绘…

2-2.STM32之定时器TIM---输入捕获--实验2( PWMI模式测频率占空比)

输入捕获模式测频率、PWMI模式测频率占空比-CSDN博客 参考这篇文章&#xff01; 来利用一个GPIO的定时器的两个通道进行捕获占空比和频率&#xff0c;看出可以看出。TI1FP1和TI2FP2&#xff0c;计数值分别在CCR1和CCR2中取&#xff0c; 测周法 IC.c #include "stm32f1…

2024年转行指南:大学生进军就业前景广阔的领域——人工智能大模型

据教育部数据统计&#xff0c;2024高校毕业生规模预计达1179万人&#xff0c;将再创历史新高&#xff0c;“就业难”仍是当前大学毕业生需要直面的问题。在此背景下&#xff0c;选择一个就业前景好的专业尤为重要。 究竟学什么样的专业好就业呢&#xff1f;给毕业生们推荐3个当…

suanfabiji

1 差分练习 1 模板题 代码实现&#xff1a; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int m sc.nextInt();int num sc.nextInt();long[][] arr new long[n 2][m 2…

WPS单元格重复值提示设置

选中要检查的所有的单元格 设置提示效果 当出现单元格值重复时&#xff0c;重复的单元格就会自动变化 要修改或删除&#xff0c;点击

一.Linux文件基本属性

前言&#xff1a;Linux系统是一个多用户系统&#xff0c;不同的用户处于不同的地位&#xff0c;也就是说具有不同的权限。为了安全&#xff0c;对于不同用户访问同一个文件&#xff0c;设置不同权限是很有必要的。 一.文件的基本属性理解 在Linux中&#xff0c;通常是这两个命…

【学习记录】使用CARLA录制双目摄像头SLAM数据

一、数据录制 数据录制的部分参考了网上的部分代码&#xff0c;代码本身并不复杂&#xff0c;基本都是简单的CARLA语法&#xff0c;关键的一点在于&#xff0c;CARLA内部本身并没有预设的双目摄像头&#xff0c;需要我们添加两个朝向相同的摄像头来组成双目系统&#xff0c;这…

算法的基础知识

算法的定义 算法是为了解决某类问题而规定的一个有限长的操作序列。 算法的特性 1. 有穷性&#xff08;Finiteness&#xff09; 含义&#xff1a;一个算法必须总是在执行有穷步后结束&#xff0c;且每一步都必须在有穷时间内完成。重要性&#xff1a;确保算法能够在合理的时…

城镇保障性住房管理:SpringBoot技术应用

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

【ComfyUI】flux人像摄影风格迁移的最优解?这个效果应该暂时无敌了吧?效果不好你打我!

大家好&#xff0c;这期我们主要讨论如何使用stable diffusion comfyUI 制作基于flux的人像摄影&#xff0c;主要实现风格迁移的功能。 我们都知道flux的生态目前不太完善&#xff0c;flux的controlnet和flux ipadapter虽然有&#xff0c;但效果不太好&#xff0c;可控性不强。…

基于微信的追星小程序+ssm(lw+演示+源码+运行)

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;追星小程序被用户普遍使用&#xff0c;为方便用户能够可以…

esp32cam+Arduino IDE在编译时提示找不到 esp_camera.h 的解决办法

多半是因为你的ESP32库升级了&#xff0c;不再是 1.02版本&#xff0c;或者根本就没有 ESp32 库。如果被升级了&#xff0c;还原为1.02版本就可以了。如果没有&#xff0c;按照下述方法添加&#xff1a; 首先&#xff0c;在"文件"->"首选项"->"…

基于物联网设计的地下煤矿安全监测与预警

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 1.5 模块的技术详情介绍【1】NBIOT-BC26模块【2】MQ5传感器【4】DHT11传感器【5】红外热释电人体检…

第8章 利用CSS制作导航菜单作业

1.利用CSS技术&#xff0c;结合链接和列表&#xff0c;设计并实现“山水之间”页面。 浏览效果如下&#xff1a; HTML代码如下&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>山水之间</title><…

32单片机HAL库的引脚初始化

在使用HAL库时&#xff0c;GPIO初始化函数定义在stm32f4xx_hal_gpio.c文件中&#xff0c;如下&#xff1a; void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); 由这个函数可以看出&#xff0c;在初始化GPIO时&#xff0c;需要向函数传入2个结构体&…

Django安装

在终端创建django项目 1.查看自己的python版本 输入对应自己本机python的版本&#xff0c;列如我的是3.11.8 先再全局安装django依赖包 2.在控制窗口输入安装命令&#xff1a; pip3.11 install django 看到Successflully 说明我们就安装成功了 python的Scripts文件用于存…