1.整体初始化
初始化分两步,先初始化DMA,再初始化串口,串口初始化细节在测试代码中,此处主要介绍DMA相关初始化。
U1_RX_DMA_Config();
U1_TX_DMA_Config();
uart_init1(115200);
2.DMA初始化
DMA初始化又分为接收DMA初始化和发送DMA初始化,如上图所示
3.DMA配置
每个DMA通道控制不同的外设数据传输,详细请查看数据手册。本次使用的时USART1,串口发送和接收对应不同的数据流!!!(刚开始不懂,为这事花费了2小时。。。)USART1_TX对应的是DMA2的数据流7,USART1_RX对应的是DMA2的数据流5,如下图所示。
4.DMA接收
每次开始传输和传输完成都要开启和关闭,这也是挺重要的一点。
//开启DMA接收
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
//传输完成以后关闭串口DMA
HAL_UART_DMAStop(&UART1_Handler);
5.DMA发送
有两种方式,一种是用HAL库函数(HAL_UART_Transmit_DMA),另一种是寄存器,我偏向用寄存器。以下为使用寄存器的方式封装的发送函数,更方便理解底层。
void MYDMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//开启DMA传输
huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA发送
}
6.以下为测试代码
usart.h
#ifndef _USART_H
#define _USART_H
#include "sys.h"
#include "stdio.h"
#include <stdbool.h>
//////////////////////////////////////////////////////////////////////////////////
#define USART_REC_LEN 200
extern u8 USART1_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
extern UART_HandleTypeDef UART1_Handler; //UART句柄
extern u8 USART1_RX_CNT;
//如果想串口中断接收,请不要注释以下宏定义
void uart_init1(u32 bound1);
void Usart_Send(u8 *ch, u8 len);
#endif
usart.c
#include "usart.h"
#include "delay.h"
#include "dma.h"
#include <string.h>
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
u8 USART1_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
UART_HandleTypeDef UART1_Handler; //UART句柄
u8 USART1_RX_CNT = 0;
extern bool RX_OK;
UART_HandleTypeDef UART1_Handler; //UART句柄
void uart_init1(u32 bound1)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PA9 TX
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
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
GPIO_Initure.Pin=GPIO_PIN_10; //PA10 RX
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10
UART1_Handler.Instance=USART1; //USART1
UART1_Handler.Init.BaudRate=bound1; //波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
HAL_UART_Init(&UART1_Handler); //HAL_UART_Init()会使能UART1
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0); //抢占优先级2,子优先级0
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
//开启空闲中断
__HAL_UART_ENABLE_IT(&UART1_Handler,UART_IT_IDLE);
//开启DMA接收
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
}
void USART1_IRQHandler(void)
{
//空闲中断判断
if(__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_IDLE) != RESET) //判断是否为IDLE中断
{
__HAL_UART_CLEAR_IDLEFLAG(&UART1_Handler);
//传输完成以后关闭串口DMA
HAL_UART_DMAStop(&UART1_Handler);
//串口DMA接收的数据字节
USART1_RX_CNT = USART_REC_LEN - UART1RxDMA_Handler.Instance->NDTR;
//标记接收完成
RX_OK = 1;
}
//开启DMA接收
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
}
//常用串口发送函数
void Usart_Send(u8 *ch, u8 len)
{
u8 i = 0;
for(i = 0; i < len; i++)
{
USART1->DR = ch[i];
while((USART1->SR & 0X40) == 0); //等待发送结束
}
}
dma.h
#ifndef __DMA_H
#define __DMA_H
#include "sys.h"
extern DMA_HandleTypeDef UART1RxDMA_Handler; //DMA句柄
extern DMA_HandleTypeDef UART1TxDMA_Handler; //定义句柄
void U1_TX_DMA_Config(void );
void U1_RX_DMA_Config(void );
void DMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
#endif
dma.c
#include "dma.h"
#include "usart.h"
DMA_HandleTypeDef UART1RxDMA_Handler; //定义句柄
DMA_HandleTypeDef UART1TxDMA_Handler; //定义句柄
u8 USART1_DMATX_FLAG=2;
void U1_RX_DMA_Config(void )
{
__HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能
__HAL_LINKDMA(&UART1_Handler, hdmarx, UART1RxDMA_Handler);//将rx和dma连接
//Rx DMA配置
UART1RxDMA_Handler.Instance = DMA2_Stream5;
UART1RxDMA_Handler.Init.Channel = DMA_CHANNEL_4;
UART1RxDMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY;
UART1RxDMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
UART1RxDMA_Handler.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
UART1RxDMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
UART1RxDMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
UART1RxDMA_Handler.Init.Mode = DMA_CIRCULAR;
UART1RxDMA_Handler.Init.Priority = DMA_PRIORITY_MEDIUM;
UART1RxDMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_DeInit(&UART1RxDMA_Handler);
HAL_DMA_Init(&UART1RxDMA_Handler);
}
void U1_TX_DMA_Config(void )
{
__HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能
__HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //将DMA与USART1联系起来(发送DMA)
//Tx DMA配置
UART1TxDMA_Handler.Instance=DMA2_Stream7; //数据流选择
UART1TxDMA_Handler.Init.Channel=DMA_CHANNEL_4; //通道选择
UART1TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设
UART1TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
UART1TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式
UART1TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度:8位
UART1TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位
UART1TxDMA_Handler.Init.Mode=DMA_NORMAL; //外设普通模式
UART1TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
UART1TxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE;
UART1TxDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
UART1TxDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //存储器突发单次传输
UART1TxDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设突发单次传输
HAL_DMA_DeInit(&UART1TxDMA_Handler);
HAL_DMA_Init(&UART1TxDMA_Handler);
}
void DMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//开启DMA传输
huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA发送
}
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "dma.h"
#include <string.h>
#include <stdbool.h>
bool RX_OK = false;
//const u8 TEXT_TO_SEND[] = {"ALIENTEK Apollo STM32F4 DMA 串口实验"};
u8 test_flag = 0;
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(360, 25, 2, 8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
U1_RX_DMA_Config();
U1_TX_DMA_Config();
uart_init1(115200);
while(1)
{
if(RX_OK)
{
RX_OK = 0;
printf("\r\n常规串口输出:");
Usart_Send(USART1_RX_BUF, USART1_RX_CNT);//常规串口输出
//DMA串口输出
// printf("\r\nDMA_HAL库方式串口输出:");
// HAL_UART_Transmit_DMA(&UART1_Handler,(uint8_t*)USART1_RX_BUF,USART1_RX_CNT);
// DMA_USART_Transmit(&UART1_Handler, (uint8_t*)TEXT_TO_SEND, sizeof(TEXT_TO_SEND));
printf("\r\nDMA_寄存器方式串口输出:");
DMA_USART_Transmit(&UART1_Handler, (uint8_t*)USART1_RX_BUF, USART1_RX_CNT);
while(1)
{
if(__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7)) //等待DMA2_Steam7传输完成
{
__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
HAL_UART_DMAStop(&UART1_Handler); //传输完成以后关闭串口DMA
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);//开启DMA接收
break;
}
}
}
}
}