STM32F4使用DMA接受数据(HAL库)
前言
STM32使用USART串口接受数据有多种模式,其中一种是直接使用串口中断,来一个数据就处理一个数组,当很多数据传过来时候,频繁进入中断会占用CPU太多时间。这是我们可以使用DMA(直接存储器访问),将接受到的数据先放到DMA中,等传送完全了再去处理数据。而使用DMA接收也有多种方式,其中就有DMA定长中断接收和串口空闲中断接收。
一、DMA串口空闲中断接收
头文件:usart.h
#ifndef __USART_H__
#define __USART_H__
#define usart3_RX_BUFF_SIZE 128
#define DMA_UsART3_SIZE 128
void MX_USART3_UART_Init(void);
#endif
配置函数:
#include "usart.h"
UART_HandleTypeDef huart3;
DMA_HandleTypeDef hdma_usart3_rx;
DMA_HandleTypeDef hdma_usart3_tx;
/* USART3 init function */
void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART3)
{
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**USART3 GPIO Configuration
PD9 ------> USART3_RX
PD8 ------> USART3_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USART3_RX Init */
hdma_usart3_rx.Instance = DMA1_Stream1;
hdma_usart3_rx.Init.Channel = DMA_CHANNEL_4;
hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_usart3_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart3_rx);
/* USART3_TX Init */
hdma_usart3_tx.Instance = DMA1_Stream3;
hdma_usart3_tx.Init.Channel = DMA_CHANNEL_4;
hdma_usart3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_tx.Init.Mode = DMA_CIRCULAR;
hdma_usart3_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_usart3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_usart3_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart3_tx);
/* USART3 interrupt Init */
HAL_NVIC_SetPriority(USART3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspInit 1 */
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
/* USER CODE END USART3_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART3)
{
/* USER CODE BEGIN USART3_MspDeInit 0 */
/* USER CODE END USART3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART3_CLK_DISABLE();
/**USART3 GPIO Configuration
PD9 ------> USART3_RX
PD8 ------> USART3_TX
*/
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_9|GPIO_PIN_8);
HAL_DMA_DeInit(uartHandle->hdmarx);
HAL_DMA_DeInit(uartHandle->hdmatx);
HAL_NVIC_DisableIRQ(USART3_IRQn);
}
}
中断接受处理函数
uint8_t usart3_rx_data[usart3_RX_BUFF_SIZE];
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart3)
{
if(__HAL_UART_GET_FLAG(huart3, UART_FLAG_IDLE)!=RESET) //判断是否是空闲中断
{
__HAL_UART_CLEAR_IDLEFLAG(huart3);
HAL_UART_DMAStop(huart3); //停止本次DMA传输
uint8_t data_length = DMA_UsART3_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_tx); //计算接收到的数据长度
process_judge_message( usart3_rx_data );
memset(usart3_rx_data,0,data_length); //清零接收缓冲区
HAL_UART_Receive_DMA(huart3, usart3_rx_data, DMA_UsART3_SIZE); //重启开始DMA传输 每次最长255字节数据
}
}
void USART3_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart3);
USAR_UART_IDLECallback(&huart3);
}
二、DMA定长中断接收
本例的定长设置为30个字节
头文件:
#ifndef USART1_H
#define USART1_H
#include "stm32f4xx_hal.h"
#define CALI_DATA_PACKAGE_SIZE (40u)
void usart1_configuration(uint32_t bound);
#endif
配置函数:
DMA_HandleTypeDef dma2_handler; //DMA句柄
UART_HandleTypeDef usart1_handler; //UART句柄
uint8_t usart1_rx_cali_data[CALI_DATA_PACKAGE_SIZE];
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init;
if(huart->Instance == USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOH时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
gpio_init.Pin = GPIO_PIN_9;
gpio_init.Alternate = GPIO_AF7_USART1;
gpio_init.Mode = GPIO_MODE_AF_PP; //输出
gpio_init.Pull = GPIO_PULLUP; //上拉
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio_init);
gpio_init.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOB, &gpio_init);
}
}
void usart1_configuration(uint32_t bound)
{
__HAL_RCC_DMA1_CLK_ENABLE();//DMA1时钟使能
__HAL_RCC_DMA2_CLK_ENABLE();//DMA1时钟使能
__HAL_LINKDMA(&usart1_handler, hdmarx, dma2_handler); //将DMA与USART1联系起来(接收DMA)
dma2_handler.Instance = DMA2_Stream5; //dma2 数据流5(可查表)
dma2_handler.Init.Channel = DMA_CHANNEL_4;//通道4(可查表)
dma2_handler.Init.Direction = DMA_PERIPH_TO_MEMORY;//从外设到存储器
dma2_handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
dma2_handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
dma2_handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
dma2_handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
dma2_handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //关闭
dma2_handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;//无
dma2_handler.Init.MemBurst = DMA_MBURST_SINGLE;
dma2_handler.Init.PeriphBurst = DMA_PBURST_SINGLE;
dma2_handler.Init.Mode = DMA_CIRCULAR;//循环模式
dma2_handler.Init.Priority = DMA_PRIORITY_MEDIUM;//优先级设置
HAL_DMA_DeInit(&dma2_handler);//复位寄存器
HAL_DMA_Init(&dma2_handler);
//usart1 初始化
usart1_handler.Instance=USART1; //USART1
usart1_handler.Init.BaudRate=bound; //波特率
usart1_handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
usart1_handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
usart1_handler.Init.Parity=UART_PARITY_NONE;
usart1_handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
usart1_handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&usart1_handler); //HAL_UART_Init()会使能UART1
HAL_UART_Receive_DMA(&usart1_handler, usart1_rx_cali_data, 30);//开启usart1 rx dma接收中断
HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
}
中断处理函数
void DMA2_Stream5_IRQHandler(void)
{
HAL_DMA_IRQHandler(&dma2_handler);
get_pc_cali_data(&pc_cali, usart1_rx_cali_data); //回调
}
以上就是串口配置DMA的基本流程