STM32 —— 串口接收数据

我们已经在前面的博客中讲过了串口通信中发送数据和中断的一些基本原理,这里主要介绍串口接收数据的相关内容

定长字符串的接收

当接收单字节时,我们就可以使用最简单的接收方式即可,这种接收方式,我们只需要调用对应接口的中断函数,每一个串口都有对应的中断函数,每次中断只能接收一串定长数据,然后利用接收的函数 USART_ReceiveData ,以及接收的标志位状态,当我们的接收状态没有在的时候,我们就可以接收字符了,接收字符我们可以定义一个数组用来接收收到的字符,同时接收完字符后,就要清除这个标志位状态,这样我们才是一个接收完整的流程。

接收字符串主要有两种方法,一种是对中断函数进行改造,另一种是对接收回调函数进行改造

在讲解这两种方法之前,我们需要了解一个函数:

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

该函数的作用是用户自定义一个缓冲区(即参数 pData),接收一定(固定)数量(参数 Size 决定)的字符串存入缓冲区,同时,参数 Size 数量还决定着进入回调函数的频率,即每接收 Size 个字符,就进入一次回调函数

Size 只决定进入回调函数的频率,而不能影响进入接收中断的频率,无论 Size 是多少,每接收完成一个字符都会进入一次接收中断

改造回调函数

首先在主函数中进入主循环前的位置调用一次 HAL_UART_Receive_IT 函数,定义一个字符数组 getBuffer[] 作为缓冲区,参数Size 设定为 10。即每接收 10 个字符,就进入一次回调函数

重写中断函数,在 stm32f1xx_it.c 中找到 void USART1_IRQHandler(void) 函数,重写为:

void USART1_IRQHandler(void) {HAL_UART_IRQHandler(&huart1); //该函数会清空中断标志,取消中断使能,并间接调用回调函数
}

在文件 stm32l4xx_hal_uart.h 中,我们可以看到串口接收回调函数的定义。使用 _weak 关键字定义的函数,其具有如下特性:

一般情况下和一般函数相同,但是当有一个同名函数但是不带 __weak 被定义时,所有对这个函数的调用都是指向后者(不带 __weak 那个)。也就是说,ST 官方提供的这个回调函数需要我们自己进行改写:

#define COUNTOF(a) (sizeof(a)/sizeof(*(a)))  // 计算字符串 / 数组长度
uint8_t myBuffer[] = "I have gotten your message: "; //用户提示信息
uint8_t Enter[] = "\r\n";    //回车换行
uint8_t getBuffer[100]; //用户自定义的缓冲区
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart1)
{while(HAL_UART_Transmit(huart1, (uint8_t*)myBuffer, COUNTOF(myBuffer), 5000)!= HAL_OK);   //发送字符串,用户提示信息while(HAL_UART_Transmit(huart1, (uint8_t*)getBuffer, 10, 5000)!= HAL_OK); //发送用户自定义缓冲区中的数据while(HAL_UART_Transmit(huart1, (uint8_t*)Enter, COUNTOF(Enter), 5000)!= HAL_OK);  //发送回车换行
}

以上代码的作用是把用户发送给单片机数据再返回给用户。运行效果如下图:

方法改进

我们可以看到,用户向单片机发送了 10 个字符,单片机向串口助手返回了这 10 个数据。但是以上程序只能实现一次,当我们再次向单片机发送数据时,单片机却不再返回数据。这是因为我们在中断函数中取消了中断使能,所以导致了进入一次中断后,中断被关闭,无法再次进入中断的现象。为了实现多次数据返回,我们要在中断处理函数中添加一行代码:

void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1); //该函数会清空中断标志,取消中断使能,并间接调用回调函数HAL_UART_Receive_IT(&huart1, (uint8_t *)getBuffer,10);  //添加的一行代码
}

这样就可以实现多次数据返回了,新的执行结果如下图:

可见,函数 HAL_UART_Receive_IT 还有中断使能的作用。这一功能的实现我们可以在 HAL_UART_Receive_IT 函数中找到

改造中断处理函数

首先在主函数中进入主循环前的位置调用一次 HAL_UART_Receive_IT 函数,定义一个字符 value 作为缓冲区,参数 Size 设定为1 。即每接收 1 个字符,就进入一次回调函数。使得进入回调函数的频率与进入中断处理函数的频率相同。这样,我们就可以直接在中断函数中对接收的数据进行处理了

重写中断函数

#define COUNTOF(a) (sizeof(a)/sizeof(*(a)))    // 计算字符串 / 数组长度
uint8_t myBuffer[] = "I have gotten your message: ";
uint8_t getBuffer[10];
uint8_t Enter[] = "\r\n";
int countofGetBuffer = 0;
void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1); //该函数会清空中断标志,取消中断使能,并间接调用回调函数getBuffer[countofGetBuffer++] = value;if(countofGetBuffer == 10){while(HAL_UART_Transmit(&huart1, (uint8_t*)myBuffer, COUNTOF(myBuffer), 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)getBuffer, countofGetBuffer, 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)Enter, COUNTOF(Enter), 0xFFFF)!= HAL_OK);countofGetBuffer = 0;}HAL_UART_Receive_IT(&huart1, (uint8_t *)&value,1);  //由于接收中断是每接收一个字符便进入一次,所以这一行代码必须添加,否则只能接收一个字符,而无法接收整个字符串
}

以上代码的作用是接收每个来自用户的字符,并依次存入用户自定义的缓冲区中,数量达到 10 个后,将缓冲区中的所有数据返回给用户,同时清空计数,准备接下来 10 个字符的接收。运行效果如下图:

回调函数与中断函数关系

其实是这样的,单片机每完成接收一个字符,就会进入一次中断处理函数,而在中断处理函数中,我们又调用了函数 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) ,该函数会间接调用回调函数,也就是说回调函数是由中断处理函数间接调用的

而函数 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 决定了中断处理函数调用回调函数的频率,若 Size 为 1 ,则每进入一次中断处理函数都会调用一次回调函数;若 Size 为 10 ,则每第十次进入中断处理函数时,才会调用回调函数。方法 2 使用了标准库中断处理数据的思想

不定长但固定发送内容字符串的接收

前面我们已经提到了改造中断处理函数的接收数据方法,我们接收已知内容字符串的方法与之十分相似

前面在改造中断处理函数方法中,我们已知的是字符串的长度,所以我们判断的是符串的长度,这里我们已知的是字符串的内容,所以,这里我们直接判断字符串的内容即可

uint8_t myBuffer[] = "I have gotten your message: ";
char getBuffer[100];
uint8_t Enter[] = "\r\n";
char value;
char str1[] = "ppqppl";
char str2[] = "Hello ppqppl!";
uint8_t out1[] = "str1";
uint8_t out2[] = "str2";
int countofGetBuffer = 0;
void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1); //该函数会清空中断标志,取消中断使能,并间接调用回调函数getBuffer[countofGetBuffer++] = value;if(strcmp(str1,getBuffer) == 0)    // 这里把判断长度改为判断内容{while(HAL_UART_Transmit(&huart1, (uint8_t*)myBuffer, COUNTOF(myBuffer), 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)out1, COUNTOF(out1), 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)Enter, COUNTOF(Enter), 0xFFFF)!= HAL_OK);countofGetBuffer = 0;memset(getBuffer,0,COUNTOF(getBuffer));}else if(strcmp(str2,getBuffer) == 0){while(HAL_UART_Transmit(&huart1, (uint8_t*)myBuffer, COUNTOF(myBuffer), 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)out2, COUNTOF(out2), 0xFFFF)!= HAL_OK);while(HAL_UART_Transmit(&huart1, (uint8_t*)Enter, COUNTOF(Enter), 0xFFFF)!= HAL_OK);countofGetBuffer = 0;memset(getBuffer,0,COUNTOF(getBuffer));}HAL_UART_Receive_IT(&huart1, (uint8_t *)&value,1);  //由于接收中断是每接收一个字符便进入一次,所以这一行代码必须添加,否则只能接收一个字符,而无法接收整个字符串
}

这里在使用 strcmp 的时候需要引用 string.h 头文件

这样我们就能根据不同的内容执行不通过的命令,但是这种方法有一个弊端,就是输入的字符串必须在判断范围内,否则将再无相应,只能手动 reset

运行效果如下:

这种方法只适合有选择性的输入(设置只能输入固定的语句1或语句2),而不适合开放性输入,如果是开放性输入,则会出现前面输入错误,导致必须手动 reset 的效果,十分的不方便

不定长字符串的输入

这里涉及到 DMA 相关内容,请看我的另一篇博客:STM32 —— 串口不定长数据接收 DMA 详解

参考文档

  1. STM32串口接收中断——基于HAL库

  2. STM32_串口接收中断_实现定长数据接收(1)

  3. 记录STM32中的函数

STM32 —— 串口数据接收相关推荐

  1. STM32串口实时接收数据与所提前定义的比较,并作出相应的操作

    STM32串口实时接收数据与所提前定义的比较,并作出相应的操作 //typedef const uint16_t uc16; /*!< Read Only */uc16 BUF[10]={162 ...

  2. HAL库教程6:串口数据接收

    STM32的串口接收机制   与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_ ...

  3. STM32串口中断接收实验

    STM32串口中断接收实验的详细说明 准备 代码实现 总结 准备 材料:STM32F407ZGT6最小系统板,串口1通过跳线帽连接到了CH340上. 需求:从电脑向板子的串口1发送一个字符串(以回车和 ...

  4. 【Qt串口调试助手】1.2 - 串口数据接收不发生换行,CH340 / CP2102 多硬件兼容

    上一篇提到:对串口接收的显示,使用的是当前位置插入+移动鼠标光标到末尾的方式.这种方法可以有效解决 串口数据接收发生换行的问题,并且解决 CH340 / CP2102 多硬件显示结果不一致的问题.那有 ...

  5. 串口数据实时处理:定时器+串口 判断串口数据接收完成

    使用背景: 之前在做项目的时候,串口接收的数据要及时进行处理,虽然采用了自定义的串口协议,但是协议的包尾只有一个字节,经常判断不准数据是否接受完毕,所以就采用计时器+串口的方式来判定串口是否接受完成. ...

  6. STM32G070RBT6基于Arduino框架下串口数据接收使用示例

    STM32G070RBT6基于Arduino框架下串口数据接收使用示例 相关篇<STM32G070RBT6基于Arduino串口的使用>

  7. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  8. STM32串口中断接收帧数据并返回给上位机总结(配合MAX3483)

    一.前言 这是我的第一篇CSDN,记录一些代码总结,一方面与大家分享交流,另一方面方便以后再次使用能够快速回忆,再就是提高自身写作水平.如有错误之处,欢迎各位大佬批评指正. 二.所涉及的芯片 1.ST ...

  9. STM32 HAL库 串口DMA(收发)和STM32串口中断接收(接收时间管理机制)+ESP8266 wifi模组通信问题

    一.HAL库 串口 DMA+ESP8266模组通信问题 用STM32 HAL库串口的DMA发送和空闲中断接收处理数据,单片机发送AT指令给ESP8266 wifi模组问题:单片机连续几次给wifi模组 ...

最新文章

  1. 哪个术语描述了服务器软件在专用计算机,计算机网络基础
  2. 8.QML Qt Quick Controls 2中常用的界面形式——堆叠式界面(StackView)
  3. python 列表表达式 if_python中if else如何判断表达式成立?
  4. orm 通用方法——RunProc调用存储过程
  5. iptables规则备份恢复,firewalld的9个zone
  6. R有序因子和无序因子(4)
  7. 面试中回答离职原因的万能公式
  8. Android中OnItemClick的四个参数意义
  9. 【报告分享】2019年12月郭广昌混沌课程ppt(附下载链接)
  10. mvc中嵌入html,MVC Razor与javascript混编(js中嵌入razor)
  11. 介词短语做后置定语(由of引导)
  12. 孙鑫VC学习笔记:第六讲 菜单
  13. jd反编译java_java反编译工具jd
  14. 如何免费下载百度文库,豆丁网等付费文章
  15. React-CRON表达式生成器
  16. 腾讯android 热更新,Android 腾讯 Bugly 热更新
  17. python办公自动化 使用openpyxl合并excel表格
  18. 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
  19. Windows注册表知识
  20. 【Mac】快捷键锁屏

热门文章

  1. python多线程编程模块不包括_python 学习_第四模块 并发编程(多线程)
  2. 百度百科词条义项排名靠前秘诀
  3. Embedding+MLP
  4. bat脚本循环关闭端口
  5. 中国进出口银行运维的精细化管理
  6. 什么是OADM光分插复用器
  7. 【蓝桥杯笔记01】小蜜蜂老师-LED指示灯的基本控制
  8. 联想服务器显示叹号,桌面图标出现“红色叹号”
  9. “温酒吧”不得不说的故事
  10. 沈阳东软java面试题_东软集团面试经验