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_core | 5 | 1024 | 业务主任务(WiFi demo 逻辑入口) |
sys_event | 3 | 512 | 系统事件分发 |
systimer | 6 | 256 | 硬件定时器服务 |
sys_timer | 4 | 512 | 软件定时器服务(128 队列) |
net_ota | 5 | 1024 | 网络 OTA 升级任务,使用静态 TCB/栈 |
tasklet | 5 | 1600 | WiFi MAC 收发包软中断处理(最大栈,平衡 CPU 占比) |
RTMLTask | 6 | 800 | RTM 链路管理 |
RTCQTask | 6 | 400 | RT 控制队列 |
WLRQTask | 6 | 356 | WiFi 接收队列 |
WLQUTask | 6 | 356 | WiFi 发送队列 |
tcpiptask | 6 | 800 | lwip 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
流程要点:
- 系统上电后,RTOS 依据
task_info_table[]创建各任务;app_core是业务入口,负责初始化demo_wifi应用; wifi_demo_task初始化 WiFi(STA 或 SoftAP 模式,由wifi_conf.c配置决定),并注册连接/断开事件回调;- 当 STA 接入(或 STA 成功连上 AP)时,协议栈经
tasklet上报事件,demo 任务打印对端 MAC 与 IP,并通过wifi_connect_sta_mac(0, mac, 6, 0)将 MAC 记录到存储区; - 之后
net_ota任务通过net_update.c以 lwip TCP 向升级服务器发起下载,固件包经 WiFi 协议栈收发; - 连接断开时,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.c | WiFi 运行参数(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 | 常量 | 1024 | net_ota 任务栈大小(字节数/4 为栈单元数) |
NET_OTA_Q_SIZE | 常量 | 0 | net_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 | 常量 | 3 | PowerPin 结构可记录的最大电源引脚数 |
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
- 新增业务任务:在
task_info_table[]的哨兵行{0,0,0,0,0}之前追加条目即可,无需修改 RTOS 初始化代码;注意任务名 ≤ 12 字符、需要确定性内存时提供静态 TCB 数组(参考net_ota_tcb_stk_q模式); - 新增升级通道:参照
net_update.c的模式实现"取数"层(如 USB、串口、BLE),再挂接到code/common/update_main.c的升级主流程;网络层与写入层分离是框架的既定边界; - 自定义 MAC 白名单策略:扩展
wifi_connect_sta_mac的index语义或在wifi_demo_task.c的事件分支中增加授权判断,存储可落reserved_wifi.c预留区; - 配置持久化适配:替换
syscfg_*桩实现为真实的 VM/Flash 读写即可在 uboot 阶段启用配置保存(需保证与正式 App 的syscfg_id.hID 不冲突); - 低压保护定制:通过
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)