STM32 HAL USART 驱动程序:这种语法是如何工作的?

Posted

技术标签:

【中文标题】STM32 HAL USART 驱动程序:这种语法是如何工作的?【英文标题】:STM32 HAL USART drivers: How does this syntax work? 【发布时间】:2018-08-07 08:25:15 【问题描述】:

我正在学习 STM32 编程并尝试在 GPIO 引脚上使用 USART 外设实现简单的异步串行通信。

HAL manual 描述了如何使用 HAL USART 驱动程序:

    声明一个 USART_HandleTypeDef 结构 实现 HAL_USART_MspInit() 启用 USART 和 GPIO 时钟 配置 GPIO 在 USART_InitTypeDef 中编程通信参数 调用 HAL_USART_Init()

在编写代码时,我声明了 USART_HandleTypeDef,本能地填充了我的 USART_InitTypeDef 结构并开始填充 HandleTypeDef:

  USART_HandleTypeDef UsartHandle;

  USART_InitTypeDef UsartInit;
  UsartInit.BaudRate   = 9600;
  UsartInit.WordLength = USART_WORDLENGTH_8B;
  UsartInit.StopBits   = USART_STOPBITS_1;
  UsartInit.Parity     = USART_PARITY_NONE;
  UsartInit.Mode       = USART_MODE_TX_RX;

  UsartHandle.Instance = USART6;
  UsartHandle.Init     = &UsartInit;
  /* do I really have to init EVERY data field? */

  HAL_USART_Init(&UsartHandle);

然后我注意到有很多数据字段要填写。参考手册和网络上的代码示例,我注意到没有人真正定义所有 USART_HandleTypeDef 字段 - 他们以某种方式将 HandleTypeDef 和 InitTypeDef 组合在一个步骤中,如下所示:

UART_HandleTypeDef UartHandle;

UartHandle.Init.BaudRate = 9600;
UartHandle.Init.WordLength = UART_DATABITS_8;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.Instance = USART1;
HAL_UART_Init(&UartHandle);

这是如何运作的?我必须学习 C 语法的哪一部分,才能理解 从哪里来 UartHandle.Init.xxx 来自? 是否有可能按照我的计划“走很长的路”?如果我不填写 HandleTypeDef 的每个数据字段,它们在哪里初始化?

PS。我没有使用 STM32 推荐的 IDE 或 CubeMX,在 Linux 上工作,使用 PlatformIO。板子:STM32F746 发现套件

PPS。我真的不确定是将这个问题放在这里还是放在电子堆栈上。如果它不适合此 stackexchange,请纠正我或将问题移到那里。

【问题讨论】:

ST“HAL”实际上并不是一个 API,它更多的是围绕“可怕的硬件寄存器”的一系列包装函数。这背后并没有什么特别的 C 语法秘密,一切都是为了理解奇怪的 ST 库。 关于您的 PPS:关于微控制器固件的问题在这两个网站上都非常热门。但请不要同时在两个站点上交叉发布!如果您有与 SW/HW 交界的问题,则应使用 EE 站点。一般来说,像微控制器驱动程序这样的问题在 EE 上会得到更好的答案。 好的!感谢 PPS 的澄清。此外,很高兴知道我不是唯一一个认为 ST 库很奇怪的人......但作为 STM32 的初学者,我不想立即深入研究裸机编程。 简单的答案。不要使用英国媒体报道。只需使用裸寄存器。 对于 uC 编程,您需要非常了解 C。花更多时间学习这门语言 你的问题表明你不懂 C。你在没有很好理解基本主题的情况下学到了一些东西 【参考方案1】:

这是如何工作的?我必须学习 C 语法的哪一部分,才能 明白那个 UartHandle.Init.xxx 是从哪里来的吗?

这是基本的 C 结构语法。 USART_HandleTypeDef 结构包含一个名为 InitUSART_InitTypeDef 结构的实例。您可以将其视为嵌套结构。您可以使用重复的“.”引用嵌套结构的成员。请注意,Init 成员不是指向USART_InitTypeDef 结构的指针。它实际上是一个完整的 USART_InitTypeDef 实例,包含在 USART_HandleTypeDef 实例中。

是否有可能像我计划的那样“从长远来看”?

是的,除非您的代码包含错误。你需要像这样做作业。

UsartHandle.Init     = UsartInit;    // Note no `&`

记住USART_HandleTypeDefInit 成员不是指针,而是一个完整的结构。因此,您需要为它分配一个完整的结构,而不是一个指针。

但请注意,当您定义 UsartInit 变量时,您正在为结构的实例分配空间。如果UsartInit 是一个函数局部变量,那么该空间很可能在堆栈上。您的初始化语句正在初始化您的结构副本。然后,当您将UsartInit 分配给UsartHandle.Init 时,编译器会创建将复制结构的全部内容的代码。复制后,如果你的UsartInit 是一个局部变量,它将超出范围并被释放。

真的没有必要为自己的USART_InitTypeDef结构体定义和分配空间,然后将整个结构体复制到UsartHandle.Init中。 UsartHandle 已经包含为其USART_InitTypeDef 成员分配的空间。所以像ST示例代码那样直接初始化UsartHandle.Init成员会更有效率。

如果我不填写 HandleTypeDef 的每个数据字段,他们从哪里得到 初始化了吗?

您不需要填写USART_HandleTypeDef 的每个数据字段。请参阅 HAL 参考手册以了解您需要初始化的内容。您可能只需要初始化InstanceInit 成员。其余成员由 HAL USART 驱动程序内部使用,它们将被驱动程序函数初始化和使用(如果有帮助,您可以将它们视为私有变量)。 API 的设计者将该结构成员命名为“Init”,以提示您这是您需要初始化的内容。 ST 示例代码进一步证明了您需要初始化什么。

[Stack Overflow 上的几位经验丰富的开发人员建议不要使用 ST HAL,并鼓励人们根据设备的参考手册开发自己的驱动程序。意识到这些开发人员拥有多年的经验,他们使用过各种微控制器系列和外围设备,他们能够理解参考手册并从头开始编写驱动程序。我同意 ST HAL 增加了一些可能对某些应用程序有害的膨胀。但我不同意初学者应该避免使用 ST HAL。 ST HAL 适用于许多应用程序,对于初学者来说,它比从头开始编写自己的驱动程序更容易使用(特别是考虑到 HAL 提供的许多示例)。]

【讨论】:

哇,非常感谢您的时间和回答!后来我注意到我把 & 放在 UsartInit 前面的错误。 “Instance”成员需要一个地址,而不是 Init >。 这个问答对于像我这样的STM32新手非常有帮助。谢谢。

以上是关于STM32 HAL USART 驱动程序:这种语法是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

STM32F429开发板用户手册第23章 STM32F429的USART串口基础知识和HAL库API

STM32F407开发板用户手册第23章 STM32F407的USART串口基础知识和HAL库API

HAL库 STM32CubeMX--USART串口通信--补充

.\Objects\Usart.axf: Error: L6218E: Undefined symbol HAL_NVIC_SetPriority (引用自stm32f4xx_hal.o)

STM32CubeMx配置USART注意的一个问题

带有 STM32 HAL 驱动程序的 FreeRTOS 中的 UART 中断