1.整体初始化

初始化分两步,先初始化DMA,再初始化串口,串口初始化细节在测试代码中,此处主要介绍DMA相关初始化。

U1_RX_DMA_Config();
	U1_TX_DMA_Config();
	uart_init1(115200);
  • 1.
  • 2.
  • 3.

2.DMA初始化

DMA初始化又分为接收DMA初始化和发送DMA初始化,如上图所示

3.DMA配置

每个DMA通道控制不同的外设数据传输,详细请查看数据手册。本次使用的时USART1,串口发送和接收对应不同的数据流!!!(刚开始不懂,为这事花费了2小时。。。)USART1_TX对应的是DMA2的数据流7,USART1_RX对应的是DMA2的数据流5,如下图所示。

stm32f429串口中断接收和发送,使用DMA加空闲中断的方式_串口

4.DMA接收

每次开始传输和传输完成都要开启和关闭,这也是挺重要的一点。

//开启DMA接收
HAL_UART_Receive_DMA(&UART1_Handler, USART1_RX_BUF, USART_REC_LEN);
//传输完成以后关闭串口DMA
HAL_UART_DMAStop(&UART1_Handler);
  • 1.
  • 2.
  • 3.
  • 4.

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发送
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

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); //等待发送结束
		}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

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发送								
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.

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;
                }

            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.