赞
踩
直接存储器访问
将数据从一个地址空间复制到另一个地址空间
DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和回复现场过程
而是通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高
其实简言之:就是为CPU减负
内存-》外设
外设-》内存
内存-》内存
①DMA请求:DMA传输数据,先向DMA控制器发送请求
多个请求通过逻辑或输入到DMA控制器,只能由一个请求有效
②DMA通道:DMA1有七个通道,DMA2有5个通道
③DMA优先级:优先级管理分软件和硬件,由仲裁器管理
软件阶段 在DMA_CCRx寄存器中设置,有四个等级 最高 高 中 低
硬件阶段 较低编号的通道有更高的优先级
DMA1
该寄存器用于配置DMA,数据传输方向 通道优先级 外设/存储器数据宽度
外设/存储器增量模式 循环模式 中断使能 通道开启
该寄存器主要用于查询DMA是否传输完成
该寄存器主要用于清除传输完成标志位
该寄存器主要用于写入传输长度
这里注意在非循环模式情况下传输完成,要开启新的DMA传输,需要先关闭DMA通道,重新写入传输数量再开启通道
该寄存器用于存放外设地址
该寄存器用于存放存储器地址
驱动函数 | 关联寄存器 | 功能描述 |
__HAL_RCC_DMAx_CLK_ENABLE() | RCC_AHBENR | 使能DMAx时钟 |
HAL_DMA_Init() | DMA_CCR | 初始化DMA |
HAL_DMA_Start_IT() | DMA_CCR/CPAR/CMAR/CNDTR | 开始DMA传输 |
__HAL_LINKDMA() | 用来连接DMA和外设句柄 | |
HAL_UART_Transmit_DMA() | CCR/CPAR/CMAR/CNDTR/ | 使能DMA发送,启动传输 |
__HAL_DMA_GET_FLAG() | DMA_ISR | 查询DMA传输通道的状态 |
__HAL_DMA_ENABLE() | DMA_CCR(EN) | 使能DMA外设 |
__HAL_DMA_DISABLE() | DMA_CCR(EN) | 失能DMA外设 |
- //相关结构体
- typedef struct
- {
- DMA_Channel_TypeDef *Instance
- DMA_InitTypeDef Init
- ...
- }DMA_HandleTypeDef;
-
- typedef struct
- {
- uint32_t Direction /* DMA传输方向 */
- uint32_t PeriphInc /* 外设地址(非)增量 */
- uint32_t MemInc /* 存储器地址(非)增量*/
- uint32_t PeriphDataAlignment /* 外设数据宽度 */
- uint32_t MemDataAlignment /* 存储器数据宽度 */
- uint32_t Mode /* 操作模式 */
- uint32_t Priority /* DMA通道优先级 */
- }DMA_InitTypeDef;
- //DMA.c
- #include "DMA.h"
-
- DMA_HandleTypeDef DMA_Handler; // DMA初始化句柄
-
- uint8_t old_buf[10] = {0x0a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
- uint8_t new_buf[10] = {0};
- uint8_t key;
-
- void DMA_Init()
- {
- __HAL_RCC_DMA1_CLK_ENABLE(); // 使能DMA1外设时钟
-
- DMA_Handler.Instance = DMA1_Channel1; // DMA1,通道1
- DMA_Handler.Init.Direction = DMA_MEMORY_TO_MEMORY; // 存储器到存储器,这里是数组到数组
- DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器内存:字节
- DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 存储器地址增量
- DMA_Handler.Init.Mode = DMA_NORMAL; // 不开启循环模式
- DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存(存储器):字节,这里也是数组
- DMA_Handler.Init.PeriphInc = DMA_PINC_ENABLE; // 外设地址增量
- DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH; // 优先级高
- HAL_DMA_Init(&DMA_Handler); // DMA初始化
- HAL_DMA_Start(&DMA_Handler, (uint32_t)old_buf, (uint32_t)new_buf, 0);//DMA开启传输
- }
-
- void DMA_Transmit(uint16_t cndtr)
- {
- __HAL_DMA_DISABLE(&DMA_Handler);//传输完后失能
-
- // DMA1_Channel1->CNDTR = cndtr;
- DMA_Handler.Instance->CNDTR = cndtr;//写入传输数量
-
- __HAL_DMA_ENABLE(&DMA_Handler);//启动传输
- }
-
- void DMA_Transmit_Get()
- {
- key = KEY_Scan(0); /* 得到键值 */
- if (key == KEY0_PRES)
- {
- memset(new_buf, 0, 10);//将数组清0
- DMA_Transmit(10);//传输10个数量
- while (1)
- {
- if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC1))//如果获取到传输完成标志位
- {
- __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC1);//清除传输完成标志位
- printf("传输完成 \r\n");//串口打印传输完成
- }
- }
- }
- }
- //DMA.h
- #ifndef __DMA_H
- #define __DMA_H
-
- #include "sys.h"
- #include "KEY.h"
- #include <stdio.h>
- #include <string.h>
- void DMA_Init();
- void DMA_Transmit(uint16_t cndtr);
- void DMA_Transmit_Get();
-
- #endif // !
- //main.c
- //本例程是按下按键KEY0 将一个数据转移到另一个数组,并在串口提示传输完成
- //使用的是正点原子F1精英开发板
- //我这里调用了很多初始化,但其实如果不考虑其他的
- //这里必要的只有
- // HAL_Init(); // 初始化HAL库
- // Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
- // uart_init(115200); // 初始化串口
- // DMA_Init();
-
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "usmart.h"
- #include "KEY.h"
- #include "led.h"
- #include "lcd.h"
- #include "DMA.h"
-
- int main(void)
- {
- HAL_Init(); // 初始化HAL库
- Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
- delay_init(72); // 初始化延时函数
- uart_init(115200); // 初始化串口
- LED_Init(); // 初始化LED
- LCD_Init(); // 初始化LCD FSMC接口
- KEY_Init(); // 初始化按键
- usmart_dev.init(84); // 初始化USMART
- POINT_COLOR = RED; // 画笔颜色:红色
- LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
- DMA_Init();
-
- while (1)
- {
- DMA_Transmit_Get();
- LED0 = 1;
- }
- }
- //DMA.c
- #include "DMA.h"
-
- DMA_HandleTypeDef DMA_Handler; // DMA初始化句柄
- extern UART_HandleTypeDef UART1_Handler; // UART1初始化句柄
-
- const uint8_t SEND[] = {"YMZ yy爱你"}; /* 要循环发送的字符串 */
- uint8_t SEND_buf[SEND_BUF_SIZE]; /* 发送数据缓冲区 */
-
- 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时钟使能 */
- }
- DMA_Handler.Instance = DMAx_CHx;
- DMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH; // 从存储器到外设
- DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器内存:字节
- DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 存储器地址增量
- DMA_Handler.Init.Mode = DMA_NORMAL; // 不循环模式
- DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存:字节
- DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不增量
- DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH; // 高优先级
- HAL_DMA_Init(&DMA_Handler); // DMA初始化
-
- __HAL_LINKDMA(&UART1_Handler, hdmatx, DMA_Handler); // 将DMA和UART1建立联系
- }
-
- uint16_t len= sizeof(SEND);// 字符串长度
-
- void SEND_Transmit_buf()
- {
- uint16_t i = 0; // 得到项
- uint16_t k = 0; // 传输项
- uint8_t mask = 0; // 换行标志
- for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
- {
- if (k >= len) // 入换行符,先判断回车,在判断换行
- {
- if (mask)
- {
- SEND_buf[i] = 0x0a; // 换行符
- k = 0; // 下一行传输
- }
- else
- {
- SEND_buf[i] = 0x0d; // 回车
- mask++; // 换行标志
- }
- }
- else /* 复制SEND语句 */
- {
- mask = 0;
- SEND_buf[i] = SEND[k]; // 将数据赋予得到项
- k++; // 传输下一个数据
- }
- }
- }
-
-
- float pro = 0; /* 进度 */
- uint8_t key = 0;
- void DMA_SHOW()
- {
- key = KEY_Scan(0);
- if (key == KEY0_PRES) /* KEY0按下 */
- {
- printf("\r\nDMA DATA:\r\n");
- LCD_ShowString(30, 130, 200, 16, 16, "Start Transimit....");
- LCD_ShowString(30, 150, 200, 16, 16, " %"); /* 显示百分号 */
-
- HAL_UART_Transmit_DMA(&UART1_Handler, SEND_buf, SEND_BUF_SIZE);
- /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯
- * 实际应用中,传输数据期间,可以执行另外的任务
- */
- while (1)
- {
- if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC4)) /* 等待 DMA1_Channel4 传输完成 */
- {
- __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC4); // 清除传输完成标志位
- HAL_UART_DMAStop(&UART1_Handler); /* 传输完成以后关闭串口DMA */
- break;
- }
-
- pro = DMA1_Channel4->CNDTR; /* 得到当前还剩余多少个数据 */
- len = SEND_BUF_SIZE; /* 总长度 */
- pro = 1 - (pro / len); /* 得到百分比 */
- pro *= 100; /* 扩大100倍 */
- LCD_ShowNum(30, 150, pro, 3, 16);
- }
- LCD_ShowNum(30, 150, 100, 3, 16); /* 显示100% */
- LCD_ShowString(30, 130, 200, 16, 16, "Transimit Finished!"); /* 提示传送完成 */
- }
- }
- //DMA.h
- #ifndef __DMA_H
- #define __DMA_H
-
- #include "sys.h"
- #include "usart.h"
- #include "KEY.h"
- #include "lcd.h"
- #include <stdio.h>
- #define SEND_BUF_SIZE (sizeof(SEND) + 2) * 200 // 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍.
- // 这里加了2是有一个\r\n
-
- void DMA_Init(DMA_Channel_TypeDef *DMAx_CHx);
- void SEND_Transmit_buf();
- void DMA_SHOW();
-
- #endif // !
- //main.c
- //本例程是按下按键KEY0 将串口数据传输到DMA,然后打印到串口上
- //使用的是正点原子F1精英开发板
-
- #include "sys.h"
- #include "delay.h"
- #include "usart.h"
- #include "usmart.h"
- #include "KEY.h"
- #include "led.h"
- #include "lcd.h"
- #include "DMA.h"
-
- int main(void)
- {
- HAL_Init(); // 初始化HAL库
- Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
- delay_init(72); // 初始化延时函数
- uart_init(115200); // 初始化串口
- LED_Init(); // 初始化LED
- LCD_Init(); // 初始化LCD FSMC接口
- KEY_Init(); // 初始化按键
- usmart_dev.init(84); // 初始化USMART
- POINT_COLOR = RED; // 画笔颜色:红色
- LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
-
- DMA_Init(DMA1_Channel4);
- SEND_Transmit_buf();
- while (1)
- {
- DMA_SHOW();
- LED0 = 1;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。