【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;
}
相关推荐
User_芊芊君子几秒前
CANN数学计算基石ops-math深度解析:高性能科学计算与AI模型加速的核心引擎
人工智能·深度学习·神经网络·ai
小白|3 分钟前
CANN与联邦学习融合:构建隐私安全的分布式AI推理与训练系统
人工智能·机器学习·自动驾驶
艾莉丝努力练剑11 分钟前
hixl vs NCCL:昇腾生态通信库的独特优势分析
运维·c++·人工智能·cann
梦帮科技12 分钟前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
程序员泠零澪回家种桔子14 分钟前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构
我在人间贩卖青春15 分钟前
C++之new和delete
c++·delete·new
Echo_NGC223716 分钟前
【FFmpeg 使用指南】Part 3:码率控制策略与质量评估体系
人工智能·ffmpeg·视频·码率
Trouvaille ~25 分钟前
TCP Socket编程实战(三):线程池优化与TCP编程最佳实践
linux·运维·服务器·网络·c++·网络协议·tcp/ip
纤纡.26 分钟前
PyTorch 入门精讲:从框架选择到 MNIST 手写数字识别实战
人工智能·pytorch·python
大大大反派27 分钟前
CANN 生态中的自动化部署引擎:深入 `mindx-sdk` 项目构建端到端 AI 应用
运维·人工智能·自动化