与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_Receive_IT。HAL库的串口中断比较复杂,主要流程如下:
USART1_IRQHandler:由硬件调用,不是HAL库函数,寄存器编程或固件库编程也需要调用此函数;
HAL_UART_IRQHandler:通过中断类型(发送中断还是接收中断)来判断调用哪个函数;
UART_Receive_IT:此函数可以指定,每收到若干个数据,调用一次回调函数;这是因为,每收到一个字节,都会把此函数的接收计数器-1,如果接收计数器为零,调用串口接收回调函数HAL_UART_RxCpltCallback(实际上HAL库一共提供了5个回调函数,只有这个函数在接收完成时调用)。
HAL_UART_RxCpltCallback:弱函数,用户可以在此函数中编写业务逻辑。清除中断标记,是中断处理函数一定要做的事情,但是对于用户函数,把这个操作给隐藏了
由于串口不方便传参数,所以我通常会定义一些用于串口通信的全局变量。也可以模仿库函数,把这些变量打包成一个结构体。
//UART.c
unsigned char UART1_Rx_Buf[MAX_REC_LENGTH] = {
0}; //USART1存储接收数据
unsigned char UART1_Rx_flg = 0; //USART1接收完成标志
unsigned int UART1_Rx_cnt = 0; //USART1接受数据计数器
unsigned char UART1_temp[REC_LENGTH] = {
0}; //USART1接收数据缓存
由于这些变量也要在main.c文件中使用,跨文件使用,可以在头文件中做外部声明:
#ifndef __UART_H
#define __UART_H
#ifdef __cplusplus
extern "C" {
#endif
#define REC_LENGTH 1
#define MAX_REC_LENGTH 1024
extern unsigned char UART1_Rx_Buf[MAX_REC_LENGTH];
extern unsigned char UART1_Rx_flg ;
extern unsigned int UART1_Rx_cnt ;
extern unsigned char UART1_temp[REC_LENGTH];
#ifdef __cplusplus
}
#endif
#endif
要使用中断来接收串口数据,则必须开启中断。并且,每次处理完串口接收中断以后,会自动关闭中断,如果想循环接收数据,则必须在处理完中断以后,再次开启中断。
我们希望完成初始化以后就开始接收串口数据,所以要修改串口初始化函数。
//main.c
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 2 */
HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);
/* USER CODE END USART1_Init 2 */
}
程序的逻辑:
如果接收到了指定数量的串口数据(在本例中,指定的数量是1字节),则会执行回调函数HAL_UART_RxCpltCallback。此函数是个弱函数,用户可以根据业务逻辑来“重载”。我们要在此函数中,把串口收到的数据打包,并判断结束符判断数据结束。我们规定,只发送ASCII码,并以0x0a作为结束符。
//UART.c
/**
* @brief 串口中断回调函数
* @param 调用回调函数的串口
* @note 串口每次收到数据以后都会关闭中断,如需重复使用,必须再次开启
* @retval None
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
UART1_Rx_Buf[UART1_Rx_cnt] = UART1_temp[0];
UART1_Rx_cnt++;
if(0x0a == UART1_temp[0])
{
UART1_Rx_flg = 1;
}
HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);
}
}
主函数中,实现“串口应声虫”的功能,收到什么就发送什么。如果串口数据接收完成,则发送出去然后把数组,计数器,标志都恢复初始状态。
//main() while(1)
if(UART1_Rx_flg)
{
HAL_UART_Transmit(&huart1,UART1_Rx_Buf,UART1_Rx_cnt,0x10); //发送接收到的数据
for(int i = 0;i<UART1_Rx_cnt;i++)
UART1_Rx_Buf[i] = 0;
UART1_Rx_cnt = 0;
UART1_Rx_flg = 0;
}
现象:向串口发送ASCII码,单片机收到什么数据,就返回什么数据。注意,发送给串口的数据结尾要有回车键。
一组数据怎么判断是否结束?
2种方法:
1特定时间,特定的时间内没有收到新的数据,认为这一组数据就结束了。这种方法在定时器的章节来实现。
2特定字符,通信双方约定,用特定的字符作为结束,比如把0xff作为结束符。收到0xff就把数据截断。就像我们演讲,最后说一句谢谢大家,下边的人就知道了你讲完了,该鼓掌了。谢谢就是结束符。
但是这种做法有一个弊端,就是正常通信的数据不允许在使用0xff。
对于ASCII码,正常情况下是不会发送0x0d与0x0a(回车与换行)的,所以可以用作结束符。
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的系统,并等同于国际标准ISO/IEC 646。
ASCII码主要用于英文字符的显示,不包含中文。标准ASCII码只有7位(最高位是校验位),所以只能显示2^7=128个字符,其中0-31还是不能显示的字符,例如回车,作用是控制字符或通信字符。
假如,发送的数据是0x31,它可能代表着十六进制的数字0x31,也可能表示十进制的数字49(十六进制与十进制虽然看上去不一样,但表示的数是同样的大小),还可能表示ASCII码,字符’1’。这三者,在传输线上使用示波器来观察,波形是一模一样的,接收方把它理解为0x31还是理解为字符,要看通信双方的约定。
有读者反馈不好用,这段代码是经过测试,确认可用的,附上源码如下,供参考
https://download.csdn.net/download/geek_monkey/13195359
另外,此串口数据需要结束符,后续文章中有更好的方法,定时器截断,文章地址是:
https://yatao.blog.csdn.net/article/details/89326199
文章浏览阅读965次,点赞18次,收藏21次。现在我们有了 2021 年和 2023 年的 NDVI 数据帧,我们需要从 2021 年的值中减去 2023 年的值以捕获 NDVI 的差异。该数据集包括像素级别的植被值,我们将编写一个自定义函数来根据红色和绿色波段的表面反射率计算 NDVI。在我的上一篇文章中,我演示了如何将单个多边形分割/镶嵌为一组大小均匀的六边形。现在我们有了植被损失数据,让我们使用 Kepler.gl 可视化每个六边形的植被损失。将地图保存为 HTML 文件,在浏览器中打开 HTML 以获得更好的视图。现在我们将调用该函数并使用、
文章浏览阅读3.3k次,点赞6次,收藏5次。正态分布,又称高斯分布或钟形曲线,是统计学中最为重要和常用的分布之一。_echarts正态分布图
文章浏览阅读217次。首先要在Mainfest.xml中加入所需要的权限:[html] view plain copyprint?uses-permission android:name="android.permission.SEND_SMS"/> uses-permission android:name="android.permission.READ_SMS"/> _android bundle.get("pdus");
文章浏览阅读2.6k次。0、说明最近在学习 Data Assimilation Research Testbed (DART) 相关内容,其软件是在 Unix/Linux 操作系统下编译和运行的 ,由于我的电脑是 Windows 10 的,DART 推荐可以使用 Windows Subsystem For Linux (WSL) 来创建一个 Windows 下的 Linux 子系统。以下的内容主要介绍如何安装 WSL2,以及 WSL2 的联网。1、如何在 Windows 10 下安装WSL具体的安装流程可以在 microso_wsl2 联网
文章浏览阅读1k次。DB_LINK 介绍在本机数据库orcl上创建了一个prod_link的publicdblink(使用远程主机的scott用户连接),则用sqlplus连接到本机数据库,执行select * from scott.emp@prod_link即可以将远程数据库上的scott用户下的emp表中的数据获取到。也可以在本地建一个同义词来指向scott.emp@prod_link,这样取值就方便多了..._添加 database link重复的数据库链接命
文章浏览阅读3.1k次。ylbtech-云-腾讯云-实时音视频:实时音视频(TRTC)支持跨终端、全平台之间互通,从零开始快速搭建实时音视频通信平台1.返回顶部 1、腾讯实时音视频(Tencent Real-Time Communication,TRTC)拥有QQ十几年来在音视频技术上的积累,致力于帮助企业快速搭建低成本、高品质音视频通讯能力的完整解决方案。..._腾讯实时音视频 分享链接
文章浏览阅读534次,点赞10次,收藏8次。编写一个完整的日历表需要处理许多细节,包括公历和农历之间的转换、节气、闰年等。运行程序后,会输出指定年份的日历表。注意,这个程序只是一个简单的示例,还有很多可以改进和扩展的地方,例如添加节气、节日等。_农历库c语言
文章浏览阅读1w次,点赞28次,收藏27次。FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一,日新月异。它是一款录音机和编辑器,可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。为方便用户,FL Studio 21提供三种不同的版本——Fruity 版、Producer 版和签名版。所有这些版本都是独一无二的,同样具有竞争力。用户可以根据自己的需要选择其中任何一种。FL Studio21.1.1.3750中文版可以说是一站式综合音乐制作单位,可以让您录制、作曲、混音和编辑音乐。_fl studio 21 注册机
文章浏览阅读1.3k次。冯诺依曼计算机工作原理冯 诺依曼计算机工作原理的核心是 和 程序控制世界上不同型号的计算机,就其工作原理而言,一般都是认为冯 诺依曼提出了什么原理冯 诺依曼原理中,计算机硬件系统由那五大部分组成的 急急急急急急急急急急急急急急急急急急急急急急冯诺依曼结构计算机工作原理的核心冯诺依曼结构和现代计算机结构模型 转载重学计算机组成原理 一 冯 诺依曼体系结构从冯.诺依曼的存储程序工作原理及计算机的组成来..._简述冯诺依曼计算机结构及工作原理
文章浏览阅读559次。这次在随机乱下的基础上加上了一些简单的处理,如进营、炸棋、吃子等功能,在和敌方棋子产生碰撞之后会获取敌方棋子大小的一些信息,目前采用的是事件驱动模型,当下完一步棋界面返回结果后会判断是否触发了相关事件,有事件发生则处理相关事件,没有事件发生则仍然是随机下棋。1.事件驱动模型首先定义一个各种事件的枚举变量,目前的事件有工兵吃子,摸暗棋,进营,明确吃子,炸棋。定义如下:enum MoveE..._军棋引擎
文章浏览阅读85次。1, 模板观念与函数模板简单模板: template< typename T > T Function( T a, T b) {… }类模板: template struct Object{……….}; 函数模板 template< class T> inline T Function( T a, T b){……} 不可以使用不同型别的..._geekband 讲义
文章浏览阅读158次。"^\d+$" //非负整数(正整数 + 0)"^[0-9]*[1-9][0-9]*$" //正整数"^((-\d+)|(0+))$" //非正整数(负整数 + 0)"^-[0-9]*[1-9][0-9]*$" //负整数"^-?\d+$" //整数"^\d+(\.\d+)?$" //非负浮点数(正浮点数 + 0)"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0..._vb.net 正则表达式 取html中的herf