STM32CubeMX+DMA+IDLE实现串口不定长度数据接收和发送

前言

不定长数据接收的原理及其解决的方法

在 STM32 中。USART 发送接收有三种基本方式,轮询、中断和 DMA。

轮询方式为堵塞模式,使用超时管理机制。它每次接收一个字节,在规定时间内接收固定长度的数据。在对于某些数据不固定长度接收的数据,轮询的方式有时候不够灵活。

中断的方式为每一个字节都中断一次,当时比较消耗系统资源。特别是HAL库中,从中断到回调函数运行了不少的程序,频繁的中断很可能造成数据溢出。为了避免这个问题,我们使用指定接收一定长度的数据,再调用回调函数,这会让我们可以接收大数据,但是这种情况则造成了,要求每次的包是固定长度。

为了解决以上一些问题,网上最常用的办法是使用空闲中断,即在串口空闲的时候,触发一次中断,通知内核,本次运输完成了。数据传输过程为了尽量不占用CPU的处理数据时间,所以就使用DMA接收串口的数据。

什么是串口空闲

一般我们串口接收的时候都是使用的RXNE,接收到一个字节数据就进入一次中断,然后把它放入缓存,但是数据量很大的时候会频繁进入中断影响单片机的时效性。这时就可以使用到IDLE空闲中断,即在接收到一段数据后在一定的时间检测到没有数据到来,就认为此时串口总线空闲便产生一个空闲中断。
在这里插入图片描述
空闲中断的标志也就是停止位,一个上升沿

轮询和中断方式的简单实现 介绍

轮询

生成

STM32CubeMX 配置 STM32F407时钟树和烧录方式
在这里插入图片描述

API代码

串口阻塞式发送数据,使用超时管理机制 HAL_UART_Transmit()
串口阻塞式接收数据,使用超时管理机制 HAL_UART_Receive()

中断

生成

在上面的基础上,在NVIC中使能串口中断,并配置优先级。
在这里插入图片描述

串口中断模式发送 HAL_UART_Transmit_IT(),发送完会回调 HAL_UART_TxCpltCalback()
串口中断模式接收 HAL_UART_Receive_IT(),接收完会回调 HAL_UART_RxCpltCallback()

每个字节进一次中断,在 HAL_UART_IRQHandler() 中由HAL库自行处理。
对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用

总结:使用中断方式收发串口的流程
1、调用函数开启一次中断收发的流程
2、每发送或接收到一个字节会进入中断,由HAL库进行处理,用户程序无需参与
3、HAL调用发送或接收完成回调函数

在这里插入图片描述

拓展:其他常用串口中断

  • UART_IT_TXE 发送寄存器空中断,开启后当一个字节传送完毕就会进入中断,HAL中断方式发送依赖该中断。
  • UART_IT_RXE 接收寄存器非空中断,开启后当接收到一个字节会进入中断,HAL库中断方式接收依赖该中断。

DMA

DMA(Direct memory access),即直接存储器访问。用于在外设与存储器之间以及存储器与存储器之间提供一种高速数据传输的方式。它在开始发送和接收完成数据时会给CPU相应的信号或者中断,在数据传输过程中无需CPU参与,通过硬件方式为RAM与I/O设备提供一条直接传送数据的通道。

DMA发送函数

HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
函数主要功能是以DAM模式发送pData指针指向的数据中固定长度的数据,并同时设置和使能DMA中断,具体怎么设置和使能中断的,打开此函数源码会发现下面这个函数。
HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size);
此函数是启动DMA传输并启用中断。从函数源码可以看出此函数首先判断DMA传输状态是否是Ready:

  • 如果是Ready则使能了DMA三个中断:DMA 半传输,DMA传输完成和DMA传输出错。如果发送数据正常,进入2次进入DMA中断(DMA 半传输和DMA传输完成);错误的话进入DMA传输出错中断;从这里也能看出HAL库比标准库严谨但效率低,DMA 半传输中断如果觉得效率低可以在程序中屏蔽掉,这样数据正常发送完成就只会进入一次DMA完成中断,三种DMA中断其实是同一个函数HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
    此函数功能是处理DMA中断请求,主要工作是清除中断标志位,改写DMA的状态,只有把状态改成HAL_DMA_STATE_READY,下一次才能正常使用DMA功能,否则会进入 HAL_BUSY状态
  • 如果不是Ready状态,则进入HAL_BUSY状态,这也是为什么连续使用HAL_UART_Transmit_DMA()函数发送数据,第二次会发不出来数据,而且第二次函数会进入HAL_BUSY状态,所以要想使用HAL_UART_Transmit_DMA()函数连续发送数据,相邻两次之间要有延时间隔或者检测DMA数据是否完成。

DMA 接收函数

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
函数说明:此函数的功能在DMA模式下接收大量数据,同时设置DMA线和哪个串口外设连接,以及将DMA线接收到的数据搬 *pData对应地内存中,和上面DMA发送函数一样,此函数同时具有设置和使能DMA中断的功能。可以看出此函数不仅是一个接收函数同时也是一个初始化函数,在main()之前调用,初始化串口和DMA连接和DMA接收BUF,以及设置和使能中断。如果DMA模式设置成循环模式时,只需设置这一次,如果DMA模式设置成正常模式时,每次读取完数据后需要再从新设置一次(就是再调用一次此函数),分析函数源码会发现此函数内部同样会调用以下两个函数,使用方法和分析和发送类似。

  • HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t *)tmp, (uint32_t)&huart->Instance->DR, Size);

  • HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma);
    主要说说HAL_UART_Receive_DMA(),怎么配合IDLE串口空闲中断使用,main()函数之前一般调用此函数,一个主要目的是指明DMA传输串口数据存到指定的地方。一般情况我们会开辟一个全局变量的缓存
    extern uint8_t receive_buff[BUFFER_SIZE]。比如函数初始化为HAL_UART_Receive_DMA(&huart2, (uint8_t*)receive_buff, BUFFER_SIZE); 就是设置串口2接收到数据通过DMA线直接到receive_buff中了,配合串口空闲中断,当进入串口空闲中断,说明一帧数据已接收完成。我们读取receive_buff相应长度的数据就是此次接收一帧的数据,这里还需要再介绍一个函数;

  • __HAL_DMA_GET_COUNTER(__HANDLE__)
    此函数的功能:获取当前DMA通道传输中 receive_buff[BUFFER_SIZE]缓存还剩余多少个数据单元。这样就能算出这一帧数据到底接收了多少单元的数据(数据长度=缓存总长度-缓存剩余的长度),
    length = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);

空闲中断

空闲中断介绍

空闲中断是接受数据后出现一个byte的高电平(空闲)状态,就会触发空闲中断.并不是空闲就会一直中断,准确的说应该是上升沿(停止位)后一个byte,如果一直是低电平是不会触发空闲中断的(会触发break中断)。所以为了减少误进入串口空闲中断,串口RX的IO管脚建议设置成Pull-up<上拉模式>,串口空闲中断只是接收的数据时触发,发送时不触发。

# STM32系列-串口-uart-软件引脚内部上拉 或者 外部电阻上拉-原因问题的搜寻

在这里插入图片描述
但是我本人通过软件,把RX配置成PULL UP之后,串口就无法正常工作了

空闲中断使用

串口空闲中断的判定是:当串口开始接收数据后,检测到1字节数据的时间内没有数据发生,则认为串口空闲了,进入相应的串口中断。在中断内清除空闲中断标志位和调用串口回调函数,在回调函数内处理读取,判断,处理接收的数据。处理完数据以后我再把串口中断打开重复上面的流程,就可以完整的接收一帧一帧的数据。同时利用空闲中断也可以省去很多的的判断。

实现DMA+IDLE

生成

在前面的基础上,因为我使用了USART1的DMA发送和接收数据,首先是将UART1_RX和UART1_TX映射到DMA对应的通道上,对应的配置就是在CubeMX配置就是添加相应的DMA通道。再设置DMA几个参数。
在这里插入图片描述

  • DMA模式,DMA有循环模式和正常模式2种模式。
    (1) DMA接收如果用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效 若是 正常模式,需要在每次接收完成后重复调用
    (2) DMA发送要用 正常模式,需在每次发送时重复调用 还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY) 若是 循环模式,则会连续不断地发送,不会停止。
  • 地址增加
    (1)在传输数据时,可以配置指向寄存器数据的指针是否自动向后递增。
    (2)通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。

我这里将RX和

<think>好的,用户想用STM32F103CubeMX配置UART,通过DMAIDLE中断实现不定长数据接收。首先,我需要回忆一下HAL库中相关函数的使用方法。用户提到的DMAIDLE中断组合,通常用于处理串口接收不定长数据的情况,这样可以避免轮询或固定缓冲区溢出的问题。 首先,CubeMX配置部分。需要启用UART的DMA接收,并且可能需要在NVIC中配置相应的中断。不过IDLE中断在CubeMX中可能没有直接的选项,可能需要手动启用。记得DMA要配置成循环模式,这样在接收数据时,DMA可以持续工作,不需要每次重新初始化。 接下来是代码部分。HAL库提供了HAL_UARTEx_ReceiveToIdle_DMA函数,这个函数可以启动DMA接收,并在IDLE中断时触发回调。用户需要重写HAL_UARTEx_RxEventCallback函数来处理接收完成后的数据。同时,可能需要关闭DMA的半传输中断,防止处理不完整的数据。 用户提供的引用中有提到启动DMA空闲中断,并且关闭半满中断。所以代码中需要调用HAL_UARTEx_ReceiveToIdle_DMA,并在初始化后关闭HT中断。此外,DMA的配置需要正确设置,比如内存地址递增,外设地址不递增,数据宽度等。 还需要注意,每次处理完数据后,要重新启动DMA接收,否则可能无法继续接收后续的数据。在回调函数中,获取当前接收数据长度,可以通过__HAL_DMA_GET_COUNTER来获取剩余未传输的数据量,从而计算出已接收的长度。 可能遇到的问题包括DMA缓冲区溢出,或者IDLE中断未正确触发。需要确保中断优先级设置正确,避免被其他中断阻塞。此外,内存复制时要注意缓冲区大小,防止越界。 最后,示例代码部分要结合CubeMX生成的初始化代码,添加自定义的回调函数中断处理。确保用户能够按照步骤配置CubeMX,并添加必要的代码到正确的位置,比如main函数中的初始化回调函数的定义。</think>### STM32F103 UART DMA+IDLE中断接收配置教程 根据你提供的引用内容,以下是分步骤实现方法: 一、CubeMX配置步骤 1. 启用USART模块 - 选择异步模式 - 配置波特率(如115200) - 启用DMA接收通道 2. DMA设置 ```c Mode: Circular // 循环模式 Data Width: Byte // 字节传输 Increment Address: Memory侧启用 // 内存地址递增[^3] ``` 3. NVIC配置 - 启用USART全局中断 - 启用DMA通道中断 二、关键代码实现 1. 主函数初始化 ```c /* USER CODE BEGIN 2 */ HAL_UARTEx_ReceiveToIdle_DMA(&huart3, RxBuffer, BUFFER_SIZE); // 启动DMA接收[^2] __HAL_DMA_DISABLE_IT(&hdma_usart3_rx, DMA_IT_HT); // 关闭半传输中断 /* USER CODE END 2 */ ``` 2. 中断回调函数 ```c void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if(huart->Instance == USART3){ uint16_t recv_len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx); // 处理接收数据 memcpy(ProcessBuffer, RxBuffer, recv_len); // 重新启动接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart3, RxBuffer, BUFFER_SIZE); } } ``` 三、注意事项 1. 必须使用`HAL_UARTEx_ReceiveToIdle_DMA`而非普通DMA接收函数[^1] 2. IDLE中断会自动清除,无需手动处理标志位 3. DMA缓冲区建议设置为最大预期数据长度的2倍[^3] 4. 在CubeMX中配置DMA为循环模式可避免重复初始化
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Harvey1104

感谢您的认可,无限飓风不断进步

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值