DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM 与I/O设备开辟一条直接传送数据的通路,能使CPU 的效率大为提高。
一、DMA请求映像
STM32F10x有两个DMA控制器,使用DMA控制器可使数据从存储器到存储器、存储器到外设、外设到存储器。每个控制器有若干通道,参考《STM32参考手册》,各通道请求一览如下图:
二、DMA初始化
1、使能DMA时钟
__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能
2、关联DMA与UART1
DMA_HandleTypeDef UART1TxDMA_Handler; //DMA句柄 __HAL_LINKDMA(&UART1_Handler,hdmatx,UART1TxDMA_Handler); //将DMA与USART1联系起来(发送DMA)
3、配置DMA句柄
//Tx DMA配置 UART1TxDMA_Handler.Instance=chx; //通道选择 通道4指的是UART1Tx UART1TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设 /*由于是从存储器读数据给外设,所以存储器设置为增量模式,这样的话,它地址可以自动增加;而外设因为是固定的地址,所以设为非增量模式。*/ UART1TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式 UART1TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式 /*外设数据长度和存储器数据长度要设置一样的位数,其可以定义一次传输数据量的大小*/ UART1TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度:8位 UART1TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //存储器数据长度:8位 UART1TxDMA_Handler.Init.Mode=DMA_NORMAL; //外设普通模式 UART1TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级 HAL_DMA_DeInit(&UART1TxDMA_Handler); HAL_DMA_Init(&UART1TxDMA_Handler);
其中 Instance 参数根据请求映像表需要用到哪个设备就选择相应通道; Init.Direction 可选择“存储器到外设”、“外设到存储器”、“存储器到存储器”,决定数据传输方向; Init.PeriphInc 参数选择外设是否增量模式,本例中使用外设串口1的发送端,所以地址是固定的,要使用非增量模式;Init.MemInc 参数选择存储器是否为增量模式,由于存储器地址是连续的,本例中要读取连续的地址区域,所以要使用增量模式;Init.PeriphDataAlignment和Init.MemDataAlignment 分别表示外设和存储器长度,两个长度要相同,否则可能读取不完全;其余参数lue。
4、开启DMA传输
//开启一次DMA传输 //huart:串口句柄 //pData:传输的数据指针 //Size:传输的数据量 void MYDMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//开启DMA传输 huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA发送 }
我中使用到了寄存器编程使能DMA发送。开启DMA传输时直接调用该函数即可。
5、其他API
__HAL_DMA_GET_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TC4) //传输完成返回1,否则返回0
__HAL_DMA_CLEAR_FLAG(&UART1TxDMA_Handler,DMA_FLAG_TC4);//清除DMA1通道4传输完成标志
HAL_UART_DMAStop(&UART1_Handler); //传输完成以后关闭串口DMA
__HAL_DMA_GET_COUNTER(&UART1TxDMA_Handler);//得到当前还剩余多少个数据
6、(补充)DMA的接收
在用cubemx配置DMA的时候,要注意配置一下RX的mode,配置为Circular,意思是DMA接收处于循环接受状态,否则DMA只能接收一次。
在使用DMA接收的时候,使用函数
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
注意:如果在cubemx设置为循环接收模式,该函数可以不放在while循环里;如果没有设置为循环接收模式(即设置为normal模式),需要放在while里循环是能DMA接收中断。
DMA接收中断使用回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
实测使用回掉函数void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)的话不知道为什么进入不了中断(即进入不了这个回掉函数),不知道为什么,希望有大神来解答一下,谢谢。
三、总结
使用DMA传输可以使大量的数据传输交给DMA控制器执行,CPU空闲出来做其他的事,提高了运行效率。
来源:https://www.cnblogs.com/sovagxa/p/9135653.html