13 工程扩展:自动注册与 Linux 内核中的对象组织
13 工程扩展:自动注册与 Linux 内核中的对象组织 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织(当前篇) 前面的 LED 框架已经能够通过统一接口管理不同设备。最后一篇讨论模块和设备数量增加后的组织方式:如何安排初始化、注册对象,以及如何对照这些概念阅读 Linux 内核代码。 模块增多后的初始化管理简单项目可以在 main() 中依次调用初始化函数: 1234567891011int main(void){ gpio_driver_init(); pwm_driver_init(); ...
12 框架整合:应用、驱动与平台的分层
12 框架整合:应用、驱动与平台的分层 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层(当前篇) 工程扩展:自动注册与 Linux 内核中的对象组织 前面分别介绍了封装、结构体嵌套、函数指针和操作表。本节将它们放进一个 LED 框架,让应用通过统一接口使用设备,由具体驱动处理硬件差异。 下面给出各层的主要代码片段,部分初始化、释放函数和错误处理没有展开。 从应用中的直接硬件调用开始如果应用直接操作 GPIO: 123456void app_run(void){ gpio_write(5, true); delay_ms(100); gpio_write(5,...
11 接口约定:必选操作、可选操作与默认实现
11 接口约定:必选操作、可选操作与默认实现 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现(当前篇) 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 操作表中的函数指针可能为 NULL。设计接口时,需要先约定哪些操作必须提供、哪些允许缺省,以及缺省时如何处理。 本节在前面的 ops 基础上加入这些约定,让调用方和实现方遵循同一规则。 明确空函数指针的含义例如,这份 PWM 操作表没有提供 deinit: 123456static const LedOps_t pwm_led_ops = { .on = pwm_led_on, .off = pwm_led_off,...
10 多态:统一接口、行为分发与类型转换
10 多态:统一接口、行为分发与类型转换 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换(当前篇) 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 前面已经有了公共数据 base 和操作表 ops。本节将两者结合,让上层通过同一个 led_on() 接口操作 GPIO、PWM 或 RGB LED。 相同接口根据对象调用不同实现,这种用法称为多态。 将具体类型的选择放在对象中如果上层分别判断 LED 类型,就需要调用不同函数: 1234567if (type == LED_TYPE_GPIO) { gpio_led_on(&gpio_led);} else...
09 操作表:使用 ops 组织对象接口
09 操作表:使用 ops 组织对象接口 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口(当前篇) 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 函数指针可以替换一个操作,而设备通常需要一组相关操作。例如 LED 有开启、关闭、亮度设置和释放资源;文件有打开、读取、写入和关闭;网卡也有启动、停止和发送等操作。 本节将这些相关函数指针放进一个结构体,称为操作表,通常命名为 ops。 将相关操作放在一起如果每次调用都单独传入具体函数,代码可能呈现为下面的形式: 123led_run_action(led, gpio_led_on);led_run_action(led,...
08 函数指针:可替换行为与回调
08 函数指针:可替换行为与回调 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调(当前篇) 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 上一节通过 base 复用了公共数据,但不同 LED 仍需要不同的操作。GPIO LED 设置电平,PWM LED 设置占空比,RGB LED 可能同时控制多个通道。 本节先学习函数指针:保存一个函数的地址,再通过这个指针调用函数。 将固定调用改为可传入的操作下面的函数每次都调用 led_base_on(): 1234void led_run_once(LedBase_t *led){ ...
07 结构体嵌套:公共数据与行为的复用
07 结构体嵌套:公共数据与行为的复用 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用(当前篇) 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 前面的封装让每个 LED 拥有独立数据。接下来考虑多种 LED 实现:GPIO LED、PWM LED、呼吸灯和 RGB LED 都需要记录状态,但各自还需要不同的配置。 本节通过结构体嵌套提取公共部分。这种写法可以实现类似继承的数据复用,C 代码仍然需要显式访问内嵌成员、调用公共函数。 找出重复的字段分别定义三种 LED 时,可能会重复填写公共成员: 12345678910111213141516171819202122typedef...
06 HAL 实例:STM32 外设接口中的封装设计
06 HAL 实例:STM32 外设接口中的封装设计 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计(当前篇) 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 前面用 LED 示例介绍了结构体、对象指针、函数前缀和初始化接口。本节对照 STM32 HAL 的 GPIO 接口,看看这些写法在外设库中如何使用。 下文的结构体和函数是教学简化版,用于说明数据与接口的关系。实际项目应使用对应芯片的头文件和 HAL 实现。 先辨认接口中的类型和参数GPIO...
05 数据组织:实例状态、模块状态与常量
05 数据组织:实例状态、模块状态与常量 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期 数据组织:实例状态、模块状态与常量(当前篇) HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 一个模块中可能同时存在多种数据:每颗 LED 的状态、整个模块的初始化次数,以及固定的参数上限。它们的使用范围不同,存放方式也应有所区别。 多个实例共用全局变量的问题如果把 LED 数据全部放在全局变量中: 12345int g_pin = 0;int g_brightness = 0;int init_count = 0;int MAX_BRIGHTNESS = 255;int g_debug_flag =...
04 模块组织:命名约定与对象生命周期
04 模块组织:命名约定与对象生命周期 C 语言面向对象系列 总纲:C 语言面向对象设计概览 封装:结构体与多实例接口 信息隐藏:公共接口与内部实现 模块组织:命名约定与对象生命周期(当前篇) 数据组织:实例状态、模块状态与常量 HAL 实例:STM32 外设接口中的封装设计 结构体嵌套:公共数据与行为的复用 函数指针:可替换行为与回调 操作表:使用 ops 组织对象接口 多态:统一接口、行为分发与类型转换 接口约定:必选操作、可选操作与默认实现 框架整合:应用、驱动与平台的分层 工程扩展:自动注册与 Linux 内核中的对象组织 前面已经有了 LED 类型和操作接口。本节把这些内容组织成一个模块:用统一前缀命名函数,通过 init/deinit 管理对象从初始化、使用到释放的过程。这个过程称为对象的生命周期。 用函数前缀区分模块假设 LED 模块的接口命名如下: 123int init(void);int on(void);int off(void); 电机模块也采用相同的命名方式: 123int init(void);int start(void);int...









