一、DMA介绍(了解)
二、DMA结构框图介绍(熟悉)
三、DMA相关寄存器介绍(熟悉)
四、DMA相关HAL库驱动介绍(掌握)
五、DMA配置步骤(掌握)
六、编程实战(掌握)
dma.c
#include "./BSP/DMA/dma.h"
#include "./SYSTEM/delay/delay.h"
DMA_HandleTypeDef g_dma_handle; /* DMA句柄 */
extern UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
/**
* @brief 串口TX DMA初始化函数
* @note 这里的传输形式是固定的, 这点要根据不同的情况来修改
* 从存储器 -> 外设模式/8位数据宽度/存储器增量模式
*
* @param dmax_chy : DMA的通道, DMA1_Channel1 ~ DMA1_Channel7, DMA2_Channel1 ~ DMA2_Channel5
* 某个外设对应哪个DMA, 哪个通道, 请参考<<STM32中文参考手册 V10>> 10.3.7节
* 必须设置正确的DMA及通道, 才能正常使用!
* @retval 无
*/
void dma_init(DMA_Channel_TypeDef* DMAx_CHx)
{
if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7) /* 大于DMA1_Channel7, 则为DMA2的通道了 */
{
__HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */
}
else
{
__HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */
}
__HAL_LINKDMA(&g_uart1_handle, hdmatx, g_dma_handle); /* 将DMA与USART1联系起来(发送DMA) */
/* Tx DMA配置 */
g_dma_handle.Instance = DMAx_CHx; /* USART1_TX使用的DMA通道为: DMA1_Channel4 */
g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH; /* DIR = 1 , 存储器到外设模式 */
g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设非增量模式 */
g_dma_handle.Init.MemInc = DMA_MINC_ENABLE; /* 存储器增量模式 */
g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据长度:8位 */
g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; /* 存储器数据长度:8位 */
g_dma_handle.Init.Mode = DMA_NORMAL; /* DMA模式:正常模式 */
g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM; /* 中等优先级 */
HAL_DMA_Init(&g_dma_handle);
}
dma.h
#ifndef __DMA_H
#define __DMA_H
#include "./SYSTEM/sys/sys.h"
void dma_init(DMA_Channel_TypeDef* DMAx_CHx);
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/DMA/dma.h"
const uint8_t TEXT_TO_SEND[] = {"正点原子 STM32 DMA 串口实验"}; /* 要循环发送的字符串 */
#define SEND_BUF_SIZE (sizeof(TEXT_TO_SEND) + 2) * 200 /* 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍. */
uint8_t g_sendbuf[SEND_BUF_SIZE]; /* 发送数据缓冲区 */
extern DMA_HandleTypeDef g_dma_handle; /* DMA句柄 */
extern UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
int main(void)
{
uint8_t key = 0;
uint16_t i, k;
uint16_t len;
uint8_t mask = 0;
float pro = 0; /* 进度 */
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
dma_init(DMA1_Channel4); /* 初始化串口1 TX DMA */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);
len = sizeof(TEXT_TO_SEND);
k = 0;
for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
{
if (k >= len) /* 入换行符 */
{
if (mask)
{
g_sendbuf[i] = 0x0a;
k = 0;
}
else
{
g_sendbuf[i] = 0x0d;
mask++;
}
}
else /* 复制TEXT_TO_SEND语句 */
{
mask = 0;
g_sendbuf[i] = TEXT_TO_SEND[k];
k++;
}
}
i = 0;
while (1)
{
key = key_scan(0);
if (key == KEY0_PRES) /* KEY0按下 */
{
printf("\r\nDMA DATA:\r\n");
lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
lcd_show_string(30, 150, 200, 16, 16, " %", BLUE); /* 显示百分号 */
HAL_UART_Transmit_DMA(&g_uart1_handle, g_sendbuf, SEND_BUF_SIZE);
/* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯
* 实际应用中,传输数据期间,可以执行另外的任务
*/
while (1)
{
if ( __HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TC4)) /* 等待 DMA1_Channel4 传输完成 */
{
__HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FLAG_TC4);
HAL_UART_DMAStop(&g_uart1_handle); /* 传输完成以后关闭串口DMA */
break;
}
pro = __HAL_DMA_GET_COUNTER(&g_dma_handle); /* 得到当前还剩余多少个数据 */
len = SEND_BUF_SIZE; /* 总长度 */
pro = 1 - (pro / len); /* 得到百分比 */
pro *= 100; /* 扩大100倍 */
lcd_show_num(30, 150, pro, 3, 16, BLUE);
}
lcd_show_num(30, 150, 100, 3, 16, BLUE); /* 显示100% */
lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE); /* 提示传送完成 */
}
i++;
delay_ms(10);
if (i == 20)
{
LED0_TOGGLE(); /* LED0闪烁,提示系统正在运行 */
i = 0;
}
}
}