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

基于C++抽象类的OLED屏幕控制程序架构实现咨询

这个需求刚好是C++抽象类+依赖倒置原则的典型应用场景,能完美实现分层解耦和可替换性。我给你一步步拆解实现思路和代码示例:

核心设计逻辑

先明确三层的职责边界和依赖关系:

  • 通信接口管理类(最底层):抽象所有硬件通信的共性能力(发送命令/数据),不管是I2C、SPI还是UART,都要实现这套统一接口
  • OLED基础命令类(中间层):依赖通信接口的抽象,实现OLED芯片的基础操作(设像素、初始化),自身也做成抽象类,让不同芯片的具体实现去继承
  • 用户接口类(最上层):依赖OLED基础命令的抽象,提供友好的上层绘图API(画圆、画线等),仅对用户暴露这一层

层级依赖关系是用户接口类持有OLED基础命令类的抽象指针,OLED基础命令类持有通信接口类的抽象指针,全程依赖抽象而非具体实现,这样替换底层模块时上层完全不需要改动。


1. 通信接口管理类(抽象基类)

先定义最底层的通信抽象,所有通信方式都必须实现这些方法:

#include <cstdint>
#include <cstddef>

// 通信接口抽象基类
class ICommInterface {
public:
    // 必须声明虚析构,确保子类能被正确销毁
    virtual ~ICommInterface() = default;

    // 发送单字节命令到OLED
    virtual bool sendCommand(uint8_t cmd) = 0;
    // 发送多字节数据到OLED
    virtual bool sendData(const uint8_t* data, size_t len) = 0;
};

然后写具体的通信实现,比如I2C版本:

#include <cstring>

// I2C通信实现类
class I2CComm : public ICommInterface {
private:
    uint8_t device_addr; // OLED的I2C设备地址
    // 这里可以添加硬件相关的句柄(比如STM32的I2C_HandleTypeDef)

public:
    explicit I2CComm(uint8_t addr) : device_addr(addr) {}

    bool sendCommand(uint8_t cmd) override {
        // 实际硬件I2C发送逻辑:启动传输→发设备地址→发命令标志位→发命令字节→停止传输
        // 示例代码仅做示意,实际需对接硬件驱动
        return true;
    }

    bool sendData(const uint8_t* data, size_t len) override {
        // 实际硬件I2C发送数据逻辑
        return true;
    }
};

如果要换SPI通信,只需要再写一个SPIComm类继承ICommInterface,实现对应的发送逻辑即可。


2. OLED基础命令类(抽象基类)

这一层依赖通信接口的抽象,同时定义OLED芯片的基础操作抽象:

// OLED基础命令抽象基类
class IOLEDCommand {
protected:
    ICommInterface* comm_interface; // 持有通信接口的抽象指针

public:
    // 通过构造函数注入通信接口实例
    explicit IOLEDCommand(ICommInterface* comm) : comm_interface(comm) {}
    virtual ~IOLEDCommand() = default;

    // 设置单个像素点的亮灭
    virtual bool setPixel(uint8_t x, uint8_t y, bool on) = 0;
    // 初始化OLED芯片(不同芯片初始化命令不同)
    virtual bool init() = 0;
    // 清屏操作
    virtual bool clearScreen() = 0;
    // 设置对比度(可选的基础命令)
    virtual bool setContrast(uint8_t level) = 0;
};

然后写具体芯片的实现,比如SSD1306:

#include <cstring>

// SSD1306芯片的OLED命令实现
class SSD1306Command : public IOLEDCommand {
private:
    static constexpr uint8_t SCREEN_W = 128;
    static constexpr uint8_t SCREEN_H = 64;
    uint8_t frame_buffer[SCREEN_W * SCREEN_H / 8] = {0}; // 显存缓冲区

public:
    explicit SSD1306Command(ICommInterface* comm) : IOLEDCommand(comm) {}

    bool init() override {
        // 发送SSD1306的初始化命令序列
        comm_interface->sendCommand(0xAE); // 关闭显示
        comm_interface->sendCommand(0xD5); // 设置时钟分频
        comm_interface->sendCommand(0x80);
        comm_interface->sendCommand(0xA8); // 设置多路复用率
        comm_interface->sendCommand(0x3F);
        // ... 其他初始化命令省略
        return true;
    }

    bool setPixel(uint8_t x, uint8_t y, bool on) override {
        if (x >= SCREEN_W || y >= SCREEN_H) return false;

        uint8_t byte_idx = (y / 8) * SCREEN_W + x;
        if (on) {
            frame_buffer[byte_idx] |= (1 << (y % 8));
        } else {
            frame_buffer[byte_idx] &= ~(1 << (y % 8));
        }
        // 可选:立即刷新该像素到屏幕,或者攒够数据批量刷新
        return comm_interface->sendData(&frame_buffer[byte_idx], 1);
    }

    bool clearScreen() override {
        memset(frame_buffer, 0, sizeof(frame_buffer));
        return comm_interface->sendData(frame_buffer, sizeof(frame_buffer));
    }

    bool setContrast(uint8_t level) override {
        comm_interface->sendCommand(0x81);
        comm_interface->sendCommand(level);
        return true;
    }
};

如果要适配SH1106芯片,只需要写一个SH1106Command类继承IOLEDCommand,实现对应的初始化、设像素等方法即可。


3. 用户接口类(对外暴露的唯一类)

这个类持有OLED基础命令的抽象指针,提供用户友好的高层API:

// 用户接口类,仅对用户开放
class OLEDUserInterface {
private:
    IOLEDCommand* oled_cmd; // 依赖OLED命令的抽象指针

public:
    explicit OLEDUserInterface(IOLEDCommand* cmd) : oled_cmd(cmd) {}
    ~OLEDUserInterface() = default;

    // 画圆API(使用Bresenham算法)
    bool drawCircle(uint8_t center_x, uint8_t center_y, uint8_t radius) {
        int x = radius;
        int y = 0;
        int err = 0;

        while (x >= y) {
            // 绘制8个对称点
            oled_cmd->setPixel(center_x + x, center_y + y, true);
            oled_cmd->setPixel(center_x + y, center_y + x, true);
            oled_cmd->setPixel(center_x - y, center_y + x, true);
            oled_cmd->setPixel(center_x - x, center_y + y, true);
            oled_cmd->setPixel(center_x - x, center_y - y, true);
            oled_cmd->setPixel(center_x - y, center_y - x, true);
            oled_cmd->setPixel(center_x + y, center_y - x, true);
            oled_cmd->setPixel(center_x + x, center_y - y, true);

            if (err <= 0) {
                y += 1;
                err += 2*y + 1;
            }
            if (err > 0) {
                x -= 1;
                err -= 2*x + 1;
            }
        }
        return true;
    }

    // 画线API(示例)
    bool drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
        // 实现画线算法,底层调用setPixel
        return true;
    }

    // 转发底层基础操作给用户(可选)
    bool initOLED() {
        return oled_cmd->init();
    }

    bool clearOLED() {
        return oled_cmd->clearScreen();
    }
};

4. 用户使用示例

用户只需要创建具体的通信类、OLED命令类,注入到用户接口类即可,完全不需要关心底层细节:

int main() {
    // 1. 创建通信实例(这里用I2C)
    I2CComm i2c_comm(0x3C); // SSD1306默认I2C地址
    // 如果换SPI,改成:SPIComm spi_comm(CS_PIN, DC_PIN);

    // 2. 创建OLED命令实例(这里用SSD1306)
    SSD1306Command ssd1306_cmd(&i2c_comm);
    // 如果换SH1106,改成:SH1106Command sh1106_cmd(&i2c_comm);

    // 3. 创建用户接口实例,开始使用
    OLEDUserInterface oled_ui(&ssd1306_cmd);

    // 4. 调用高层API操作OLED
    oled_ui.initOLED();
    oled_ui.drawCircle(64, 32, 20); // 在屏幕中心画一个半径20的圆
    oled_ui.clearOLED();

    return 0;
}

核心优势

  • 完全解耦:更换通信方式只需要替换ICommInterface的子类,更换OLED芯片只需要替换IOLEDCommand的子类,用户接口层完全不用修改
  • 扩展性强:新增通信方式或OLED芯片,只需要实现对应的抽象类即可,符合开闭原则
  • 可测试性高:可以编写Mock的通信类或OLED命令类,方便单元测试用户接口层的绘图逻辑

内容的提问来源于stack exchange,提问作者noearchimede

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:19:39