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

2024-05-29 18:12

本文主要是介绍关于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);}

这篇关于关于APM32F407配置串口DMA收发没有数据的问题记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1014265

相关文章

springboot3.4和mybatis plus的版本问题的解决

《springboot3.4和mybatisplus的版本问题的解决》本文主要介绍了springboot3.4和mybatisplus的版本问题的解决,主要由于SpringBoot3.4与MyBat... 报错1:spring-boot-starter/3.4.0/spring-boot-starter-

MySQL InnoDB引擎ibdata文件损坏/删除后使用frm和ibd文件恢复数据

《MySQLInnoDB引擎ibdata文件损坏/删除后使用frm和ibd文件恢复数据》mysql的ibdata文件被误删、被恶意修改,没有从库和备份数据的情况下的数据恢复,不能保证数据库所有表数据... 参考:mysql Innodb表空间卸载、迁移、装载的使用方法注意!此方法只适用于innodb_fi

Keepalived+Nginx双机配置小结

《Keepalived+Nginx双机配置小结》本文主要介绍了Keepalived+Nginx双机配置小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1.1 软硬件要求1.2 部署前服务器配置调优1.3 Nginx+Keepalived部署1.3

mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据

《mysql通过frm和ibd文件恢复表_mysql5.7根据.frm和.ibd文件恢复表结构和数据》文章主要介绍了如何从.frm和.ibd文件恢复MySQLInnoDB表结构和数据,需要的朋友可以参... 目录一、恢复表结构二、恢复表数据补充方法一、恢复表结构(从 .frm 文件)方法 1:使用 mysq

mysql8.0无备份通过idb文件恢复数据的方法、idb文件修复和tablespace id不一致处理

《mysql8.0无备份通过idb文件恢复数据的方法、idb文件修复和tablespaceid不一致处理》文章描述了公司服务器断电后数据库故障的过程,作者通过查看错误日志、重新初始化数据目录、恢复备... 周末突然接到一位一年多没联系的妹妹打来电话,“刘哥,快来救救我”,我脑海瞬间冒出妙瓦底,电信火苲马扁.

Apache伪静态(Rewrite).htaccess文件详解与配置技巧

《Apache伪静态(Rewrite).htaccess文件详解与配置技巧》Apache伪静态(Rewrite).htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令,主要的... 一、.htAccess的基本作用.htaccess是一个纯文本文件,它里面存放着Apache服务器

golang获取prometheus数据(prometheus/client_golang包)

《golang获取prometheus数据(prometheus/client_golang包)》本文主要介绍了使用Go语言的prometheus/client_golang包来获取Prometheu... 目录1. 创建链接1.1 语法1.2 完整示例2. 简单查询2.1 语法2.2 完整示例3. 范围值

nginx配置多域名共用服务器80端口

《nginx配置多域名共用服务器80端口》本文主要介绍了配置Nginx.conf文件,使得同一台服务器上的服务程序能够根据域名分发到相应的端口进行处理,从而实现用户通过abc.com或xyz.com直... 多个域名,比如两个域名,这两个域名其实共用一台服务器(意味着域名解析到同一个IP),一个域名为abc

nginx生成自签名SSL证书配置HTTPS的实现

《nginx生成自签名SSL证书配置HTTPS的实现》本文主要介绍在Nginx中生成自签名SSL证书并配置HTTPS,包括安装Nginx、创建证书、配置证书以及测试访问,具有一定的参考价值,感兴趣的可... 目录一、安装nginx二、创建证书三、配置证书并验证四、测试一、安装nginxnginx必须有"-

springboot rocketmq配置生产者和消息者的步骤

《springbootrocketmq配置生产者和消息者的步骤》本文介绍了如何在SpringBoot中集成RocketMQ,包括添加依赖、配置application.yml、创建生产者和消费者,并展... 目录1. 添加依赖2. 配置application.yml3. 创建生产者4. 创建消费者5. 使用在