杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • SDK 总览与芯片能力
    • 环境搭建与编译构建
    • 烧录与固件升级
    • 文档与版本资源
  • 应用与示例方案

    • demo 示例工程
    • WiFi 摄像头方案 (wifi_camera)
    • WiFi 音箱方案 (wifi_soundbox)
    • WiFi 婴儿监护方案 (wifi_bbm)
    • 公共应用模块库
    • 示例代码库 (example)
  • 系统架构与平台

    • 总体架构与工程分层
    • 系统启动与运行框架
    • 芯片驱动与板级适配
    • 设备管理与文件系统
    • 系统工具库与算法
  • 音频子系统

    • 音频框架与处理节点
    • 音频编解码与音效
    • 播放器与录音器
    • 语音交互与 AI 唤醒
    • LE Audio 与蓝牙音频
    • 音频调试与歌词
  • 视频与显示子系统

    • 摄像头驱动与 ISP
    • 视频编码与图像处理
    • 显示与 GPU 加速
    • 屏幕镜像 (screen_mirror)
  • 无线连接与网络

    • 蓝牙协议栈 (双模蓝牙)
    • WiFi 协议栈与配网
    • 网络协议栈
    • 云平台与 IoT 协议
  • UI 子系统

    • LVGL 集成与应用
    • UI 工程与工具链
  • 配置系统

    • 功能配置
    • 板级配置
    • 网络与蓝牙配置
    • 音频配置与提示音
  • 工具与测试

    • 产测与射频测试工具
    • 固件升级与更新机制
    • 调试与日志工具
  • 硬件参考设计

    • 原理图参考设计
    • 芯片数据手册

示例代码库

本页汇总 AC792N SDK(sdk/apps/common/example/ 目录)中面向应用开发的全部示例代码,涵盖音频、DMA2D 图形加速、网络协议(MQTT/HTTP/CoAP/FTP/NTP/WebSocket/Socket)、外设(按键/重力传感器/DVB)以及 LVGL 图形库等场景,并说明每个示例的启用方式、控制流与关键 API。

Purpose and Scope

本页是 SDK 示例代码的入口文档,目标读者是希望快速上手 AC792N 芯片外设与协议栈的应用开发者。文档覆盖:

  • 示例代码的目录组织与分类(音频、DMA2D、网络协议、外设、OTA、蓝牙、LVGL);
  • 每个示例的启用开关(app_config.h 中的配置宏)与编译方式;
  • 代表性示例的完整控制流与核心 API 用法(MQTT、按键事件、音频 buffer 播放);
  • 示例中反复出现的 SDK 基础设施(application 注册、事件系统、server 请求模型)。

以下主题不在本页范围,由各自的目录页单独讲解:Wi-Fi 连接细节(见网络协议相关页面)、音频编解码器内部实现(见音频相关页面)、LVGL 组件的每个示例(见 LVGL 页面)、OTA 升级协议(见升级相关页面)。本页只提供入口、结构与通用模式。

Overview

fw-AC792_SDK 是一套面向 AC792N 系列蓝牙音频 SoC 的嵌入式 SDK,采用"芯片基础库 + 通用 app 层 + 示例"的分层结构。示例代码统一放在 sdk/apps/common/example/ 下,每个示例是一个独立的 main.c(或少量辅助文件),通过配置宏开关编入工程,不随默认固件编译,从而保证示例之间互不干扰、可按需裁剪。

示例按功能域划分:

功能域目录代表示例
音频example/audio/JLA 解码、变速变调(pitch_speed)、buffer 播放、虚拟 DAC、虚拟编码
图形加速example/dma2d/填充/拷贝/混合测试、RLE 解码、DMA2D 快速排序
网络协议example/network_protocols/MQTT/MQTTS、HTTP(下载/上传/服务器)、CoAP、FTP、NTP、WebSocket、IPv4/IPv6 Socket
外设example/peripheral/按键(key)、重力传感器(gsensor)、TSI DVB
计量example/scale/电子秤示例(scale_main)
OTAexample/update/外置 Flash 扩展 OTA(ext_ota_example)
蓝牙扩展ext_spi_bt/、ext_uart_bt/SPI/UART 外挂蓝牙模块测试
图形库lvgl_v8/examples/LVGL v8 动画、事件等官方示例移植

每个示例都遵循相同的"三步走"模式:① 通过 #ifdef USE_XXX_TEST 编译开关使能 → ② 在 app_config.h 中定义宏并选择运行哪个示例 → ③ 示例内部通过 late_initcall 或应用注册机制挂载到系统。开发者拿到示例后只需改配置宏即可切换测试对象,无需修改启动代码。

Architecture

示例代码在 SDK 中的位置及其与系统组件的依赖关系如下图所示:

flowchart TD
    subgraph sg_Examples["sdk/apps/common/example/(示例层)"]
        MQTT["network_protocols/mqtt/main.c"]
        HTTP["network_protocols/http/*/main.c"]
        SOCK["network_protocols/sockets/*/*_example.c"]
        AUDIO["audio/play_audio_buffer/main.c"]
        KEY["peripheral/key/main.c"]
        DMA2D["dma2d/*_test.c"]
        LVGL["lvgl_v8/examples/*/*.c"]
    end

    subgraph sg_Sys["系统服务层(SDK 基础设施)"]
        APP["application 框架<br/>REGISTER_APPLICATION / start_app"]
        EVENT["事件系统<br/>sys_event / key_event / device_event"]
        SERVER["server 框架<br/>server_request / audio_server"]
        NET["网络协议栈<br/>MQTTClient / socket API"]
        WIFI["wifi/wifi_connect.h"]
    end

    subgraph sg_HW["硬件层"]
        HW["AC792N 外设:DMA2D / I2S / Key / SPI / UART"]
    end

    APP_CONFIG["app_config.h(USE_XXX_TEST 开关)"] -->|"编译期使能"| sg_Examples
    MQTT --> NET
    MQTT --> WIFI
    HTTP --> NET
    SOCK --> NET
    AUDIO --> SERVER
    KEY --> APP
    KEY --> EVENT
    DMA2D --> HW
    LVGL --> HW
    NET --> HW
    SERVER --> HW
    EVENT --> HW

架构要点:

  • 示例层只依赖系统服务层,不反向依赖。所有示例都通过 system/includes.h、app_config.h 引入 SDK 头文件,自身不实现硬件驱动,便于复用与裁剪。
  • 编译开关是唯一的"接线"方式。示例文件头部用 #ifdef USE_MQTT_TEST(或 USE_AUDIO_BUFFER_TEST 等)包裹全部代码,未定义宏时文件编译为空,不占用代码空间。
  • 应用框架(application)是交互类示例的入口。按键示例通过 REGISTER_APPLICATION(app1) 注册名为 APP1 的应用,并用 late_initcall(c_main) 创建测试任务;网络类示例则直接在 main.c 中同步执行协议流程。

示例目录详解

音频示例(example/audio/)

示例说明
jla/main.cJLA 私有音频格式解码播放
pitch_speed/main.c变速变调播放,演示 pitch_speed 音频处理链
play_audio_buffer/main.c从文件/缓存读取音频数据送入解码 server 播放,演示 audio server 事件回调与读写信号量同步
virtual_dac/main.c虚拟 DAC,演示把解码输出重定向(不真实播放)
virtual_enc/main.c虚拟编码器,演示解码后重新编码的链路

DMA2D 图形加速示例(example/dma2d/)

示例说明
fill_test.c2D 填充(fill)性能与正确性测试
copy_test.c2D 拷贝(copy/blit)测试
blend_test.c2D 混合(blend/alpha 合成)测试
dma2d_quick_sort.c/.h利用 DMA2D 并行计算能力实现快速排序的演示
dma2d_verify.cDMA2D 结果校验工具
rle_test/rle_test.cRLE(游程编码)解码测试,用于压缩位图资源

网络协议示例(example/network_protocols/)

示例说明
mqtt/main.cMQTT 客户端连接阿里云物联网平台,订阅/发布温湿度消息(QoS1)
mqtts/main.cMQTT over TLS(加密连接)版本
http/http_download/main.cHTTP 文件下载
http/http_post_get_put/main.cHTTP POST/GET/PUT 请求示例
http/http_server/main.c设备侧 HTTP 服务器
http/user_http_head/post_flash/main.c、post_sd/main.cHTTP 上报数据到 Flash / SD 卡
`coap/coap_clientserver/main.c`
ftp_client/main.cFTP 客户端
ntp/main.cNTP 网络校时
websocket/main.cWebSocket 客户端
sockets/ipv4/*_example.c、sockets/ipv6/*_example.c原生 Socket 编程示例:IPv4/IPv6 下的 TCP 客户端、TCP 服务器、UDP 客户端、UDP 服务器

外设与其它示例

  • peripheral/key/main.c:按键事件示例,演示 application 注册、SYS_KEY_EVENT 事件处理与 key_event_enable() 开关;
  • peripheral/gsensor/main.c:重力传感器驱动与数据读取;
  • peripheral/TSI_DVB/DVB/dvb0_example.c、dvb1_example.c:TSI DVB 调谐器双路示例;
  • scale/scale_main.c:电子秤应用主流程;
  • update/http_upgrade/example3/ext_ota_example.c:外置 Flash 扩展 OTA 升级示例;
  • ext_spi_bt/bt_test_example.c、ext_uart_bt/bt_test_example.c:SPI/UART 外挂蓝牙模块的 AT 交互测试;
  • lvgl_v8/examples/:LVGL v8 官方示例移植(anim 动画、event 事件等),用于验证图形栈。

示例的启用与控制流

所有示例共享同一条"配置宏 → 初始化挂载 → 任务运行"的路径:

flowchart TD
    Start([修改 app_config.h]) --> Define{"定义 USE_XXX_TEST 宏?"}
    Define -->|"否"| Skip["该示例 main.c 整体为空<br/>(#ifdef 未命中)"]
    Define -->|"是"| Compile["示例代码编入固件"]
    Compile --> Entry{"示例入口类型"}
    Entry -->|"网络类(MQTT/HTTP…)"| Sync["main.c 中同步执行协议流程<br/>(NewNetwork→Connect→Subscribe→Publish)"]
    Entry -->|"交互类(key…)"| App["late_initcall(c_main)<br/>→ os_task_create 创建任务"]
    App --> Reg["REGISTER_APPLICATION 注册应用<br/>event_handler 接收系统事件"]
    Sync --> Run["示例运行,打印 log"]
    Reg --> Run

启用步骤

  1. 打开 sdk/apps/common/include/app_config.h(各工程亦可使用自己的 app_config.h);
  2. 定义对应宏,例如 #define USE_MQTT_TEST 或 #define USE_AUDIO_BUFFER_TEST;
  3. 同一时间只启用一个示例,避免多个示例的 main.c 同时编入造成入口冲突;
  4. 重新编译烧录,观察串口 log。

注意:宏名与示例文件一一对应(USE_MQTT_TEST 对应 mqtt/main.c,USE_AUDIO_BUFFER_TEST 对应 audio/play_audio_buffer/main.c),这是示例代码中 #ifdef 守卫使用的约定。

为什么使用"守卫宏 + 独立 main.c"的设计?

这是嵌入式 SDK 常见的示例组织方式,设计意图有三点:

  • 零成本裁剪:未使能的示例编译为空文件,不占用 Flash/RAM,避免链接器因未使用函数报错;
  • 隔离命名空间:每个示例独占一个 main.c,静态函数(如 mqtt_start、app1_event_handler)不会互相冲突;
  • 降低上手门槛:开发者不需要理解整个 SDK 的构建体系,改一个宏即可切换测试对象,把"示例"与"产品代码"的边界固定下来。

核心流程:MQTT 示例(连接阿里云物联网平台)

network_protocols/mqtt/main.c 是网络类示例的典型代表:通过 MQTT 协议连接阿里云,订阅下行主题并周期性发布温湿度属性。其控制流如下:

sequenceDiagram
    participant App as mqtt_start()
    participant Net as Network(TCP)
    participant MQ as MQTTClient
    participant Ali as 阿里云 IoT 平台

    App->>Net: NewNetwork(&network)
    App->>Net: SetNetworkRecvTimeout(1000ms)
    App->>MQ: MQTTClient(&client, &network, ...)
    App->>Net: ConnectNetwork(address, 1883)
    Net-->>App: err == 0
    App->>MQ: MQTTConnect(&client, &connectData)
    MQ->>Ali: CONNECT(用户名/密码/心跳30s)
    Ali-->>MQ: CONNACK
    App->>MQ: MQTTSubscribe(topic, QOS1, messageArrived)
    MQ->>Ali: SUBSCRIBE /user/get
    Ali-->>MQ: SUBACK
    loop 每 2 次循环发布一次
        App->>MQ: MQTTPublish(publishTopic, &message)
        MQ->>Ali: PUBLISH(温湿度 JSON)
        Ali-->>MQ: PUBACK(QoS1)
    end
    Ali-->>App: 下行消息 → messageArrived 回调打印

示例代码中的关键配置常量(见 main.c):

常量值含义
COMMAND_TIMEOUT_MS30000命令超时时间(ms)
MQTT_TIMEOUT_MS10000接收阻塞时间(ms)
MQTT_KEEPALIVE_TIME30000MQTT 心跳保活时间(ms)
SEND_BUF_SIZE / READ_BUF_SIZE1024发送/接收缓冲区大小

核心流程:音频 Buffer 播放示例

audio/play_audio_buffer/main.c 演示了 audio server 的事件驱动模型:解码 server 通过回调上报事件,示例通过读写信号量(r_sem/w_sem)与音频线程同步缓存数据。回调中处理 AUDIO_SERVER_EVENT_ERR、AUDIO_SERVER_EVENT_END(停止解码并 audio_uninit())和 AUDIO_SERVER_EVENT_CURR_TIME(刷新播放进度),自定义的 audio_vfs_fread 作为 VFS 读函数从 cbuffer_t 环形缓冲取数据,缓冲不足时挂起等待写信号量。这种"生产者-消费者 + 事件回调"结构是 SDK 音频链路的通用模式,示例可作为自定义音频源的模板。

核心流程:按键事件示例

peripheral/key/main.c 演示了 application 事件模型:先通过 REGISTER_APPLICATION(app1) 注册名为 APP1 的应用并绑定 event_handler,再用 late_initcall(c_main) 创建测试任务;任务中 start_app(&it) 启动应用、key_event_enable() 打开按键事件开关,此后所有 SYS_KEY_EVENT 都会递交给 app1_event_handler。event_handler 返回 true 表示已消费事件,不再传递给 app_default_event_handler——这是 SDK 事件分发的一个重要约定。

使用示例

示例 1:MQTT 订阅与发布(网络类示例模板)

以下代码摘自 MQTT 示例的初始化与订阅发布流程,展示了 SDK 网络协议栈的标准调用序列:NewNetwork → MQTTClient → ConnectNetwork → MQTTConnect → MQTTSubscribe → MQTTPublish:

//接收回调,当订阅的主题有信息下发时,在这里接收
static void messageArrived(MessageData *data)
{
    char temp[128] = {0};

    strncpy(temp, data->topicName->lenstring.data, data->topicName->lenstring.len);
    temp[data->topicName->lenstring.len] = '\0';
    printf("Message arrived on topic (len : %d, topic : %s)\n", data->topicName->lenstring.len, temp);

    memset(temp, 0, sizeof(temp));
    strncpy(temp, data->message->payload, data->message->payloadlen);
    temp[data->message->payloadlen] = '\0';
    printf("message (len : %d, payload : %s)\n", data->message->payloadlen, temp);
}

static int mqtt_start(void)
{
    Client client;
    Network network;
    MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
    MQTTMessage message;
    int err;
    int loop_cnt = 0;
    int temperature = 0;
    int humidity = 0;

    char sendbuf[256];
    char payload[256] = "{\"id\":\"161848123\",\"version\":\"1.0\",\"params\":{\"temperature\":%d,\"Humidity\":%d},\"method\":\"thing.event.property.post\"}";
    char *address = "a1mhUx5m6Uq.iot-as-mqtt.cn-shanghai.aliyuncs.com";
    char *username = "iot-001&a1mhUx5m6Uq";
    char *password = "083dc23c92b1aad822c7c1f1a3c687dda022089b";
    char *subscribeTopic = "/a1mhUx5m6Uq/iot-001/user/get";                      //订阅的主题
    char *publishTopic = "/sys/a1mhUx5m6Uq/iot-001/thing/event/property/post";   //发布消息的主题
    char *clientID = "12345|securemode=3,signmethod=hmacsha1|";

_reconnect:
    //初始化网络接口
    NewNetwork(&network);

    SetNetworkRecvTimeout(&network, 1000);

    //初始化客户端
    MQTTClient(&client, &network, COMMAND_TIMEOUT_MS, send_buf, sizeof(send_buf), read_buf, sizeof(read_buf));

    //tcp层连接服务器
    err = ConnectNetwork(&network, address, 1883);
    if (err != 0) {
        printf("ConnectNetwork fail\n");
        return -1;
    }

    connectData.willFlag = 0;
    connectData.MQTTVersion = 3;                                   //mqtt版本号
    connectData.clientID.cstring = clientID;                       //客户端id
    connectData.username.cstring = username;                       //连接时的用户名
    connectData.password.cstring = password;                       //连接时的密码
    connectData.keepAliveInterval = MQTT_KEEPALIVE_TIME / 1000;    //心跳时间
    connectData.cleansession = 1;                                  //是否使能服务器的cleansession,0:禁止, 1:使能

    //mqtt层连接,向服务器发送连接请求
    err = MQTTConnect(&client, &connectData);
    if (err != 0) {
        network.disconnect(&network);
        printf("MQTTConnect fail, err : 0x%x\n", err);
        return -1;
    }

    //订阅主题
    err = MQTTSubscribe(&client, subscribeTopic, QOS1, messageArrived);
    if (err != 0) {
        MQTTDisconnect(&client);
        network.disconnect(&network);
        printf("MQTTSubscribe fail, err : 0x%x\n", err);
        return -1;
    }

    message.qos = QOS1;
    message.retained = 0;

    while (1) {
        if (0 == loop_cnt % 2) {
            sprintf(sendbuf, payload, temperature, humidity);
            message.payload = sendbuf;
            message.payloadlen = strlen(sendbuf) + 1;

            temperature += 1;
            humidity += 2;

            if (temperature > 100) {
                temperature = 0;
            }
            if (humidity > 100) {
                humidity = 0;
            }

            //发布消息
            err = MQTTPublish(&client, publishTopic, &message);
            ...

Source: sdk/apps/common/example/network_protocols/mqtt/main.c

要点解读:

  • messageArrived 是订阅消息回调,MessageData 中 topicName 与 message 都是"长度 + 指针"结构(lenstring/payloadlen),因此示例先用 strncpy 拷贝再加 '\0',避免越界打印;
  • 使用 _reconnect: 标号支持失败后跳回重连,配合 network.disconnect() 做资源清理;
  • 发布使用 QoS1,payload 模板为阿里云物模型属性上报 JSON,温湿度每两轮循环自增一次并做 0~100 回绕。

示例 2:按键事件与 application 注册(交互类示例模板)

#include "app_config.h"
#include "system/includes.h"
#include "event/device_event.h"
#include "event/key_event.h"
#include "key/key_driver.h"

/* 一个事件递交到当前APP处理,实际上最常用*/
static int app1_event_handler(struct application *app, struct sys_event *event)
{
    if (event->type == SYS_KEY_EVENT) {
        struct key_event *key_e = (struct key_event *)event->payload;
        printf("app1_event_handler[in %s task]  SYS_KEY_EVENT %d \r\n", os_current_task(), key_e->value);
    }

    return true;    //如果返回true则不再调用 app_default_event_handler
    //return false;
}

static void app_event_test_task(void *p)
{
    os_time_dly(100); //app_core_init初始化完成后才可以测试

    struct intent it;
    init_intent(&it);

    it.name = "APP1";
    it.action = ACTION_DO_NOTHING;
    start_app(&it);

    key_event_enable(); //针对按键事件,需要先使能开关

    while (1) {
        os_time_dly(200);
        puts("\n\n\n\n");
        os_time_dly(200);
        puts("\n\n\n\n");
    }
}

static int app1_state_machine(struct application *app, enum app_state state, struct intent *it)
{
    return 0;
}
static const struct application_operation app1_ops = {
    .state_machine  = app1_state_machine,
    .event_handler  = app1_event_handler,
};
REGISTER_APPLICATION(app1) = {
    .name   = "APP1",
    .ops    = &app1_ops,
    .state  = APP_STA_DESTROY,
};

static int c_main(void)
{
    os_task_create(app_event_test_task, NULL, 10, 1000, 128, "app_event_test_task");
    return 0;
}
late_initcall(c_main);

Source: sdk/apps/common/example/peripheral/key/main.c

要点解读:

  • REGISTER_APPLICATION(app1) 是编译期注册宏,把应用描述符链接进系统应用表,start_app(&it) 按 intent.name 查找并启动;
  • late_initcall(c_main) 让测试任务在系统初始化晚期自动创建,任务内先 os_time_dly(100) 等待 app_core_init 完成,这是示例编写时的时序约定;
  • key_event_enable() 是按键事件的全局开关,未使能时按键事件不会投递,这是 SDK 事件系统的节能设计。

示例 3:音频解码 server 事件回调(audio server 模型)

static int audio_buffer_dec_stop(void)
{
    int err = 0;
    union audio_req req = {0};

    if (!__this->file) {
        return 0;
    }

    req.dec.cmd = AUDIO_DEC_STOP;
    server_request(__this->dec_server, AUDIO_REQ_DEC, &req);

    int argv[2];
    argv[0] = AUDIO_SERVER_EVENT_END;
    argv[1] = (int)__this->file;
    server_event_handler_del(__this->dec_server, 2, argv);

    fclose(__this->file);
    __this->file = NULL;

    return 0;
}
extern void audio_uninit();
static void dec_server_event_handler(void *priv, int argc, int *argv)
{
    switch (argv[0]) {
    case AUDIO_SERVER_EVENT_ERR:
        log_i("audio_buffer: AUDIO_SERVER_EVENT_ERR\n");
    case AUDIO_SERVER_EVENT_END:
        log_i("audio_buffer: AUDIO_SERVER_EVENT_END\n");
        audio_buffer_dec_stop();
        audio_uninit();
        break;
    case AUDIO_SERVER_EVENT_CURR_TIME:
        log_d("play_time: %d\n", argv[1]);
        __this->play_time = argv[1];
        break;
    }
}
static int audio_vfs_fread(void *file, void *data, u32 len)
{
    cbuffer_t *cbuf = (cbuffer_t *)file;
    u32 rlen;

    do {
        rlen = cbuf_get_data_size(cbuf);
        if (rlen < len) {
            if (audio_get_read_file_is_end()) {
                return cbuf_read(cbuf, data, rlen);
            }
            audio_post_write_semaphore();
            audio_pend_read_semaphore();
            continue;
        }
        if (cbuf_read(cbuf, data, len) > 0) {
            return len;
        }
    } while (__this->run_flag);
    ...

Source: sdk/apps/common/example/audio/play_audio_buffer/main.c

要点解读:

  • 与解码 server 的交互统一走 server_request(server, AUDIO_REQ_DEC, &req) 命令通道,req.dec.cmd 决定具体动作(如 AUDIO_DEC_STOP);
  • 解码结束/出错通过 AUDIO_SERVER_EVENT_END/ERR 事件回调上报,示例在此处做资源释放(fclose、server_event_handler_del、audio_uninit),形成"停止-注销回调-释放"的清理序列;
  • audio_vfs_fread 把环形缓冲 cbuffer_t 包装成 VFS 读接口,数据不足时用 audio_pend_read_semaphore()/audio_post_write_semaphore() 阻塞等待生产者填充,避免忙等。

配置选项

示例代码的配置集中在两个层面:编译期开关(app_config.h 宏) 与 示例内部常量。

编译期开关(app_config.h)

宏对应示例说明
USE_MQTT_TESTnetwork_protocols/mqtt/main.c使能 MQTT 客户端示例
USE_AUDIO_BUFFER_TESTaudio/play_audio_buffer/main.c使能音频 buffer 播放示例
(其它示例同理)各 main.c 头部 #ifdef按示例文件名约定命名

开关未定义时,示例文件整体为空。同一时刻建议只启用一个示例,避免多个 late_initcall / 注册表项同时生效。

MQTT 示例内部常量

常量类型默认值说明
COMMAND_TIMEOUT_MSint30000命令超时时间(ms)
MQTT_TIMEOUT_MSint10000接收阻塞时间(ms)
MQTT_KEEPALIVE_TIMEint30000心跳保活时间(ms)
SEND_BUF_SIZEint1024发送 buf 大小(字节)
READ_BUF_SIZEint1024接收 buf 大小(字节)
send_buf / read_bufstatic char[]—全局发送/接收缓冲区

来源:mqtt/main.c

音频 Buffer 示例内部常量

常量值说明
FIRST_INFO_SECTOR512首个信息扇区偏移
READ_DATA_SIZE512每次读取数据大小(字节)

来源:play_audio_buffer/main.c

API 参考(示例中使用的 SDK 核心接口)

以下接口并非示例自身定义,而是 SDK 提供的公共 API,示例代码展示了它们的标准用法。

REGISTER_APPLICATION(name)

  • 说明:编译期宏,把 struct application 描述符链接进系统应用注册表,使 start_app() 可按名字查找。
  • 用法:REGISTER_APPLICATION(app1) = { .name = "APP1", .ops = &app1_ops, .state = APP_STA_DESTROY };
  • 关联结构:struct application_operation(含 state_machine、event_handler 回调)。
  • 来源:peripheral/key/main.c

start_app(struct intent *it)

  • 说明:按 intent.name 启动已注册的应用;it->action 指定启动动作。
  • 参数:it(struct intent *):包含 name、action 等字段;需先 init_intent(&it)。
  • 来源:peripheral/key/main.c

late_initcall(fn)

  • 说明:把函数注册为系统"晚期初始化"回调,系统启动流程接近完成时自动调用,适合创建示例测试任务。
  • 来源:peripheral/key/main.c

key_event_enable()

  • 说明:使能按键事件投递;未调用时按键事件不会进入事件分发。
  • 来源:peripheral/key/main.c

MQTT 客户端接口(mqtt/MQTTClient.h)

函数说明
NewNetwork(Network *n)初始化网络接口对象
SetNetworkRecvTimeout(Network *n, int ms)设置接收超时
MQTTClient(Client *c, Network *n, int timeout, ...)初始化 MQTT 客户端(绑定 buf)
ConnectNetwork(Network *n, char *addr, int port)TCP 层连接服务器,返回 0 成功
MQTTConnect(Client *c, MQTTPacket_connectData *d)MQTT 层连接(CONNECT/CONNACK)
MQTTSubscribe(Client *c, char *topic, int qos, callback)订阅主题并注册消息回调
MQTTPublish(Client *c, char *topic, MQTTMessage *msg)发布消息
MQTTDisconnect(Client *c)断开 MQTT 连接
  • 失败返回值:MQTTConnect/MQTTSubscribe/MQTTPublish 非 0 表示失败,示例打印 err : 0x%x 并清理资源。
  • 来源:network_protocols/mqtt/main.c

Audio server 接口(server/audio_server.h)

接口说明
server_request(server, AUDIO_REQ_DEC, &req)向解码 server 发送命令(req.dec.cmd 取值如 AUDIO_DEC_STOP)
server_event_handler_del(server, argc, argv)注销指定事件回调
事件 AUDIO_SERVER_EVENT_ERR/END/CURR_TIME解码出错/结束/播放进度上报
  • 来源:audio/play_audio_buffer/main.c

故障模式、边界情况与并发注意

  • 网络连接失败:MQTT 示例对 ConnectNetwork、MQTTConnect、MQTTSubscribe 分别判错,失败路径依次执行 network.disconnect() 与 MQTTDisconnect() 防止资源泄漏;_reconnect: 标号预留了重连入口,但示例本身未实现退避重试,量产代码需补充指数退避与状态机。
  • 缓冲区不足:SEND_BUF_SIZE/READ_BUF_SIZE 固定为 1024 字节,若主题名、JSON 报文或证书字段超过该长度会导致截断或错误,扩展示例时应同步加大 buf。
  • 消息回调的数据生命周期:MessageData 中的 payload 指针指向接收缓冲,回调返回后即失效,示例先拷贝到局部数组再打印,这是嵌入式 MQTT 回调的通用安全写法。
  • 音频环形缓冲并发:audio_vfs_fread 与生产者线程共享 cbuffer_t,通过 r_sem/w_sem 信号量互斥;若生产者提前结束而读端仍阻塞,可能造成永久挂起——示例通过 audio_get_read_file_is_end() 与 run_flag 双重判定退出,移植时务必保留这两个退出条件。
  • 事件消费语义:event_handler 返回 true 会短路 app_default_event_handler;多个应用同时注册时,事件只递交给当前活跃应用(intent 启动的 APP1),理解这一点才能正确设计多应用事件路由。
  • 示例互斥:多个示例同时使能时,各自的 late_initcall 任务与注册表项会同时运行,可能争抢外设或打印混乱;SDK 约定一次只启用一个示例。

性能与运维提示

  • MQTT 示例每两轮循环发布一次温湿度,实际产品应根据业务频率调整发布节奏,避免无效流量消耗网络与功耗;心跳 MQTT_KEEPALIVE_TIME(30s)需大于运营商 NAT 超时,否则长连接会被静默断开。
  • play_audio_buffer 使用 512 字节分片读取,配合双信号量实现流水线式填充;若解码跟不上,会表现为 CURR_TIME 停滞,可优先增大 cache_buf 而非降低采样率。
  • 示例代码默认开启 printf/log_i/log_d 调试输出,会占用串口带宽;量产时应通过日志等级宏关闭,避免影响实时任务。

扩展点

  • 新增协议示例:复制 network_protocols/ 下任一 main.c,按"守卫宏 + 协议初始化 + 收发循环"三段式编写,并在 app_config.h 增加对应 USE_XXX_TEST 宏。
  • 自定义音频源:以 play_audio_buffer 为模板,替换 audio_vfs_fread 的数据来源(Flash、网络流、录音 buffer),即可接入 SDK 音频解码链路。
  • 多应用事件路由:参考 REGISTER_APPLICATION + intent 机制注册多个应用,用 event_handler 的返回值控制事件分发优先级。
  • LVGL 示例:lvgl_v8/examples/ 下的官方示例可直接移植为 UI 原型,验证图形栈性能后再替换为产品界面。

相关链接

  • 示例代码根目录:sdk/apps/common/example
  • MQTT 示例源码:network_protocols/mqtt/main.c
  • 按键示例源码:peripheral/key/main.c
  • 音频 Buffer 播放示例:audio/play_audio_buffer/main.c
  • 相关目录页:网络协议(MQTT/HTTP/CoAP)、音频服务、DMA2D 图形加速、LVGL 图形库、OTA 升级、外设(按键/传感器/DVB)。
Prev
公共应用模块库