【温酒笔记】DMA

时间:2024-11-01 07:17:13

参考文档:野火STM32F103
网友资料整理

1. Direct Memory Access-直接内存访问

DMA控制器独立于内核
是一个单独的外设

  • DMA1有7个通道
  • DMA2有5个通道
  • DMA有四个等级,非常高,高,中,低四个优先级
  • 如果优先等级相同,通道编号越小优先级越高
  • 数据流向: 存储器到存储器,存储器到外设,外设到存储器
  • 单次传输或循环传输
  • 传输过半中断,传输完成中断,传输错误中断

2. 串口示例

  1. 初始化

// 定义一个GPIO初始化结构体,并初始化为0
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 检查当前USART实例是否为USART1
if(uartHandle->Instance==USART1)
{
  // 用户自定义代码区域开始(USART1_MspInit 0)
  /* USER CODE BEGIN USART1_MspInit 0 */

  // 用户自定义代码区域结束(USART1_MspInit 0)
  /* USER CODE END USART1_MspInit 0 */

  // 使能USART1时钟
  __HAL_RCC_USART1_CLK_ENABLE();

  // 使能GPIOA时钟
  __HAL_RCC_GPIOA_CLK_ENABLE();

  // USART1 GPIO配置说明
  /**USART1 GPIO Configuration
  PA9     ------> USART1_TX
  PA10     ------> USART1_RX
  */

  // 配置GPIO引脚
  GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; // 选择PA9和PA10引脚
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;       // 设置为复用推挽输出模式
  GPIO_InitStruct.Pull = GPIO_NOPULL;           // 不使用上拉或下拉电阻
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 设置最高频率
  GPIO_InitStruct.Alternate = GPIO_AF4_USART1;  // 选择USART1复用功能
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);       // 初始化GPIO

  // USART1 DMA初始化
  // USART1_TX DMA通道初始化
  hdma_usart1_tx.Instance = DMA1_Channel2;      // 选择DMA1通道2
  hdma_usart1_tx.Init.Request = DMA_REQUEST_3;  // 选择DMA请求3
  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;       // 设置优先级为低
  if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)           // 初始化DMA
  {
    Error_Handler();                                     // 如果初始化失败,调用错误处理函数
  }

  // 将DMA与USART1_TX关联
  __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

  // USART1中断初始化
  HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);               // 设置USART1中断优先级
  HAL_NVIC_EnableIRQ(USART1_IRQn);                       // 使能USART1中断

  // 用户自定义代码区域开始(USART1_MspInit 1)
  /* USER CODE BEGIN USART1_MspInit 1 */

  // 用户自定义代码区域结束(USART1_MspInit 1)
  /* USER CODE END USART1_MspInit 1 */
}
  1. DMA发送

  /* USART1 向 DMA发出TX请求 */
     HAL_UART_Transmit_DMA(&UartHandle, (uint8_t *)SendBuff ,SENDBUFF_SIZE);
  1. 使用DMA传输完成中断
//初始化
void MX_DMA_Init(void)
{
  // 使能DMA控制器时钟
  __HAL_RCC_DMA1_CLK_ENABLE();

  // DMA中断初始化
  // 配置DMA1通道2和3的中断
  HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0); // 设置DMA1通道2和3中断的优先级为2,子优先级为0
  HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);         // 使能DMA1通道2和3的中断
}
//中断服务函数
/**
  * @brief This function handles DMA1 channel 2 and channel 3 interrupts.
  */
void DMA1_Channel2_3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 */

  /* USER CODE END DMA1_Channel2_3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_tx);
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 */

  /* USER CODE END DMA1_Channel2_3_IRQn 1 */
}
//中断回调函数-打印传输完成结果

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		printf(" dma_ok\r\n");
	}
}
//主函数
  while (1)
  {
		HAL_UART_Transmit_DMA(&huart1, Tx_buff, 10);
		HAL_Delay(1000);
  }

结果:传输完数据到中断回调函数中打印 : dma ok
在这里插入图片描述

3. 库函数备注

串口发送/接收函数
HAL_UART_Transmit():串口发送数据,使用超时管理机制
HAL_UART_Receive():串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT():串口中断模式发送
HAL_UART_Receive_IT():串口中断模式接收
HAL_UART_Transmit_DMA():串口DMA模式发送
HAL_UART_Transmit_DMA():串口DMA模式接收

串口中断函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数