细说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、时间数值超过范围
显示输入的指令字符串,并消息提示,无效的数据。
相关文章:

细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的另一种方法
目录 一、工程目的 1、目标 2、通讯协议及应对错误指令的处理目标 二、工程设置 三、程序改进 四、下载与调试 1、合规的指令 2、不以#开头,但以;结束,长度不限 3、以#开头,不以;结束,也不包含;,长…...

python使用turtle画图快速入门,轻松完成作业练习
turtle介绍 turtle是一个绘图库,可以通过编程进行绘图。其模拟了一个乌龟在屏幕上的运动过程。该库通常用于给青少年学习编程,当然,也可以使用其进行作图。 在一些学校中,可能在python学习的课程中,要求完成turtle绘…...

【C++】新手入门指南
> 🍃 本系列为初阶C的内容,如果感兴趣,欢迎订阅🚩 > 🎊个人主页:[小编的个人主页])小编的个人主页 > 🎀 🎉欢迎大家点赞👍收藏⭐文章 > ✌️ 🤞 …...

C++使用开源ConcurrentQueue库处理自定义业务数据类
ConcurrentQueue开源库介绍 ConcurrentQueue是一个高性能的、线程安全的并发队列库。它旨在提供高效、无锁的数据结构,适用于多线程环境中的数据交换。concurrentqueue 支持多个生产者和多个消费者,并且提供了多种配置选项来优化性能和内存使用。 Conc…...

在vue3的vite网络请求报错 [vite] http proxy error:
在开发的过程中 代理proxy报错: [vite] http proxy error: /ranking/hostRank?dateType1 Error: connect ETIMEDOUT 43.xxx.xxx.xxx:443 网络请求是http的: // vite.config.ts import { Agent } from node:http;server: {host: 0.0.0.0,port: port,open: true,https: false,…...
ElasticSearch 简单的查询。查询存在该字段的资源,更新,统计
1.查询存在该字段的数据 {"query": {"bool": {"must": [{"exists": { "field": "chainCode"}}],"must_not": {"exists": {"field": "isDelete"}}}} } 备注:…...
FOFA使用教程之从零到精通
FOFA使用教程之从零到精通 前言一、关于网络资产测绘的概念1、啥是网络空间资产测绘2、啥是互联网资产二、FOFA的简要介绍1、FOFA地址是啥?2、关于FOFA的简要介绍三、FOFA精讲1、运算符规则详解① 关于 = 号的使用说明② 关于 == 号的使用说明③ 关于 && 号的使用说明…...

【提高篇】3.2 GPIO(二,基本结构)
目录 一,GPIO的基本结构 二,保护二极管 三,上拉、下拉电阻 四,施密特触发器 五,P-MOS 管和 N-MOS 管 P-MOS管和N-MOS管的区别 六,片上外设 七,IDR,ODR,BSRR寄存器 7.1 IDR(Input Data Register) 7.2 ODR(Output Data Register) 7.3 BSRR(Bit Set/Rese…...

UE hard/soft reference| DDX DDY | Unity pcg color
目录 1.虚幻引擎性能优化 (附0跳转Unity对应机制) hard reference and soft reference 1. 硬引用(Hard Reference) 2. 软引用(Soft Reference) 3. 使用原则 2.空间梯度转法线 DDX DDY节点 编辑 …...
macOS 应用公证指南:使用 fastlane 实现自动化公证流程
背景介绍 在 macOS 系统上,为了保护用户安全,Apple 要求开发者对未通过 Mac App Store 分发的应用程序进行公证(Notarization)。如果应用程序没有经过公证,用户在运行时会看到警告弹窗,这会影响用户体验。虽然开启沙箱模式的应用可以直接通过 App Store 分发来避免这个问题…...
深度学习:解密图像、音频和视频数据的“理解”之道20241105
🔍 深度学习:解密图像、音频和视频数据的“理解”之道 深度学习已然成为人工智能领域的中流砥柱,它如何处理不同类型的数据(如图像、音频、视频)?如何将这些数据转换成计算机能理解和学习的“语言”&#…...

uniapp 实现瀑布流
效果演示 组件下载 瀑布流布局-waterfall - DCloud 插件市场...

计算机毕业设计 | springboot+vue智慧工地管理系统 前后端分离后台管理(附源码+文档)
1,项目介绍 管理信息是重要的资源、管理信息是决策的基础。同时管理信息是实施管理控制的依据以及是联系组织内外的纽带。对于企业,最重要的5大资源包括人、物资、能源、资金、信息。人、物资、能源、资金是可以看见的有形资源,信息则是一种…...

vue中html如何转成pdf下载,pdf转base64,忽略某个元素渲染在pdf中,方法封装
一、下载 html2Canvas jspdf npm install jspdf html2canvas二、封装转换下载方法 htmlToPdf.js import html2Canvas from html2canvas import JsPDF from jspdf/*** param {*} reportName 下载时候的标题* param {*} isDownload 是否下载默认为下载,传false不…...
Ubuntu下如何管理多个ssh密钥
Ubuntu下如何管理多个ssh密钥 前言 我一直在逃避这个问题,误以为我能够单纯地用一个 ssh 走天下。 好吧,现实是我不得不管理多个 ssh 做,那就写个博客总结一下吧。 查阅后发现前人已经总结了不少,那我就结合之后ÿ…...

[vulnhub] DarkHole: 1
https://www.vulnhub.com/entry/darkhole-1,724/ 端口扫描主机发现 探测存活主机,184是靶机 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-08 09:59 CST Nmap scan report for 192.168.75.1 Host is up (0.00027s latency). MA…...

商淘云连锁企业管理五大功能 收银系统助力门店进销存同步
连锁企业管理的五大功能相互协作,共同确保连锁门店能够高效运营、降低成本、提升客户满意度,并最终实现盈利目标。今天,商淘云分享连锁企业管理的五大功能: 1、进销存管理:进销存管理是连锁企业的基础功能之一…...

统信UOS开发环境支持Perl
UOS凭借广泛的编程语言支持,为开发者构建了一个高效灵活的开发环境,无需担心环境兼容性问题。 文章目录 一、环境部署1. Perl开发环境安装2. Perl开发环境配置环境变量配置模块管理器编辑器集成调试工具二、代码示例文件处理Web开发三、常见问题1. 依赖管理问题2. 性能问题3.…...

Stable Diffusion Web UI - ControlNet 姿势控制 openpose
openpose 是 ControlNet 中常用的控制模式之一。 通过 openpose 可以锁定人物姿势,把姿势信息传递给 Stable Diffusion 扩散模型,让其在扩散生成图片的时候遵照特定的任务姿势。 通过 openpose 能够得到类似如下效果: 同样的姿势࿰…...
java中Json字符串转换
文章目录 map与json互转map转jsonmap形式的json转map list与json互转list转jsonlist形式的json转list map形式的json串中含有列表转列表 map与json互转 map转json JSONObject.toJSONString(map); public static void main(String[] args) {Map<String, Object> map n…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...