HAL库STM32串口DMA不定长收发,空闲中断

本文详细介绍了如何解决STM32串口传输数据量限制的问题,通过配置STM32CUBE,启用串口空闲中断和DMA,实现了接收发送2K以上数据的成功案例。关键步骤包括配置串口参数、编写相关代码、启用接收中断和IDLE中断,并在空闲中断回调函数中处理数据。实验结果显示,该方法能稳定接收和发送大量数据。
摘要由CSDN通过智能技术生成

在网上看了很多的教程,我移植之后,一直存在最多收发200来个字节的问题,没有发现是哪里的原因,查阅了很多资料,总结出来一个靠谱的方案,实测收发2K以上的数据没有问题。记录如下。

参考链接: (主要参考了链接5中的代码)
1.C语言中volatile关键字的学习
2.【STM32】HAL库 STM32CubeMX教程十一—DMA (串口DMA发送接收)
3.C语言中volatile关键字的学习
4.一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制
5.真正实现了STM32 HAL串口不定长数据的接收发送功能(DMA方式,不用限定单次接收长度和添加结束标志)


1.配置STM32CUBE

  • 我以STM32L071为例,该教程原封不动的移植到STM32F103我试过没有任何问题
1)时钟

自己看着配就行,配的时钟频率越高,程序执行速度越快,越不容易出问题
在这里插入图片描述

在这里插入图片描述

2)SWD两线刷写

不配置刷不进去程序
在这里插入图片描述

3)USART1

看图,参数设置中可以设置自己需要的波特率,我这里配置的115200
在这里插入图片描述
再USART1页面中还需要配置DMA Setting和NVIC Setting
在这里插入图片描述
在这里插入图片描述

4)生成代码


2.代码编写

1)增加位带操作、u8、u16、u32

我粘贴在了在gpio.h中,这个例程里头没有用到位带操作

//u8u16u32
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
//位带
typedef struct
{
    u16 bit0 : 1;
    u16 bit1 : 1;
    u16 bit2 : 1;
    u16 bit3 : 1;
    u16 bit4 : 1;
    u16 bit5 : 1;
    u16 bit6 : 1;
    u16 bit7 : 1;
    u16 bit8 : 1;
    u16 bit9 : 1;
    u16 bit10 : 1;
    u16 bit11 : 1;
    u16 bit12 : 1;
    u16 bit13 : 1;
    u16 bit14 : 1;
    u16 bit15 : 1;
} Bits16_TypeDef;
#define PAin(n) ( ( GPIOA->IDR&(1 << (n)) )>>n )
#define PBin(n) ( ( GPIOB->IDR&(1 << (n)) )>>n )
#define PCin(n) ( ( GPIOC->IDR&(1 << (n)) )>>n )
#define PDin(n) ( ( GPIOD->IDR&(1 << (n)) )>>n )
#define PEin(n) ( ( GPIOE->IDR&(1 << (n)) )>>n )
#define PFin(n) ( ( GPIOF->IDR&(1 << (n)) )>>n )
 
#define PAout(n)  ( ((Bits16_TypeDef *)(&(GPIOA->ODR)))->bit##n )
#define PBout(n)  ( ((Bits16_TypeDef *)(&(GPIOB->ODR)))->bit##n )
#define PCout(n)  ( ((Bits16_TypeDef *)(&(GPIOC->ODR)))->bit##n )
#define PDout(n)  ( ((Bits16_TypeDef *)(&(GPIOD->ODR)))->bit##n )
#define PEout(n)  ( ((Bits16_TypeDef *)(&(GPIOE->ODR)))->bit##n )
#define PFout(n)  ( ((Bits16_TypeDef *)(&(GPIOF->ODR)))->bit##n )

2)usart.c
  • 加入DMA接收用缓存数组
/* USER CODE BEGIN 0 */
u8 RxBuffer[UART_RX_BUF_SIZE];
/* USER CODE END 0 */
  • 加入三个函数,fputc用来printf打印输出,本例程没有用到该函数的功能
  • HAL_UART_TxCpltCallback,串口空闲中断回调函数,当串口空闲时调用该函数
  • ProcessData,用来将RxBuffer中的数据通过DMA发送至串口
/* USER CODE BEGIN 1 */
int fputc(int ch,FILE *f)
{
	HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,100);
	return ch;
}
/*
*********************************************************************************************************
* 函 数 名: HAL_UART_IdleCallback
* 功能说明: 串口空闲中断回调函数,当串口空闲时调用该函数
* 形  参: *huart
* 返 回 值: 无
*********************************************************************************************************
*/

void HAL_UART_IdleCallback(UART_HandleTypeDef *huart)  
{
	  __HAL_UART_CLEAR_IDLEFLAG(huart);      					             //清除空闲标志位
		HAL_UART_DMAStop(huart);                                     //停止DMA,防止其继续接收数据捣乱        
		ProcessData();                                               //将DMA接收到的数据通过DMA发送至串口                      
    HAL_UART_Receive_DMA(&huart1, RxBuffer, UART_RX_BUF_SIZE);   //使用DMA继续接收数据到RxBuffer
}
/*
*********************************************************************************************************
* 函 数 名: ProcessData
* 功能说明: 将RxBuffer中的数据通过DMA发送至串口
* 形  参: *huart
* 返 回 值: 无
*********************************************************************************************************
*/
void ProcessData(void)
{
    u32 len;        
    len = UART_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//已经接收了多少个字节 = 总共要接收的字节数 - NDTR     
    if(len > 0)                                                     //如果确实接收到了数据
    {             
			HAL_UART_Transmit_DMA(&huart1, RxBuffer, len);              //将RxBuffer中的数据通过DMA发送到串口
    }
}
/* USER CODE END 1 */

这里如果是hlpuart的话,为__HAL_DMA_GET_COUNTER(&hdma_lpuart1_rx)

3)usart.h
  • 头文件
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.h"
#include "gpio.h"
/* USER CODE END Includes */
  • 函数及变量声明,UART_RX_BUF_SIZE是RxBuffer数组的大小,可以随便设,不可超过65535
/* USER CODE BEGIN Private defines */
#define UART_RX_BUF_SIZE 2048
void ProcessData(void);
void HAL_UART_IdleCallback(UART_HandleTypeDef *huart);
/* USER CODE END Private defines */
4)stm32l0xx_it.c
  • 我们在usart.c中定义了空闲中断回调函数,应该在串口中断中引用一下
  • 引用usart.h头文件,因为有空闲中断回调函数
/* USER CODE BEGIN Includes */
#include "usart.h"
/* USER CODE END Includes */
  • 中断响应函数
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))
  {
	  HAL_UART_IdleCallback(&huart1);
  }  
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
5)main.c
  • 定义一下RxBuffer
/* USER CODE BEGIN 0 */
extern u8 RxBuffer[UART_RX_BUF_SIZE];
/* USER CODE END 0 */
  • 在MX_USART1_UART_Init()后加入以下指令,这些指令可以整合到一个函数中,有的人喜欢放MX_USART1_UART_Init()中,那样CUBE更新就没了。
  • 使能接收中断和使能IDLE中断是必须开的
  • DMA接收函数在while之前,保证了在进入while之前能够接收到DMA的指令,否则第一次数据是空的。
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); //使能接收中断
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
	//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
	HAL_UART_Receive_DMA(&huart1,RxBuffer,UART_RX_BUF_SIZE);
  /* USER CODE END 2 */

3.实验结果

发送2047字节,接受到2047字节,实验成功。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值