当前位置:   article > 正文

STM32hal库学习(F1)-DMA_stm32 hal库dma

stm32 hal库dma

DMA简介

DMA:

直接存储器访问

DMA传输:

将数据从一个地址空间复制到另一个地址空间

DMA作用:

DMA传输无需CPU直接控制传输,也没有中断处理方式那样保留现场和回复现场过程

而是通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高

其实简言之:就是为CPU减负

DMA路径

内存-》外设

外设-》内存

内存-》内存

DMA框图

结构框图

①DMA请求:DMA传输数据,先向DMA控制器发送请求

                       多个请求通过逻辑或输入到DMA控制器,只能由一个请求有效

②DMA通道:DMA1有七个通道,DMA2有5个通道

③DMA优先级:优先级管理分软件和硬件,由仲裁器管理

                          软件阶段 在DMA_CCRx寄存器中设置,有四个等级 最高 高 中 低

                          硬件阶段 较低编号的通道有更高的优先级 

DMA处理过程

DMA通道映射框图

DMA1

DMA相关寄存器

DMA_CCRx (DMA通道x配置寄存器)

该寄存器用于配置DMA,数据传输方向 通道优先级 外设/存储器数据宽度

                                      外设/存储器增量模式 循环模式 中断使能 通道开启

DMA_ISR (DMA中断状态寄存器)

该寄存器主要用于查询DMA是否传输完成

DMA_IFCR(DMA中断标志清除寄存器)

该寄存器主要用于清除传输完成标志位

DMA_CNDTRx(DMA通道x传输数量寄存器)

该寄存器主要用于写入传输长度

这里注意在非循环模式情况下传输完成,要开启新的DMA传输,需要先关闭DMA通道,重新写入传输数量再开启通道

DMA_CPARx(DMA通道x外设地址寄存器)

该寄存器用于存放外设地址

DMA_CMARx(DMA通道x存储器地址寄存器)

该寄存器用于存放存储器地址

HAL库函数介绍

驱动函数

关联寄存器

功能描述

__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外设

  1. //相关结构体
  2. typedef struct
  3. {
  4. DMA_Channel_TypeDef *Instance
  5. DMA_InitTypeDef Init
  6. ...
  7. }DMA_HandleTypeDef;
  8. typedef struct
  9. {
  10. uint32_t Direction /* DMA传输方向 */
  11. uint32_t PeriphInc /* 外设地址(非)增量 */
  12. uint32_t MemInc /* 存储器地址(非)增量*/
  13. uint32_t PeriphDataAlignment /* 外设数据宽度 */
  14. uint32_t MemDataAlignment /* 存储器数据宽度 */
  15. uint32_t Mode /* 操作模式 */
  16. uint32_t Priority /* DMA通道优先级 */
  17. }DMA_InitTypeDef;

配置步骤

  1. //DMA.c
  2. #include "DMA.h"
  3. DMA_HandleTypeDef DMA_Handler; // DMA初始化句柄
  4. uint8_t old_buf[10] = {0x0a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
  5. uint8_t new_buf[10] = {0};
  6. uint8_t key;
  7. void DMA_Init()
  8. {
  9. __HAL_RCC_DMA1_CLK_ENABLE(); // 使能DMA1外设时钟
  10. DMA_Handler.Instance = DMA1_Channel1; // DMA1,通道1
  11. DMA_Handler.Init.Direction = DMA_MEMORY_TO_MEMORY; // 存储器到存储器,这里是数组到数组
  12. DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器内存:字节
  13. DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 存储器地址增量
  14. DMA_Handler.Init.Mode = DMA_NORMAL; // 不开启循环模式
  15. DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存(存储器):字节,这里也是数组
  16. DMA_Handler.Init.PeriphInc = DMA_PINC_ENABLE; // 外设地址增量
  17. DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH; // 优先级高
  18. HAL_DMA_Init(&DMA_Handler); // DMA初始化
  19. HAL_DMA_Start(&DMA_Handler, (uint32_t)old_buf, (uint32_t)new_buf, 0);//DMA开启传输
  20. }
  21. void DMA_Transmit(uint16_t cndtr)
  22. {
  23. __HAL_DMA_DISABLE(&DMA_Handler);//传输完后失能
  24. // DMA1_Channel1->CNDTR = cndtr;
  25. DMA_Handler.Instance->CNDTR = cndtr;//写入传输数量
  26. __HAL_DMA_ENABLE(&DMA_Handler);//启动传输
  27. }
  28. void DMA_Transmit_Get()
  29. {
  30. key = KEY_Scan(0); /* 得到键值 */
  31. if (key == KEY0_PRES)
  32. {
  33. memset(new_buf, 0, 10);//将数组清0
  34. DMA_Transmit(10);//传输10个数量
  35. while (1)
  36. {
  37. if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC1))//如果获取到传输完成标志位
  38. {
  39. __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC1);//清除传输完成标志位
  40. printf("传输完成 \r\n");//串口打印传输完成
  41. }
  42. }
  43. }
  44. }
  1. //DMA.h
  2. #ifndef __DMA_H
  3. #define __DMA_H
  4. #include "sys.h"
  5. #include "KEY.h"
  6. #include <stdio.h>
  7. #include <string.h>
  8. void DMA_Init();
  9. void DMA_Transmit(uint16_t cndtr);
  10. void DMA_Transmit_Get();
  11. #endif // !
  1. //main.c
  2. //本例程是按下按键KEY0 将一个数据转移到另一个数组,并在串口提示传输完成
  3. //使用的是正点原子F1精英开发板
  4. //我这里调用了很多初始化,但其实如果不考虑其他的
  5. //这里必要的只有
  6. // HAL_Init(); // 初始化HAL库
  7. // Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
  8. // uart_init(115200); // 初始化串口
  9. // DMA_Init();
  10. #include "sys.h"
  11. #include "delay.h"
  12. #include "usart.h"
  13. #include "usmart.h"
  14. #include "KEY.h"
  15. #include "led.h"
  16. #include "lcd.h"
  17. #include "DMA.h"
  18. int main(void)
  19. {
  20. HAL_Init(); // 初始化HAL库
  21. Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
  22. delay_init(72); // 初始化延时函数
  23. uart_init(115200); // 初始化串口
  24. LED_Init(); // 初始化LED
  25. LCD_Init(); // 初始化LCD FSMC接口
  26. KEY_Init(); // 初始化按键
  27. usmart_dev.init(84); // 初始化USMART
  28. POINT_COLOR = RED; // 画笔颜色:红色
  29. LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
  30. DMA_Init();
  31. while (1)
  32. {
  33. DMA_Transmit_Get();
  34. LED0 = 1;
  35. }
  36. }
  1. //DMA.c
  2. #include "DMA.h"
  3. DMA_HandleTypeDef DMA_Handler; // DMA初始化句柄
  4. extern UART_HandleTypeDef UART1_Handler; // UART1初始化句柄
  5. const uint8_t SEND[] = {"YMZ yy爱你"}; /* 要循环发送的字符串 */
  6. uint8_t SEND_buf[SEND_BUF_SIZE]; /* 发送数据缓冲区 */
  7. void DMA_Init(DMA_Channel_TypeDef *DMAx_CHx)
  8. {
  9. if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7) /* 大于DMA1_Channel7, 则为DMA2的通道了 */
  10. {
  11. __HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */
  12. }
  13. else
  14. {
  15. __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */
  16. }
  17. DMA_Handler.Instance = DMAx_CHx;
  18. DMA_Handler.Init.Direction = DMA_MEMORY_TO_PERIPH; // 从存储器到外设
  19. DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 存储器内存:字节
  20. DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; // 存储器地址增量
  21. DMA_Handler.Init.Mode = DMA_NORMAL; // 不循环模式
  22. DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设内存:字节
  23. DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不增量
  24. DMA_Handler.Init.Priority = DMA_PRIORITY_HIGH; // 高优先级
  25. HAL_DMA_Init(&DMA_Handler); // DMA初始化
  26. __HAL_LINKDMA(&UART1_Handler, hdmatx, DMA_Handler); // 将DMA和UART1建立联系
  27. }
  28. uint16_t len= sizeof(SEND);// 字符串长度
  29. void SEND_Transmit_buf()
  30. {
  31. uint16_t i = 0; // 得到项
  32. uint16_t k = 0; // 传输项
  33. uint8_t mask = 0; // 换行标志
  34. for (i = 0; i < SEND_BUF_SIZE; i++) /* 填充ASCII字符集数据 */
  35. {
  36. if (k >= len) // 入换行符,先判断回车,在判断换行
  37. {
  38. if (mask)
  39. {
  40. SEND_buf[i] = 0x0a; // 换行符
  41. k = 0; // 下一行传输
  42. }
  43. else
  44. {
  45. SEND_buf[i] = 0x0d; // 回车
  46. mask++; // 换行标志
  47. }
  48. }
  49. else /* 复制SEND语句 */
  50. {
  51. mask = 0;
  52. SEND_buf[i] = SEND[k]; // 将数据赋予得到项
  53. k++; // 传输下一个数据
  54. }
  55. }
  56. }
  57. float pro = 0; /* 进度 */
  58. uint8_t key = 0;
  59. void DMA_SHOW()
  60. {
  61. key = KEY_Scan(0);
  62. if (key == KEY0_PRES) /* KEY0按下 */
  63. {
  64. printf("\r\nDMA DATA:\r\n");
  65. LCD_ShowString(30, 130, 200, 16, 16, "Start Transimit....");
  66. LCD_ShowString(30, 150, 200, 16, 16, " %"); /* 显示百分号 */
  67. HAL_UART_Transmit_DMA(&UART1_Handler, SEND_buf, SEND_BUF_SIZE);
  68. /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯
  69. * 实际应用中,传输数据期间,可以执行另外的任务
  70. */
  71. while (1)
  72. {
  73. if (__HAL_DMA_GET_FLAG(&DMA_Handler, DMA_FLAG_TC4)) /* 等待 DMA1_Channel4 传输完成 */
  74. {
  75. __HAL_DMA_CLEAR_FLAG(&DMA_Handler, DMA_FLAG_TC4); // 清除传输完成标志位
  76. HAL_UART_DMAStop(&UART1_Handler); /* 传输完成以后关闭串口DMA */
  77. break;
  78. }
  79. pro = DMA1_Channel4->CNDTR; /* 得到当前还剩余多少个数据 */
  80. len = SEND_BUF_SIZE; /* 总长度 */
  81. pro = 1 - (pro / len); /* 得到百分比 */
  82. pro *= 100; /* 扩大100倍 */
  83. LCD_ShowNum(30, 150, pro, 3, 16);
  84. }
  85. LCD_ShowNum(30, 150, 100, 3, 16); /* 显示100% */
  86. LCD_ShowString(30, 130, 200, 16, 16, "Transimit Finished!"); /* 提示传送完成 */
  87. }
  88. }
  1. //DMA.h
  2. #ifndef __DMA_H
  3. #define __DMA_H
  4. #include "sys.h"
  5. #include "usart.h"
  6. #include "KEY.h"
  7. #include "lcd.h"
  8. #include <stdio.h>
  9. #define SEND_BUF_SIZE (sizeof(SEND) + 2) * 200 // 发送数据长度, 等于sizeof(TEXT_TO_SEND) + 2的200倍.
  10. // 这里加了2是有一个\r\n
  11. void DMA_Init(DMA_Channel_TypeDef *DMAx_CHx);
  12. void SEND_Transmit_buf();
  13. void DMA_SHOW();
  14. #endif // !
  1. //main.c
  2. //本例程是按下按键KEY0 将串口数据传输到DMA,然后打印到串口上
  3. //使用的是正点原子F1精英开发板
  4. #include "sys.h"
  5. #include "delay.h"
  6. #include "usart.h"
  7. #include "usmart.h"
  8. #include "KEY.h"
  9. #include "led.h"
  10. #include "lcd.h"
  11. #include "DMA.h"
  12. int main(void)
  13. {
  14. HAL_Init(); // 初始化HAL库
  15. Stm32_Clock_Init(RCC_PLL_MUL9); // 设置时钟,72M
  16. delay_init(72); // 初始化延时函数
  17. uart_init(115200); // 初始化串口
  18. LED_Init(); // 初始化LED
  19. LCD_Init(); // 初始化LCD FSMC接口
  20. KEY_Init(); // 初始化按键
  21. usmart_dev.init(84); // 初始化USMART
  22. POINT_COLOR = RED; // 画笔颜色:红色
  23. LCD_ShowString(30, 50, 200, 16, 16, " YMZ ^_^");
  24. DMA_Init(DMA1_Channel4);
  25. SEND_Transmit_buf();
  26. while (1)
  27. {
  28. DMA_SHOW();
  29. LED0 = 1;
  30. }
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/658507
推荐阅读
相关标签
  

闽ICP备14008679号