DMA的作用

  • 直接存储器访问(DMA)用于在外设于存储器之间以及存储器与存储器之间,提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速移动数据。这样节省CPU资源可供其他操作使用。
    • 总结:DMA的就是CPU的助手、数据搬运工。
  • DMA外设要点概括
    • 对于DMA这个器间,它的功能就是建立起一个数据传输的通道。

图表查看

  • STM32F207VET6为例

image.png

  • DMA支持APB1和APB2总线和AHB1、AHB2总线的数据传输

通道概念

  • 1、建立(选取)传输通道
    • 存储器->存储器
    • 外设->存储器
    • 存储器->外设
  • 2、确定传输对象
    • 具体功能
      • UART(源)-内存(目标)
      • 内存数据(源)-UART(目标)
  • 3、敲定传输细节
    • 通道优先级
    • 确定传输数据双方的数据格式
    • 确定数据传输是否双方的数据格式
    • 确定数据是否需要一直采集(循环模式是否使能)
    • 是否需要传输标志/中断

实战

  • DMA句柄结构体
    • DMA_HandleTypeDef
  • DMA初始化结构体

    • DMA_InitTypeDef ```c typedef struct __DMA_HandleTypeDef { DMA_Stream_TypeDef Instance; /指向了具体的外设以及地址*/

    DMA_InitTypeDef Init; /用来配置特定外设的参数配置,比如外设的功能(串口的波特率,奇偶校验位,停止位)/

    HAL_LockTypeDef Lock; //

    __IO HAL_DMA_StateTypeDef State; /外设状态/

    void *Parent;

    void ( XferCpltCallback)( struct __DMA_HandleTypeDef hdma); /传输完成的回调函数/

    void ( XferHalfCpltCallback)( struct __DMA_HandleTypeDef hdma);

    void ( XferM1CpltCallback)( struct __DMA_HandleTypeDef hdma);

    void ( XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef hdma);

    void ( XferErrorCallback)( struct __DMA_HandleTypeDef hdma);

    void ( XferAbortCallback)( struct __DMA_HandleTypeDef hdma);

    __IO uint32_t ErrorCode;

    uint32_t StreamBaseAddress;

    uint32_t StreamIndex;

}DMA_HandleTypeDef;

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2322648/1658126688681-f5b86b6a-4103-46db-b830-ca822521e703.png#clientId=ubb24075a-1ac0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=730&id=u9edefd07&margin=%5Bobject%20Object%5D&name=image.png&originHeight=912&originWidth=1040&originalType=binary&ratio=1&rotation=0&showTitle=false&size=159220&status=done&style=none&taskId=u7487727f-6be0-4c83-b69f-dfa4ee2bcd4&title=&width=832)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2322648/1658126716407-fef36282-7aaa-4fd1-931c-65b012f7bc24.png#clientId=ubb24075a-1ac0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=735&id=ufc23f626&margin=%5Bobject%20Object%5D&name=image.png&originHeight=919&originWidth=1044&originalType=binary&ratio=1&rotation=0&showTitle=false&size=162275&status=done&style=none&taskId=ua1e4571b-3c7c-4a91-a649-268635b1aac&title=&width=835.2)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2322648/1658126744807-942a4cad-9b64-474d-9635-6323ed4d97b3.png#clientId=ubb24075a-1ac0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=735&id=u119d6427&margin=%5Bobject%20Object%5D&name=image.png&originHeight=919&originWidth=1044&originalType=binary&ratio=1&rotation=0&showTitle=false&size=138279&status=done&style=none&taskId=u9b3fa603-1179-406b-9ce9-9cd8c41d546&title=&width=835.2)<br />按照之前的步骤生成MDK工程<br />在各自的c文件中(比如usart.c文件中)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2322648/1658127095131-65df3fd6-ccb6-430d-b6a7-fdf179de38f1.png#clientId=ubb24075a-1ac0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=404&id=ue00c597b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=505&originWidth=524&originalType=binary&ratio=1&rotation=0&showTitle=false&size=178545&status=done&style=none&taskId=u3b6cdb0f-88a2-431a-975c-85299c8c369&title=&width=419.2)
  2. - Instance: DMA通道
  3. - Direction
  4. - DMA_MEMORY_TO_MEMORY: 内存到内存的模式
  5. - DMA_MEMORY_TO_PERIPH: 内存到外设的模式
  6. - DMA_PERIPH_TO_MEMORY: 外设到内存的模式
  7. - PeriphInc: 外设地址是否递增
  8. - DMA_PINC_ENABLE: 外设地址递增
  9. - DMA_PINC_DISABLE: 外设地址不递增
  10. - MemInc: 内存地址是否递增
  11. - DMA_MINC_ENABLE: 内存地址递增
  12. - DMA_MINC_DISABLE: 内存地址不递增
  13. - PeriphDataAlignment: 外设数据宽度
  14. - DMA_PDATAALIGN_BYTE: 外设的byte
  15. - DMA_PDATAALIGN_HALFWORD: 外设的halfword
  16. - DMA_PDATAALIGN_WORD: 外设的word
  17. - MemDataAlignment: 内存数据宽度
  18. - DMA_MDATAALIGN_BYTE: 内存的byte
  19. - DMA_MDATAALIGN_BYTE: 内存的halfword
  20. - DMA_MDATAALIGN_BYTE: 内存的word
  21. <a name="yBVQT"></a>
  22. ##### 中断配置
  23. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2322648/1658127850846-b8e5aa2d-fd81-4ada-871b-7626cb420c49.png#clientId=ubb24075a-1ac0-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=239&id=ua1f1adaa&margin=%5Bobject%20Object%5D&name=image.png&originHeight=299&originWidth=423&originalType=binary&ratio=1&rotation=0&showTitle=false&size=92054&status=done&style=none&taskId=uf4159655-4644-4464-856a-962946fb248&title=&width=338.4)
  24. - 中断初始化
  25. <a name="Z5fNn"></a>
  26. ##### usart-DMA配置
  27. - HAL_DMA_Init(&hdma_usart1_rx) : DMA初始化
  28. - __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx); : 将串口的外设和DMA连接在了一起
  29. - uartHandle:串口的局柄
  30. - DMA的句柄
  31. - DMA外设初始化句柄
  32. - #define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \
  33. do{ \<br /> (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \<br /> (__DMA_HANDLE__).Parent = (__HANDLE__); \<br /> } while(0U)
  34. - __HANDLE__: `->`指向了成员(结构体指针下的成员)
  35. ```c
  36. ...
  37. //系统时钟初始化: 我设置的是72MHZ
  38. void SystemClock_Config(void);
  39. ...
  • 自己创建dma初始化(注释原来的)

1658133020360.png

  1. #ifndef __BSP_DMA_H
  2. #define __BSP_DMA_H
  3. #include "stm32f1xx_hal_gpio.h"
  4. #include "stm32f1xx_hal_dma.h"
  5. void DMA_Config(void);
  6. #endif
  1. #include "bsp_dma.h"
  2. DMA_HandleTypeDef DMA_Handle;
  3. const int32_t TxBuffer[32]={ //发送缓冲区,定义在flash中,只读
  4. 0x1000,0x2000,0x3000,0x4000,0x5000,0x6000
  5. };
  6. uint32_t RxBuffer[32]; //接收缓冲区
  7. void DMA_Config(void){
  8. HAL_StatusTypeDef status = HAL_ERROR;
  9. __HAL_RCC_DMA1_CLK_ENABLE(); //开启DMA1时钟(使能)
  10. DMA_Handle.Instance = DMA1_Channel5;//Instance: 指向的是实际的通道
  11. //DMA_Handle.Init = ;//init:指向了外设的初始化的结构体
  12. /*
  13. #define IS_DMA_DIRECTION(DIRECTION) (((DIRECTION) == DMA_PERIPH_TO_MEMORY ) || \
  14. ((DIRECTION) == DMA_MEMORY_TO_PERIPH) || \
  15. ((DIRECTION) == DMA_MEMORY_TO_MEMORY))
  16. */
  17. DMA_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存
  18. /*
  19. #define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PINC_ENABLE) || \
  20. ((STATE) == DMA_PINC_DISABLE))
  21. */
  22. DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE; //外设递增使能
  23. /*
  24. #define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MINC_ENABLE) || \
  25. ((STATE) == DMA_MINC_DISABLE))
  26. */
  27. DMA_Handle.Init.MemInc=DMA_MINC_DISABLE;//内存失能
  28. /*
  29. #define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PDATAALIGN_BYTE) || \
  30. ((SIZE) == DMA_PDATAALIGN_HALFWORD) || \
  31. ((SIZE) == DMA_PDATAALIGN_WORD))
  32. */
  33. DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据的长度
  34. /*
  35. #define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MDATAALIGN_BYTE) || \
  36. ((SIZE) == DMA_MDATAALIGN_HALFWORD) || \
  37. ((SIZE) == DMA_MDATAALIGN_WORD ))
  38. */
  39. DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;//内存数据的长度;DMA_MDATAALIGN_WORD: 32位=int
  40. /*
  41. #define IS_DMA_MODE(MODE) (((MODE) == DMA_NORMAL ) || \
  42. ((MODE) == DMA_CIRCULAR))
  43. */
  44. DMA_Handle.Init.Mode=DMA_NORMAL;//外设普通模式; DMA_CIRCULAR: 循环传输模式
  45. /*
  46. #define IS_DMA_PRIORITY(PRIORITY) (((PRIORITY) == DMA_PRIORITY_LOW ) || \
  47. ((PRIORITY) == DMA_PRIORITY_MEDIUM) || \
  48. ((PRIORITY) == DMA_PRIORITY_HIGH) || \
  49. ((PRIORITY) == DMA_PRIORITY_VERY_HIGH))
  50. */
  51. DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;//配置优先级; DMA_PRIORITY_MEDIUM: 中等
  52. HAL_DMA_Init(&DMA_Handle);//传入句柄,进入初始化
  53. }

开始DMA转化
  1. /*
  2. ==============================================================================
  3. ##### How to use this driver #####
  4. ==============================================================================
  5. [..]
  6. (#) Enable and configure the peripheral to be connected to the DMA Channel
  7. (except for internal SRAM / FLASH memories: no initialization is
  8. necessary). Please refer to the Reference manual for connection between peripherals
  9. and DMA requests.
  10. (#) For a given Channel, program the required configuration through the following parameters:
  11. Channel request, Transfer Direction, Source and Destination data formats,
  12. Circular or Normal mode, Channel Priority level, Source and Destination Increment mode
  13. using HAL_DMA_Init() function.
  14. (#) Use HAL_DMA_GetState() function to return the DMA state and HAL_DMA_GetError() in case of error
  15. detection.
  16. (#) Use HAL_DMA_Abort() function to abort the current transfer
  17. -@- In Memory-to-Memory transfer mode, Circular mode is not allowed.
  18. *** Polling mode IO operation ***
  19. =================================
  20. [..]
  21. (+) Use HAL_DMA_Start() to start DMA transfer after the configuration of Source
  22. address and destination address and the Length of data to be transferred
  23. (+) Use HAL_DMA_PollForTransfer() to poll for the end of current transfer, in this
  24. case a fixed Timeout can be configured by User depending from his application.
  25. *** Interrupt mode IO operation ***
  26. ===================================
  27. [..]
  28. (+) Configure the DMA interrupt priority using HAL_NVIC_SetPriority()
  29. (+) Enable the DMA IRQ handler using HAL_NVIC_EnableIRQ()
  30. (+) Use HAL_DMA_Start_IT() to start DMA transfer after the configuration of
  31. Source address and destination address and the Length of data to be transferred.
  32. In this case the DMA interrupt is configured
  33. (+) Use HAL_DMA_IRQHandler() called under DMA_IRQHandler() Interrupt subroutine
  34. (+) At the end of data transfer HAL_DMA_IRQHandler() function is executed and user can
  35. add his own function by customization of function pointer XferCpltCallback and
  36. XferErrorCallback (i.e. a member of DMA handle structure).
  37. *** DMA HAL driver macros list ***
  38. =============================================
  39. [..]
  40. Below the list of most used macros in DMA HAL driver.
  41. (+) __HAL_DMA_ENABLE: Enable the specified DMA Channel.
  42. (+) __HAL_DMA_DISABLE: Disable the specified DMA Channel.
  43. (+) __HAL_DMA_GET_FLAG: Get the DMA Channel pending flags.
  44. (+) __HAL_DMA_CLEAR_FLAG: Clear the DMA Channel pending flags.
  45. (+) __HAL_DMA_ENABLE_IT: Enable the specified DMA Channel interrupts.
  46. (+) __HAL_DMA_DISABLE_IT: Disable the specified DMA Channel interrupts.
  47. (+) __HAL_DMA_GET_IT_SOURCE: Check whether the specified DMA Channel interrupt has occurred or not.
  48. [..]
  49. */
  1. /**
  2. * @brief Start the DMA Transfer.
  3. * @param hdma: pointer to a DMA_HandleTypeDef structure that contains
  4. * the configuration information for the specified DMA Channel.
  5. * @param SrcAddress: The source memory Buffer address
  6. * @param DstAddress: The destination memory Buffer address
  7. * @param DataLength: The length of data to be transferred from source to destination
  8. * @retval HAL status
  9. */
  10. HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
  11. {
  12. HAL_StatusTypeDef status = HAL_OK;
  13. /* Check the parameters */
  14. assert_param(IS_DMA_BUFFER_SIZE(DataLength));
  15. /* Process locked */
  16. __HAL_LOCK(hdma);
  17. if(HAL_DMA_STATE_READY == hdma->State)
  18. {
  19. /* Change DMA peripheral state */
  20. hdma->State = HAL_DMA_STATE_BUSY;
  21. hdma->ErrorCode = HAL_DMA_ERROR_NONE;
  22. /* Disable the peripheral */
  23. __HAL_DMA_DISABLE(hdma);
  24. /* Configure the source, destination address and the data length & clear flags*/
  25. DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);
  26. /* Enable the Peripheral */
  27. __HAL_DMA_ENABLE(hdma);
  28. }
  29. else
  30. {
  31. /* Process Unlocked */
  32. __HAL_UNLOCK(hdma);
  33. status = HAL_BUSY;
  34. }
  35. return status;
  36. }
  • hdma: 指向了DMA的句柄结构体
  • SrcAddress: 原地址(内存地址) 我们文件中就是指TxBuffer
  • DstAddress: 目标的储存器(内存地址),我们的文件中就是指RxBuffer
  • DataLength: 传输的大小
    1. void DMA_Config(void){
    2. ....
    3. DMA_Status = HAL_DMA_Start(&DMA_Handle,(uint32_t)TxBuffer,(uint32_t)RxBuffer,32);
    4. /*判断DMA状态: DMA_Status !=HAL_OK */
    5. if(DMA_Status !=HAL_OK){
    6. while(1){
    7. HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
    8. HAL_Delay(1000);
    9. }
    10. }
    11. }

完整函数

  1. #include "bsp_dma.h"
  2. DMA_HandleTypeDef DMA_Handle;
  3. HAL_StatusTypeDef DMA_Status;
  4. const int32_t TxBuffer[32]={ //发送缓冲区,定义在flash中,只读
  5. 0x1000,0x2000,0x3000,0x4000,0x5000,0x6000
  6. };
  7. uint32_t RxBuffer[32]; //接收缓冲区
  8. void DMA_Config(void){
  9. HAL_StatusTypeDef status = HAL_ERROR;
  10. __HAL_RCC_DMA1_CLK_ENABLE(); //开启DMA1时钟(使能)
  11. DMA_Handle.Instance = DMA1_Channel5;//Instance: 指向的是实际的通道
  12. //DMA_Handle.Init = ;//init:指向了外设的初始化的结构体
  13. /*
  14. #define IS_DMA_DIRECTION(DIRECTION) (((DIRECTION) == DMA_PERIPH_TO_MEMORY ) || \
  15. ((DIRECTION) == DMA_MEMORY_TO_PERIPH) || \
  16. ((DIRECTION) == DMA_MEMORY_TO_MEMORY))
  17. */
  18. DMA_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存
  19. /*
  20. #define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PINC_ENABLE) || \
  21. ((STATE) == DMA_PINC_DISABLE))
  22. */
  23. DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE; //外设递增使能
  24. /*
  25. #define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MINC_ENABLE) || \
  26. ((STATE) == DMA_MINC_DISABLE))
  27. */
  28. DMA_Handle.Init.MemInc=DMA_MINC_DISABLE;//内存失能
  29. /*
  30. #define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PDATAALIGN_BYTE) || \
  31. ((SIZE) == DMA_PDATAALIGN_HALFWORD) || \
  32. ((SIZE) == DMA_PDATAALIGN_WORD))
  33. */
  34. DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据的长度
  35. /*
  36. #define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MDATAALIGN_BYTE) || \
  37. ((SIZE) == DMA_MDATAALIGN_HALFWORD) || \
  38. ((SIZE) == DMA_MDATAALIGN_WORD ))
  39. */
  40. DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;//内存数据的长度;DMA_MDATAALIGN_WORD: 32位=int
  41. /*
  42. #define IS_DMA_MODE(MODE) (((MODE) == DMA_NORMAL ) || \
  43. ((MODE) == DMA_CIRCULAR))
  44. */
  45. DMA_Handle.Init.Mode=DMA_NORMAL;//外设普通模式; DMA_CIRCULAR: 循环传输模式
  46. /*
  47. #define IS_DMA_PRIORITY(PRIORITY) (((PRIORITY) == DMA_PRIORITY_LOW ) || \
  48. ((PRIORITY) == DMA_PRIORITY_MEDIUM) || \
  49. ((PRIORITY) == DMA_PRIORITY_HIGH) || \
  50. ((PRIORITY) == DMA_PRIORITY_VERY_HIGH))
  51. */
  52. DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;//配置优先级; DMA_PRIORITY_MEDIUM: 中等
  53. HAL_DMA_Init(&DMA_Handle);//传入句柄,进入初始化
  54. DMA_Status = HAL_DMA_Start(&DMA_Handle,(uint32_t)TxBuffer,(uint32_t)RxBuffer,32);
  55. /*判断DMA状态: DMA_Status !=HAL_OK */
  56. if(DMA_Status !=HAL_OK){
  57. while(1){
  58. HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
  59. HAL_Delay(1000);
  60. }
  61. }
  62. }