当前位置:   article > 正文

STM32 HAL(硬件抽象层)--I2C_HandleTypeDef结构体句柄设计分析

handletypedef

目录

一 I2C_HandleTypeDef 结构体

二 I2C_HandleTypeDef结构体的定义优势

三 封装、抽象、灵活性和可扩展性

封装(Encapsulation)

抽象(Abstraction)

灵活性(Flexibility)

可扩展性(Extensibility)

四  I2C_HandleTypeDef 结构体成员分析

1 I2C_TypeDef Instance

2 I2C_InitTypeDef Init

五 I2C_HandleTypeDef句柄

六 用户如何使用STM32 HAL库控制外设


一 I2C_HandleTypeDef 结构体

I2C_HandleTypeDef 结构体是STM32 HAL(硬件抽象层)库中用于管理I2C通信的一个关键数据结构,如下所示STM32 HAL(硬件抽象层):

  1. /** @defgroup I2C_handle_Structure_definition I2C handle Structure definition
  2. * @brief I2C handle Structure definition
  3. * @{
  4. */
  5. typedef struct __I2C_HandleTypeDef
  6. {
  7. I2C_TypeDef *Instance; /*!< I2C registers base address */
  8. I2C_InitTypeDef Init; /*!< I2C communication parameters */
  9. uint8_t *pBuffPtr; /*!< Pointer to I2C transfer buffer */
  10. uint16_t XferSize; /*!< I2C transfer size */
  11. __IO uint16_t XferCount; /*!< I2C transfer counter */
  12. __IO uint32_t XferOptions; /*!< I2C sequantial transfer options, this parameter can
  13. be a value of @ref I2C_XFEROPTIONS */
  14. __IO uint32_t PreviousState; /*!< I2C communication Previous state */
  15. HAL_StatusTypeDef(*XferISR)(struct __I2C_HandleTypeDef *hi2c, uint32_t ITFlags, uint32_t ITSources); /*!< I2C transfer IRQ handler function pointer */
  16. DMA_HandleTypeDef *hdmatx; /*!< I2C Tx DMA handle parameters */
  17. DMA_HandleTypeDef *hdmarx; /*!< I2C Rx DMA handle parameters */
  18. HAL_LockTypeDef Lock; /*!< I2C locking object */
  19. __IO HAL_I2C_StateTypeDef State; /*!< I2C communication state */
  20. __IO HAL_I2C_ModeTypeDef Mode; /*!< I2C communication mode */
  21. __IO uint32_t ErrorCode; /*!< I2C Error code */
  22. __IO uint32_t AddrEventCount; /*!< I2C Address Event counter */
  23. #if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
  24. void (* MasterTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Master Tx Transfer completed callback */
  25. void (* MasterRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Master Rx Transfer completed callback */
  26. void (* SlaveTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Slave Tx Transfer completed callback */
  27. void (* SlaveRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Slave Rx Transfer completed callback */
  28. void (* ListenCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Listen Complete callback */
  29. void (* MemTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Memory Tx Transfer completed callback */
  30. void (* MemRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Memory Rx Transfer completed callback */
  31. void (* ErrorCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Error callback */
  32. void (* AbortCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Abort callback */
  33. void (* AddrCallback)(struct __I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode); /*!< I2C Slave Address Match callback */
  34. void (* MspInitCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Msp Init callback */
  35. void (* MspDeInitCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Msp DeInit callback */
  36. #endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
  37. } I2C_HandleTypeDef;

二 I2C_HandleTypeDef结构体的定义优势

这个I2C_HandleTypeDef结构体的定义带来了许多好处,特别是对于那些使用HAL(硬件抽象层)库的开发者来说。以下是这个结构体定义的一些主要好处:

  1. 封装性:该结构体封装了与I2C通信相关的所有关键信息和状态,包括I2C实例、初始化参数、传输缓冲区、传输大小、状态、模式等。这有助于将相关的数据和操作集中在一起,便于管理和维护。

  2. 抽象化:通过I2C_HandleTypeDef,HAL库为开发者提供了一个高级的、抽象化的接口来操作I2C硬件,而无需深入了解底层的硬件细节。这降低了开发的复杂性,并提高了代码的可读性和可维护性。

  3. 灵活性:结构体中包含了多个回调函数指针,如MasterTxCpltCallbackSlaveRxCpltCallback等,这些允许开发者在特定的I2C事件发生时执行自定义的操作。这种设计提供了极大的灵活性,使开发者能够根据自己的需求定制I2C通信的行为。

  4. 状态跟踪:结构体中的StateModeErrorCode等成员用于跟踪I2C通信的当前状态和可能发生的错误。这有助于开发者在调试过程中快速定位问题,并确保通信的稳定性和可靠性。

  5. DMA集成:通过包含hdmatxhdmarx成员,该结构体支持直接内存访问(DMA)传输,这可以显著提高数据传输的效率,特别是在处理大量数据时。

  6. 可扩展性:通过条件编译指令(如#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)),结构体可以支持额外的功能或回调,而无需修改现有的代码结构。这增加了代码的模块性和可扩展性。

  7. 代码重用:由于所有的I2C相关信息和操作都被封装在一个结构体中,因此可以更容易地在不同的项目或模块中重用这段代码,从而提高开发效率。

总的来说,I2C_HandleTypeDef结构体的设计体现了面向对象编程的许多优点,如封装、抽象、灵活性和可扩展性,这些都有助于简化I2C通信的开发过程,并提高代码的质量和可维护性。

三 封装、抽象、灵活性和可扩展性

这个结构体的设计确实体现了面向对象编程(OOP)的多个核心概念,包括封装、抽象、灵活性和可扩展性。下面是对这些概念的详细解释,以及它们在I2C_HandleTypeDef结构体中的体现:

封装(Encapsulation)

封装是面向对象编程中的一个核心概念,它隐藏了对象的内部状态和实现细节,仅对外提供必要的接口。在I2C_HandleTypeDef中,封装的概念体现在以下几个方面:

  • 数据隐藏:结构体内部封装了与I2C通信相关的所有关键信息,如I2C外设的实例、初始化参数、错误代码、状态标志等。这些信息对用户是隐藏的,用户不需要直接访问或修改它们。
  • 接口提供:结构体与一组函数(如初始化、发送、接收等)一起使用,这些函数提供了操作I2C外设的接口。用户通过这些接口与I2C外设交互,而无需关心内部的具体实现。

抽象(Abstraction)

抽象是提取事物的主要特征而忽略其他次要特征的过程。在I2C_HandleTypeDef中,抽象体现在:

  • 统一接口:无论底层硬件如何变化,用户总是通过相同的函数接口(如HAL_I2C_Master_TransmitHAL_I2C_Slave_Receive等)来操作I2C外设。这些接口为用户提供了一个抽象的、与硬件无关的视图。
  • 简化复杂性:结构体和相关的函数库隐藏了底层硬件的复杂性,使用户能够更专注于应用逻辑的开发,而不是纠结于硬件细节。

灵活性(Flexibility)

灵活性指的是系统或组件能够适应不同需求和环境的能力。在I2C_HandleTypeDef中,灵活性体现在:

  • 配置多样性:用户可以通过初始化函数(如HAL_I2C_Init)灵活地配置I2C外设的参数,如通信速率、寻址模式、电源模式等,以满足不同的应用需求。
  • 多种工作模式:结构体支持I2C的多种工作模式(如主模式、从模式),用户可以根据需要切换不同的工作模式。

可扩展性(Extensibility)

可扩展性指的是系统或组件能够容易地增加新功能或适应新需求的能力。在I2C_HandleTypeDef中,可扩展性体现在:

  • 模块化设计:由于结构体的设计和相关函数的模块化实现,当需要增加新的I2C功能或支持新的硬件时,可以通过添加新的函数或修改现有函数来实现,而无需对现有代码进行大规模的修改。
  • 兼容性:STM32 HAL库设计为与STM32系列的多款微控制器兼容。因此,当迁移到新的STM32型号时,基于I2C_HandleTypeDef的代码通常只需进行少量的修改即可继续工作。

综上所述,I2C_HandleTypeDef结构体的设计确实充分体现了面向对象编程的封装、抽象、灵活性和可扩展性等优点,使得开发者能够更高效、更灵活地利用STM32的I2C外设进行通信。

四  I2C_HandleTypeDef 结构体成员分析

1 I2C_TypeDef Instance

  1. /**
  2. * @brief Inter-integrated Circuit Interface
  3. */
  4. typedef struct
  5. {
  6. __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */
  7. __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */
  8. __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */
  9. __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */
  10. __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */
  11. __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */
  12. __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */
  13. __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */
  14. __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */
  15. __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */
  16. __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */
  17. } I2C_TypeDef;

I2C_HandleTypeDef结构体中,成员I2C_TypeDef *Instance;定义了一个指向I2C_TypeDef结构体的指针,该结构体代表了I2C外设的寄存器映射。这一设计有以下几个显著好处:

  1. 间接访问寄存器:通过使用指针,可以在不直接硬编码硬件寄存器地址的情况下访问I2C外设的所有寄存器。这样做增加了代码的可移植性和灵活性,因为如果目标硬件发生变化(例如使用不同系列的微控制器),只需更新I2C_TypeDef结构体的定义,而不需要修改所有访问寄存器的代码。

  2. 结构清晰:将寄存器访问抽象为结构体成员,使得代码更加整洁、结构化。开发者可以按寄存器名称(如CR1, OAR1, TIMINGR等)而非内存地址来操作硬件,这提高了代码的可读性和可维护性。

  3. 类型安全和错误预防:使用结构体成员访问寄存器相比直接使用指针算术或常量地址更安全,因为编译器可以在编译时检查访问是否越界,减少因错误地址引起的运行时错误。

  4. 便于使用HAL库功能:HAL(Hardware Abstraction Layer,硬件抽象层)库广泛采用这种结构体+指针的方式来封装硬件访问,使得开发者能够利用HAL提供的高层API进行外设配置和控制,同时在需要时也能直接访问底层寄存器,达到灵活性和便利性的平衡。

综上所述,I2C_TypeDef *Instance;成员的设计,通过提供对I2C外设寄存器的间接访问,增强了代码的抽象层次,提升了可维护性和可移植性,同时也方便了使用HAL库的高级功能。

2 I2C_InitTypeDef Init

I2C_InitTypeDef Init;结构体成员在I2C_HandleTypeDef中扮演了非常关键的角色,它用于存储I2C通信的初始化配置参数。这一设计带来了一系列的好处:

  1. 集中管理配置:将所有的初始化参数封装在一个结构体内,使得配置管理变得集中且有序。开发者可以通过修改Init结构体的成员来一次性设置I2C的多项参数,如时钟速度、地址模式、通信模式(主/从)、是否使能DMA等,而不需要分别调用多个函数或设置多个变量。

  2. 代码清晰度提升:通过使用结构体,可以清晰地看到所有影响I2C初始化的参数集合,而不必在代码中分散查找和理解各种初始化调用。这极大地提升了代码的可读性和可维护性。

  3. 易于配置和复用:由于所有配置项都集中在一个结构体中,复制和修改配置变得简单直接,便于在不同场景或不同I2C设备间复用或快速调整配置。

  4. 模块化设计:这种设计促进了模块化编程,I2C_InitTypeDef结构体可以作为参数传递给初始化函数,使得初始化逻辑与具体的硬件操作分离,提高了软件架构的灵活性和解耦。

  5. 便于API设计和扩展:对于HAL库或类似的硬件抽象层而言,这种结构体作为参数的模式简化了API的设计,使得添加新的初始化选项时只需扩展I2C_InitTypeDef结构体,而不会影响到已有的API接口,保证了向后兼容性。

综上所述,I2C_InitTypeDef Init;成员通过集中存储和管理I2C外设的初始化配置参数,提升了代码的组织性、可读性和可维护性,同时也增强了软件设计的灵活性和模块化程度。

五 I2C_HandleTypeDef句柄

I2C_HandleTypeDef可以被视为一个句柄(Handle)。在计算机编程尤其是面向对象和系统编程的上下文中,句柄是一个抽象概念,通常是一个指针、引用、索引或其他类型的轻量级数据结构,用于间接访问或操纵一些资源、数据结构、对象或系统内的其他实体。句柄的关键作用在于提供一种间接访问方式,这带来了几个重要的优势:

  1. 封装与隔离:句柄作为资源的代理,隐藏了资源的具体实现细节,使得用户不需要了解资源是如何存储或管理的。这样可以简化接口,降低耦合度,便于模块化设计。

  2. 安全性:通过句柄而非直接暴露资源,可以实施访问控制和权限管理,确保资源只能被适当的方式和权限级别下的操作所访问,增强了系统的安全性。

  3. 生命周期管理:句柄可以用来管理资源的生命周期,比如引用计数机制中,当句柄不再被任何地方引用时,系统可以自动释放相关资源,防止内存泄漏或资源占用。

  4. 灵活性与扩展性:句柄机制使得在不改变对外接口的情况下,可以修改资源的内部实现或增加新的功能,提高了软件的灵活性和未来的可扩展性。

I2C_HandleTypeDef这个特定的例子中,它作为一个句柄,封装了I2C外设的全部操作所需的信息和状态,包括但不限于外设寄存器基地址、通信参数、状态信息、错误码、DMA句柄等。通过这个句柄,开发者可以安全、高效地与I2C外设交互,进行配置、数据传输、错误处理等操作,而无需直接与底层硬件细节打交道。这种设计极大地提升了代码的可读性、可维护性和跨平台的移植性。

六 用户如何使用STM32 HAL库控制外设

是的,用户在使用STM32 HAL库控制外设(如I2C、SPI、UART等)时,通常需要遵循以下步骤:

  1. 定义句柄实体:首先,用户需要定义一个句柄实体,这个实体是对应外设句柄类型的变量。例如,对于I2C外设,用户需要定义一个I2C_HandleTypeDef类型的变量。

c复制代码

I2C_HandleTypeDef hi2c;
  1. 初始化句柄:接下来,用户需要初始化这个句柄,将其与特定的外设实例相关联,并配置外设的相关参数。这通常是通过调用HAL库提供的初始化函数来完成的,例如HAL_I2C_Init()

c复制代码

I2C_InitTypeDef i2cInit = {0};
// 配置i2cInit结构体的成员来设定I2C的参数(如时钟速度、寻址模式等)
// ...
HAL_I2C_Init(&hi2c, &i2cInit);
  1. 操作外设:一旦句柄初始化完成并与外设实例相关联,用户就可以通过调用HAL库中的各种函数来操作外设了。这些函数通常会要求传入句柄实体的指针作为参数,以便知道要操作哪个外设实例。

c复制代码

// 发送数据
HAL_I2C_Master_Transmit(&hi2c, deviceAddress, dataToSend, dataSize, timeout);
// 接收数据
HAL_I2C_Master_Receive(&hi2c, deviceAddress, dataToReceive, dataSize, timeout);

在这个过程中,句柄实体充当了用户代码与底层外设之间的桥梁。用户通过句柄与HAL库函数交互,而HAL库函数则使用句柄中的信息来访问和控制具体的外设实例。这种抽象层的设计使得用户无需关心底层硬件的具体细节,只需要通过调用HAL库提供的API就可以实现对外设的操作。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/987839
推荐阅读
相关标签
  

闽ICP备14008679号