shell_usmart.h
DEBUG_SUMART 的编译期作用范围4.2 任务调度时序(启用时)4.3 关键全局变量的读写5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/debug/shell_usmart.h所属模块:midware/debug(调试支撑 — USMART 串口命令行外壳) 代码行数:21
1. 文件职责
本文件是 USMART 调试外壳(源自正点原子的 usmart 组件)在本工程中的胶水头文件,只有两个作用:定义一个总开关宏 DEBUG_SUMART,并把 USMART 外壳任务的入口函数 task_usmart() 声明出来供外部创建任务使用。
它处在调试链路的启动环节:user/main.c 在 DEBUG_SUMART 为真时于 main.c:322 用 OSTaskCreateExt() 创建 task_usmart 任务(优先级 TASK_USMART_PRIO,任务名 "task_usmart_parse",栈 TASK_USMART_STK),该任务体在 midware/debug/shell_usmart.c:54 实现,循环里调 usmart_scan() 解析串口 0 收到的命令行。
它同时被另外两个 USMART 底层文件引用:common/usmart/usmart.c 与 common/usmart/usmart_str.c 都 include 本文件,并统一用 #if DEBUG_SUMART 包裹整份实现。因此本文件是"三份 USMART 源码 + main.c 任务创建"这四处共用的唯一开关与声明入口。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
__SHELL_USMART_H | 宏 | 头文件保护宏,防止重复 include(第 9-10 行定义,第 20 行 #endif) |
DEBUG_SUMART | 宏 | 值为 0(第 13 行)——本工程默认关闭 USMART。作为编译总开关,被 midware/debug/shell_usmart.c:6、common/usmart/usmart.c:27、common/usmart/usmart_str.c:24、user/main.c:50 与 user/main.c:321 共 5 处引用 |
task_usmart | 函数声明(全局) | void task_usmart(void *p_arg);(第 18 行),USMART 外壳任务体,符合 uCOS-II 任务原型 |
本文件不定义任何结构体、枚举或联合体。USMART 表格结构 struct _m_usmart_nametab(common/usmart/usmart.h:39)、设备结构 struct _m_usmart_dev(common/usmart/usmart.h:45)定义在 common/usmart/usmart.h 中,命令表数组实例 usmart_nametab[]、设备实例 usmart_dev 定义在 midware/debug/shell_usmart.c:18、:36。
任务相关参数不在本文件内:TASK_USMART_PRIO(第 22 号优先级,注释标"任务间隔 100ms")与 TASK_USMART_STK_SIZE(512 字)定义在 UCOSII/uCOS-CONFIG/app_cfg.h:73、:100。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
task_usmart | 全局(本文件中仅为声明,定义在 midware/debug/shell_usmart.c:54) | USMART 外壳任务:usmart_init(SystemCoreClock) 初始化后进入死循环,每轮 usmart_scan() 再 OSTimeDlyHMSM(0,0,0,100),即以 100 ms 周期轮询串口 0 的命令行 |
本文件自身没有任何函数定义,只有一个函数原型。
4. 核心逻辑解析
本文件是无逻辑的纯声明头,可讲的"逻辑"只有开关语义与编译期取舍。
4.1 DEBUG_SUMART 的编译期作用范围
DEBUG_SUMART 为 0 表示常量假,因此下面 5 处条件编译体全部不参与编译:
| 引用位置 | 被裁掉的内容 |
|---|---|
midware/debug/shell_usmart.c:6 | 整个文件体(usmart_nametab[]、usmart_dev、task_usmart() 定义)——即 shell_usmart.c 编译成空目标文件 |
common/usmart/usmart.c:27 | TIMER3_IRQHandler()、usmart_init()、usmart_scan() 等全部 USMART 内核实现 |
common/usmart/usmart_str.c:24 | usmart_strcmp()、usmart_strcopy() 等全部字符串工具 |
user/main.c:50 | OS_STK TASK_USMART_STK[TASK_USMART_STK_SIZE]; 任务栈数组——不占 RAM |
user/main.c:321 | OSTaskCreateExt(task_usmart, ...) 与 OSTaskNameSet(...)——不创建任务 |
由此推出一个重要结论:关闭状态下 task_usmart 在整个工程中没有任何引用者,本文件的声明实际上处于"悬空但合法"的状态(声明未使用不会报错,只有定义了却未使用的 static 函数才会有告警)。反过来,若把 DEBUG_SUMART 改成 1,必须确认 midware/debug/shell_usmart.c、common/usmart/usmart.c、common/usmart/usmart_str.c 三份实现都存在且能通过编译,且 TASK_USMART_PRIO/TASK_USMART_STK_SIZE(在 UCOSII/uCOS-CONFIG/app_cfg.h:73、:100)与 main.c 中的 TASK_USMART_STK 声明能同时生效。
4.2 任务调度时序(启用时)
main() 启动阶段
└─ OSTaskCreateExt(task_usmart, ..., TASK_USMART_PRIO, ...) main.c:322
└─ OSTaskNameSet(TASK_USMART_PRIO, "task_usmart_parse", ...) main.c:331
│
▼
task_usmart(p_arg) shell_usmart.c:54
├─ (void)p_arg;
├─ usmart_init(SystemCoreClock); // 传主频给 USMART 做延时换算
└─ for(;;) {
usmart_scan(); // 从 USART0 收命令、查表、执行
OSTimeDlyHMSM(0,0,0,100); // 让出 100 ms
}
任务本身不持有信号量、不申请消息邮箱、不等待事件,是纯轮询式设计:usmart_scan() 内部按 usmart_dev 里配置的超时参数判断一条命令是否收齐,收齐后经 usmart_exe() 查 usmart_nametab[] 表并调用对应函数指针。任务优先级由 TASK_USMART_PRIO 决定,100 ms 的周期让出决定了它不会成为 CPU 时间的主要竞争者——这也是它作为"调试壳"可以常驻的代价。
4.3 关键全局变量的读写
| 变量 | 位置 | 本文件的关系 |
|---|---|---|
usmart_nametab[] | shell_usmart.c:18 | 可被调试调用的函数表:read_addr/write_addr(仅 USMART_USE_WRFUNS == 1 时;本工程 common/usmart/usmart.h:26 把它设为 0,故这两条未注册)、usmart_test(usmart.h:77)、get_e_linker_sn、e_linker_protocol_support_state —— 其中后两个来自无线模块(e_linker_wifi.h),说明这套调试壳在本工程里的实用价值主要是"现场读 W5500 无线模组序列号与协议支持状态" |
usmart_dev | shell_usmart.c:36 | 关联函数指针(usmart_init/usmart_cmd_rec/usmart_exe/usmart_scan,声明于 common/usmart/usmart.h:69-72)、函数条目数、显示进制(1 = 十六进制)、参数缓冲(parm[PARM_LEN],usmart.h:19 定义 PARM_LEN = 200)与索引 |
SystemCoreClock | system/(CMSIS 提供) | task_usmart 用它做 USMART 的延时/超时基准 |
TASK_USMART_PRIO / TASK_USMART_STK_SIZE | UCOSII/uCOS-CONFIG/app_cfg.h:73、:100 | 任务创建参数(22 号优先级、512 字栈) |
TASK_USMART_STK | user/main.c:50 | 任务栈数组,由 DEBUG_SUMART 门控 |
5. 对外接口
本文件导出
| 导出项 | 供谁使用 |
|---|---|
宏 DEBUG_SUMART | midware/debug/shell_usmart.c:6、common/usmart/usmart.c:27、common/usmart/usmart_str.c:24、user/main.c:50、user/main.c:321 —— 共 5 处 #if 门控 |
函数声明 void task_usmart(void *p_arg); | user/main.c:322 的 OSTaskCreateExt(task_usmart, ...)(仅当 DEBUG_SUMART 为真时编译) |
本文件不导出任何全局变量。
本文件(间接)依赖的外部符号
shell_usmart.h 本身不 include 任何东西(第 9-20 行之间没有任何 #include),因此它不引入任何外部依赖;真正的依赖都在引用它的 .c 文件里:shell_usmart.c 依赖 common/usmart/usmart.h(usmart_init/usmart_cmd_rec/usmart_exe/usmart_scan、usmart_dev/usmart_nametab 的 extern 声明、USMART_USE_WRFUNS)、user/main.h(任务优先级与栈长的再导出)、midware/ethernt/wireless/e_linker_wifi.h(get_e_linker_sn、e_linker_protocol_support_state)。
6. 依赖关系
依赖
无。本文件是一个零 include 的自包含头(仅用 #ifndef/#define/#endif 保护与一条函数原型),这是它与同目录 debug.h、ate.h 等头文件最大的区别——那些头会链式引入 includes.h。
被依赖(4 个文件)
midware/debug/shell_usmart.c— 自身实现体,被#if DEBUG_SUMART整体门控common/usmart/usmart.c— USMART 内核(收命令、解析、执行)common/usmart/usmart_str.c— USMART 字符串工具user/main.c— 任务栈定义与任务创建
三者构成"声明(本文件)+ 内核(common/usmart/)+ 应用绑定(midware/debug/)+ 任务创建(user/main.c)"的四层结构。common/usmart 属于通用工具库,midware/debug 属于调试支撑,两边靠本文件解耦:common/usmart 不依赖 midware/debug 的任何实现,只依赖这一个宏与一条声明。
7. 备注与风险点
- 文件头
@brief与@version/@date全为空(shell_usmart.h:5-6):只有文件名与作者,没有任何版本、日期或职责描述。对比同目录debug.h、ate.h都有基本说明,本文件在"这个宏干什么用"上没有留下任何线索——只能靠 5 处引用点反推。
- 开关宏放在头文件里且值为
0(shell_usmart.h:13):这带来一个容易被忽视的后果——DEBUG_SUMART是"全局常量 0",因此#if DEBUG_SUMART保护下的代码永远不会被编译器语法检查。也就是说shell_usmart.c、usmart.c、usmart_str.c三份实现长期处于"未编译状态",一旦有人把宏改成 1,很可能直接编译不过(例如它们引用的usmart_test()、USMART_USE_WRFUNS、PARM_LEN等符号若已从usmart.h中移除,只有到那时才会暴露)。这是典型的"调试开关腐化"风险,建议定期以DEBUG_SUMART=1做一次编译验证。
DEBUG_SUMART命名与DEBUG_*家族不一致:同目录下shell_usmart.c用的是DEBUG_SUMART,而evc_upgrade.h用DEBUG_EVC_UPGRADE、evc_ii.h用DEBUG_EVC_II,evc.h/evc_ii.h用(x == 1)形式判别。本工程对"调试开关"没有统一约定,本文件是"裸宏 +#if"这一种。
task_usmart的字面命名与任务名不符:main.c:331把任务命名为"task_usmart_parse",而函数名是task_usmart。uCOS-II 的OSTaskNameSet()只影响OSTaskNameGet()与调试工具显示,不影响功能,但检索日志时会误导。
- 本文件没有
extern "C"保护:工程是纯 C(Keil MDK),无影响;若将来有 C++ 单元参与链接,这条原型会被名字修饰,故记录为潜在约束。
task_usmart未在本文件中声明为static/extern:原型默认是extern。配合第 2 点,当DEBUG_SUMART == 0时shell_usmart.c不提供该符号,链接期不会有任何提示(因为没有引用者)。这属于"声明与定义同时被开关裁掉"的安全情形,但如果main.c的#if被误写(例如只包了任务创建、没包栈数组),就会出现"栈溢出/未定义符号"这类连锁问题——第 4.1 节表格中标出的 5 处必须同时修改。