杰理 SDK 文档中心
首页
首页
  • fw-Bootloader:JL 系列定制 Bootloader

    • Bootloader 架构与芯片适配
    • uboot 升级协议与流程
    • 上位机升级工具
    • 编译环境与快速开始
  • ac792n-ota-loader:AC792N 系列 OTA Loader

    • 工程结构与公共运行时框架
    • SD 卡与 USB 基础升级通道
    • 安全升级通道(SD/USB)
    • 用户自定义升级通道(UART/USB HID)
    • LVGL 图形化升级界面与模拟器
  • ac791n-ota-loader:AC791N 系列 OTA Loader

    • uboot 应用框架与 WiFi 示例
    • 升级通道变体(AP/STA/USB HID)
    • 网络与系统库依赖

uboot 应用框架与 WiFi 示例

本文档详细解析 Jieli(杰理)AC791N 系列 OTA Bootloader(uboot)中的应用框架(uboot/apps/code/common)以及基于该框架的 WiFi 示例应用(uboot/apps/code/demo_wifi),涵盖启动流程、任务系统、中断注册、系统补充函数、WiFi 协议栈任务布局、STA MAC 管理与网络 OTA 升级机制。

Purpose and Scope

本页面围绕 AC791N OTA Loader 中 uboot 应用层(apps)的两部分内容展开:

  • 应用框架:ac791n-ota-loader/uboot/apps/code/common/main.c 所定义的 uboot 应用入口、.boot_bss 全局变量、升级模式判断(jump_mode_check)、芯片重启逻辑(chip_restart)、低压检测配置(ota_vlvd_sel)等基础设施;
  • WiFi 示例应用:ac791n-ota-loader/uboot/apps/code/demo_wifi/ 目录下的 app_main.c、wifi_demo_task.c、wifi_conf.c、net_update.c、vm_wifi.c、reserved_wifi.c,演示如何基于框架搭建 WiFi 网络任务并通过 lwip 完成 OTA。

不在本页面范围内(属于兄弟页面主题,仅作引用):BLE RCSP 服务(code/bluetooth/ble_rcsp_server.c)、USB HID 设备升级(code/common/usb/)、UART 升级模式(dev_update/updata_mode.h)、以及各芯片平台(wl82/wl83)的具体驱动实现。如需了解其他升级通道,请参见对应 catalog 页面。

Overview

uboot 是芯片上电后、正式 App 运行前的一段引导/升级程序。AC791N 的 uboot 工程在 ac791n-ota-loader/uboot 下组织为:apps(应用层)+ include_lib(库)+ 各 post_build 产物。应用层内部又分为:

  • code/common:与业务无关的公共框架——main 入口、消息、LTV 格式、升级主流程(update_main.c)、USB 设备栈、版本信息等;
  • code/demo_wifi:以 WiFi 为升级通道的示例应用,展示了"框架 + 业务任务"的标准组织方式;
  • code/bluetooth:BLE RCSP 服务示例;
  • code/cpu/wl82:平台相关配置(config.c)。

WiFi 示例的核心设计思路是:通过 task_info_table 静态声明任务集合(包括 app_core 主任务与 net_ota 网络升级任务),在 CONFIG_WIFI_ENABLE 宏开启时追加 WiFi 协议栈所需的 6 个任务(tasklet/RTMLTask/RTCQTask/WLRQTask/WLQUTask/tcpiptask),然后由 wifi_demo_task 驱动 STA/AP 连接状态机,最终借助 lwip TCP 栈完成固件下载与升级。整个框架刻意保持"最小操作系统 + 静态任务表 + 回调/事件驱动"的轻量结构,以适配 Bootloader 阶段有限的 RAM 资源。

Architecture

下图展示了 uboot 应用框架与 WiFi 示例的组件关系与数据流:

flowchart TD
    subgraph sg_Loader["uboot 应用框架 (code/common/main.c)"]
        BootInfo["g_boot_info / boot_sys_cfg (.boot_bss)"]
        JumpCheck["jump_mode_check() / get_share_osc_en()"]
        Restart["chip_restart() / ota_vlvd_sel()"]
    end

    subgraph sg_TaskSys["任务系统 (task_info_table)"]
        AppCore["app_core"]
        SysEvent["sys_event"]
        SysTimer["sys_timer / systimer"]
        NetOta["net_ota"]
    end

    subgraph sg_WifiStack["WiFi 协议栈任务 (CONFIG_WIFI_ENABLE)"]
        Tasklet["tasklet"]
        Rtml["RTMLTask"]
        Rtcq["RTCQTask"]
        Wlrq["WLRQTask"]
        Wlqu["WLQUTask"]
        Tcpip["tcpiptask"]
    end

    subgraph sg_DemoWifi["WiFi 示例应用 (demo_wifi)"]
        AppMain["app_main.c"]
        WifiDemo["wifi_demo_task.c"]
        WifiConf["wifi_conf.c"]
        NetUpdate["net_update.c"]
        WifiStore["vm_wifi.c / reserved_wifi.c"]
    end

    AppCore -->|"创建并驱动"| AppMain
    AppMain -->|"声明任务"| Tasklet
    BootInfo --> Restart
    JumpCheck --> Restart
    NetOta -->|"lwip TCP 下载"| NetUpdate
    WifiDemo --> WifiConf
    WifiDemo --> WifiStore
    NetUpdate --> Tcpip
    Tasklet -->|"事件上报"| WifiDemo
    WifiDemo -->|"连接/断开事件"| NetOta

架构要点说明:

  • uboot 应用框架(左侧)是全部应用共享的底座:main.c 在 .boot_bss 段放置 g_boot_info 与 boot_sys_cfg,并在启动早期完成升级模式判断与复位原因处理;chip_restart() 依据 RAM 中的跳转标志决定是进入正式 App 还是留在升级模式。
  • 任务系统(中间)由 app_main.c 中的 task_info_table[] 静态表驱动。该表声明了 app_core(业务主任务)、sys_event(事件任务)、systimer/sys_timer(定时器任务)与 net_ota(网络升级任务)。其中 net_ota 使用了静态分配的 TCB 与任务栈(net_ota_tcb_stk_q),避免 Bootloader 阶段动态内存不足。
  • WiFi 协议栈任务(右上方)仅在 CONFIG_WIFI_ENABLE 定义时编译进任务表,注释明确说明"通过调节任务优先级平衡 WIFI 收发占据总 CPU 的比重"——这是 Bootloader 场景下 CPU 争用的关键设计决策。
  • WiFi 示例应用(右下)是框架的消费方:wifi_demo_task.c 管理 STA 连接事件与 MAC 记录,net_update.c 通过 lwip TCP 下载固件,vm_wifi.c/reserved_wifi.c 负责 WiFi 配置的掉电保存。

应用框架实现分析 (code/common/main.c)

.boot_bss 段全局变量与启动早期状态

框架在 main.c 开头以 SEC(.boot_bss) 段属性声明了两个关键全局变量,确保它们在 Bootloader 的 BSS 清零/重定位阶段位于固定位置:

//变量定义
static struct boot_info_t g_boot_info SEC(.boot_bss);
struct SYS_CFG boot_sys_cfg SEC(.boot_bss);

Source: main.c

设计意图:g_boot_info 与 boot_sys_cfg 必须贯穿"加载器 → 升级模式 → 正式 App"的整个生命周期,boot_bss 段保证其地址在链接期固定、不受 malloc 堆影响,且正式 App 侧也可以通过约定地址读取这些信息。

升级模式判断:jump_mode_check()

该函数用于读取上次复位前写入 RAM 末尾的跳转标志(EXT_JUMP_FLAG),从而决定复位后是否执行特殊跳转:

static u8 *latch_flag = NULL;
u8 jump_mode_check()
{
    if (latch_flag == NULL) {
        UPDATA_PARM *p;
        p = (u8 *)get_updata_ram_end_addr(); //定位到ram1最后地址
        latch_flag = update_param_ext_get(p, EXT_JUMP_FLAG);
    }
    if (latch_flag) {
        printf(">>>[test]:flag = %d\n", latch_flag[0]);
        return latch_flag[0];
    }
    printf(">>>[test]:latch_flag == NULL\n");
    return 0;
}

Source: main.c

关键点:latch_flag 使用惰性初始化(首次调用时定位 RAM1 末尾地址),之后重复调用直接返回缓存值。update_param_ext_get(p, EXT_JUMP_FLAG) 是升级参数扩展区解析接口,若标志不存在则返回 0(默认不跳转)。

芯片重启逻辑:chip_restart()

chip_restart() 是框架中最能体现"升级状态机"思想的分支点:只有 jump_mode_check() 返回 0 且 get_update_jump_flag() 置位时,才会执行关闭蓝牙协议栈、释放 RAM 保护等清理动作后跳转:

void chip_restart(void)
{
    /* 寄存器不会复位 */
#if (0 == UART_UPDATE_ONLY_TEST_MODE)
    u8 latch_reset = jump_mode_check();
    if (latch_reset == 0) {
        if ((latch_flag == NULL) && get_update_jump_flag()) {
            ram_protect_close();
#if(BT_UPDATA_MODULE_CONTROL == 1)
            puts("bredr_bd_close\n");
            bredr_bd_close();
#endif
#if((BT_UPDATA_MODULE_CONTROL  == 2) || BLE_GATT_UPDATA_MODULE_CONTROL)
            ll_hci_destory();
#endif

Source: main.c

设计意图:BT_UPDATA_MODULE_CONTROL 宏区分升级时的蓝牙协议栈关闭策略(1=经典蓝牙 bredr_bd_close,2/BLE=LL/HCI 销毁 ll_hci_destory),避免跳转升级后残留的中断或 DMA 破坏正式 App 运行。ram_protect_close() 必须在跳转前调用,否则受保护 RAM 段无法被新固件使用。

低压检测配置:ota_vlvd_sel()

该函数封装了 P33 低压检测(VLVD)寄存器的时序操作,用于升级场景下监测电压跌落:

void ota_vlvd_sel(int lvd)
{
    P33_VLVD_OE(0);
    udelay(1);
    P33_VLVD_EN(0);
    VLVD_SEL(lvd);
    udelay(1);
    P33_VLVD_EN(1);
    P33_VLVD_OE(1);
}

Source: main.c

时序要点:先关闭输出使能(OE)与使能位(EN),在断电状态下写入阈值选择 VLVD_SEL(lvd),再依次恢复 EN 与 OE。每次寄存器写操作之间插入 udelay(1) 保证 P33 内部稳定。这一"先关后写再开"的序列是为了避免写阈值瞬间产生误触发中断。

其他框架设施

  • PowerPin 结构(gpio[PP_N]/level[PP_N],PP_N = 3)记录最多 3 个电源检测引脚及其期望电平,用于 get_power_pin_multi() 等上电判定;
  • get_share_osc_en():通过 update_param_ext_get(p, EXT_SHARE_OSC_EN) 判断是否共享振荡器时钟,返回 1 时为 TRUE;
  • boot_sys_cfg(struct SYS_CFG)为全局可访问的系统配置,供升级流程读取时钟/存储配置。

WiFi 示例应用框架 (demo_wifi/app_main.c)

任务列表:task_info_table[]

WiFi 示例以静态任务表定义整个系统的并发骨架。每个条目为 {名称, 优先级, 栈大小, 队列大小, 可选静态TCB}:

/*任务列表 */
const struct task_info task_info_table[] = {
    {"app_core",            5,     1024,    64},
    {"sys_event",           3,     512,     0},
    {"systimer",            6,     256,     0},
    {"sys_timer",           4,     512,   128},
    {"net_ota",             5,     NET_OTA_STK_SIZE, 0, net_ota_tcb_stk_q},

#ifdef CONFIG_WIFI_ENABLE
    {"tasklet",             5,     1600,     0},//通过调节任务优先级平衡WIFI收发占据总CPU的比重
    {"RTMLTask",            6,     800,      0},//任务名称长度<12
    {"RTCQTask",            6,     400,      0},//任务名称长度<12
    {"WLRQTask",            6,     356,      0},//任务名称长度<12
    {"WLQUTask",            6,     356,      0},//任务名称长度<12
    {"tcpiptask",           6,     800,      0},//任务名称长度<12
#endif
    {0, 0, 0, 0, 0},
};

Source: app_main.c

任务角色分析:

任务名优先级栈大小职责
app_core51024业务主任务(WiFi demo 逻辑入口)
sys_event3512系统事件分发
systimer6256硬件定时器服务
sys_timer4512软件定时器服务(128 队列)
net_ota51024网络 OTA 升级任务,使用静态 TCB/栈
tasklet51600WiFi MAC 收发包软中断处理(最大栈,平衡 CPU 占比)
RTMLTask6800RTM 链路管理
RTCQTask6400RT 控制队列
WLRQTask6356WiFi 接收队列
WLQUTask6356WiFi 发送队列
tcpiptask6800lwip TCP/IP 协议栈任务

设计意图:WiFi 协议栈任务统一使用优先级 6(高于 app_core 的 5),保证协议栈及时响应;tasklet 栈高达 1600 字节且注释明确"通过调节任务优先级平衡 WIFI 收发占据总 CPU 的比重"——这是 Bootloader 中 CPU 资源受限时的可调旋钮。任务名被限制在 12 字符以内以适配 RTOS 的命名缓冲区。

中断注册表:irq_info_table[]

示例展示了双核(CPU0/CPU1)场景下的中断强制注册机制:

const struct irq_info irq_info_table[] = {
    //中断号   //优先级0-7   //注册的cpu(0或1)
#if CPU_CORE_NUM == 1
    { IRQ_SOFT5_IDX,      7,   0    }, //此中断强制注册到cpu0
    { IRQ_SOFT4_IDX,      7,   1    }, //此中断强制注册到cpu1
    { -2,                 -2,  -2   },//如果加入了该行, 那么只有该行之前的中断注册到对应核, 其他所有中断强制注册到CPU0
#endif
    { -1,     -1,   -1    },
};

Source: app_main.c

-2 哨兵行表示"此前的中断按指定核注册,之后的所有中断强制注册到 CPU0",-1 为表结束标志。这是 AC791N 双核架构下将软中断(IRQ_SOFT5/IRQ_SOFT4)分别绑定到两个核、其余外设中断统一收敛到 CPU0 的典型配置,可显著减少核间同步开销。

静态分配的 net_ota 任务栈

#define NET_OTA_STK_SIZE 1024
#define NET_OTA_Q_SIZE 0
static u8 net_ota_tcb_stk_q[sizeof(StaticTask_t) + NET_OTA_STK_SIZE * 4 + sizeof(struct task_queue) + NET_OTA_Q_SIZE] ALIGNED(4);

Source: app_main.c

该数组按 4 字节对齐,一次性容纳 StaticTask_t(静态 TCB)、任务栈(STK_SIZE * 4,按 4 字节/栈单元)与任务队列结构。选择静态分配是因为网络 OTA 是升级主流程,不能在 malloc 失败时中断,同时也避免升级过程中堆碎片化。

系统补充函数(OS 适配层)

app_main.c 为上层库提供了必须的"胶水"函数,其中存储类接口在 uboot 阶段为桩实现:

int syscfg_read(u16 item_id, void *buf, u16 len)
{
    return -1;
}
int syscfg_write(u16 item_id, void *buf, u16 len)
{
    return 0;
}
int syscfg_dma_write(u16 item_id, void *buf, u16 len)
{
    return 0;
}
void *get_save_frame_ptr()
{
    return NULL;
}
u32 OSGetTime()
{
    return jiffies_to_msecs(jiffies);
}
unsigned int random32(int type)
{
    return JL_RAND->R64L;
}

Source: app_main.c

设计意图:syscfg_* 在 uboot 阶段返回失败/空操作,因为正式 App 的系统配置区尚未初始化;random32 直接读取芯片随机数寄存器 JL_RAND->R64L(硬件真随机源),供 WiFi 协议栈生成随机数;OSGetTime 基于内核 jiffies 换算毫秒。这些桩函数让 lwip/wifi 库可以在无完整文件系统的 Bootloader 环境中编译通过。

核心流程:WiFi 示例的端到端运行

从系统启动到网络 OTA 的完整时序如下:

sequenceDiagram
    participant RTOS as 任务系统 (task_info_table)
    participant App as app_core / demo_wifi
    participant Demo as wifi_demo_task
    participant Wifi as WiFi 协议栈 (tasklet 等)
    participant Mac as wifi_connect_sta_mac 存储区
    participant Tcp as lwip TCP (net_update)

    RTOS->>App: 创建 app_core 等任务
    App->>Demo: 创建 WiFi demo 任务
    Demo->>Wifi: 初始化并连接 AP / 开启 SoftAP
    Wifi-->>Demo: 上报 STA 连接事件 (hwaddr, ipaddr)
    Demo->>Demo: 打印 MAC 与 IP (r_printf)
    Demo->>Mac: wifi_connect_sta_mac(0, mac, 6, 0) 存入 MAC
    Demo->>Tcp: 触发网络 OTA 升级 (net_update)
    Tcp->>Wifi: lwip TCP 下载固件数据
    Wifi-->>Tcp: 数据包
    Tcp-->>Demo: 升级结果回调
    Wifi-->>Demo: 上报断开事件
    Demo->>Mac: wifi_connect_sta_mac(0, mac, 6, 2) 清空 MAC

流程要点:

  1. 系统上电后,RTOS 依据 task_info_table[] 创建各任务;app_core 是业务入口,负责初始化 demo_wifi 应用;
  2. wifi_demo_task 初始化 WiFi(STA 或 SoftAP 模式,由 wifi_conf.c 配置决定),并注册连接/断开事件回调;
  3. 当 STA 接入(或 STA 成功连上 AP)时,协议栈经 tasklet 上报事件,demo 任务打印对端 MAC 与 IP,并通过 wifi_connect_sta_mac(0, mac, 6, 0) 将 MAC 记录到存储区;
  4. 之后 net_ota 任务通过 net_update.c 以 lwip TCP 向升级服务器发起下载,固件包经 WiFi 协议栈收发;
  5. 连接断开时,demo 任务用 wifi_connect_sta_mac(0, mac, 6, 2) 清空对应 MAC 记录,进入重连等待。

STA MAC 管理:wifi_connect_sta_mac()

wifi_demo_task.c 中的核心工具函数用 index 参数区分三种操作:0=存储、1=查找、2=删除:

static int wifi_connect_sta_mac(int offset, u8 *mac, int len, int index)//存储、查找、删除连接的STA设备的mac

Source: wifi_demo_task.c

其调用模式(从事件处理分支可见):

do {
    i = wifi_connect_sta_mac(i, &mac, 6, 1);   // 遍历查找已存 MAC
    if (i) { ... }
} while (i);

wifi_connect_sta_mac(0, hwaddr->addr, 6, 0);   // 存储 MAC
wifi_connect_sta_mac(0, hwaddr->addr, 6, 2);   // 清空 MAC

Sources: wifi_demo_task.c、wifi_demo_task.c、wifi_demo_task.c

MAC 记录流程的状态图:

flowchart TD
    Start([WiFi 事件]) --> Ev{"事件类型?"}
    Ev -->|"STA 接入"| Print["打印 hwaddr->addr 六字节 MAC"]
    Print --> Store["wifi_connect_sta_mac(0, mac, 6, 0) 存储"]
    Store --> Loop["wifi_connect_sta_mac(i, &mac, 6, 1) 遍历查重"]
    Ev -->|"STA 断开"| Clear["wifi_connect_sta_mac(0, mac, 6, 2) 清空"]
    Loop --> End([结束])
    Clear --> End

设计意图:SoftAP 模式下 Bootloader 需要维护"已授权 STA 列表",因此用带 offset/index 的线性表存储 MAC;index=1 的遍历式调用配合 do-while 循环可在固定内存(无动态链表)内完成查重,符合 Bootloader 对确定性内存开销的要求。

网络 OTA:net_update.c

net_update.c 依赖 lwip 的 TCP 栈(包含 lwip/ip4_addr.h、lwip/tcp.h),实现了固件的网络下载:

#include "lwip/ip4_addr.h"
#include "wifi/wifi_connect.h"
#include "lwip/tcp.h"

Source: net_update.c

  • 它通过 wifi_connect.h 获取已建立的 IP 配置,解析服务器地址(ip4_addr),建立 TCP 连接后按块接收固件数据并写入 Flash;
  • 该任务以静态栈(net_ota_tcb_stk_q,1024×4 字节)运行,规避堆碎片风险;
  • 升级过程中的数据校验与写入调度由 code/common/update_main.c 的升级主流程统一协调,net_update 只负责"网络侧取数"。

配置与存储:wifi_conf.c / vm_wifi.c / reserved_wifi.c

WiFi 示例将配置持久化拆成三份职责:

文件职责
wifi_conf.cWiFi 运行参数(SSID/密码/信道/模式)初始化,依赖 asm/crc16.h 做校验、lwip.h 做网络初始化
vm_wifi.c基于 VM(掉电易失管理)区的 WiFi 参数读写,头文件引用 wifi_connect.h 与 syscfg_id.h
reserved_wifi.c预留区存储,用于存放保留的 WiFi 参数块(如 MAC 白名单),同样引用 syscfg_id.h

这种"运行配置 + VM 存储 + 预留区"三层设计保证了:升级过程中即使 VM 区被改写,预留区仍保留出厂校准数据;syscfg_id.h 统一定义配置项 ID,避免不同模块间 ID 冲突。

Usage Examples

示例 1:声明任务与中断表(应用入口)

在 demo_wifi 中通过 task_info_table[] 声明整个系统的并发骨架,是"框架用法"的标准模板:

const struct task_info task_info_table[] = {
    {"app_core",            5,     1024,    64},
    {"sys_event",           3,     512,     0},
    {"systimer",            6,     256,     0},
    {"sys_timer",           4,     512,   128},
    {"net_ota",             5,     NET_OTA_STK_SIZE, 0, net_ota_tcb_stk_q},

#ifdef CONFIG_WIFI_ENABLE
    {"tasklet",             5,     1600,     0},
    {"RTMLTask",            6,     800,      0},
    {"RTCQTask",            6,     400,      0},
    {"WLRQTask",            6,     356,      0},
    {"WLQUTask",            6,     356,      0},
    {"tcpiptask",           6,     800,      0},
#endif
    {0, 0, 0, 0, 0},
};

Source: app_main.c

示例 2:读取升级跳转标志(框架基础设施)

任何需要判断"是否处于升级模式"的模块都可以复用 jump_mode_check():

static u8 *latch_flag = NULL;
u8 jump_mode_check()
{
    if (latch_flag == NULL) {
        UPDATA_PARM *p;
        p = (u8 *)get_updata_ram_end_addr(); //定位到ram1最后地址
        latch_flag = update_param_ext_get(p, EXT_JUMP_FLAG);
    }
    if (latch_flag) {
        printf(">>>[test]:flag = %d\n", latch_flag[0]);
        return latch_flag[0];
    }
    printf(">>>[test]:latch_flag == NULL\n");
    return 0;
}

Source: main.c

示例 3:OS 适配桩函数(库依赖提供)

当第三方库(lwip/wifi)依赖 syscfg_*、gettimeofday、random32 等接口时,按以下模式提供实现:

int syscfg_read(u16 item_id, void *buf, u16 len) { return -1; }
int syscfg_write(u16 item_id, void *buf, u16 len) { return 0; }
unsigned int random32(int type) { return JL_RAND->R64L; }
int gettimeofday(struct timeval *tv, void *tz)
{
    if (tv) {
        time_t t = (time_t)timer_get_ms();
        tv->tv_sec = t / 1000;
        tv->tv_usec = t % 1000 * 1000;
    }
    return 0;
}

Sources: app_main.c、app_main.c

示例 4:STA MAC 存储操作(WiFi demo 业务)

SoftAP 模式下对连接设备的 MAC 管理:

wifi_connect_sta_mac(0,  hwaddr->addr, 6, 0);//存储mac
wifi_connect_sta_mac(0,  hwaddr->addr, 6, 2);//清空mac

Source: wifi_demo_task.c

Configuration Options

以下配置宏/常量分散在 app_main.c 与框架代码中,控制 WiFi 示例的编译与运行行为:

配置项类型默认值说明
CONFIG_WIFI_ENABLE宏未定义定义后追加 WiFi 协议栈 6 个任务(tasklet/RTML/RTCQ/WLRQ/WLQU/tcpiptask)
CPU_CORE_NUM宏1为 1 时启用 IRQ_SOFT5/4 双核中断注册与 -2 哨兵
NET_OTA_STK_SIZE常量1024net_ota 任务栈大小(字节数/4 为栈单元数)
NET_OTA_Q_SIZE常量0net_ota 任务消息队列深度
BT_UPDATA_MODULE_CONTROL宏—1=升级时 bredr_bd_close();2/BLE=ll_hci_destory()
UART_UPDATE_ONLY_TEST_MODE宏0非 0 时 chip_restart() 跳过跳转标志判断(仅 UART 测试模式)
SDRAM_SIZE宏0非 0 时 cache_ram_config() 将 SDRAM(0x4000000 起)用作缓存内存
CONFIG_WL_AP_ENABLE宏未定义定义后不释放 Cache way(AP 模式需保留更多 Cache)
PP_N常量3PowerPin 结构可记录的最大电源引脚数
tasklet 优先级/栈任务参数5 / 1600调节以平衡 WIFI 收发占 CPU 的比重

调整建议:当 WiFi 吞吐与业务任务(如 USB/串口升级)并发时,优先调节 tasklet 的优先级(源码注释明确此为平衡旋钮);RAM 紧张时优先缩减 tcpiptask/RTMLTask 的栈(800→512)。

API Reference

u8 jump_mode_check(void)

读取 RAM 末尾升级参数区中的 EXT_JUMP_FLAG,返回跳转模式标志;标志不存在或参数区为空时返回 0。

Returns: 跳转标志字节(0 表示无特殊跳转)。 Throws: 无(纯 C 实现,失败返回 0)。

void chip_restart(void)

依据跳转标志决定是否执行升级清理(关闭蓝牙协议栈、关闭 RAM 保护)后重启芯片。

注意: UART_UPDATE_ONLY_TEST_MODE == 1 时整个分支被编译剔除。

void ota_vlvd_sel(int lvd)

配置 P33 低压检测阈值。lvd 为 VLVD 选择值;函数内部按"关 OE → 关 EN → 写阈值 → 开 EN → 开 OE"的时序操作,写前写后各 udelay(1)。

static int wifi_connect_sta_mac(int offset, u8 *mac, int len, int index)

SoftAP 模式下 STA MAC 的存储/查找/删除统一入口。

Parameters:

  • offset(int):线性表的起始偏移(遍历时由调用方递增);
  • mac(u8*):6 字节 MAC 地址缓冲区;
  • len(int):MAC 长度(调用方固定传 6);
  • index(int):操作类型,0=存储、1=查找、2=删除。

Returns: 查找模式下返回下一个偏移(用于 do-while 遍历),0 表示结束/未命中。

unsigned int random32(int type)

返回硬件随机数寄存器 JL_RAND->R64L 的低位随机值,供协议栈随机化使用。type 参数保留。

int syscfg_read(u16 item_id, void *buf, u16 len)

uboot 阶段的系统配置读取桩实现,恒返回 -1(正式 App 的系统配置区未初始化,读取无意义)。

Failure Modes, Edge Cases & Concurrency

存储桩函数的静默失败

syscfg_read 恒返回 -1、syscfg_write 恒返回 0:上层若在 uboot 阶段依赖配置持久化,会得到"读取失败/写入假装成功"的结果。设计意图:uboot 不管理正式 App 的系统配置区,桩实现保证库代码可链接、可编译,但任何依赖持久化配置的模块必须自行判断返回值,避免把 0 误认为写入成功。

跳转标志的时序依赖

jump_mode_check() 依赖 RAM1 末尾由上一个加载器(get_updata_ram_end_addr)写入的 UPDATA_PARM。若升级参数区被破坏或地址被其他模块覆写,函数返回 0(默认路径),可能导致本应进入升级模式的复位走到了正常启动路径。框架对此的防护是 chip_restart() 中叠加 get_update_jump_flag() 双重判断,任一标志缺失都不执行破坏性清理。

VLVD 配置的竞态

ota_vlvd_sel() 在写阈值前先关闭 EN/OE,但两次 udelay(1) 之间若被高优先级中断抢占并读取 VLVD 状态,可能读到中间值。该函数被设计为在早期初始化阶段(中断未全开)调用,业务代码不应在运行时反复切换 VLVD。

WiFi 任务并发与 CPU 争用

WiFi 协议栈 6 个任务均以优先级 6 运行(高于 app_core 的 5),tasklet 注释明确警告"通过调节任务优先级平衡 WIFI 收发占据总 CPU 的比重"。风险点:

  • tasklet 栈 1600 字节为全系统最大,若实际回调嵌套过深会栈溢出(RTOS 无硬件栈保护时表现为随机崩溃);
  • 升级下载(tcpiptask/net_ota)与 USB/UART 升级通道并发时,需保证低优先级通道不被饿死;
  • wifi_connect_sta_mac 的线性表遍历(do-while)在 STA 列表较大时占用 CPU,但 MAC 白名单规模通常 ≤ 8,可接受。

静态栈的确定性内存

net_ota_tcb_stk_q 静态分配(StaticTask_t + 1024*4 + task_queue,4 字节对齐)是本框架的关键可靠性设计:网络 OTA 是升级主路径,不能因 malloc 失败中断;静态分配同时消除了堆碎片。

定时器回绕(2^32)

time_lapse 用无符号差值 t2 = t - *handle 判断超时,天然处理 32 位毫秒计数回绕;但 timer_get_sec() 直接做 /1000 除法,若上层基于秒计数做长周期判断需自行处理回绕。

Flash Cache 与延迟

delay_us() 依据 JL_SFC->CON 的 BIT(0)(SFC 使能)选择 8 或 4 分频系数,再乘 clk_get("sys") 计算忙等次数——若时钟源切换(如切 HRC)后未重新计算,微秒延时可能偏差。cache_ram_config() 在无 SDRAM 且未开 AP 模式时释放 7 路 DCache + 7 路 ICache(0x1f20000 + (8 - way)*4096)作为普通 RAM,扩容内存的同时牺牲 Cache 命中率,WiFi 高吞吐场景需评估。

Performance & Operational Considerations

  • 任务栈预算:WiFi 协议栈合计占用约 4312 字节栈(1600+800+400+356+356+800),加 app_core(1024)、net_ota(4096 字节含 TCB) 与系统任务,Bootloader 的 RAM 预算需按此静态核算;
  • CPU 占比旋钮:吞吐敏感时上调 tasklet 优先级或加大其栈;业务响应敏感时下调,使 app_core 获得更多调度机会;
  • 升级链路:net_ota → tcpiptask 为数据主路径,TCP 窗口与 NET_OTA_STK_SIZE 共同决定单次可缓冲的数据量,栈过小会触发 lwip 内存不足告警;
  • 双核绑定:irq_info_table 的 -2 哨兵把绝大多数外设中断收敛到 CPU0,降低核间同步;若新外设中断需要绑定 CPU1,必须在 -2 行之前声明。

Extension Points

  1. 新增业务任务:在 task_info_table[] 的哨兵行 {0,0,0,0,0} 之前追加条目即可,无需修改 RTOS 初始化代码;注意任务名 ≤ 12 字符、需要确定性内存时提供静态 TCB 数组(参考 net_ota_tcb_stk_q 模式);
  2. 新增升级通道:参照 net_update.c 的模式实现"取数"层(如 USB、串口、BLE),再挂接到 code/common/update_main.c 的升级主流程;网络层与写入层分离是框架的既定边界;
  3. 自定义 MAC 白名单策略:扩展 wifi_connect_sta_mac 的 index 语义或在 wifi_demo_task.c 的事件分支中增加授权判断,存储可落 reserved_wifi.c 预留区;
  4. 配置持久化适配:替换 syscfg_* 桩实现为真实的 VM/Flash 读写即可在 uboot 阶段启用配置保存(需保证与正式 App 的 syscfg_id.h ID 不冲突);
  5. 低压保护定制:通过 ota_vlvd_sel(lvd) 在升级写 Flash 前设置合适的阈值,防止擦写期间电压跌落导致变砖。

Tests

仓库中未见针对 uboot 应用框架的单元测试(该工程为嵌入式固件,以真机/升级工具验证为主,fw-Bootloader/doc/attch/ 下的截图与 update_tools 中的 UbootUpdate APK 即为验证手段)。源码内嵌的调试手段包括:

  • printf(">>>[test]:flag = %d\n", ...) 等 [test] 标记输出,用于验证跳转标志路径;
  • wifi_demo_task.c 中连接/断开事件分支对 MAC 与 IP 的 r_printf 打印,可据此验证 STA 接入/白名单逻辑。

Related Links

  • uboot 应用公共代码(common) — 应用框架入口
  • WiFi 示例应用入口(demo_wifi/app_main.c) — 任务/中断表定义
  • WiFi demo 任务(wifi_demo_task.c) — STA MAC 管理
  • 网络 OTA(net_update.c) — lwip TCP 升级
  • 升级主流程(update_main.c) — 升级状态机
  • 相关兄弟页面:BLE RCSP 升级示例(code/bluetooth/ble_rcsp_server.c)、USB HID 设备升级(code/common/usb/)、平台配置(code/cpu/wl82/config.c)
Next
升级通道变体(AP/STA/USB HID)