河南做网站找谁,牡丹江定制软件开发,wordpress移动顶部导航,基于html5的美食网页设计我估计大多数人学这么久连听说都没听说过DMA#xff0c;更不用提知道它是干嘛的。其实DMA的本质就是一个数据的搬运工。平常的时候当我们没有配置的时候#xff0c;一直都是CPU在搬运数据#xff0c;但是这个活又累又没有技术含量#xff0c;所以DMA的重要性还是有的。
目…我估计大多数人学这么久连听说都没听说过DMA更不用提知道它是干嘛的。其实DMA的本质就是一个数据的搬运工。平常的时候当我们没有配置的时候一直都是CPU在搬运数据但是这个活又累又没有技术含量所以DMA的重要性还是有的。
目录
1.DMA的数据搬运
1.DMA数据路径
2.DMA搬运模式
优先级的设定
通道
搬运是否循环
指针是否递增
2.DMA的框架
3.DMA函数与配置
函数
配置
内存-内存配置
内存-外设的配置
外设-内存重点
好了祝你看完就会。 1.DMA的数据搬运
1.DMA数据路径
简单来说只有三种
内存--内存 DMA_MEMORY_TO_MEMORY
外设--内存 DMA_PERIPH_TO_MEMORY
内存--外设 DMA_MEMORY_TO_PERIPH
这里的外设其实是指的STM32板子上的外设比如GPIO口啊串口啊等等。而所谓内存从代码上来看其实就是一个变量。
2.DMA搬运模式
这里可配置的东西有
优先级设定指针是否递增搬运模式是否循环通道的选择。其实都没有什么难度只不过看起来花里胡哨的我依次讲解。
优先级的设定
DMA的优先级采用的是硬件软件什么意思呢其实本质是 通道号软件优先级。在使用过程中我们往往同时配置多个通道当两个通道内的数据同时到达时优先看软件优先级再看通道号。注意通道号越小优先级越。
通道
DMA的通道其实和定时器的有点像DMA通过通道连接各种外设从而实现数据传输。在配置中外设往往也配置了对应的DMA句柄来接应。所以这里对于通道只需要知道要按照表格对应来选择就行。 注意通常情况下芯片都只有DMA1没有DMA2.
搬运是否循环
DMA_Mode_Normal正常模式一次DMA数据传输完后停止DMA传送 也就是只传输一次 DMA_Mode_Circular循环传输模式 当传输结束时硬件自动会将传输数据量寄存器进行重装进行下一轮的数据传输。 也就是多次传输。注意当循环结束后假设给内存设定为了指针递增此时指针会重置回到缓存最开始指向的地方。假设缓存为arr[4]传输完成时指向arr[3]循环结束后指针会自动回归为arr[0]
指针是否递增
数据源和目标缓存都有一个对应的指针来指向它从而使得数据知道存哪在配置中也是分别配置是否递增的。不用想那么复杂就一句话数据来自或者要去内存则为递增外设则不递增。
解释因为外设来源的数据往往放在xx寄存器里数据一直是更新覆盖的所以不用递增。内存就不一样了内存往往是数组或者一个xxbuf不递增的话就覆盖掉了。
另外还有一项配置叫数据对齐模式其实都一般配置为DMA_PDATAALIGN_BYTE
2.DMA的框架 3.DMA函数与配置
函数
__HAL_RCC_DMA1_CLK_ENABLE(…) 使能DMA时钟的
HAL_DMA_Init(…) 跟TIM的INIT用法一样
HAL_DMA_Start(…) 搬运函数。
__HAL_LINKDMA(…) 连接内存到外设数据通道的。
__HAL_DMA_GET_FLAG(…) 获取DMA寄存器标志位的
配置
基本的配置的步骤为 DMA时钟使能__HAL_RCC_DMA1_CLK_ENABLE(); | DMA初始化通道选择优先级指针递增数据对齐搬运模式HAL_DMA_Init() | DMA搬运如果是和外设进行交互那么这一步会变化在代码中详解。 | 查询DMA数据是否传输正常完成
内存-内存配置
DMA_HandleTypeDef hdma_handle {0};
void DMA_INIT(){__HAL_RCC_DMA1_CLK_ENABLE(); //内存配置hdma_handle.Init.MemDataAlignment DMA_MDATAALIGN_BYTE;hdma_handle.Init.MemInc DMA_MINC_ENABLE;//外设配置目标存储配置hdma_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE;hdma_handle.Init.PeriphInc DMA_PINC_ENABLE;//模式优先级转运方向配置hdma_handle.Init.Mode DMA_NORMAL;hdma_handle.Init.Priority DMA_PRIORITY_MEDIUM;hdma_handle.Init.Direction DMA_MEMORY_TO_MEMORY;hdma_handle.Instance DMA1_Channel1; HAL_DMA_Init(hdma_handle);}
void DMA_Transport(){HAL_DMA_Start(hdma_handle,(uint32_t)Sroce_buf,(uint32_t)Target_buf,sizeof(uint32_t) * 16);while(__HAL_DMA_GET_FLAG(hdma_handle,DMA_FLAG_TC1) RESET){int i 0;for(i 0;i16;i){printf(data[%d] %X \r\n,i,Target_buf[i]);}}
}
内存-外设的配置
#include dma.h
extern UART_HandleTypeDef uart1_handle;
DMA_HandleTypeDef hdma_handle {0};
void DMA_INIT(){__HAL_RCC_DMA1_CLK_ENABLE();hdma_handle.Init.MemDataAlignment DMA_MDATAALIGN_BYTE;hdma_handle.Init.MemInc DMA_MINC_ENABLE;hdma_handle.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE;hdma_handle.Init.PeriphInc DMA_PINC_DISABLE; //外设内存不可递增。hdma_handle.Init.Mode DMA_NORMAL;hdma_handle.Init.Priority DMA_PRIORITY_MEDIUM;hdma_handle.Init.Direction DMA_MEMORY_TO_PERIPH; //内存到外设hdma_handle.Instance DMA1_Channel4;HAL_DMA_Init(hdma_handle);//链接函数这里是吧DMA和外设连接起来中间的参数是外设句柄中的DMA成员变量//可以理解为每一个外设都配有DMA成员变量为的就是和DMA连接。__HAL_LINKDMA(uart1_handle,hdmatx,hdma_handle);
}在main中DMA_INIT(); HAL_UART_Transmit_DMA(uart1_handle,Send_buf,1024); 这里注意 HAL_UART_Transmit_DMA(uart1_handle,Send_buf,1024); 是使用了外设对应的API利用传输过来的句柄进行传输。
外设-内存重点
#include dma.h
#include stdio.h
#include uart1.hextern UART_HandleTypeDef uart1_handle;
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
DMA_HandleTypeDef dma_handle {0};
void dma_init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();dma_handle.Instance DMA1_Channel5;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);__HAL_LINKDMA(uart1_handle, hdmarx, dma_handle);HAL_UART_Receive_DMA(uart1_handle, uart1_rx_buf, UART1_RX_BUF_SIZE);
}
基本配置中有两个重点一个是extern外面串口的变量另一个是链接到外设的函数如果展开的来看的话LINK函数内部是把该句柄赋值给了串口句柄中的成员变量DMA句柄。
第二个重点是 HAL_UART_Receive_DMA该函数配置的是 串口句柄 目标缓存 传输大小其中传输大小最为重要这里传输大小配置为了缓存区的长度大小。
void USART1_IRQHandler(void)
{if (__HAL_UART_GET_FLAG(uart1_handle, UART_FLAG_IDLE) ! RESET) { __HAL_UART_CLEAR_IDLEFLAG(uart1_handle);HAL_UART_DMAStop(uart1_handle);uart1_rx_len UART1_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(dma_handle);printf(recv: %s, recv_len: %d\r\n, uart1_rx_buf, uart1_rx_len);uart1_rx_clear();HAL_UART_Receive_DMA(uart1_handle, uart1_rx_buf, UART1_RX_BUF_SIZE);}
}这里代码很短但是非常有机制可谈其中长度的计算就非常有的说首先DMA的传输长度被配置为了缓存器的长度 其次DMA在传输过程中当没有传输足够配置的长度但数据源为空时会进入等待 然后这里配置了HAL_UART_DMAStop函数使得DMA停止传输并保持了当前状态。 再然后使用__HAL_DMA_GET_COUNTER会返回当前未传输任务中还未传输的长度 最后使用缓存大小 - __HAL_DMA_GET_COUNTER的返回值 已传输的值。
我用数字再解释一遍假设配置DMA传输长度为10此时串口接收到了4DMA传输进缓存区4的数据此时数据源为空DMA进入等待于此同时串口接收完成触发空闲中断此时调用函数Stop使得DMA停止传输保持当前状态GET_COUNTER返回的值为6所以直接10 - 6 4也就是接收到的数据长度。
其中Stop函数是必要的因为如果长时间没有接收或者新的数据进入都会导致GET返回值变化。
好了祝你看完就会。