嵌入式驱动:实现支持同类型多硬件接口的单例模式需求
针对多外设的UART驱动单例实现方案
在开发嵌入式微控制器设备时,我们常为UART这类外设编写轻量级HAL驱动,这类驱动会通过锁定外设保证线程安全,同时封装厂商提供的C库调用。
微控制器通常具备多个同类型物理外设,比如UART0、UART1、UART2等。以下是用C++实现的基础UART驱动类:
#include <stdint.h> class DevUart { public: enum class interfaces_t { UART0 = 0, UART1 = 1, UART2 = 2, UART3 = 3, UART4 = 4 }; DevUart(interfaces_t interface) : Interface{interface} { switch(interface) { case interfaces_t::UART0: HardwareRegister = (void *)0xE0001000; break; /* UART1等其他外设的寄存器映射逻辑... */ } } void sendByte(uint8_t data) { /* 锁定外设、发送数据、解锁外设等线程安全逻辑 */ } private: interfaces_t Interface; void * HardwareRegister; };
原有单例方案的局限
为保证指定外设仅被一个驱动实例使用,最初实现了如下单例类:
class DevUartSingleton { public: static DevUart & Instance(DevUart::interfaces_t interface) { static DevUart S{interface}; return S; } DevUartSingleton() = delete; ~DevUartSingleton() = delete; };
但该方案存在明显局限:它只能维护一个全局驱动实例(比如仅能获取UART2的实例):
DevUart & uart_driverC = DevUartSingleton::Instance(DevUart::interfaces_t::UART2);
如果想同时使用UART0、UART1等多个外设,这个实现无法满足需求。我们需要的是每个物理接口对应唯一实例,同时支持多个不同外设实例共存的方案。
解决方案:按外设接口区分的单例实现
方案1:预初始化所有外设实例(推荐)
利用静态数组存储所有外设的驱动实例,通过枚举值作为数组索引,确保每个接口对应唯一实例。该方案在程序启动时完成初始化,C++11及以后的静态变量初始化具备线程安全性。
#include <array> #include <cstdint> class DevUart { public: enum class interfaces_t { UART0 = 0, UART1 = 1, UART2 = 2, UART3 = 3, UART4 = 4, COUNT // 标记枚举总数,用作数组大小 }; DevUart(interfaces_t interface) : Interface{interface} { switch(interface) { case interfaces_t::UART0: HardwareRegister = (void *)0xE0001000; break; case interfaces_t::UART1: HardwareRegister = (void *)0xE0002000; break; case interfaces_t::UART2: HardwareRegister = (void *)0xE0003000; break; case interfaces_t::UART3: HardwareRegister = (void *)0xE0004000; break; case interfaces_t::UART4: HardwareRegister = (void *)0xE0005000; break; default: HardwareRegister = nullptr; break; } } void sendByte(uint8_t data) { /* 锁定外设、发送数据、解锁外设等线程安全逻辑 */ } private: interfaces_t Interface; void * HardwareRegister; }; class DevUartManager { public: static DevUart& GetInstance(DevUart::interfaces_t interface) { // 静态数组存储所有外设实例,枚举值作为索引 static std::array<DevUart, static_cast<size_t>(DevUart::interfaces_t::COUNT)> instances = { DevUart(DevUart::interfaces_t::UART0), DevUart(DevUart::interfaces_t::UART1), DevUart(DevUart::interfaces_t::UART2), DevUart(DevUart::interfaces_t::UART3), DevUart(DevUart::interfaces_t::UART4) }; size_t idx = static_cast<size_t>(interface); if (idx >= static_cast<size_t>(DevUart::interfaces_t::COUNT)) { // 嵌入式系统可根据需求替换为断言或其他错误处理逻辑 while(true); } return instances[idx]; } DevUartManager() = delete; ~DevUartManager() = delete; };
使用方式
// 获取UART0的唯一实例 DevUart& uart0 = DevUartManager::GetInstance(DevUart::interfaces_t::UART0); // 获取UART2的唯一实例 DevUart& uart2 = DevUartManager::GetInstance(DevUart::interfaces_t::UART2); // 调用驱动方法 uart0.sendByte(0xAA); uart2.sendByte(0xBB);
方案2:延迟初始化未使用的外设
如果部分外设不会被使用,可改用指针数组实现延迟初始化,仅在首次调用时创建对应外设的实例,节省内存资源:
class DevUartManager { public: static DevUart& GetInstance(DevUart::interfaces_t interface) { static std::array<DevUart*, static_cast<size_t>(DevUart::interfaces_t::COUNT)> instances = {nullptr}; size_t idx = static_cast<size_t>(interface); if (idx >= static_cast<size_t>(DevUart::interfaces_t::COUNT)) { while(true); // 错误处理 } if (!instances[idx]) { instances[idx] = new DevUart(interface); } return *instances[idx]; } DevUartManager() = delete; ~DevUartManager() = delete; };
注意:该方案存在内存泄漏风险,但嵌入式系统通常运行至断电,泄漏影响可忽略;若需动态启停外设,需额外添加资源释放逻辑。
内容的提问来源于stack exchange,提问作者Daniel K.
相关产品推荐
相关产品推荐

