DMA 介绍

DMA(Direct Memory Access,直接内存访问)是一种计算机系统中的硬件技术,允许外部设备(如硬盘、网卡、传感器等)​直接与内存交换数据,而无需中央处理器(CPU)的全程干预。其核心目标是通过减少CPU负担、提高数据传输效率,从而优化系统整体性能。

串口DMA

AT32 MCU 芯片使用串口的DMA功能传输

打开Work Bench工具
进行这些配置

  • 配置RX DMA

在这里插入图片描述

  • 配置TX DMA

在这里插入图片描述

  • 配置中断

在这里插入图片描述

生成代码

DMA生成的代码如下 需要用到的时候要进行修改

/* init dma1 channel1 */
  wk_dma1_channel1_init();
  /* config dma channel transfer parameter */
  /* user need to modify define values DMAx_CHANNELy_XXX_BASE_ADDR and DMAx_CHANNELy_BUFFER_SIZE in at32xxx_wk_config.h */
  wk_dma_channel_config(DMA1_CHANNEL1, 
                        (uint32_t)&USART3->dt, 
                        DMA1_CHANNEL1_MEMORY_BASE_ADDR, 
                        DMA1_CHANNEL1_BUFFER_SIZE);
  dma_channel_enable(DMA1_CHANNEL1, TRUE);

  /* init dma1 channel2 */
  wk_dma1_channel2_init();
  /* config dma channel transfer parameter */
  /* user need to modify define values DMAx_CHANNELy_XXX_BASE_ADDR and DMAx_CHANNELy_BUFFER_SIZE in at32xxx_wk_config.h */
  wk_dma_channel_config(DMA1_CHANNEL2, 
                        (uint32_t)&USART3->dt, 
                        DMA1_CHANNEL2_MEMORY_BASE_ADDR, 
                        DMA1_CHANNEL2_BUFFER_SIZE);
  dma_channel_enable(DMA1_CHANNEL2, TRUE);

修改及添加的部分

  • 创建用户初始化
		dma_channel_enable(DMA1_CHANNEL1, FALSE);	
		wk_dma_channel_config(DMA1_CHANNEL1,  //重新映射接受的地址和长度
							  (uint32_t)&USART3->dt,
							  (uint32_t)g_uart3_rx_buf,
							  256);
		dma_channel_enable(DMA1_CHANNEL1, TRUE);				// 开启DMA
		dma_interrupt_enable(DMA1_CHANNEL2, DMA_FDT_INT, TRUE); // 启动DMA发送中断
		usart_interrupt_enable(RS485_USART,USART_IDLE_INT,TRUE);// 启动Usart空闲中断
	   usart_interrupt_enable(RS485_USART, USART_TDBE_INT, TRUE);//发送完成中断
  • 定义用户发送的函数
void usart_dma_data_send(uint8_t *datas, uint32_t len)
{
	RS485_TX_EN()	;//	使能发送数据
	// while(dma_flag_get(DMA1_FDT1_FLAG) == RESET);
	dma_channel_enable(DMA1_CHANNEL2, FALSE);

	wk_dma_channel_config(DMA1_CHANNEL2, (uint32_t)&USART3->dt, (uint32_t)datas, len);
	// 重新初始化DMA

	dma_channel_enable(DMA1_CHANNEL2, TRUE);
	RS485_RX_EN()	;//	使能接收数据
}
  • 定义中断函数

```c
/**
 * @brief dma 接收回调  放到中断服务函数中去
 * @param
 */
void UART_DMA_RX_CallBack(void)
{
	if (dma_interrupt_flag_get(DMA1_FDT1_FLAG))
	{
		dma_flag_clear(DMA1_FDT1_FLAG);
	}
}
/**
 * @brief dma 发送回调  放到中断服务函数中去
 * @param
 */
void UART_DMA_TX_CallBack(void)
{
	if (dma_interrupt_flag_get(DMA1_FDT2_FLAG))
	{
		Led_485_TX_Flag = 1;
		xSemaphoreGiveFromISR(g_UART3_TX_Semaphore, NULL); //操作信号量
		dma_flag_clear(DMA1_FDT2_FLAG);
		dma_channel_enable(DMA1_CHANNEL2, FALSE); // 关闭发生DMA
	}
}
//这个函数放到串口中断中
void UART_DEVICE_CallBack(void)
{
	uint32_t size=0;
	//	uint8_t uart_data = 0;
	if (usart_flag_get(USART3, USART_RDBF_FLAG) != RESET)
	{
	for (int i = 0; i < 256; i++)
	{
		xQueueSendFromISR(g_xUART3_RX_Queue, (const void *)&g_uart3_rx_buf[i], NULL);
	}

		Led_485_RX_Flag = 1;
		usart_flag_clear(USART3, USART_RDBF_FLAG);
		usart_interrupt_enable(USART3, USART_RDBF_FLAG, FALSE);
	}

	// 发送中断
	if (usart_interrupt_flag_get(USART3, USART_TDBE_FLAG) != RESET) // 发送空闲
	{
		Led_485_TX_Flag = 1;
		xSemaphoreGiveFromISR(g_UART3_TX_Semaphore, NULL);
		usart_flag_clear(USART3, USART_TDBE_FLAG);
		 usart_interrupt_enable(USART3, USART_TDBE_INT, FALSE);
	}
	if (usart_interrupt_flag_get(USART3, USART_IDLEF_FLAG) != RESET) 
	{
		 dma_channel_enable(DMA1_CHANNEL1, FALSE);	
		size = dma_data_number_get(DMA1_CHANNEL1);
		for (int i = 0; i < 256-size; i++)
		{
			xQueueSendFromISR(g_xUART3_RX_Queue, (const void *)&g_uart3_rx_buf[i], NULL);
		}
		
		wk_dma_channel_config(DMA1_CHANNEL1,
							  (uint32_t)&USART3->dt,
							  (uint32_t)g_uart3_rx_buf,
							  256);
		Led_485_RX_Flag = 1;
		dma_flag_clear(DMA1_FDT1_FLAG);
		dma_channel_enable(DMA1_CHANNEL1, TRUE);	
		usart_flag_clear(USART3, USART_IDLEF_FLAG);
	}
}

天坑

注意在上面这个函数中不添加 重新映射的代码 每次收到的数据长度都是不固定的

在这里插入图片描述
这个函数也是迷惑用户 明明写的是接受多少数据长度 其实 DMA空间中剩余的长度,这个bug困扰了我两天 一直以为对消息队列操作有问题 导致我解析数据从第二帧开始不对。仿真也是看来很久才发现。
在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐