STM32学习笔记-SMT32使用HAL库UART中断方式使用

2023-05-16

目录

1、引言

2、配置

3、流程


1、引言

近来需要使用蓝牙模块,再了解到蓝牙模块等无线模块许多使用串口透传,便决定在研究一下串口UART 的使用方法。

由于用的板子种类每次都不一样,有F103 有G0 有G4 还有公司的8位自研芯片,所以每次用起来都需要重头开始复习

每次使用UART的目标都是能够发送不定长数据,接收不定长数据。

使用8位单片机的时候,总体思路是使用定时器定一个时间,在里面对一个时间标志进行加法,在使用串口接收数据时候,每当缓存区的一个字节数据存储到定义好的数组里面,就对这个时间标志进行一次清零,当串口不定长数据接收完成以后就是最后一次清零,此后该事件标志位不再清零将一直进行加法,在随后实用查询方式对该时间标志进行查询,当超过一定时间(要远大于字符串发送时候两个字节之间的时间,一般定义为ms级别)则表明数据接收完成,随后对存储数据的数组进行处理,在对串口各个状态标志和刚才制定的时间标志进行清零。这就完成了一次不定长数据的接收。

在使用STM32的时候,也借鉴了这个思路,同时STM32提供的HAL库大体上有三种UART方式可以使用:

轮询模式(Polling mode IO operation)

使用HAL_UART_Transmit()与HAL_UART_Receive()来配合超时操作来发送与接收数据。

中断模式(Interrupt mode IO operation)

使用HAL_UART_Transmit_IT()与HAL_UART_Receive_IT来发送接收,在发送或接收完之后,再进行函数回调HAL_UART_TxCpltCallback与HAL_UART_RxCpltCallback来进行处理这两个函数都是由用户重新定义的,来实现用户自己的操作。

DMA模式(DMA mode IO operation)

使用HAL_UART_Transmit_DMA()与HAL_UART_Receive_DMA()来发送接收,在发送或接收完之后,也使用HAL_UART_TxCpltCallback与HAL_UART_RxCpltCallback来完成实际操作,同时接收到一半的时候,也可以调用相应的                                                  HAL_UART_TxHalfCpltCallback  与HAL_UART_RxHalfCpltCallback,如果需要用到这个操作的情况下可以添加自己的操作,当然来还用到一关于DMA的API函数,如HAL_UART_DMAPause,HAL_UART_DMAResume, HAL_UART_DMAStop等

其中DMA方式加空闲中断方式效率高,但是由于我没有这么高的需求而且对dma模块还不够熟悉,就是用最常用的中断方式实现功能。

这里主要介绍一下中断方式:

2、配置

这里按照流程讲解一下相关的HAL库函数使用

首先使用cubmax设置所需要的GPIO端口,选定UART功能 ,然后配置基础功能(时钟等)生成文件。

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
  {
    Error_Handler();
  }


	 __HAL_UART_CLEAR_IT(&huart1, UART_CLEAR_IDLEF); //此处初始化一下ISR寄存器IDLE位

}

在这里面设置uart的各种参数 ,根据需要调节;

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_USART1_UART_Init();
	
  HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);//串口中断接收初始化
	
  /* USER CODE BEGIN 2 */

uart配置就完成了(基本没有变化)

这里注意如果要使用中段方式初始化位置(main.c的开头部分)一定要加入                                    HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);这个函数,函数可以去工程里搜索,aRXBuffer是自己设置的接收缓存数组,RXBUFFERSIZE是一个宏定义,为20,代表着接收20个字节以后就完成接收。

这个函数里面重点注意两部分:

1:hart指向的这几个 :重点理解                                                                                                          huart->pRxBuffPtr  = pData;
    huart->RxXferSize  = Size;
    huart->RxXferCount=Size;

      这几行代码是开头初始化要添加HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);函数的原因之一,对后面要用到的一些参数进行配置。

      理论上接收完成一次都要重新调用一次这个函数,但是由于__HAL_LOCK(huart)对函数有一个锁定的过程,所以后续我选择直接处理huart里的那些数据,即上述的三个数据


    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

    __HAL_LOCK(huart);
    
    huart->pRxBuffPtr  = pData;
    huart->RxXferSize  = Size;
    huart->RxXferCount = Size;
    huart->RxISR       = NULL;

2: 这里掌握住huart->RxISR = UART_RxISR_8BIT;这行代码,至于8BIT是每次八个字节为一个数据,还有7BIT和9BIT,不过8BIT最常用(不包括停止位)这里是把hart中的空函数地址RxISR附上UART_RxISR_8BIT这个函数的地址;以后调用huart->RxISR就是指向UART_RxISR_8BIT(可以理解为“huart->RxISR” = “UART_RxISR_8BIT“,具体原因查看句柄huart的定义)


      /* Set the Rx ISR function pointer according to the data word length */
      if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
      {
        huart->RxISR = UART_RxISR_16BIT;
      }
      else
      {
        huart->RxISR = UART_RxISR_8BIT;
      }

此外我还设置了一个RXbuffer[20]数组用于处理接收道德数据

    /*定义接收缓冲区块 */
    #define RXBUFFERSIZE                      20//TXBUFFERSIZE
    uint8_t aRxBuffer[RXBUFFERSIZE]={0};
	int   RXnum=0;
    char  RXbuffer[20]={0};

之后进入stm32g4xx_it.c文件(中断服务函数入口在这里)找到uart1的中断服务函数

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */

   HAL_UART_IRQHandler(&huart1);//这里是自动生成的

  /* USER CODE BEGIN USART1_IRQn 1 */

   RXnum++;//每次进终端标志位加一

  /* USER CODE END USART1_IRQn 1 */
}

这里添加一个在main.c里面设置好的全局变量RXnum来计算发送的字节数。

3、流程

当发送一串数据,首先进入中断服务函数

USART1_IRQHandler(void)(每个字节进入一次)

这里只有一个HAL_UART_IRQHandler(&huart1)函数 还有刚才设置的RXnum来计算发送的字节数

之后进入HAL_UART_IRQHandler(&huart1),(截取一部分)

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  uint32_t errorflags;
  uint32_t errorcode;

/* 如果没有错误发生 */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
  if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);///从此处进入接收数据处理函数
      }
      return;
    }
  }
......
}

后面代码大部分是一些出错的情况的处理方法和一些不太常用的功能的条件逻辑的定义。

进来以后函数进入到里面的 huart->RxISR(huart);刚才说了可以理解为“huart->RxISR” = “UART_RxISR_8BIT“),因此 huart->RxISR(huart);即为UART_RxISR_8BIT(huart);因此就进入到HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);函数中的UART_RxISR_8BIT(huart);函数中:

static void UART_RxISR_8BIT(UART_HandleTypeDef *huart)
{
  uint16_t uhMask = huart->Mask;
  uint16_t  uhdata;

  /* Check that a Rx process is ongoing */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {

    uhdata = (uint16_t) READ_REG(huart->Instance->RDR);
    *huart->pRxBuffPtr = (uint8_t)(uhdata & (uint8_t)uhMask);
    huart->pRxBuffPtr++;
    huart->RxXferCount--;
		
    if (huart->RxXferCount == 0U)
    {
      /* Disable the UART Parity Error Interrupt and RXNE interrupts */
      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE));

      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;

      /* Clear RxISR function pointer */
      huart->RxISR = NULL;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
      /*Call registered Rx complete callback*/
      huart->RxCpltCallback(huart);
#else
      /*Call legacy weak Rx complete callback*/
      HAL_UART_RxCpltCallback(huart);//接收完成回调函数
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
    }
  }
  else
  {
    /* Clear RXNE interrupt flag */
    __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
  }
}

 uhdata = (uint16_t) READ_REG(huart->Instance->RDR);//读取当前字节
 *huart->pRxBuffPtr = (uint8_t)(uhdata & (uint8_t)uhMask);//掩蔽多余的位数(数据为8位,定义的    读取为16位,无效位掩蔽掉)
  huart->pRxBuffPtr++; //pRxBuffPtr为数组aRXBuffer的首地址,pRxBuffPtr=aRXBuffer[0];
  huart->RxXferCount--;//此处为刚才定义的RXBUFFERSIZE,为20相当于20个数据

读取完成以即

    if (huart->RxXferCount == 0U)

则进入下述回调函数里面:

HAL_UART_RxCpltCallback(huart);

此处依旧为接收定好长度的数据方式,长度为刚才设置好的20,

要想接收不定长,还要引入一个标志,可以表示接收完成的标志,这里使用了ISR寄存器的IDLE位

 关于IDLE有两个操作:

  __HAL_UART_GET_IT(&huart1,UART_IT_IDLE); //读取IDLE位的值

  __HAL_UART_CLEAR_IT(&huart1, UART_CLEAR_IDLEF); //清空IDLE位,即设置为0

 当接收数据小于设置好的值20,IDLE依旧可以变为1,而且此位置只有RXNE置为1时候才置为1,因此完全可以用来检测数据完成情况:检测这个IDLE的入口放在主函数while循环里;

当检测到IDLE非零以后进入回调函数HAL_UART_RxCpltCallback(&huart1);

    if(RXnum!= 0 && __HAL_UART_GET_IT(&huart1,UART_IT_IDLE)!=0)
    { 
      HAL_UART_RxCpltCallback(&huart1);
      __HAL_UART_CLEAR_IT(&huart1, UART_CLEAR_IDLEF); //手动置0清空标志位
    }

注意使用完一次IDLE以后一定要手动清0;

之后配置一下回调函数:

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

//  UNUSED(huart);
	 int a=0;
	 for(a=0;a<RXnum;a++)
	 {
	     RXbuffer[a]=aRxBuffer[a];
	 }
	 memset(aRxBuffer,0,20);
	 RXnum=0;
	 huart->pRxBuffPtr  = aRxBuffer;
     huart->RxXferSize  = 20;
     huart->RxXferCount = 20;
}

将aRxBuffer[];值赋给RXbuffer[],将aRxBuffer[];清空,RXnum清空,huart 的三个标志重置,方便下一次接收数据,此处就设置完成。

下面是使用:

#include <string.h>
if(RXbuffer[0]==0x11)
{
    LED_Toggle(GPIOC, GPIO_PIN_8);
	HAL_Delay(50);
	LED_Toggle(GPIOC, GPIO_PIN_8);
	memset(RXbuffer,0,20);
}

当接收到0x11的数据 灯闪烁一下;

最后加入一下串口发送:

1.HAL_UART_Transmit(&huart1, (uint8_t *)aTxBuffer, TXBUFFERSIZE,1000);

定义好发送缓存数组,即可发送数据,缺点是每次都要重新定义数组内容

2.printf的重映射:在uart.c以后

#include <stdio.h>
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

之后就可以直接调用printf函数了。

部分介绍参考:STM32 HAL库UART的使用 - cau_par - 博客园 (cnblogs.com)

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

STM32学习笔记-SMT32使用HAL库UART中断方式使用 的相关文章

随机推荐

  • Java+TestNG+Maven+Selenium的web自动化测试脚本环境的搭建

    一 环境搭建 1 安装java环境 a 安装JDK b 安装eclipse c 安装maven 参考 xff1a http www cnblogs com s1328 p 4620812 html 2 安装eclipse下的testng插件
  • UWP UI自动化测试(一)------WinAppDriver/Inspect.exe环境准备

    安装的软件 xff1a 1 WinAppDriver WinAppDriver官网 xff1a GitHub microsoft WinAppDriver Windows Application Driver上面有些 demo Applic
  • python自动化测试(2) 自动化基本技术原理

    1 概述 在之前的文章里面提到过 xff1a 做自动化的首要本领就是要会 透过现象看本质 xff0c 落实到实际的IT工作中就是 透过界面看数据 掌握上面的这样的本领可不是容易的事情 xff0c 必须要有扎实的计算机理论基础 xff0c 才
  • Python自动化测试(1)-自动化测试及基本技术手段概述

    生产力概述 在如今以google为首的互联网时代 xff0c 软件的开发和生产模式都已经发生了变化 xff0c 在 参与感 一书提到 xff1a 某位从微软出来的工程师很困惑 xff0c 微软在google还有facebook这些公司发展的
  • python自动化测试(3) 自动化框架及工具

    1 概述 手续的关于测试的方法论 xff0c 都是建立在之前的文章里面提到的观点 xff1a 功能测试不建议做自动化接口测试性价比最高接口测试可以做自动化 后面所谈到的 测试自动化 也将围绕着 接口自动化 来介绍 本系列选择的测试语言是 p
  • python自动化测试(4)-使用第三方python库技术实现

    1 概述 关于测试的方法论 xff0c 都是建立在之前的文章里面提到的观点 xff1a 功能测试不建议做自动化接口测试性价比最高接口测试可以做自动化做好接口自动化 xff0c 一定要有透过界面看到数据本质的能力 后面所谈到的 测试自动化 也
  • 测试开发-怎么能忘记打卡呢-神器AppleScript

    写在前面 今天给大家介绍一款我自认为比较好用的东西 xff0c Mac OS 下的 AppleScript xff0c AppleScript像是 mac电脑给开发者留下的一个入口一样 xff0c 他不同于其他语言那么复杂 xff0c 可以
  • 自动化测试_Mac安装python+selenium

    1 下载安装 参照下文 https blog csdn net kacylining article details 60587484 https www zhihu com question 30496889 a 下载 pip https
  • 使用Postman实现批量接口执行自动化测试

    一 创建文件夹 1 点击 34 Collections 34 TAB进入 xff0c 再次点击 New Collection 按钮创建文件夹 示例图文 xff1a 二 定义变量及调用执行测试 xff0c 方便灵活调用 1 定义全局变量 xf
  • 真人踩过的坑,告诉你避免自动化测试常犯的10个错误

    虽然从自己的错误中学习也不错 xff0c 但从别人的错误中学习总是更好的 作为一个自动化测试人员 xff0c 分享常见的容易犯的10个错误 xff0c 可以从中吸取教训 xff0c 引以为鉴 一 必要时才自动化 新人小王接到为Web应用程序
  • linux上传下载网速脚本

    bin sh LANG 61 34 34 while true do up time1 61 96 ifconfig 1 grep 34 bytes 34 awk 39 print 6 39 awk F 39 print 2 39 96 d
  • 部门刚来的00后太卷,还没2年班,跳到我们公司起薪20k....

    都说00后躺平了 xff0c 但是有一说一 xff0c 该卷的还是卷 这不 xff0c 前段时间我们公司来了个00后 xff0c 工作都没两年 xff0c 跳槽到我们公司起薪18K xff0c 都快接近我了 后来才知道人家是个卷王 xff0
  • 在阿里我是如何当面试官的

    前言 由于疫情关系 xff0c 最近看到很多测试同学的工作情况内心还是蛮触动的 xff08 降薪 变相裁员 辞退等 xff09 可能这并不是当下一个普遍的现象 xff0c 但仍然使我感受到前端这碗青春饭不好混 于此同时联系我内推的同学很多都
  • 3月面试了一个4年的测试,一问三不知,还反怼我...

    最近看了很多简历 xff0c 很多候选人年限不小 xff0c 但是做的都是一些非常传统的项目 xff0c 想着也不能通过简历就直接否定一个人 xff0c 何况现在大环境越来 越难 xff0c 大家找工作也不容易 xff0c 于是就打算见一见
  • Jenkins参数化构建自动化测试

    测试人员在进行支付类型相关的场景测试过程中 xff0c 需要真实支付的金额较大 xff0c 测试过程中又难以控制测试支付成本 xff0c 无法确切的向公司申请测试备用金 那么问题来了 xff1a 在无法避免自掏腰包进行巨额支付测试时 xff
  • 运维实战 kubernetes(k8s) 之 service

    64 TOC 运维实战 kubernetes k8s 之 service 1 service 介绍 Service可以看作是一组提供相同服务的Pod对外的访问接口 借助Service xff0c 应用可以方便地实现服务发现和负载均衡 ser
  • 企业运维之 openstack 的私有网络以及图形化配置

    企业运维之 openstack 的私有网络以及图形化 1 私有网络2 图形化配置虚拟机3 封装镜像4 上传镜像5 块存储6 kolla ansible 在上一篇的基础上 xff0c 来继续完善 openstack 的相关配置 先将控制节点启
  • AD 软件的学习--基本操作

    一 界面认识 如果某个空间消失了就在设置中的VIEW进行一个复位 device 元器件 xff1b connector 连接器 库所在文件夹 原理图大小设置 二 基本过程 放元器件时 xff0c 空格键进行旋转 先选好元件 xff0c 摆在
  • ROS学习记录(三)C++与Python文件的配置

    本篇文章依托于赵虚左老师的ROS课程 xff0c 实现HelloWorld打印 xff0c 并安装VScode环境 课程链接 讲义链接 文章目录 前言一 基本操作过程1 先创建一个工作空间 xff1b 2 再创建一个功能包 xff1b 二
  • STM32学习笔记-SMT32使用HAL库UART中断方式使用

    目录 1 引言 2 配置 3 流程 1 引言 近来需要使用蓝牙模块 xff0c 再了解到蓝牙模块等无线模块许多使用串口透传 xff0c 便决定在研究一下串口UART 的使用方法 由于用的板子种类每次都不一样 xff0c 有F103 有G0