前言:Cube 软件包的提供,极大的降低了开发难度。使用者在开发的过程中,只需参考 Cube 包中提供的例
程就能快速的实现对应功能开发。开发者为了快速开发 UART 功能,参考 Cube 包中的 UART 例程,
并根据应用情况,扩展了另一组 UART 接口。但是在应用过程中,发现两路 UART 不能共存。本文分
析了这种情况出现的原因。
背景介绍
在 Cube 软件包中 CDC 例程实现了虚拟串口通信功能,数据传输链路如下图所示。
在开发者的应用中,需要实现下图数据传输链路。根据应用需求,推荐参考 Cube 软件包中的 USB
CDC 例程。
问题描述
问题复现平台: STM324xG-Eval
例程路径:
STM32Cube_FW_F4_V1.15.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalon
e
在对 CDC 例程了解后,参考已有的 UART 应用,新增了一路 UART。对应初始化程序如下所示。其中
USARTx 为例程原有部分,而 USARTy 为新增的一路 UART 初始化部分。
void HAL_UART_MspInit(UART_HandleTypeDef *huart) { static DMA_HandleTypeDef hdma_tx; GPIO_InitTypeDef GPIO_InitStruct; if(huart->Instance == USARTx ) { /*##-1- Enable peripherals and GPIO Clocks ####################*/ /* Enable GPIO clock */ USARTx_TX_GPIO_CLK_ENABLE(); USARTx_RX_GPIO_CLK_ENABLE(); /* Enable USARTx clock */ USARTx_CLK_ENABLE(); /* Enable DMAx clock */ DMAx_CLK_ENABLE(); /*##-2- Configure peripheral GPIO #############################*/ /* UART TX GPIO pin configuration */ GPIO_InitStruct.Pin = USARTx_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = USARTx_TX_AF; HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct); /* UART RX GPIO pin configuration */ GPIO_InitStruct.Pin = USARTx_RX_PIN; GPIO_InitStruct.Alternate = USARTx_RX_AF; HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct); /*##-3- Configure the NVIC for UART ###########################*/ HAL_NVIC_SetPriority(USARTx_IRQn, 5, 0); HAL_NVIC_EnableIRQ(USARTx_IRQn); /*##-4- Configure the DMA streams #############################*/ /* Configure the DMA handler for Transmission process */ hdma_tx.Instance = USARTx_TX_DMA_STREAM; hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL; hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx.Init.Mode = DMA_NORMAL; hdma_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx.Init.MemBurst = DMA_MBURST_INC4; hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4; HAL_DMA_Init(&hdma_tx); /* Associate the initialized DMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx); /*##-5- Configure the NVIC for DMA ############################*/ /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */ HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 6, 0); HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn); } if(huart->Instance == USARTy ) { /*##-1- Enable peripherals and GPIO Clocks ####################*/ /* Enable GPIO clock */ USARTy_TX_GPIO_CLK_ENABLE(); USARTy_RX_GPIO_CLK_ENABLE(); /* Enable USARTx clock */ USARTy_CLK_ENABLE(); /* Enable DMAx clock */ DMAy_CLK_ENABLE(); /*##-2- Configure peripheral GPIO #############################*/ /* UART TX GPIO pin configuration */ GPIO_InitStruct.Pin = USARTy_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = USARTy_TX_AF; HAL_GPIO_Init(USARTy_TX_GPIO_PORT, &GPIO_InitStruct); /* UART RX GPIO pin configuration */ GPIO_InitStruct.Pin = USARTy_RX_PIN; GPIO_InitStruct.Alternate = USARTy_RX_AF; HAL_GPIO_Init(USARTy_RX_GPIO_PORT, &GPIO_InitStruct); /*##-3- Configure the NVIC for UART ###########################*/ HAL_NVIC_SetPriority(USARTy_IRQn, 7, 0); HAL_NVIC_EnableIRQ(USARTy_IRQn); /*##-4- Configure the DMA streams #############################*/ /* Configure the DMA handler for Transmission process */ hdma_tx.Instance = USARTy_TX_DMA_STREAM; hdma_tx.Init.Channel = USARTy_TX_DMA_CHANNEL; hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx.Init.Mode = DMA_NORMAL; hdma_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx.Init.MemBurst = DMA_MBURST_INC4; hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4; HAL_DMA_Init(&hdma_tx); /* Associate the initialized DMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx); /*##-5- Configure the NVIC for DMA ############################*/ /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */ HAL_NVIC_SetPriority(USARTy_DMA_TX_IRQn, 8, 0); HAL_NVIC_EnableIRQ(USARTy_DMA_TX_IRQn); } …
在之前的 UART 发送和接收处,同样新增了一路 UART 的发送和接收。为了方便描述,这里不对应用
层面进行描述。而是直接在例程中,时钟配置后执行下述语句,复现问题。
#ifdef USART_X_INIT_ENABLE UartHandle.Instance = USARTx; … if(HAL_UART_Init(&UartHandle) != HAL_OK) … if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)UserTxBuffer, 1) != HAL_OK) … #endif // USART_X_INIT_ENABLE #ifdef USART_Y_INIT_ENABLE UartHandley.Instance = USARTy; … if(HAL_UART_Init(&UartHandley) != HAL_OK) … if(HAL_UART_Receive_IT(&UartHandley, (uint8_t *)UserTxBuffer, 1) != HAL_OK) … #endif //USART_Y_INIT_ENABLE while(1) { #ifdef USART_X_ENABLE HAL_UART_Transmit_DMA(&UartHandle, “UARTx Test”, 11); #endif //USART_X_ENABLE #ifdef USART_Y_ENABLE HAL_UART_Transmit_DMA(&UartHandley,”UARTy Test”, 11); #endif // USART_Y_ENABLE HAL_Delay(100); }
结果如下表。
问题分析
不同 UART 接口相互独立。UART IP 设计上,不同 UART 同时发送不会相互影响。初步判断应该是
UART 初始化过程中,存在冲突。
通过对结果分析,在 UARTx 和 UARTy 都进行初始化后,只执行 UARTx DMA 发送时,执行异常;只
执行 UARTy DMA 发送时,执行正常。而在初始化中,UARTx 先于 UARTy 初始化,进一步确认是初
始化时,存在冲突。并且将问题范围缩小到 HAL_UART_MspInit()函数中。
在线调试,发现在执行 UARTy 初始化时(UARTx 已经在之前初始化结束),UARTx 发送 DMA 对应
的 Instant 和 Channel 值都被改变。其中 Instant 中保存着对应 DMA 的寄存器基地址。
仔细检查 HAL_UART_MspInit()函数,发现对于 DMA 的初试化,两个 UART 共用了例程中原有的
DMA 句柄变量,为上述程序中的 static DMA_HandleTypeDef hdma_tx;
而在 HAL_UART_Transmit_DMA 函数中,会对对应的 DMA 进行配置并使能,而配置过程会依据
DMA 句柄中的参数。从而导致发送异常。
对问题的产生原因清楚后,在新增 UART 接口的时候,初始化函数 HAL_UART_MspInit()中同样需要
新增 DMA 句柄变量,并利用在新增 UART 的 DMA 初始化中。如下所示。
void HAL_UART_MspInit(UART_HandleTypeDef *huart) { static DMA_HandleTypeDef hdma_tx; static DMA_HandleTypeDef hdma_tx_y; … if(huart->Instance == USARTy ) { … hdma_tx_y.Instance = USARTy_TX_DMA_STREAM; hdma_tx_y.Init.Channel = USARTy_TX_DMA_CHANNEL; hdma_tx_y.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx_y.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx_y.Init.MemInc = DMA_MINC_ENABLE; hdma_tx_y.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx_y.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx_y.Init.Mode = DMA_NORMAL; hdma_tx_y.Init.Priority = DMA_PRIORITY_LOW; hdma_tx_y.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx_y.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx_y.Init.MemBurst = DMA_MBURST_INC4; hdma_tx_y.Init.PeriphBurst = DMA_PBURST_INC4; HAL_DMA_Init(&hdma_tx_y); /* Associate the initialized DMA handle to the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx_y); … }
总结
所描述的问题,可以归结为一个开发漏洞。而导致这个开发漏洞的原因,更多的可能是在参考 Cube 例
程时,对于各函数的了解以及变量的使用情况,没有逐个了解。建议在基于 Cube 例程进行开发时,能
够对各函数及变量等加以了解,在针对应用进行修改过程中,能够减小错误率,从而减少排错时间。