串口数据收发(DMA)初始化步骤:
第一步:调用HAL_DMA_Init函数初始化DMA参数,函数传入结构体参数如下:
typedef struct __DMA_HandleTypeDef
{
DMA_Stream_TypeDef *Instance; // DMA数据流地址
DMA_InitTypeDef Init; // DMA通讯的参数
HAL_LockTypeDef Lock; // DMA锁定对象
__IO HAL_DMA_StateTypeDef State; // DMA传输的状态
void *Parent; // 父对象的状态
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); // DMA转换完成回调函数
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); // DMA转换完成一半回调函数
void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma); // DMA内存1转换完成回调函数
void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); // DMA内存1转换完成一半回调函数
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma); // DMA转换错误回调
void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma); // DMA转换中止回调
__IO uint32_t ErrorCode; // DMA错误回调代码
uint32_t StreamBaseAddress; // DMA数据流地址
uint32_t StreamIndex; // DMA数据流索引
}DMA_HandleTypeDef;
第二步:初始化串口参数,将DMA初始化结构体参数传入串口结构体中,详情请参考下面代码。
第三步:调用数据收发函数。
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
函数中主要实现以下功能:
函数如下:
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t *tmp;
/* 检查Rx进程是否已经在进行中 */
if(huart->RxState == HAL_UART_STATE_READY)
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* 进程锁 */
__HAL_LOCK(huart);
/* 初始化接收数据缓冲区地址以及数据长度 */
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
/* 初始化DMA错误代码状态和Rx进程工作状态*/
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* 设置串口DMA转换完成中断回调 */
huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;
/*设置UART DMA半传输完全回调 */
huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
/* 设置DMA错误回调 */
huart->hdmarx->XferErrorCallback = UART_DMAError;
/* 设置DMA中止回调 */
huart->hdmarx->XferAbortCallback = NULL;
/* 使能DMA 设置传输起始地址、目标地址 */
tmp = (uint32_t*)&pData;
HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);
/* 在启用DMA Rx请求之前清除溢出标志:对于第二次传输可能是强制性的 */
__HAL_UART_CLEAR_OREFLAG(huart);
/* 进程解锁 */
__HAL_UNLOCK(huart);
/* 使能串口校验错误中断 */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
/* 使能串口错误中断:帧错误、噪声错误、溢出错误 */
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* 使能串口DMA接收 UART->CR3 Bit6 DMAR*/
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
函数主要实现功能如下:
函数如下:
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
uint32_t *tmp;
/* 检查Tx进程是否已经在进行中 */
if(huart->gState == HAL_UART_STATE_READY)
{
if((pData == NULL ) || (Size == 0))
{
return HAL_ERROR;
}
/* 进程锁 */
__HAL_LOCK(huart);
/* 配置发送缓冲区地址 数据个数*/
huart->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
/* 初始化DMA错误代码状态和Tx进程工作状态 */
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;
/* 设置串口DMA发送数据完成中断回调 */
huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt;
/* 设置串口DMA发送一半数据完成中断回调 */
huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt;
/* 设置DMA错误回调函数 */
huart->hdmatx->XferErrorCallback = UART_DMAError;
/* 设置DMA中止回调函数 */
huart->hdmatx->XferAbortCallback = NULL;
/* 使能DMA 设置传输起始地址、目标地址 */
tmp = (uint32_t*)&pData;
HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t*)tmp, (uint32_t)&huart->Instance->DR, Size);
/* 清除发送完成标志位 SR TC */
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC);
/* 进程解锁 */
__HAL_UNLOCK(huart);
/* 使能串口DMA发送 UART->CR3 Bit6 DMAT */
SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
函数实现功能如下:
HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
HAL_StatusTypeDef status = HAL_OK;
/* 检查参数 */
assert_param(IS_DMA_BUFFER_SIZE(DataLength));
/* 进程锁 */
__HAL_LOCK(hdma);
if(HAL_DMA_STATE_READY == hdma->State)
{
/* 改变DMA外设状态 */
hdma->State = HAL_DMA_STATE_BUSY;
/* 初始化错误代码 */
hdma->ErrorCode = HAL_DMA_ERROR_NONE;
/* 配置DMA源地址 目标地址 数据长度*/
DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
/* 使能DMA */
__HAL_DMA_ENABLE(hdma);
}
else
{
/* 进程解锁 */
__HAL_UNLOCK(hdma);
/* 返回错误状态 */
status = HAL_BUSY;
}
return status;
}
HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma);
函数主要功能实现:
HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma)
{
/*校验DMA地址和数据流*/
DMA_Base_Registers *regs = (DMA_Base_Registers *)hdma->StreamBaseAddress;
uint32_t tickstart = HAL_GetTick();
if(hdma->State != HAL_DMA_STATE_BUSY)
{
hdma->ErrorCode = HAL_DMA_ERROR_NO_XFER;
/* 设置进程锁 */
__HAL_UNLOCK(hdma);
return HAL_ERROR;
}
else
{
/* 失能所有转换中断 */
hdma->Instance->CR &= ~(DMA_IT_TC | DMA_IT_TE | DMA_IT_DME);
hdma->Instance->FCR &= ~(DMA_IT_FE);
if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL))
{
hdma->Instance->CR &= ~(DMA_IT_HT);
}
/* 失能DMA */
__HAL_DMA_DISABLE(hdma);
/* 检查DMA是否是有效的使能 */
while((hdma->Instance->CR & DMA_SxCR_EN) != RESET)
{
/* 检查时间是否溢出 */
if((HAL_GetTick() - tickstart ) > HAL_TIMEOUT_DMA_ABORT)
{
/* 更新错误代码 */
hdma->ErrorCode = HAL_DMA_ERROR_TIMEOUT;
/* 进程解锁 */
__HAL_UNLOCK(hdma);
/* 改变DMA状态 */
hdma->State = HAL_DMA_STATE_TIMEOUT;
return HAL_TIMEOUT;
}
}
/* 清除寄存器中所有正确偏移量的中断标志 */
regs->IFCR = 0x3FU << hdma->StreamIndex;
/* 进程解锁 */
__HAL_UNLOCK(hdma);
/* 改变DMA状态*/
hdma->State = HAL_DMA_STATE_READY;
}
return HAL_OK;
}
#define __HAL_DMA_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR |= DMA_SxCR_EN)
#define __HAL_DMA_DISABLE(__HANDLE__) ((__HANDLE__)->Instance->CR &= ~DMA_SxCR_EN)
#define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__)\
(((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA2_Stream3)? (DMA2->HISR & (__FLAG__)) :\
((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream7)? (DMA2->LISR & (__FLAG__)) :\
((uint32_t)((__HANDLE__)->Instance) > (uint32_t)DMA1_Stream3)? (DMA1->HISR & (__FLAG__)) : (DMA1->LISR & (__FLAG__)))
串口DMA数据收发程序如下:
DMA_HandleTypeDef UART1RxDMA_Handler; // 串口接收DMA句柄
DMA_HandleTypeDef UART1TxDMA_Handler; // 串口发送DMA句柄
UART_HandleTypeDef UART1_Handler; // UART 句柄
// 串口DMA初始化
void UART_DMA_Init(void)
{
__HAL_RCC_DMA2_CLK_ENABLE(); // 使能DMA2时钟
// 接收DMA配置
UART1RxDMA_Handler.Instance = DMA2_Stream5; // 数据流选择
UART1RxDMA_Handler.Init.Channel = DMA_CHANNEL_4; // 通道选择
UART1RxDMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设到存储器
UART1RxDMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; // FIFO不使能
UART1RxDMA_Handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // FIFO阈值
UART1RxDMA_Handler.Init.MemBurst = DMA_MBURST_SINGLE; // 内存突发传输配置
UART1RxDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器数据长度
UART1RxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 内存寄存器地址是否自增
UART1RxDMA_Handler.Init.Mode = DMA_CIRCULAR; // 循环模式
UART1RxDMA_Handler.Init.PeriphBurst = DMA_PBURST_SINGLE; // 外设突发传输配置
UART1RxDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据长度
UART1RxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址非自增
UART1RxDMA_Handler.Init.Priority = DMA_PRIORITY_MEDIUM; // 中等优先级
HAL_DMA_Init(&UART1RxDMA_Handler);
// 发送DMA配置
UART1TxDMA_Handler.Instance = DMA2_Stream7; // 数据流选择
UART1TxDMA_Handler.Init.Channel = DMA_CHANNEL_4; // 通道选择
UART1TxDMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH; // 外设到存储器
UART1TxDMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; // FIFO不使能
UART1TxDMA_Handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // FIFO阈值
UART1TxDMA_Handler.Init.MemBurst = DMA_MBURST_SINGLE; // 内存突发传输配置
UART1TxDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器数据长度
UART1TxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 内存寄存器地址是否自增
UART1TxDMA_Handler.Init.Mode = DMA_NORMAL; // 循环模式
UART1TxDMA_Handler.Init.PeriphBurst = DMA_PBURST_SINGLE; // 外设突发传输配置
UART1TxDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据长度
UART1TxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址非自增
UART1TxDMA_Handler.Init.Priority = DMA_PRIORITY_MEDIUM; // 中等优先级
HAL_DMA_Init(&UART1TxDMA_Handler);
}
// 初始化串口函数
void UART_Init(void)
{
UART1_Handler.Instance = USART1; // 串口1
UART1_Handler.Init.BaudRate = 115200; // 波特率
UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控
UART1_Handler.Init.Mode = UART_MODE_TX_RX; // 收发模式
UART1_Handler.Init.Parity = UART_PARITY_NONE; // 无奇偶校验
UART1_Handler.Init.StopBits = UART_STOPBITS_1; // 一个停止位
UART1_Handler.Init.WordLength = UART_WORDLENGTH_8B; // 字长为8位格式
UART1_Handler.hdmarx = &UART1RxDMA_Handler; // 传入接收DMA句柄
UART1_Handler.hdmatx = &UART1TxDMA_Handler; // 传入发送DMA句柄
HAL_UART_Init(&UART1_Handler); // 初始化串口
}
// 初始化低级硬件
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIO引脚
__HAL_RCC_USART1_CLK_ENABLE(); // 使能串口
GPIO_Initure.Mode = GPIO_MODE_AF_PP; // 复用推挽模式
GPIO_Initure.Pin = GPIO_PIN_9|GPIO_PIN_10; // PA9、PA10
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate = GPIO_AF7_USART1; // 复用为 USART1
HAL_GPIO_Init(GPIOA, &GPIO_Initure); // 初始化 PA9、PA10
}
}
// 串口DMA接收数据
void UART_DMA_Receive(uint8_t *pData, uint16_t Size)
{
HAL_UART_Receive_DMA(&UART1_Handler, pData, Size);
}
// 串口DMA发送数据
void UART_DMA_Transmit(uint8_t *pData, uint16_t Size)
{
HAL_UART_Transmit_DMA(&UART1_Handler, pData, Size);
}
uint8_t Rx_buffer[RXBUFFERSIZE]; // 串口接收数据缓冲区
// 获取状态标志位
void Get_Flag(void)
{
if(__HAL_DMA_GET_FLAG(&UART1RxDMA_Handler, DMA_FLAG_TCIF1_5)) // 如果数据接收完成
{
__HAL_DMA_ENABLE(&UART1TxDMA_Handler);
__HAL_DMA_CLEAR_FLAG(&UART1RxDMA_Handler,DMA_FLAG_TCIF1_5); // 清除状态标志位
}
if(__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7)) // 如果数据发送完成
{
__HAL_DMA_DISABLE(&UART1TxDMA_Handler);
__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TCIF3_7); // 清除状态标志位
}
}
主函数程序如下:
extern uint8_t Rx_buffer[RXBUFFERSIZE];
int main(void)
{
CLOCLK_Init(); // 配置系统时钟为168M
UART_DMA_Init(); // 串口DMA初始化
UART_Init(); // 串口初始化
UART_DMA_Receive(Rx_buffer, RXBUFFERSIZE); // 使用DMA接收数据
UART_DMA_Transmit(Rx_buffer, RXBUFFERSIZE); // 使用DMA发送数据
while(1)
{
Get_Flag(); // 获取状态标志位,控制DMA
}
}