【AI小智硬件程序(八)】

AI小智硬件程序(八)

构建开发板抽象层

我们已经完成了音频抽象的部分,现在开始实现板子抽象的部分。

一、创建板子抽象层

board.h

cpp 复制代码
#pragma once
#include <audio_hal.h>
void* create_board();
class Board
{
protected:
    Board(); 
private:
    // 禁用拷贝构造函数
    Board(const Board&) = delete; 
    // 禁用赋值操作
    Board& operator=(const Board&) = delete; 
public:
    virtual ~Board() = default;
    static Board& GetInstance() {
        static Board* instance = static_cast<Board*>(create_board());
        return *instance;
    }
    virtual AudioHAL* GetAudioHAL() = 0;
};
#define REGISTER_BOARD(BOARD_TYPE_CLASS_NAME) \ 
void* create_board() { \
    return new BOARD_TYPE_CLASS_NAME(); \
} 

board.c

cpp 复制代码
#include "board.h"
Board::Board()
{
}

本质上是在实现一个基于单例模式和工厂模式的开发板抽象层,为不同硬件的开发板提供统一的接口,同时保证整个程序中只有一个开发板实例。

bash 复制代码
static Board* instance = static_cast<Board*>(create_board());

这句是最关键的,定义了一个全局只初始化一次的Board基类指针instance,它的初始值是:调用「开发板工厂函数」create_board()创建一个具体的开发板子类对象(比如 ESP32 板子),再把这个子类对象的指针转成基类指针,存到instance里。

二、修改 CmakeList.txt

cpp 复制代码
"board/board.cpp"  


"board" 

三、创建开发板类型

ioelin_dev_board.h

cpp 复制代码
#pragma once
#include "board.h"
#include <audio_hal.h>

class IoelinDevBoard:public Board
{
private:
    /* data */
public:
    IoelinDevBoard();
    ~IoelinDevBoard();

    AudioHAL* GetAudioHAL() override;
};

ioelin_dev_board.cpp

cpp 复制代码
#include "ioelin_dev_board.h"


IoelinDevBoard::IoelinDevBoard()
{
}

IoelinDevBoard::~IoelinDevBoard()
{
}

AudioHAL* IoelinDevBoard::GetAudioHAL()
{
    return nullptr;
}

REGISTER_BOARD(IoelinDevBoard);

这两段代码,是针对「IoelinDevBoard」这款具体开发板的实现代码,也是我们之前聊的「开发板抽象层」的实际落地------ 简单说,就是为这款特定板子实现了抽象基类Board要求的接口,并且完成了注册,让整个程序能识别并使用这款板子。

四、创建 Kconfig.projbuild

Kconfig.projbuild是项目级的配置文件(常见于 ESP-IDF/Zephyr 等嵌入式框架),核心作用是通过menuconfig图形界面定义项目的可配置选项。

cpp 复制代码
menu "Board Configuration"
 
    
choice BOARD_TYPE
    prompt "Board Type"
    default BOARD_TYPE_IOELIN_DEV 
    config BOARD_TYPE_IOELIN_DEV
        bool "杯面_白ESP32-S3开发板"
endchoice

endmenu

五、修改 CmakeList.txt

cpp 复制代码
# 新增开发板需要再这里类似写一个配置BOARD_TYPE的宏为板子类型文件夹名称
if(CONFIG_BOARD_TYPE_IOELIN_DEV)
    set(BOARD_TYPE "ioelin-dev")     
# elseif(CONFIG_BOARD_TYPE_XXX_XXX)
#     set(BOARD_TYPE "xxx-xxx")  
endif()
file(GLOB BOARD_TYPE_SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/board/${BOARD_TYPE}/*.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/board/${BOARD_TYPE}/*.c
)
list(APPEND SOURCES ${BOARD_TYPE_SOURCES})

通过 Kconfig.projbuild 定义 CONFIG_BOARD_TYPE_XXX 配置项,再结合 CMake 的 if-else 和 file(GLOB) 自动匹配文件,核心价值就是避免手动修改 CMakeLists.txt 里的文件列表------ 新增开发板时,只需要加 Kconfig 选项和对应的文件夹,不用改 CMake 代码,极大降低了维护成本。

总的结构

project/

├── board/

│ ├── ioelin-dev/ # Ioelin板子的代码

│ │ ├── ioelin_dev_board.h

│ │ └── ioelin_dev_board.cpp

│ └── esp32-dev/ # ESP32板子的代码

│ ├── esp32_dev_board.h

│ └── esp32_dev_board.cpp

├── CMakeLists.txt

└── Kconfig.projbuild

开发板抽象功能实现

1、管脚配置

cpp 复制代码
/* I2C port and GPIOs */
#define AUDIO_I2C_NUM            (I2C_NUM_0)
#define AUDIO_I2C_SDA_IO         GPIO_NUM_2
#define AUDIO_I2C_SCL_IO         GPIO_NUM_1

/* I2S port and GPIOs */
#define AUDIO_I2S_MCK_IO         GPIO_NUM_38
#define AUDIO_I2S_BCK_IO         GPIO_NUM_14
#define AUDIO_I2S_WS_IO          GPIO_NUM_13
#define AUDIO_I2S_DI_IO          GPIO_NUM_12
#define AUDIO_I2S_DO_IO          GPIO_NUM_45

/* 功放使能IO */
#define AUDIO_PA_EN_IO    GPIO_NUM_18 

/*** 音频输入输出采样率*/
#define AUDIO_OUT_SAMPLE_RATE     (16000)
#define AUDIO_IN_SAMPLE_RATE     (16000)
 
#define ES8311_I2C_ADDR     0x18
#define ES7210_I2C_ADDR     0x41

这里是在为ioelin_dev_board这款开发板,定义硬件相关的 "管脚、通信参数、芯片配置" 等宏常量,相当于把这款板子的硬件细节(比如 I2C 用哪个端口、音频芯片的 I2C 地址)统一集中在头文件里,方便后续代码直接引用。

2、初始化 i2c

cpp 复制代码
i2c_master_bus_handle_t init_i2c(i2c_port_t i2c_port,gpio_num_t i2c_sda_pin,gpio_num_t i2c_scl_pin);

定义一个通用的 I2C 初始化接口,规定 "初始化 I2C 需要传入 3 个参数"(I2C 端口号、SDA 引脚、SCL 引脚),返回初始化后的 I2C 总线句柄

cpp 复制代码
i2c_master_bus_handle_t Board::init_i2c(i2c_port_t i2c_port,gpio_num_t i2c_sda_pin,gpio_num_t i2c_scl_pin) {
    i2c_master_bus_handle_t i2c_bus;
    i2c_master_bus_config_t i2c_bus_cfg = {
        .i2c_port = i2c_port,
        .sda_io_num = i2c_sda_pin,
        .scl_io_num = i2c_scl_pin,
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .glitch_ignore_cnt = 7,
        .intr_priority = 0,
        .trans_queue_depth = 0,
        .flags = {
            .enable_internal_pullup = 1,
        },
    };
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus));
    return i2c_bus;
}

实现通用的 I2C 初始化逻辑 ------ 不管什么板子,只要传入正确的端口和引脚,就能用这段代码初始化出可用的 I2C 总线

cpp 复制代码
    i2c_master_bus_handle_t i2c0_bus;

为IoelinDevBoard专属的 I2C0 总线预留 "存储位置",后续初始化后的 I2C 句柄会存在这里,方便这款板子的其他函数(比如音频初始化)调用

cpp 复制代码
i2c0_bus = Board::init_i2c(AUDIO_I2C_NUM,AUDIO_I2C_SDA_IO,AUDIO_I2C_SCL_IO);

给通用的 I2C 初始化逻辑 "喂" 上IoelinDevBoard的专属硬件参数(这些宏就是你之前看到的AUDIO_I2C_NUM=I2C_NUM_0、AUDIO_I2C_SDA_IO=GPIO2等),初始化出这款板子的 I2C0 总线,并把句柄存到i2c0_bus里

3、初始化音频

cpp 复制代码
AudioHAL* audio_hal;
cpp 复制代码
audio_hal = new AudioEs8311Es7210(i2c0_bus,
                                    AUDIO_I2C_NUM,
                                    AUDIO_IN_SAMPLE_RATE,
                                    AUDIO_OUT_SAMPLE_RATE,
                                    AUDIO_I2S_MCK_IO,
                                    AUDIO_I2S_BCK_IO,
                                    AUDIO_I2S_WS_IO,
                                    AUDIO_I2S_DO_IO,
                                    AUDIO_I2S_DI_IO,
                                    AUDIO_PA_EN_IO,
                                    true,
                                    ES8311_I2C_ADDR,
                                    ES7210_I2C_ADDR,
                                    true);
cpp 复制代码
AudioHAL* IoelinDevBoard::GetAudioHAL()
{
    return audio_hal;
}
相关推荐
基层小星2 小时前
用ai写完材料有个差不多后,材料星如何精准修改润色?
人工智能·ai·ai写作·笔杆子·公文写作·修改润色
码农幻想梦2 小时前
实验7 知识表示与推理
开发语言·人工智能·python
_YiFei2 小时前
从 “选题卡壳” 到 “PPT 定稿”,AI 如何搞定开题全流程?
人工智能
IT_陈寒2 小时前
SpringBoot 3.0实战:10个高效开发技巧让你的启动时间减少50%
前端·人工智能·后端
源于花海2 小时前
迁移学习的第二类方法:特征选择
人工智能·机器学习·迁移学习·特征选择
8K超高清2 小时前
2026科技风口:有哪些前沿场景即将落地?
网络·人工智能·科技·数码相机·计算机视觉
王老师青少年编程2 小时前
2025年12月GESP真题及题解(C++七级): 学习小组
c++·gesp·csp·信奥赛·七级·csp-s·提高组
迷途之人不知返2 小时前
C++初识(2)
c++
老兵发新帖2 小时前
X-AnyLabeling实现自动预标注可行性方案分析
人工智能·计算机视觉·目标跟踪