DMA的作用
- 直接存储器访问(DMA)用于在外设于存储器之间以及存储器与存储器之间,提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速移动数据。这样节省CPU资源可供其他操作使用。
- 总结:DMA的就是CPU的助手、数据搬运工。
- DMA外设要点概括
- 对于DMA这个器间,它的功能就是建立起一个数据传输的通道。
图表查看
- STM32F207VET6为例
- 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;
<br /><br /><br />按照之前的步骤生成MDK工程<br />在各自的c文件中(比如usart.c文件中)<br />- Instance: DMA通道- Direction- DMA_MEMORY_TO_MEMORY: 内存到内存的模式- DMA_MEMORY_TO_PERIPH: 内存到外设的模式- DMA_PERIPH_TO_MEMORY: 外设到内存的模式- PeriphInc: 外设地址是否递增- DMA_PINC_ENABLE: 外设地址递增- DMA_PINC_DISABLE: 外设地址不递增- MemInc: 内存地址是否递增- DMA_MINC_ENABLE: 内存地址递增- DMA_MINC_DISABLE: 内存地址不递增- PeriphDataAlignment: 外设数据宽度- DMA_PDATAALIGN_BYTE: 外设的byte位- DMA_PDATAALIGN_HALFWORD: 外设的halfword位- DMA_PDATAALIGN_WORD: 外设的word位- MemDataAlignment: 内存数据宽度- DMA_MDATAALIGN_BYTE: 内存的byte位- DMA_MDATAALIGN_BYTE: 内存的halfword位- DMA_MDATAALIGN_BYTE: 内存的word位<a name="yBVQT"></a>##### 中断配置- 中断初始化<a name="Z5fNn"></a>##### usart-DMA配置- HAL_DMA_Init(&hdma_usart1_rx) : DMA初始化- __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx); : 将串口的外设和DMA连接在了一起- uartHandle:串口的局柄- DMA的句柄- DMA外设初始化句柄- #define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \do{ \<br /> (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \<br /> (__DMA_HANDLE__).Parent = (__HANDLE__); \<br /> } while(0U)- __HANDLE__: 用`->`指向了成员(结构体指针下的成员)```c...//系统时钟初始化: 我设置的是72MHZvoid SystemClock_Config(void);...
- 自己创建dma初始化(注释原来的)

#ifndef __BSP_DMA_H#define __BSP_DMA_H#include "stm32f1xx_hal_gpio.h"#include "stm32f1xx_hal_dma.h"void DMA_Config(void);#endif
#include "bsp_dma.h"DMA_HandleTypeDef DMA_Handle;const int32_t TxBuffer[32]={ //发送缓冲区,定义在flash中,只读0x1000,0x2000,0x3000,0x4000,0x5000,0x6000};uint32_t RxBuffer[32]; //接收缓冲区void DMA_Config(void){HAL_StatusTypeDef status = HAL_ERROR;__HAL_RCC_DMA1_CLK_ENABLE(); //开启DMA1时钟(使能)DMA_Handle.Instance = DMA1_Channel5;//Instance: 指向的是实际的通道//DMA_Handle.Init = ;//init:指向了外设的初始化的结构体/*#define IS_DMA_DIRECTION(DIRECTION) (((DIRECTION) == DMA_PERIPH_TO_MEMORY ) || \((DIRECTION) == DMA_MEMORY_TO_PERIPH) || \((DIRECTION) == DMA_MEMORY_TO_MEMORY))*/DMA_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存/*#define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PINC_ENABLE) || \((STATE) == DMA_PINC_DISABLE))*/DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE; //外设递增使能/*#define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MINC_ENABLE) || \((STATE) == DMA_MINC_DISABLE))*/DMA_Handle.Init.MemInc=DMA_MINC_DISABLE;//内存失能/*#define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PDATAALIGN_BYTE) || \((SIZE) == DMA_PDATAALIGN_HALFWORD) || \((SIZE) == DMA_PDATAALIGN_WORD))*/DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据的长度/*#define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MDATAALIGN_BYTE) || \((SIZE) == DMA_MDATAALIGN_HALFWORD) || \((SIZE) == DMA_MDATAALIGN_WORD ))*/DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;//内存数据的长度;DMA_MDATAALIGN_WORD: 32位=int/*#define IS_DMA_MODE(MODE) (((MODE) == DMA_NORMAL ) || \((MODE) == DMA_CIRCULAR))*/DMA_Handle.Init.Mode=DMA_NORMAL;//外设普通模式; DMA_CIRCULAR: 循环传输模式/*#define IS_DMA_PRIORITY(PRIORITY) (((PRIORITY) == DMA_PRIORITY_LOW ) || \((PRIORITY) == DMA_PRIORITY_MEDIUM) || \((PRIORITY) == DMA_PRIORITY_HIGH) || \((PRIORITY) == DMA_PRIORITY_VERY_HIGH))*/DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;//配置优先级; DMA_PRIORITY_MEDIUM: 中等HAL_DMA_Init(&DMA_Handle);//传入句柄,进入初始化}
开始DMA转化
/*==============================================================================##### How to use this driver #####==============================================================================[..](#) Enable and configure the peripheral to be connected to the DMA Channel(except for internal SRAM / FLASH memories: no initialization isnecessary). Please refer to the Reference manual for connection between peripheralsand DMA requests.(#) For a given Channel, program the required configuration through the following parameters:Channel request, Transfer Direction, Source and Destination data formats,Circular or Normal mode, Channel Priority level, Source and Destination Increment modeusing HAL_DMA_Init() function.(#) Use HAL_DMA_GetState() function to return the DMA state and HAL_DMA_GetError() in case of errordetection.(#) Use HAL_DMA_Abort() function to abort the current transfer-@- In Memory-to-Memory transfer mode, Circular mode is not allowed.*** Polling mode IO operation ***=================================[..](+) Use HAL_DMA_Start() to start DMA transfer after the configuration of Sourceaddress and destination address and the Length of data to be transferred(+) Use HAL_DMA_PollForTransfer() to poll for the end of current transfer, in thiscase a fixed Timeout can be configured by User depending from his application.*** Interrupt mode IO operation ***===================================[..](+) Configure the DMA interrupt priority using HAL_NVIC_SetPriority()(+) Enable the DMA IRQ handler using HAL_NVIC_EnableIRQ()(+) Use HAL_DMA_Start_IT() to start DMA transfer after the configuration ofSource address and destination address and the Length of data to be transferred.In this case the DMA interrupt is configured(+) Use HAL_DMA_IRQHandler() called under DMA_IRQHandler() Interrupt subroutine(+) At the end of data transfer HAL_DMA_IRQHandler() function is executed and user canadd his own function by customization of function pointer XferCpltCallback andXferErrorCallback (i.e. a member of DMA handle structure).*** DMA HAL driver macros list ***=============================================[..]Below the list of most used macros in DMA HAL driver.(+) __HAL_DMA_ENABLE: Enable the specified DMA Channel.(+) __HAL_DMA_DISABLE: Disable the specified DMA Channel.(+) __HAL_DMA_GET_FLAG: Get the DMA Channel pending flags.(+) __HAL_DMA_CLEAR_FLAG: Clear the DMA Channel pending flags.(+) __HAL_DMA_ENABLE_IT: Enable the specified DMA Channel interrupts.(+) __HAL_DMA_DISABLE_IT: Disable the specified DMA Channel interrupts.(+) __HAL_DMA_GET_IT_SOURCE: Check whether the specified DMA Channel interrupt has occurred or not.[..]*/
/*** @brief Start the DMA Transfer.* @param hdma: pointer to a DMA_HandleTypeDef structure that contains* the configuration information for the specified DMA Channel.* @param SrcAddress: The source memory Buffer address* @param DstAddress: The destination memory Buffer address* @param DataLength: The length of data to be transferred from source to destination* @retval HAL status*/HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength){HAL_StatusTypeDef status = HAL_OK;/* Check the parameters */assert_param(IS_DMA_BUFFER_SIZE(DataLength));/* Process locked */__HAL_LOCK(hdma);if(HAL_DMA_STATE_READY == hdma->State){/* Change DMA peripheral state */hdma->State = HAL_DMA_STATE_BUSY;hdma->ErrorCode = HAL_DMA_ERROR_NONE;/* Disable the peripheral */__HAL_DMA_DISABLE(hdma);/* Configure the source, destination address and the data length & clear flags*/DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);/* Enable the Peripheral */__HAL_DMA_ENABLE(hdma);}else{/* Process Unlocked */__HAL_UNLOCK(hdma);status = HAL_BUSY;}return status;}
- hdma: 指向了DMA的句柄结构体
- SrcAddress: 原地址(内存地址) 我们文件中就是指
TxBuffer - DstAddress: 目标的储存器(内存地址),我们的文件中就是指
RxBuffer - DataLength: 传输的大小
void DMA_Config(void){....DMA_Status = HAL_DMA_Start(&DMA_Handle,(uint32_t)TxBuffer,(uint32_t)RxBuffer,32);/*判断DMA状态: DMA_Status !=HAL_OK */if(DMA_Status !=HAL_OK){while(1){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);HAL_Delay(1000);}}}
完整函数
#include "bsp_dma.h"DMA_HandleTypeDef DMA_Handle;HAL_StatusTypeDef DMA_Status;const int32_t TxBuffer[32]={ //发送缓冲区,定义在flash中,只读0x1000,0x2000,0x3000,0x4000,0x5000,0x6000};uint32_t RxBuffer[32]; //接收缓冲区void DMA_Config(void){HAL_StatusTypeDef status = HAL_ERROR;__HAL_RCC_DMA1_CLK_ENABLE(); //开启DMA1时钟(使能)DMA_Handle.Instance = DMA1_Channel5;//Instance: 指向的是实际的通道//DMA_Handle.Init = ;//init:指向了外设的初始化的结构体/*#define IS_DMA_DIRECTION(DIRECTION) (((DIRECTION) == DMA_PERIPH_TO_MEMORY ) || \((DIRECTION) == DMA_MEMORY_TO_PERIPH) || \((DIRECTION) == DMA_MEMORY_TO_MEMORY))*/DMA_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;//外设到内存/*#define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PINC_ENABLE) || \((STATE) == DMA_PINC_DISABLE))*/DMA_Handle.Init.PeriphInc=DMA_PINC_ENABLE; //外设递增使能/*#define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MINC_ENABLE) || \((STATE) == DMA_MINC_DISABLE))*/DMA_Handle.Init.MemInc=DMA_MINC_DISABLE;//内存失能/*#define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PDATAALIGN_BYTE) || \((SIZE) == DMA_PDATAALIGN_HALFWORD) || \((SIZE) == DMA_PDATAALIGN_WORD))*/DMA_Handle.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据的长度/*#define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MDATAALIGN_BYTE) || \((SIZE) == DMA_MDATAALIGN_HALFWORD) || \((SIZE) == DMA_MDATAALIGN_WORD ))*/DMA_Handle.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;//内存数据的长度;DMA_MDATAALIGN_WORD: 32位=int/*#define IS_DMA_MODE(MODE) (((MODE) == DMA_NORMAL ) || \((MODE) == DMA_CIRCULAR))*/DMA_Handle.Init.Mode=DMA_NORMAL;//外设普通模式; DMA_CIRCULAR: 循环传输模式/*#define IS_DMA_PRIORITY(PRIORITY) (((PRIORITY) == DMA_PRIORITY_LOW ) || \((PRIORITY) == DMA_PRIORITY_MEDIUM) || \((PRIORITY) == DMA_PRIORITY_HIGH) || \((PRIORITY) == DMA_PRIORITY_VERY_HIGH))*/DMA_Handle.Init.Priority=DMA_PRIORITY_MEDIUM;//配置优先级; DMA_PRIORITY_MEDIUM: 中等HAL_DMA_Init(&DMA_Handle);//传入句柄,进入初始化DMA_Status = HAL_DMA_Start(&DMA_Handle,(uint32_t)TxBuffer,(uint32_t)RxBuffer,32);/*判断DMA状态: DMA_Status !=HAL_OK */if(DMA_Status !=HAL_OK){while(1){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);HAL_Delay(1000);}}}
