嵌入式驱动工厂模式:接口+实现+工厂三件套

发布时间:2026/9/16 1:21:32

嵌入式驱动工厂模式:接口+实现+工厂三件套 上一篇聊数组和链表选型有朋友私信问业务代码里到处spi_flash_read()、spi_flash_write()换个存储介质就要改一大片这种耦合怎么破这问题太典型了答案是工厂模式把创建驱动和使用驱动分开。工厂模式在应用软件开发里是老面孔但嵌入式里很多人没用过觉得我就一个驱动搞什么工厂。真到产品线一铺开SPI Flash、SDIO Flash、EEPROM 三种存储要兼容就傻眼了。这篇把嵌入式驱动工厂模式讲透给个能直接套的模板。先看痛点业务代码直接依赖具体驱动我刚工作时写过一个数据记录模块长这样void log_save(uint32_t addr, const uint8_t *data, uint32_t len) { spi_flash_write(addr, data, len); /* 直接调 SPI Flash 驱动 */ } void log_read(uint32_t addr, uint8_t *buf, uint32_t len) { spi_flash_read(addr, buf, len); }当时觉得没问题。后来产品要出高低配两个版本高配用 SPI Flash低配用 EEPROM。我一改就傻了log_save/log_read里全是spi_flash_xxx每个调用点都要改成e2prom_xxx要么加if (high_version)分支代码瞬间丑陋。更要命的是还有个 SDIO Flash 的中配版本在路上。这就是业务代码直接依赖具体驱动的代价换硬件就改业务代码加一种硬件就加一坨分支。业务逻辑什么时候存、存哪里和驱动细节怎么写 Flash搅在一起改一个动全身。解法抽象一个驱动接口工厂模式的第一步不是写工厂是抽象接口。把存储设备能干什么抽象出来业务代码只依赖接口不依赖具体驱动。/* 存储设备接口抽象出所有存储设备共有的操作 */ typedef struct { int (*init)(void); int (*read)(uint32_t addr, uint8_t *buf, uint32_t len); int (*write)(uint32_t addr, const uint8_t *data, uint32_t len); int (*erase)(uint32_t addr, uint32_t len); const char *name; } StorageDevice;这个结构体就是 C 语言里接口的标准写法一组函数指针。它定义了存储设备这个抽象类型该有什么行为但不关心具体是 SPI Flash 还是 EEPROM。业务代码改成依赖这个抽象接口static const StorageDevice *g_storage NULL; void log_save(uint32_t addr, const uint8_t *data, uint32_t len) { g_storage-write(addr, data, len); /* 调接口不调具体驱动 */ } void log_read(uint32_t addr, uint8_t *buf, uint32_t len) { g_storage-read(addr, buf, len); }注意g_storage-write这一行业务代码不再认识spi_flash或e2prom它只知道有个存储设备能 write。具体是哪种设备运行时再定。这就是依赖抽象不依赖具体。具体驱动实现接口每种具体驱动实现这个接口把自己填进函数指针表/* SPI Flash 驱动实现 */ static int spi_flash_init(void) { /* 初始化 SPI Flash */ return 0; } static int spi_flash_read(uint32_t addr, uint8_t *buf, uint32_t len) { /* ... */ return 0; } static int spi_flash_write(uint32_t addr, const uint8_t *data, uint32_t len) { /* ... */ return 0; } static int spi_flash_erase(uint32_t addr, uint32_t len) { /* ... */ return 0; } const StorageDevice spi_flash_dev { .init spi_flash_init, .read spi_flash_read, .write spi_flash_write, .erase spi_flash_erase, .name spi_flash, }; /* EEPROM 驱动实现 */ static int e2prom_init(void) { /* 初始化 EEPROM */ return 0; } static int e2prom_read(uint32_t addr, uint8_t *buf, uint32_t len) { /* ... */ return 0; } static int e2prom_write(uint32_t addr, const uint8_t *data, uint32_t len) { /* ... */ return 0; } static int e2prom_erase(uint32_t addr, uint32_t len) { /* ... */ return 0; } const StorageDevice e2prom_dev { .init e2prom_init, .read e2prom_read, .write e2prom_write, .erase e2prom_erase, .name e2prom, };每个驱动就是一个StorageDevice实例把自己的函数填进去。这其实就是 C 语言的面向对象结构体当类函数指针当方法实例当对象。上一篇讲链表管 GUI 时用过类似手法这里是同一个思路在不同场景的应用。工厂函数根据参数返回不同实例接口和实现都有了谁来决定用哪个这就是工厂函数的活根据参数返回对应的驱动实例typedef enum { STORAGE_SPI_FLASH, STORAGE_SDIO_FLASH, STORAGE_E2PROM, } StorageType; const StorageDevice *storage_factory(StorageType type) { switch (type) { case STORAGE_SPI_FLASH: return spi_flash_dev; case STORAGE_SDIO_FLASH: return sdio_flash_dev; case STORAGE_E2PROM: return e2prom_dev; default: return NULL; } }业务代码初始化时调一次工厂拿到驱动实例之后就用这个实例int log_init(StorageType type) { g_storage storage_factory(type); if (g_storage NULL) return -1; return g_storage-init(); }现在回到开头的痛点高配 SPI Flash、低配 EEPROM、中配 SDIO Flash。用工厂模式后业务代码一行不用改只在初始化时传不同的StorageType。log_init(STORAGE_SPI_FLASH)跑高配log_init(STORAGE_E2PROM)跑低配。换硬件、加硬件全在工厂函数一处搞定。三个优势一个比一个值钱工厂模式在嵌入式驱动里的优势我列三条一条比一条值钱。第一业务代码不依赖具体驱动。这是基本盘。业务逻辑和驱动细节解耦换硬件不动业务代码。这一条就值回票价产品线一铺开维护成本断崖式下降。第二新增驱动类型只改两处。加一种新存储比如 NAND Flash只要写一个nand_flash_dev实例实现接口再在工厂函数加一个case。业务代码、其他驱动一行不用动。这符合开闭原则对扩展开放对修改封闭。在多人协作的项目里这个特性让新增驱动不会误伤老代码。第三便于单元测试 Mock 替换。这条很多人没意识到。业务代码依赖的是StorageDevice接口测试时可以塞一个 Mock 实现/* 测试用 Mock 驱动 */ static int mock_read(uint32_t addr, uint8_t *buf, uint32_t len) { memcpy(buf, mock_mem[addr], len); /* 从内存读不碰真硬件 */ return 0; } const StorageDevice mock_dev { .read mock_read, /* ... */ }; void test_log_save(void) { g_storage mock_dev; /* 替换成 Mock */ log_save(0x100, test_data, 4); assert(memcmp(mock_mem[0x100], test_data, 4) 0); }不用接真硬件就能测业务逻辑。嵌入式单元测试一直是个老大难硬件依赖重、跑得慢工厂模式加 Mock 是破局的关键之一。我见过不少团队嵌入式代码零测试理由是没法跑硬件其实业务逻辑层完全可以用 Mock 测起来只是架构上没留接口。落地建议第一接口别设计太大。只放真正共有的操作别为了统一硬塞。比如 EEPROM 没有擦除概念按字节写接口里硬加erase就得让 EEPROM 实现个空函数污染抽象。这种差异要么接口分两层要么用可选函数指针NULL 表示不支持调用前判断。第二工厂函数用 switch 没问题别被switch 是坏味道洗脑。嵌入式里驱动类型有限、编译期基本确定switch 直白高效。真要动态注册插件式再用链表注册表那是过度设计。第三实例用静态全局变量const StorageDevice spi_flash_dev不要每次工厂调用都 malloc。驱动实例是编译期就确定的单例静态分配即可省去内存管理也避免碎片。第四工厂可以分层。storage_factory选存储类型sensor_factory选传感器类型display_factory选显示类型。每类硬件一个工厂别一个工厂管所有。这套模式不挑 MCU不挑 RTOS核心就是接口 实现 工厂三件套。它真正解决的是硬件多样性和业务逻辑稳定性之间的矛盾业务逻辑要稳硬件要灵活换工厂模式用一层抽象把它们隔开。在产品线多、硬件版本多的项目里这是必修课。下一篇讲应用层感知底层变化的三种姿势轮询、回调、观察者把驱动和应用之间的另一条线也理清。有用的话点个在看让更多被驱动耦合折磨的嵌入式工程师看到。标签嵌入式 工厂模式 驱动设计 C语言面向对象
延伸阅读

更多相关文章

2026/9/16 1:19:15

常微分方程边值问题求解:打靶法原理、Python实现与调参实践

简介:面向需要处理常微分方程边界值问题的数值计算学习者,这份资源提供了Matlab实现的打靶法(Shooting Method)求解程序。打靶法通过将边界值问题转化为初值问题,配合初始猜测、迭代优化和收敛判断等步骤逼近精确解&am…

2026/9/16 1:19:15

Qwen Chatbot 跑 Token 监控:Key 用 TaoToken,total_tokens 这样核

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/16 1:19:15

企业网站HTML模板剖析:结构、样式与响应式改造实践

简介:面向各类公司及建站初学者的通用企业网站网页模板,提供一套完整的前端页面框架,可快速搭建包括首页、关于我们、产品详情、团队介绍、联系我们等栏目的企业官网。压缩包共三十八个文件,包含八个网页页面、两套样式表、两个脚…

2026/9/16 1:19:15

STM32F1标准库RTC万年历开发:从秒计数到日期转换

简介:一套基于STM32 RTC模块的万年历完整工程,面向嵌入式开发与STM32学习者,解决RTC初始化、时间日期设置、闹钟触发及万年历推算等实际问题。包内共109个文件,包含28个C源文件与26个头文件,附有MDK工程配置&#xff0…

2026/9/15 4:54:30

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/16 0:04:09

PHP源码部署实战:从环境配置到运行情侣游戏全攻略

简介:这是一套面向情侣互动场景的PHP完整源码,集成情侣飞行棋、真心话大冒险、情趣骰子等玩法,并内置完整分销制度,可自定义多种返佣比例,源码完全开源无加密,支持微信无感自动授权登录与第三方授权&#x…

2026/9/15 14:22:53

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/15 21:31:11

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码