关于APM32F407配置串口DMA收发没有数据的问题记录

一.问题环境

​ 整活了一套APM32F407的板子,用了APM32F4xx_SDK_V1.4的标准外设库,正在搭建移植底层BSP框架串口部分,BSP底层配置逻辑是从STM32F407移植过来的。DMA发送时才使能通道及配置外设地址及缓存大小。

​ 串口1DMA配置过程如下:


static USART_DMAConfig_t USART_ComDmaTx(DMA_ChannelType_t channel,DMA_StreamType_t stream,uint32_t peripheralBaseAddr,uint8_t IRQChannel,uint32_t dmaFlag)
{
	USART_DMAConfig_t dmaConfig = {0};
	 
	dmaConfig.channel = channel;
	dmaConfig.stream = stream;
 
	dmaConfig.dma.channel = channel;
	dmaConfig.dma.peripheralBaseAddr = peripheralBaseAddr;			//DMA外设地址:USART数据寄存器地址
	dmaConfig.dma.memoryBaseAddr = (uint32_t)0;									//内存地址,使用时再配置
	dmaConfig.dma.dir = DMA_DIR_MEMORYTOPERIPHERAL;									//外设地址为目的地址
	dmaConfig.dma.bufferSize = (uint32_t)0;										//传输时缓冲区大小,使用时再配置
	dmaConfig.dma.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;					//外设地址固定,不递增
	dmaConfig.dma.memoryInc = DMA_MEMORY_INC_ENABLE;								//内存地址递增
	dmaConfig.dma.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_BYTE;				//外设数据格式为字节
	dmaConfig.dma.memoryDataSize = DMA_MEMORY_DATA_SIZE_BYTE;						//内存数据格式为字节
	dmaConfig.dma.loopMode = DMA_MODE_NORMAL;										//工作在正常模式,不循环
	dmaConfig.dma.priority = DMA_PRIORITY_HIGH;									//DMA传输优先级为高(VeryHigh/High/Medium/Low)
	dmaConfig.dma.fifoMode = DMA_FIFOMODE_DISABLE;										//禁能DMA的两个Memory中变量相互访问
	
	dmaConfig.dma.peripheralBurst = DMA_PERIPHERALBURST_SINGLE;						
	dmaConfig.dma.fifoThreshold = DMA_FIFOTHRESHOLD_QUARTER;
	dmaConfig.dma.memoryBurst = DMA_MEMORYBURST_SINGLE;
	
	dmaConfig.nvic.NVIC_IRQChannel = IRQChannel;
	
	dmaConfig.nvic.NVIC_IRQChannelPreemptionPriority = 6;							//抢占优先级
	dmaConfig.nvic.NVIC_IRQChannelSubPriority = 0;									//响应优先级
	dmaConfig.nvic.NVIC_IRQChannelCmd = DISABLE;									//使能中断
	 
	dmaConfig.dmaFlag = dmaFlag;
	   
	return dmaConfig;
}



static USART_DMAConfig_t USART_Com1DmaTx(void)
{
	
	DMA_ChannelType_t channel;
	
	DMA_StreamType_t stream;
	uint32_t peripheralBaseAddr;
	uint8_t IRQChannel;
	uint32_t dmaFlag;
	
	channel = DMA_CHANNEL_4;
	stream = DMA2_Stream7;
	peripheralBaseAddr = (uint32_t)(&(USART1->DATA));
	IRQChannel = DMA2_STR7_IRQn;
	dmaFlag = DMA_INT_TCIFLG7;
	
	return USART_ComDmaTx(channel,stream,peripheralBaseAddr,IRQChannel,dmaFlag);
};



static void USART_DmaTxConfig(USART_t USART)
{
	NVIC_InitType_t		NVIC_InitStructure;
	
	 /* 使能时钟 */
    if ((uint32_t)USART->config.dmaTx.stream > (uint32_t)DMA2)
    {
		GPIO_RcmAHB1PeriphClockCmd(RCM_AHB1_PERIPH_DMA2, ENABLE);		//开启DMA时钟,用于USART发射
    }
    else
    {
    	GPIO_RcmAHB1PeriphClockCmd(RCM_AHB1_PERIPH_DMA1, ENABLE);		//开启DMA时钟,用于USART发射
    }
     
	 
	NVIC_InitStructure = USART->config.dmaTx.nvic;
	
	 
	DMA_Disable(USART->config.dmaTx.stream);									//先禁止DMA通道,若之前有DMA传输,则会终止 ,必须先关闭DMA通道才能配置
	DMA_Reset(USART->config.dmaTx.stream);														//复位DMA1通道2的初始化
	  
	  
	DMA_Config(USART->config.dmaTx.stream,&USART->config.dmaTx.dma);		//DMA初始化
 
	   
	if (NVIC_InitStructure.NVIC_IRQChannelCmd)  
	{  
		NVIC_EnableIRQRequest((IRQn_Type)NVIC_InitStructure.NVIC_IRQChannel,NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority,NVIC_InitStructure.NVIC_IRQChannelSubPriority);									//使能中断
	}  
		
	DMA_ClearStatusFlag(USART->config.dmaTx.stream,USART->config.dmaTx.dmaFlag);	//清除 全部标志
		
	DMA_EnableInterrupt(USART->config.dmaTx.stream,DMA_INT_TCIFLG);			//使能DMA通道4传输完成中断
	  
	DMA_Disable(USART->config.dmaTx.stream);								//更新内存地址和传输大小之后再使能
 
	 							 												
}



void USART_TypeInit(USART_t USART)
{
	 
	USART_Reset(USART->usart);
	
	USART_DisableDMA(USART->usart,USART_DMA_TX);
	USART_DisableDMA(USART->usart,USART_DMA_RX);
	 
	USART_Construct(USART);
	 
	USART_RccConfig(USART);
	
	USART_GpioConfig(USART);
	
	
	USART_Disable(USART->usart);
	
	USART_InitConfig(USART);
	
	USART_NvicConfig(USART);
	
	
	if (USART->config.isDmaTxEnable)
	{
		USART_DmaTxConfig(USART);
		USART_EnableDMA(USART->usart, USART_DMA_TX);
	}
	if (USART->config.isDmaRxEnable)
	{
		USART_DmaRxConfig(USART);
		USART_EnableDMA(USART->usart, USART_DMA_RX);
	}
}


static void USART_SendMessage(USART_t USART, uint8_t *sendBuf, uint32_t len)
{
	if (USART->config.isRs485Enable)
	{
		USART_Rs485TxEnable(USART);
	}
	
	if (USART->config.isDmaTxEnable)
	{
		DMA_Disable(USART->config.dmaTx.stream);			//先禁止DMA通道,若之前有DMA传输,则会终止 ,必须先关闭DMA通道才能配置
		
		

		while (DMA_ReadCmdStatus(USART->config.dmaTx.stream) != DISABLE);		//确保DMA可以被设置
     
	//=======直接操作寄存器更新内存地址和传输大小-----------------------------------------------------------
		
		 
		USART->config.dmaTx.stream->M0ADDR = (uint32_t)(sendBuf);	//更新内存地址
		USART->config.dmaTx.stream->NDATA = len;	//更新传输时缓冲区大小
 
		DMA_ClearStatusFlag(USART->config.dmaTx.stream,USART->config.dmaTx.dmaFlag);//清除Channel2全部标志,主要是清除传输完成标志
				
		DMA_Enable(USART->config.dmaTx.stream);
		
		
		//等待DMA发送结束
	 	while(DMA_ReadIntFlag(USART->config.dmaTx.stream,USART->config.dmaTx.dmaFlag) == RESET);
		
        //清除标志
		DMA_ClearStatusFlag(USART->config.dmaTx.stream,USART->config.dmaTx.dmaFlag);
		
		 
		if (USART->config.isRs485Enable)
		{
			USART_Rs485TxDisable(USART);
		}
		 
		USART->send->flag = true;
		 
    }
}

二.问题现象

​ 串口DMA接收和中断接收都没有问题,发送中断也没有问题。但发送配置为DMA就不能发送数据。所有串口端口配置都有这个问题,确定同样的板子刷入STM32F407的的程序也能正常使用,唯独使用APM的外设库就不行。

三.原因分析

​ 当同时使用APM32F4的DMA接收和发送时,虽然同时配置了USART_EnableDMA(USART->usart, USART_DMA_TX)和USART_EnableDMA(USART->usart, USART_DMA_RX),但USART_EnableDMA这个函数你看他是怎么实现的:

/*!
 * @brief     Enables the USART DMA interface
 *
 * @param     usart:   Select the USART or the UART peripheral
 *
 * @param     dmaReq:  Specifies the DMA request
 *                     This parameter can be one of the following values:
 *                     @arg USART_DMA_TX    : USART DMA receive request
 *                     @arg USART_DMA_RX    : USART DMA transmit request
 *                     @arg USART_DMA_TX_RX : USART DMA transmit/receive request
 *
 * @retval    None
 *
 * @note      The usart can be USART1, USART2, USART3, UART4, UART5, USART6, UART7 and UART8
 */
void USART_EnableDMA(USART_T* usart, USART_DMA_T dmaReq)
{
    usart->CTRL3_B.DMARXEN = dmaReq & 0x01;
    usart->CTRL3_B.DMATXEN = dmaReq >> 1;
}

是不是很明显的看出了问题所在,原来usart->CTRL3_B.DMARXEN = dmaReq & 0x01;这一句用的是直接赋值操作,而不是我们熟悉的按位或,这样当配置了USART_EnableDMA(USART->usart, USART_DMA_RX)后,原来的USART_EnableDMA(USART->usart, USART_DMA_TX);就被重置了,所以只能接收不能发送。

四.问题解决

发现问题后,对代码初始化部分进行改进,经过测试,正常收发。代码如下:

	
	if (USART->config.isDmaTxEnable && USART->config.isDmaRxEnable)
	{
		USART_DmaTxConfig(USART);
		USART_DmaRxConfig(USART);
		USART_EnableDMA(USART->usart, USART_DMA_TX_RX);
	}
	
	else if (USART->config.isDmaTxEnable)
	{
		USART_DmaTxConfig(USART);
		USART_EnableDMA(USART->usart, USART_DMA_TX);
	}
	else if (USART->config.isDmaRxEnable)
	{
		USART_DmaRxConfig(USART);
		USART_EnableDMA(USART->usart, USART_DMA_RX);
	}
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值