外设到内存
模式:单次、外设地址不自增,内存地址自增
c
hdma_usart1_rx.Instance = DMA1_Channel5; // 使用通道 5
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 方向:外设 to 内存
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址是否自增:不自增
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址是否自增:自增
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据位宽:字节
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 内存数据位宽:字节
hdma_usart1_rx.Init.Mode = DMA_NORMAL; // DMA 模式:普通,即单次传输
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; // 优先级:低优先级
c
函数原型:
HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);
函数使用:
HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);
源地址:(uint32_t)&huart->Instance->DR,即串口的数据寄存器,虽然占 32 位地址空间,但高 24 位为保留位,所以该寄存器保存一个字节。
目的地址:用户自定义的一个 buf
数据长度:传送的数据长度
c
HAL_UART_Receive_DMA(&huart1, recv_buf, sizeof(recv_buf));
/* Enable the DMA transfer for the receiver request by setting the DMAR bit
in the UART CR3 register */
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR); // UART 控制寄存器3,bit6 使能/禁止 DMA 接收
CPU 使能 DMA 传输后,活由 DMA 来干,CPU 就去忙其它事情了。
演示:
模式:循环、外设地址不自增,内存地址自增
c
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; // // DMA 模式:循环
演示:
内存到外设
模式:单次
c
hdma_usart1_tx.Instance = DMA1_Channel4;
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; // 内存到外设
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不自增
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址自增
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据位宽:字节
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 内存数据位宽:字节
hdma_usart1_tx.Init.Mode = DMA_NORMAL; // 单次
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
c
uint8_t buf[10] = "hello";
HAL_UART_Transmit_DMA(&huart1, buf, sizeof(buf));
DMA 将内存数据 buf[10] 一个字节一个字节地搬运到串口的数据寄存器,搬运时,内存地址按字节递增,外设地址保持不变。
模式:循环
c
hdma_usart1_tx.Init.Mode = DMA_CIRCULAR; // 循环
即 DMA 一遍又一遍地将 buf[10] 的数据按字节方式依次拷贝到串口的数据寄存器 DR 中。
由于 DMA 搬运效率太高,串口输出太快,可以看到串口助手接收计数增长很猛,但显示的数据不多,串口助手的bug?
将波特率提高到 1500000 再跑下效果
可以看到 DMA 的效率是非常高的。
内存到内存
模式:单次,源地址自增,内存地址自增
c
/* Configure DMA request hdma_memtomem_dma1_channel1 on DMA1_Channel1 */
hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;
hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE; // 源地址自增
hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE; // 目的地址自增
hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_LOW;
演示:
模式:单次,源地址自增,内存地址不自增
c
hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE; // 源地址自增
hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE; // 目的地址自增
模式:单次,源地址不自增,内存地址自增