You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入式驱动:实现支持同类型多硬件接口的单例模式需求

针对多外设的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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 07:23:19