目录
1 DMA介绍
1.1 什么是DMA?
令人头秃的描述:
DMA(Direct Memory Access,直接存储器访问)提供在外设与内存、存储器和存储器之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来说就无法使用。
简单描述:
就是一个数据搬运工!!
1.2 DMA的意义
代替 CPU 搬运数据,为 CPU 减负。
- 数据搬运的工作比较耗时间;
- 数据搬运工作时效要求高(有数据来就要搬走);
- 没啥技术含量(CPU 节约出来的时间可以处理更重要的事);
1.3 搬运什么数据?
存储器、外设
这里的外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,而这里的存储器包括自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的。
三种搬运方式:
- 存储器→存储器(例如:复制某特别大的数据buf)
- 存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
- 外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
存储器→存储器

存储器→外设

外设→存储器

2 DMA框图

3 DMA控制器
STM32F103 有 2 个 DMA 控制器,DMA1 有 7 个通道,DMA 2 有 5 个通道。
一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
STM32F103C8T6 只有 DMA1!
DMA1有7个通道:

DMA2 有 5 个通道:

4 DMA优先级管理
优先级管理采用软件+硬件:
- 软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级
最高级>高级>中级>低级 - 硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
比如:如果软件优先级相同,通道2优先于通道4

5 DMA传输方式
- DMA_Mode_Normal(正常模式)
一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次 - DMA_Mode_Circular(循环传输模式)
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式
6 指针递增模式
外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。

7 DMA数据对齐方式



8 DMA寄存器及库函数介绍
__HAL_RCC_DMA1_CLK_ENABLE(…)
HAL_DMA_Init(…)
HAL_DMA_Start(…)
__HAL_LINKDMA(…)
HAL_UART_Transmit_DMA(…)
HAL_UART_Receive_DMA(…)
__HAL_DMA_GET_FLAG(…)
__HAL_DMA_ENABLE(…)
__HAL_DMA_DISABLE(…)
小实验1:DMA内存到内存数据搬运
实验目的
使用 DMA 将一个大数组的数据搬运到另一个位置。
流程

代码
main.c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//DMA初始化
dma_init();
printf("打印测试:hello world\r\n");
//数据转运
dma_transmit();
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
dma.c
#include "dma.h"
#include <stdio.h>
#define BUF_SIZE 16
uint32_t src_buf[BUF_SIZE] = {
0x00000000,0x11111111,0x22222222,0x33333333,
0x44444444,0x55555555,0x66666666,0x77777777,
0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
};
uint32_t dst_buf[BUF_SIZE] = {0};
DMA_HandleTypeDef dma_handle = {0};
//DMA初始化
void dma_init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel1;
dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
//内存相关配置
dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_ENABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_NORMAL;
//初始化函数
HAL_DMA_Init(&dma_handle);
}
//数据转运
void dma_transmit(void)
{
//开始DMA转运
HAL_DMA_Start(&dma_handle, (uint32_t)src_buf, (uint32_t)dst_buf, BUF_SIZE*sizeof(uint32_t));
//查看数据转运标志位
while(__HAL_DMA_GET_FLAG(&dma_handle, DMA_FLAG_TC1) == RESET);
int i = 0;
//打印数据
for(i = 0; i < BUF_SIZE; i++){
printf("buf[%d] = %x\r\n",i,dst_buf[i]);
}
}
dma.h
#ifndef __DMA_H__
#define __DMA_H__
#include "sys.h"
//DMA初始化
void dma_init(void);
//数据转运
void dma_transmit(void);
#endif
小实验2:DMA内存到外设数据搬运
实验目的
使用 DMA 将一个大数组的数据通过串口 1 发送。
流程

代码
main.c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"
extern UART_HandleTypeDef uart1_handle;
uint8_t send_buf[1000] = {0};
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//DMA初始化
dma_init();
printf("打印测试:hello world\r\n");
int i = 0;
for(i = 0;i < 1000;i ++){
send_buf[i] = 'A';
}
//串口DMA发送
HAL_UART_Transmit_DMA(&uart1_handle,send_buf,1000);
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
dma.c
#include "dma.h"
#include <stdio.h>
extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef dma_handle = {0};
//DMA初始化
void dma_init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel4; //DMA1通道4
dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH; //从内存到外设
//内存相关配置
dma_handle.Init .MemDataAlignment = DMA_MDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_NORMAL;
//初始化函数
HAL_DMA_Init(&dma_handle);
//链接串口和DMA
__HAL_LINKDMA(&uart1_handle, hdmatx, dma_handle);
}
dma.h
#ifndef __DMA_H__
#define __DMA_H__
#include "sys.h"
//DMA初始化
void dma_init(void);
#endif
小实验3:DMA外设到内存数据搬运
实验目的
使用 DMA 接收串口 1 数据。
流程

代码
main.c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dma.h"
extern UART_HandleTypeDef uart1_handle;
uint8_t send_buf[1000] = {0};
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//DMA初始化
dma_init();
printf("打印测试:hello world\r\n");
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
dma.c
#include "dma.h"
#include <stdio.h>
#include "uart1.h"
extern UART_HandleTypeDef uart1_handle;
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
DMA_HandleTypeDef dma_handle = {0};
//DMA初始化
void dma_init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel5; //DMA1通道5
dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //从外设到内存
//内存相关配置
dma_handle.Init .MemDataAlignment = DMA_MDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_NORMAL;
//初始化函数
HAL_DMA_Init(&dma_handle);
//链接串口和DMA
__HAL_LINKDMA(&uart1_handle, hdmarx, dma_handle);
//使能串口DMA接收
HAL_UART_Receive_DMA(&uart1_handle, uart1_rx_buf, UART1_RX_BUF_SIZE);
}
dma.h
#ifndef __DMA_H__
#define __DMA_H__
#include "sys.h"
//DMA初始化
void dma_init(void);
#endif
8330

被折叠的 条评论
为什么被折叠?



