STM32 HAL USART 驱动程序:此语法如何工作?
STM32 HAL USART drivers: How does this syntax work?
我正在学习 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 从哪里来的?
是否有可能像我计划的那样"the long way"?如果我不填充 HandleTypeDef 的每个数据字段,它们在哪里初始化?
PS. 我没有使用 STM32 推荐的 IDE 或 CubeMX,在 Linux 上工作,使用 PlatformIO。电路板:STM32F746 探索套件
PPS. 我真的不确定是把这个问题放在这里还是放在电子堆栈上。如果它不适合这个 stackexchange,请纠正我或将问题移到那里。
How does this work? What part of C syntax do I have to learn, to
understand where did that UartHandle.Init.xxx come from?
这是基本的 C 结构语法。 USART_HandleTypeDef
结构包含一个名为 Init
的 USART_InitTypeDef
结构的实例。您可以将其视为嵌套结构。您可以使用重复的“.
”引用嵌套结构的成员。请注意,Init
成员不是 指向 USART_InitTypeDef
结构的指针 。它实际上是包含在 USART_HandleTypeDef
个实例中的完整 USART_InitTypeDef
个实例。
Is it possible to do it "the long way", as I planned to?
是的,除非您的代码包含错误。您需要像这样进行分配。
UsartHandle.Init = UsartInit; // Note no `&`
请记住,USART_HandleTypeDef
的 Init
成员不是指针,而是一个完整的结构。因此你需要给它分配一个完整的结构,而不是一个指针。
但是请注意,当您定义 UsartInit
变量时,您是在为结构的一个实例分配 space。如果 UsartInit
是函数局部变量,那么 space 可能在堆栈上。您的初始化语句正在初始化您的结构副本。然后,当您将 UsartInit
分配给 UsartHandle.Init
时,编译器会创建将复制结构的全部内容的代码。复制后,如果你的 UsartInit
是局部变量,它将超出范围并被释放。
确实没有必要为自己的USART_InitTypeDef
结构定义和分配space,然后将整个结构复制到UsartHandle.Init
中。 UsartHandle
已经包含为其 USART_InitTypeDef
成员分配的 space。所以直接初始化 UsartHandle.Init
成员更有效,就像 ST 示例代码那样。
If I don't fill every datafield of HandleTypeDef, where do they get
initialized?
您不需要填写 USART_HandleTypeDef
的每个数据字段。请参阅 HAL 参考手册以了解您需要初始化的内容。您可能只需要初始化 Instance
和 Init
成员。其余成员由 HAL USART 驱动程序在内部使用,它们将由驱动程序函数初始化和使用(如果有帮助,您可以将它们视为私有变量)。 API 的设计者将该结构成员命名为 "Init" 以提示您这是您需要初始化的内容。 ST 示例代码提供了您需要初始化的进一步证据。
[Stack Overflow 上的几位经验丰富的开发人员建议不要使用 ST HAL,并鼓励人们根据设备的参考手册开发自己的驱动程序。意识到这些开发人员拥有多年的经验,他们使用过各种微控制器系列和外围设备,并且他们能够理解参考手册并从头开始编写驱动程序。我同意 ST HAL 增加了一些可能对某些应用程序有害的膨胀。但我不同意初学者应该避免使用 ST HAL。 ST HAL 适用于许多应用程序,对于初学者来说比从头编写自己的驱动程序更容易使用(特别是考虑到 HAL 提供的许多示例)。]
我正在学习 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 从哪里来的?
是否有可能像我计划的那样"the long way"?如果我不填充 HandleTypeDef 的每个数据字段,它们在哪里初始化?
PS. 我没有使用 STM32 推荐的 IDE 或 CubeMX,在 Linux 上工作,使用 PlatformIO。电路板:STM32F746 探索套件
PPS. 我真的不确定是把这个问题放在这里还是放在电子堆栈上。如果它不适合这个 stackexchange,请纠正我或将问题移到那里。
How does this work? What part of C syntax do I have to learn, to understand where did that UartHandle.Init.xxx come from?
这是基本的 C 结构语法。 USART_HandleTypeDef
结构包含一个名为 Init
的 USART_InitTypeDef
结构的实例。您可以将其视为嵌套结构。您可以使用重复的“.
”引用嵌套结构的成员。请注意,Init
成员不是 指向 USART_InitTypeDef
结构的指针 。它实际上是包含在 USART_HandleTypeDef
个实例中的完整 USART_InitTypeDef
个实例。
Is it possible to do it "the long way", as I planned to?
是的,除非您的代码包含错误。您需要像这样进行分配。
UsartHandle.Init = UsartInit; // Note no `&`
请记住,USART_HandleTypeDef
的 Init
成员不是指针,而是一个完整的结构。因此你需要给它分配一个完整的结构,而不是一个指针。
但是请注意,当您定义 UsartInit
变量时,您是在为结构的一个实例分配 space。如果 UsartInit
是函数局部变量,那么 space 可能在堆栈上。您的初始化语句正在初始化您的结构副本。然后,当您将 UsartInit
分配给 UsartHandle.Init
时,编译器会创建将复制结构的全部内容的代码。复制后,如果你的 UsartInit
是局部变量,它将超出范围并被释放。
确实没有必要为自己的USART_InitTypeDef
结构定义和分配space,然后将整个结构复制到UsartHandle.Init
中。 UsartHandle
已经包含为其 USART_InitTypeDef
成员分配的 space。所以直接初始化 UsartHandle.Init
成员更有效,就像 ST 示例代码那样。
If I don't fill every datafield of HandleTypeDef, where do they get initialized?
您不需要填写 USART_HandleTypeDef
的每个数据字段。请参阅 HAL 参考手册以了解您需要初始化的内容。您可能只需要初始化 Instance
和 Init
成员。其余成员由 HAL USART 驱动程序在内部使用,它们将由驱动程序函数初始化和使用(如果有帮助,您可以将它们视为私有变量)。 API 的设计者将该结构成员命名为 "Init" 以提示您这是您需要初始化的内容。 ST 示例代码提供了您需要初始化的进一步证据。
[Stack Overflow 上的几位经验丰富的开发人员建议不要使用 ST HAL,并鼓励人们根据设备的参考手册开发自己的驱动程序。意识到这些开发人员拥有多年的经验,他们使用过各种微控制器系列和外围设备,并且他们能够理解参考手册并从头开始编写驱动程序。我同意 ST HAL 增加了一些可能对某些应用程序有害的膨胀。但我不同意初学者应该避免使用 ST HAL。 ST HAL 适用于许多应用程序,对于初学者来说比从头编写自己的驱动程序更容易使用(特别是考虑到 HAL 提供的许多示例)。]