[应用相关] 增加 UART 接口应用时的异常分析

[复制链接]
1569|10
 楼主| 可怜的小弗朗士 发表于 2021-11-10 10:11 | 显示全部楼层 |阅读模式
Cube 软件包的提供,极大的降低了开发难度。 使用者在开发的过程中, 只需参考 Cube 包中提供的例
程就能快速的实现对应功能开发。 开发者为了快速开发 UART 功能,参考 Cube 包中的 UART 例程,
并根据应用情况, 扩展了另一组 UART 接口。 但是在应用过程中, 发现两路 UART 不能共存。 本文分
析了这种情况出现的原因。  

 楼主| 可怜的小弗朗士 发表于 2021-11-10 10:12 | 显示全部楼层
背景介绍
Cube 软件包中 CDC 例程实现了虚拟串口通信功能, 数据传输链路如下图所示。  

12958618b2a6244517.png
在开发者的应用中, 需要实现下图数据传输链路。 根据应用需求, 推荐参考 Cube 软件包中的 USB
CDC 例程。
11321618b2a7f21ff9.png
 楼主| 可怜的小弗朗士 发表于 2021-11-10 10:14 | 显示全部楼层
问题描述
问题复现平台: STM324xG-Eval
例程路径:
STM32Cube_FW_F4_V1.15.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalone  

在对 CDC 例程了解后, 参考已有的 UART 应用, 新增了一路 UART。 对应初始化程序如下所示。 其中
USARTx 为例程原有部分,而 USARTy 为新增的一路 UART 初始化部分。
  1. void HAL_UART_MspInit(UART_HandleTypeDef *huart)
  2. {
  3. static DMA_HandleTypeDef hdma_tx;
  4. GPIO_InitTypeDef GPIO_InitStruct;
  5. if(huart->Instance == USARTx )
  6. {
  7. /*##-1- Enable peripherals and GPIO Clocks #################################*/
  8. /* Enable GPIO clock */
  9. USARTx_TX_GPIO_CLK_ENABLE();
  10. USARTx_RX_GPIO_CLK_ENABLE();
  11. /* Enable USARTx clock */
  12. USARTx_CLK_ENABLE();
  13. /* Enable DMAx clock */
  14. DMAx_CLK_ENABLE();
  15. /*##-2- Configure peripheral GPIO ##########################################*/
  16. /* UART TX GPIO pin configuration */
  17. GPIO_InitStruct.Pin = USARTx_TX_PIN;
  18. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  19. GPIO_InitStruct.Pull = GPIO_PULLUP;
  20. GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
  21. GPIO_InitStruct.Alternate = USARTx_TX_AF;
  22. HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
  23. /* UART RX GPIO pin configuration */
  24. GPIO_InitStruct.Pin = USARTx_RX_PIN;
  25. GPIO_InitStruct.Alternate = USARTx_RX_AF;
  26. HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
  27. /*##-3- Configure the NVIC for UART ########################################*/
  28. HAL_NVIC_SetPriority(USARTx_IRQn, 5, 0);
  29. HAL_NVIC_EnableIRQ(USARTx_IRQn);
  30. /*##-4- Configure the DMA streams ##########################################*/
  31. /* Configure the DMA handler for Transmission process */
  32. hdma_tx.Instance = USARTx_TX_DMA_STREAM;
  33. hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL;
  34. hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  35. hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  36. hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
  37. hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  38. hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  39. hdma_tx.Init.Mode = DMA_NORMAL;
  40. hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
  41. hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  42. hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  43. hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
  44. hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
  45. HAL_DMA_Init(&hdma_tx);
  46. /* Associate the initialized DMA handle to the UART handle */
  47. __HAL_LINKDMA(huart, hdmatx, hdma_tx);
  48. /*##-5- Configure the NVIC for DMA #########################################*/
  49. /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
  50. HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 6, 0);
  51. HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);
  52. }
  53. if(huart->Instance == USARTy )
  54. {
  55. /*##-1- Enable peripherals and GPIO Clocks #################################*/
  56. /* Enable GPIO clock */
  57. USARTy_TX_GPIO_CLK_ENABLE();
  58. USARTy_RX_GPIO_CLK_ENABLE();
  59. /* Enable USARTx clock */
  60. USARTy_CLK_ENABLE();
  61. /* Enable DMAx clock */
  62. DMAy_CLK_ENABLE();
  63. /*##-2- Configure peripheral GPIO ##########################################*/
  64. /* UART TX GPIO pin configuration */
  65. GPIO_InitStruct.Pin = USARTy_TX_PIN;
  66. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  67. GPIO_InitStruct.Pull = GPIO_PULLUP;
  68. GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
  69. GPIO_InitStruct.Alternate = USARTy_TX_AF;
  70. HAL_GPIO_Init(USARTy_TX_GPIO_PORT, &GPIO_InitStruct);
  71. /* UART RX GPIO pin configuration */
  72. GPIO_InitStruct.Pin = USARTy_RX_PIN;
  73. GPIO_InitStruct.Alternate = USARTy_RX_AF;
  74. HAL_GPIO_Init(USARTy_RX_GPIO_PORT, &GPIO_InitStruct);
  75. /*##-3- Configure the NVIC for UART ########################################*/
  76. HAL_NVIC_SetPriority(USARTy_IRQn, 7, 0);
  77. HAL_NVIC_EnableIRQ(USARTy_IRQn);
  78. /*##-4- Configure the DMA streams ##########################################*/
  79. /* Configure the DMA handler for Transmission process */
  80. hdma_tx.Instance = USARTy_TX_DMA_STREAM;
  81. hdma_tx.Init.Channel = USARTy_TX_DMA_CHANNEL;
  82. hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  83. hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  84. hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
  85. hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  86. hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  87. hdma_tx.Init.Mode = DMA_NORMAL;
  88. hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
  89. hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  90. hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  91. hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
  92. hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
  93. HAL_DMA_Init(&hdma_tx);
  94. /* Associate the initialized DMA handle to the UART handle */
  95. __HAL_LINKDMA(huart, hdmatx, hdma_tx);
  96. /*##-5- Configure the NVIC for DMA #########################################*/
  97. /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
  98. HAL_NVIC_SetPriority(USARTy_DMA_TX_IRQn, 8, 0);
  99. HAL_NVIC_EnableIRQ(USARTy_DMA_TX_IRQn);
  100. } …
  101. }

之前的 UART 发送和接收处, 同样新增了一路 UART 的发送和接收。 为了方便描述, 这里不对应用
层面进行描述。 而是直接在例程中, 时钟配置后执行下述语句, 复现问题。
  1. #ifdef USART_X_INIT_ENABLE
  2. UartHandle.Instance = USARTx;

  3. if(HAL_UART_Init(&UartHandle) != HAL_OK)

  4. if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)UserTxBuffer, 1) != HAL_OK)

  5. #endif // USART_X_INIT_ENABLE
  6. #ifdef USART_Y_INIT_ENABLE
  7. UartHandley.Instance = USARTy;

  8. if(HAL_UART_Init(&UartHandley) != HAL_OK)

  9. if(HAL_UART_Receive_IT(&UartHandley, (uint8_t *)UserTxBuffer, 1) != HAL_OK)

  10. #endif //USART_Y_INIT_ENABLE
  11. while(1)
  12. {
  13. #ifdef USART_X_ENABLE
  14. HAL_UART_Transmit_DMA(&UartHandle, “UARTx Test”, 11);
  15. #endif //USART_X_ENABLE
  16. #ifdef USART_Y_ENABLE
  17. HAL_UART_Transmit_DMA(&UartHandley,”UARTy Test”, 11);
  18. #endif // USART_Y_ENABLE
  19. HAL_Delay(100);
  20. }
结果如下表。
87542618b2ae69f133.png
 楼主| 可怜的小弗朗士 发表于 2021-11-10 10:15 | 显示全部楼层
问题分析
不同 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 初始化中。 如下所示。
  1. void HAL_UART_MspInit(UART_HandleTypeDef *huart)
  2. {
  3. static DMA_HandleTypeDef hdma_tx;
  4. static DMA_HandleTypeDef hdma_tx_y;

  5. if(huart->Instance == USARTy )
  6. { …
  7. hdma_tx_y.Instance = USARTy_TX_DMA_STREAM;
  8. hdma_tx_y.Init.Channel = USARTy_TX_DMA_CHANNEL;
  9. hdma_tx_y.Init.Direction = DMA_MEMORY_TO_PERIPH;
  10. hdma_tx_y.Init.PeriphInc = DMA_PINC_DISABLE;
  11. hdma_tx_y.Init.MemInc = DMA_MINC_ENABLE;
  12. hdma_tx_y.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  13. hdma_tx_y.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  14. hdma_tx_y.Init.Mode = DMA_NORMAL;
  15. hdma_tx_y.Init.Priority = DMA_PRIORITY_LOW;
  16. hdma_tx_y.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  17. hdma_tx_y.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  18. hdma_tx_y.Init.MemBurst = DMA_MBURST_INC4;
  19. hdma_tx_y.Init.PeriphBurst = DMA_PBURST_INC4;
  20. HAL_DMA_Init(&hdma_tx_y);
  21. /* Associate the initialized DMA handle to the UART handle */
  22. __HAL_LINKDMA(huart, hdmatx, hdma_tx_y);

  23. }


 楼主| 可怜的小弗朗士 发表于 2021-11-10 10:16 | 显示全部楼层
总结
所描述的问题, 可以归结为一个开发漏洞。 而导致这个开发漏洞的原因, 更多的可能是在参考 Cube
程时, 对于各函数的了解以及变量的使用情况, 没有逐个了解。 建议在基于
Cube 例程进行开发时,能
够对各函数及变量等加以了解,在针对应用进行修改过程中, 能够减小错误率, 从而减少排错时间。
  

wowu 发表于 2021-12-6 14:25 | 显示全部楼层
是硬件本身的漏洞吗
xiaoqizi 发表于 2021-12-6 14:29 | 显示全部楼层
我非常喜欢用这个软件
木木guainv 发表于 2021-12-6 14:40 | 显示全部楼层
是的 功能很强大
磨砂 发表于 2021-12-6 14:41 | 显示全部楼层
无论用什么 都需要对其本身很了解
tpgf 发表于 2021-12-6 14:44 | 显示全部楼层
初始化代码很多啊
晓伍 发表于 2021-12-6 14:45 | 显示全部楼层
UART还有 IP吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

763

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部