【stm32外设】封装在hal库上的串口接收驱动(DMA)
缘由作者能力有限,不能对实现原理做到好的阐述,故不对实现原理做过多的阐述,想学习可参考学习本文的参考链接。
cubemx配置
这里我们启用DMA发送和接收数据,传输模式都采用normal正常模式。
normal正常模式:当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次。
circular循环传输模式:当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式。
相关代码
这里实现的思想和使用(非DMA)空闲中断接收数据的思想是一样的,等待一帧数据接收完成后,会产生空闲中断,在中断里处理DMA数据即可。注意事项写在bsp_usart_dma.h文件开头处了。
bsp_usart_dma.c
#include "./usart/bsp_usart_dma.h"
#include <string.h>
#include <stdio.h>
/* 缓存串口接收数据的结构体 */
typedef struct stru_usart
{
unsigned char buf[USART_DMA_BUF_SIZE];
unsigned char flag;
unsigned int len;
} stru_usart_t;
#if USART1_DMA_EN
extern DMA_HandleTypeDef hdma_usart1_rx;
static stru_usart_t usart_dma_1; /* 缓存串口1接收到的数据 */
#endif
#if USART2_DMA_EN
extern DMA_HandleTypeDef hdma_usart2_rx;
static stru_usart_t usart_dma_2; /* 缓存串口2接收到的数据 */
#endif
#if USART3_DMA_EN
extern DMA_HandleTypeDef hdma_usart3_rx;
static stru_usart_t usart_dma_3; /* 缓存串口3接收到的数据 */
#endif
#if USART4_DMA_EN
extern DMA_HandleTypeDef hdma_usart4_rx;
static stru_usart_t usart_dma_4; /* 缓存串口4接收到的数据 */
#endif
#if USART5_DMA_EN
extern DMA_HandleTypeDef hdma_usart5_rx;
static stru_usart_t usart_dma_5; /* 缓存串口5接收到的数据 */
#endif
/* bsp_usart_dma_init:串口DMA接收初始化函数
* huart 想要初始化的串口的句柄
* huart_type 想要初始化的串口编号,BSP_USART_DMA
* return 成功返回0,失败返回-1 */
int bsp_usart_dma_init
(
UART_HandleTypeDef *huart,
unsigned char huart_type
)
{
switch (huart_type)
{
/* 串口1 */
#if USART1_DMA_EN
case BSP_USART_DMA_1:
/* 开启DMA接收中断 */
HAL_UART_Receive_DMA(huart, usart_dma_1.buf, USART_DMA_BUF_SIZE);
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
/* 清除第一次空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
return 0;
#endif
/* 串口2 */
#if USART2_DMA_EN
case BSP_USART_DMA_2:
/* 开启DMA接收中断 */
HAL_UART_Receive_DMA(huart, usart_dma_2.buf, USART_DMA_BUF_SIZE);
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
/* 清除第一次空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
return 0;
#endif
/* 串口3 */
#if USART3_DMA_EN
case BSP_USART_DMA_3:
/* 开启DMA接收中断 */
HAL_UART_Receive_DMA(huart, usart_dma_3.buf, USART_DMA_BUF_SIZE);
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
/* 清除第一次空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
return 0;
#endif
/* 串口4 */
#if USART4_DMA_EN
case BSP_USART_DMA_4:
/* 开启DMA接收中断 */
HAL_UART_Receive_DMA(huart, usart_dma_4.buf, USART_DMA_BUF_SIZE);
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
/* 清除第一次空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
return 0;
#endif
/* 串口5 */
#if USART5_DMA_EN
case BSP_USART_DMA_5:
/* 开启DMA接收中断 */
HAL_UART_Receive_DMA(huart, usart_dma_5.buf, USART_DMA_BUF_SIZE);
/* 使能空闲中断 */
__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
/* 清除第一次空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
return 0;
#endif
}
return -1;
}
/* bsp_usart_dma_handler:串口接收的中断处理函数
* huart 串口的句柄
* huart_type 串口编号,BSP_USART_DMA
* return 成功返回0,失败返回-1 */
int bsp_usart_dma_handler
(
UART_HandleTypeDef *huart,
unsigned char huart_type
)
{
switch (huart_type)
{
#if USART1_DMA_EN
case BSP_USART_DMA_1:
/* 判断是否为空闲中断 */
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止接收DMA,防止后续数据影响数据的接收 */
HAL_UART_DMAStop(huart);
usart_dma_1.flag = 1;
/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
usart_dma_1.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
HAL_UART_Receive_DMA(huart, usart_dma_1.buf, USART_DMA_BUF_SIZE);
return 0;
}
#endif
#if USART2_DMA_EN
case BSP_USART_DMA_2:
/* 判断是否为空闲中断 */
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止接收DMA,防止后续数据影响数据的接收 */
HAL_UART_DMAStop(huart);
usart_dma_2.flag = 1;
/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
usart_dma_2.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
HAL_UART_Receive_DMA(huart, usart_dma_2.buf, USART_DMA_BUF_SIZE);
return 0;
}
#endif
#if USART3_DMA_EN
case BSP_USART_DMA_3:
/* 判断是否为空闲中断 */
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止接收DMA,防止后续数据影响数据的接收 */
HAL_UART_DMAStop(huart);
usart_dma_3.flag = 1;
/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
usart_dma_3.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
HAL_UART_Receive_DMA(huart, usart_dma_3.buf, USART_DMA_BUF_SIZE);
return 0;
}
#endif
#if USART4_DMA_EN
case BSP_USART_DMA_4:
/* 判断是否为空闲中断 */
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止接收DMA,防止后续数据影响数据的接收 */
HAL_UART_DMAStop(huart);
usart_dma_4.flag = 1;
/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
usart_dma_4.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart4_rx);
HAL_UART_Receive_DMA(huart, usart_dma_4.buf, USART_DMA_BUF_SIZE);
return 0;
}
#endif
#if USART5_DMA_EN
case BSP_USART_DMA_5:
/* 判断是否为空闲中断 */
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
/* 清除空闲中断标志,方便后续接收数据 */
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* 停止接收DMA,防止后续数据影响数据的接收 */
HAL_UART_DMAStop(huart);
usart_dma_5.flag = 1;
/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
usart_dma_5.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart5_rx);
HAL_UART_Receive_DMA(huart, usart_dma_5.buf, USART_DMA_BUF_SIZE);
return 0;
}
#endif
}
return -1;
}
/* bsp_usart_dma_get:获取串口接收到的数据的函数(DMA)
* buf 获取到的数据复制到buf里
* huart_type 串口编号,BSP_USART_DMA
* return 接收到的数据长度,失败返回-1 */
int bsp_usart_dma_get
(
unsigned char buf[],
unsigned char huart_type
)
{
int len = -1;
switch (huart_type)
{
#if USART1_DMA_EN
case BSP_USART_DMA_1:
if (usart_dma_1.flag == 1) /* 判断串口是否有接收到数据 */
{
strncpy((char*)buf, (const char*)usart_dma_1.buf, usart_dma_1.len);
len = usart_dma_1.len;
memset(&usart_dma_1, 0, sizeof(stru_usart_t));
}
break;
#endif
#if USART2_DMA_EN
case BSP_USART_DMA_2:
if (usart_dma_2.flag == 1) /* 判断串口是否有接收到数据 */
{
strncpy((char*)buf, (const char*)usart_dma_2.buf, usart_dma_2.len);
len = usart_dma_2.len;
memset(&usart_dma_2, 0, sizeof(stru_usart_t));
}
break;
#endif
#if USART3_DMA_EN
case BSP_USART_DMA_3:
if (usart_dma_3.flag == 1) /* 判断串口是否有接收到数据 */
{
strncpy((char*)buf, (const char*)usart_dma_3.buf, usart_dma_3.len);
len = usart_dma_3.len;
memset(&usart_dma_3, 0, sizeof(stru_usart_t));
}
break;
#endif
#if USART4_DMA_EN
case BSP_USART_DMA_4:
if (usart_dma_4.flag == 1) /* 判断串口是否有接收到数据 */
{
strncpy((char*)buf, (const char*)usart_dma_4.buf, usart_dma_4.len);
len = usart_dma_4.len;
memset(&usart_dma_4, 0, sizeof(stru_usart_t));
}
break;
#endif
#if USART5_DMA_EN
case BSP_USART_DMA_5:
if (usart_dma_5.flag == 1) /* 判断串口是否有接收到数据 */
{
strncpy((char*)buf, (const char*)usart_dma_5.buf, usart_dma_5.len);
len = usart_dma_5.len;
memset(&usart_dma_5, 0, sizeof(stru_usart_t));
}
break;
#endif
}
return len;
}
bsp_usart_dma.h
/**
* author:临木木
* 适用于stm32裸机的usart接收驱动(DMA)
* 主程序启用前运行bsp_usart_dma_init()函数初始化串口配置
* bsp_usart_dma_handler() 需要放到#include "stm32f1xx_it.c"文件的USARTx_IRQHandler()函数里
*
* 使用HAL_UART_Transmit_DMA()前需判断所需要的串口是否空闲,再继续发送新的数据,不然会导致输出的数据会不对
* 例: while ((&huart1)->gState == HAL_UART_STATE_BUSY_TX);
* HAL_UART_Transmit_DMA(&huart1, "hello stm32", strlen("hello stm32"));
*/
#ifndef __BSP_USART_DMA_H__
#define __BSP_USART_DMA_H__
#include "usart.h"
#define USART_DMA_BUF_SIZE 256
/* 启用哪个置1即可,未启用,不应该置1,会报错 */
#define USART1_DMA_EN 1
#define USART2_DMA_EN 0
#define USART3_DMA_EN 0
#define USART4_DMA_EN 0
#define USART5_DMA_EN 0
/* huart_type */
enum BSP_USART_DMA
{
BSP_USART_DMA_1,
BSP_USART_DMA_2,
BSP_USART_DMA_3,
BSP_USART_DMA_4,
BSP_USART_DMA_5
};
int bsp_usart_dma_init
(
UART_HandleTypeDef *huart,
unsigned char huart_type
);
int bsp_usart_dma_handler
(
UART_HandleTypeDef *huart,
unsigned char huart_type
);
int bsp_usart_dma_get
(
unsigned char buf[],
unsigned char huart_type
);
#endif
例子
/* 初始化 */
bsp_usart_dma_init(&huart1, BSP_USART_DMA_1);
// 如果在初始化后立即使用HAL_UART_Transmit_DMA()输出,需使用延时函数等待,否则会报错,不知道问什么
// HAL_Delay(0);
// HAL_UART_Transmit_DMA(&huart1, "hello stm32", strlen("hello stm32"));
/* 接收数据 */
static int len = 0;
static unsigned char buf[256];
len = bsp_usart_dma_get(buf, BSP_USART_DMA_1);
if (len != -1)
{
/* 等待串口空闲再发送数据 */
while ((&huart1)->gState == HAL_UART_STATE_BUSY_TX);
HAL_UART_Transmit_DMA(&huart1, buf, len);
}