小记stm32实现串口接收的四种方法(hal库)

2023-11-13

开发环境

  1. STM32CUBMX
  2. 正点原子STM32F407ZGT6探索者开发板
  3. MDK-ARM 5.31

第一种方式:直接接收

  1. 配置外部时钟源配置时钟源
    2.配置时钟树
    配置时钟树
    3.配置串口一
    串口1
    生产代码后进入工程
    重定向printf到串口1(建议在usart.c里重定向)
    重定向代码块
int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  printf("hello wrold\r\n");
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_UART_Receive(&huart1,receive_buff,10,0xff);// 接收10个字符
      if(receive_buff[0] != 0)
      {
          printf("recive buff is %s \r\n",receive_buff);
          memset(receive_buff,0,20); // 清除接收内容
      }    
  }
  /* USER CODE END 3 */
}

实验现象
实验现象

第二种方式:中断方式

时钟准备和串口初始化如上一种方式所示,实例从设置中断开始
设置中断
打开中断
生成代码

定义相关变量

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";

在usart.h 中编写回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */
 
	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
		HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);	
	}
	else
	{
		Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
	
		if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			Uart1_RxFlag = 1;
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}

main函数内内容

int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(Uart1_RxFlag != 0)
    {
        printf("recive buff is %s\r\n",Uart1_RxBuff);
        Uart1_RxFlag = 0;
        Uart1_Rx_Cnt = 0;
        memset(Uart1_RxBuff,0x00,256);
    }
    HAL_Delay(10);
  }
  /* USER CODE END 3 */
}

实验现象
在这里插入图片描述

第三种方法:定时器中断判断串口是否传输完成

此方法可以看做是看作中断接收的一种拓展用法,不同于上一种方法介绍的需要特定的格式的数据帧来判定,此方法结合了定时器,以5ms为界限,如果5ms后没有接收新的数据则认为一包数据已经发送完成。下面是配置的具体方法
1.配置时钟和串口中断的同上,然后配置定时器中断
定时器基础配置
在这里插入图片描述
打开定时器中断
在这里插入图片描述
我们设置的5ms检测串口是否发送完成
我们使用的定时器的主频是84MHZ
那么定时器频率可用下列方式计算
F = 84 M H Z ( P r e s c a l e r + 1 ) × ( C o u n t e r P e r i o d + 1 ) ; F = \frac{84MHZ}{(Prescaler+1)\times (CounterPeriod+1)}; F=(Prescaler+1)×(CounterPeriod+1)84MHZ;
配置完成后生成代码
程序部分
定义相关变量

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";

编写串口中断服务函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart -> Instance == USART1)
    {
        if(Uart1_Rx_Cnt == 0)
        {
            __HAL_TIM_CLEAR_FLAG(&htim6,TIM_FLAG_UPDATE);
            HAL_TIM_Base_Start_IT(&htim6);
            Uart1_RxBuff[Uart1_Rx_Cnt] = aRxBuffer;
            Uart1_Rx_Cnt ++;
            
        }
        else
        {
            Uart1_RxBuff[Uart1_Rx_Cnt ++] = aRxBuffer;
        }
        if(Uart1_Rx_Cnt >= 255)
        {
            Uart1_Rx_Cnt = 0;
            Uart1_RxFlag = 0;
            memset(Uart1_RxBuff,0x00,256);
        }
        HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
    }  
}

编写定时器中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim ->Instance == TIM6)
    {
        HAL_TIM_Base_Stop(&htim6);
        __HAL_TIM_SetCounter(&htim6,0);
        Uart1_RxFlag = 1;       
    }
}

编写主程序

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM6_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
     if(Uart1_RxFlag == 1)
     {
         printf("rec buff is %s\r\n",Uart1_RxBuff);
         memset(Uart1_RxBuff,0x00,256);
         Uart1_Rx_Cnt = 0;
         Uart1_RxFlag = 0;
     }
     HAL_Delay(10);
  }
  /* USER CODE END 3 */
}

实验现象

第四种方法:串口空闲中断加DMA

使用DMA可以减少CPU负荷,当接收大批量数据的时候,可以防止频繁进入中断。这样有助于提高效率,下面我们介绍STM32带有的串口空闲中断来配合DMA接收数据的例子。
配置部分
1 串口配置
基本部分
在这里插入图片描述

配置DMA
在这里插入图片描述
中断配置
在这里插入图片描述
生产代码
定义宏定义

#define USART1_DMA_REC_SIZE 600
#define USART1_REC_SIZE 1200

编写结构体

typedef struct
{
        uint8_t UsartRecFlag; // 标志位
        uint16_t UsartRecLen; // 接收数据长度
        uint16_t UsartDMARecLEN; // DMA 接收长度
        uint8_t  Usart1DMARecBuffer[USART1_DMA_REC_SIZE]; // DMA 接收数组
        uint8_t  Usart1RecBuffer[USART1_REC_SIZE]; // 接收组
}teUsart1type;

编写打开中断函数

// 打开相关中断
void EnableUsart_It(void)
{
        __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
        __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);
        HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
}

改写中断服务函数(在stm32f4xx_it.c中)

void USART1_IRQHandler(void)
{
 
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint16_t temp = 0;
	__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	HAL_UART_DMAStop(&huart1); // 关闭DMA
	temp = huart1.Instance -> SR; // 清除SR状态寄存器
	temp = huart1.Instance -> DR; // 清除DR数据寄存器,用来清除中断
	temp = hdma_usart1_rx.Instance -> NDTR; // 获取未传输的数据个数
	//temp = hdma_usart2_rx.Instance -> NDTR; // F4
	Usart1type.UsartDMARecLEN = USART1_DMA_REC_SIZE - temp;
	HAL_UART_RxCpltCallback(&huart1);
  /* USER CODE END USART1_IRQn 0 */
    HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
  /* USER CODE END USART1_IRQn 1 */
}

编写中断回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
        if(huart -> Instance == USART1)
        {
            if(Usart1type.UsartRecLen > 0)
            {
                memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartRecLen],Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
                Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
            }
            else
            {
                memcpy(&Usart1type.Usart1RecBuffer,Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
                Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
            }
            memset(Usart1type.Usart1DMARecBuffer,0x00,600);
            Usart1type.UsartRecFlag = 1;
        }
}

编写主程序

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  EnableUsart_It();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      if(Usart1type.UsartRecFlag == 1)
      {
          printf("rec buff is %s\r\n",Usart1type.Usart1RecBuffer);
          memset(Usart1type.Usart1RecBuffer,0x00,USART1_REC_SIZE);
          Usart1type.UsartRecLen = 0;
          Usart1type.UsartRecFlag = 0;         
      }
      HAL_Delay(20);
  }
  /* USER CODE END 3 */
}

实验现象
在这里插入图片描述
END
以上工程已上传Github,需要的自取,链接在下面
https://github.com/zzq0516/usart

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小记stm32实现串口接收的四种方法(hal库) 的相关文章

  • SpringBoot定时任务设置

    1 主启动类加上注解 开启定时任务 EnableScheduling 2 创建定时任务类 import org springframework beans factory annotation Autowired import org sp
  • GAN的编写 - tensorflow形式(tensorflow与GAN同学习,重点分析训练过程)

    20200901 本文完成于20200902下午 前面内容还算整洁 越到后面因为都是自己思考的过程 就导致文章越来越乱 就算是把自己思考的过程给记录下来吧 0 引言 之前的时候对keras框架编写的GAN网络进行了介绍 GAN的学习 训练过
  • 基于控制的角度无人机集群——目标追踪

    无人机集群 目标追踪 前言 一 轨迹预测 二 单目标追踪 三 多目标追踪 前言 关于目标追踪问题 有一些研究是从视觉的角度展开 而我研究的是基于控制的角度 关于多无人机集群的一些知识点 已经在上一篇文章有了简单介绍 这次我想着重介绍一下 目

随机推荐

  • 4.抽样分布的概念与Python实现抽样

    1 总体与样本 在实际中 总体的分布一般是未知的 或只知道它具有某种形式而其中包含着未知参数 这时 常用的办法就是根据样本来推断总体 总体 个体 样本 总体 通常把研究对象的全体称为总体 一个总体对应于一个随机变量X 个体 把组成总体的每个
  • CTFshow 信息收集 web 6 7 8 9 10

    目录 第六关 提示 flag 第七关 提示 知识点 flag 第八关 提示 知识点 flag 第九关 提示 知识点 flag 第十关 提示 flag 第六关 提示 解压源码到当前目录 测试正常 收工 这道题考的是备份文件www zip 根据
  • 解决mysql占用IO过高

    created 2023 01 30T10 14 00 UTC 08 00 tags source https www bbsmax com A Ae5RyA0AJQ author 解决mysql占用IO过高 Excerpt 1 日志产生的
  • 西门子HMI设备与V20变频器如何实现通讯?

    通常情况下 要实现HMI设备与V20变频器的通讯 需要一个支持USS通讯或MODBUS通讯的PLC 比如S7 200系列PLC 其通讯电缆连接如图1所示 PLC的一个通讯端口与触摸屏连接 可以采用PPI协议通讯 PLC的另一个通讯端口与V2
  • C语言自定义类型-结构体

    一 结构体声明 C语言中为我们准备了许多现成的数据类型例如 int short float double char long long long 等等 但是我们描述一些复杂的事物 光靠上述的数据类型是描述不清的 例如 我们描述一个大学生 可
  • 安卓Android_手机安装burp的https_CA证书

    安卓Android 手机安装burp的https CA证书 文章目录 安卓Android 手机安装burp的https CA证书 1 打卡电脑wif热点 手机连上电脑的热点 2 burp点击 Proxy settings 3 点击add 新
  • java 数组中插入元素_Java数组添加元素

    java 数组中插入元素 How to add elements to an array in java We know that java array size is fixed so we can t add elements to a
  • jvm虚拟机所有垃圾回收器详细介绍

    jvm虚拟机所有垃圾回收器详细介绍 文章目录 jvm虚拟机所有垃圾回收器详细介绍 垃圾回收器概述 1 Serial回收器 串行回收 总结 2 ParNew回收器 并行回收 3 Parallel Scavenge回收器 吞吐量优先 4 CMS
  • 论文解读 《Enhancing Underwater Imagery using Generative Adversarial Networks》ICRA2018

    项目 http irvlab cs umn edu enhancing underwater imagery using gans 论文 https arxiv org pdf 1801 04011 pdf 代码 https github
  • 算法精解_C语言 链表_单链表(接口定义+类型实现)

    链表可以说是一种最为基础的数据结构 链表由一组元素以一种特定的顺序组合或链接而成 在维护数据的集合时很有用 这一点同我们常用的数组很相似 然而 链表在很多情况下比数组更有优势 特别是在执行插入和删除操作时链表拥有更高的效率 链表需要动态的开
  • 组件化依赖管理办法

    theme channing cyan 在组件化过程中 面临着非常多的复用 切换等场景 对于组件化中的dsl文件 也可以尝试将其组件出来 更好的复用 更好的管理 一 利用buildSrc buildscript 对dsl 文件进行组件化 1
  • org.postgresql.util.PSQLException: 错误: 关系 “courseinformation“ 不存在

    问题描述 在java项目中连接PSQL数据库 对courseinformation表进行操作时 运行报错 org postgresql util PSQLException 错误 关系 courseinformation 不存在 已知解决方
  • BUUCTF系列 // [极客大挑战 2019] LoveSQL

    前言 本题知识点 SQL注入 WP 这题居然是个连续剧 首先尝试使用上一题的解法绕过看看 上一题 WP 的 传送门 结果如下 注意到密码有些奇怪 尝试着用 MD5 解码失败 也没啥思路 最后事实证明确实也用不到这玩意 故回到 SQL 注入上
  • 人工智能数学基础8:两个重要极限及夹逼定理

    点此跳转到老猿Python博文目录 一 极限公式1 二 极限公式2 e为常数2 71828 变体 使用案例 三 夹逼定理 夹逼定理英文原名Squeeze Theorem 也称两边夹定理 夹逼准则 夹挤定理 挟挤定理 三明治定理 是判定极限存
  • JAVA程序设计:最短回文串(LeetCode:214)

    给定一个字符串 s 你可以通过在字符串前面添加字符将其转换为回文串 找到并返回可以用这种方式转换的最短回文串 示例 1 输入 aacecaaa 输出 aaacecaaa 示例 2 输入 abcd 输出 dcbabcd 思路 这题O N 2
  • python-selenium-运行js代码--下拉操作

    使用python selenium运行js代码完成下拉操作 在某些情况 当我们下拉到浏览器 浏览器才会给我们加载数据 所有我们需要js带代码 driver execute script 写入你要执行的js代码 下拉的js代码 documen
  • 什么是CRUD( What is CRUD)?

    在很多技术性的文章 特别是有关数据库类的文章中 经常会看到一个缩写 CRUD 那什么是CRUD呢 CRUD其实是数据库基本操作中的Create 创建 ReadRetrieve 读取 Update 更新 Delete 删除 而这里的Creat
  • pycharm中pygame模块的安装与用法

    一 pygame的安装 本文章主要展示pycharm中的pygame模块的安装 打开pycharm 在左上角点file 找到settings 如图 进入后 找到Project untitled下的Project interperter 右边
  • 谷歌VAD静音检测py-webrtcvad使用详解

    一 前言 不知道有没有人和我一样看的官方示例一脸蒙 什么采样率除以时间 看到就头大 还是英文头更大了 好了废话结束 上翻译改编版 二 人话翻译版代码 如果你问我有些行为什么没有注释 import collections import con
  • 小记stm32实现串口接收的四种方法(hal库)

    开发环境 STM32CUBMX 正点原子STM32F407ZGT6探索者开发板 MDK ARM 5 31 第一种方式 直接接收 配置外部时钟源 2 配置时钟树 3 配置串口一 生产代码后进入工程 重定向printf到串口1 建议在usart