ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

应用落地 · 两个 LED 任务实战与错误码(实战篇)

应用落地 · 两个 LED 任务实战与错误码(实战篇)

🎯 本文主线(先吃颗定心丸):放心,本篇是把第 18 篇那套"任务错相闪灯"理论落到代码上——你只做两件事:把两个任务函数写出来、用LOS_TaskCreate把它们登记进内核;登记失败别瞎猜,拿返回的错误码去速查表对号入座即可,没有新概念(比喻见下方🏠导读)。本篇主线就一句:LED 任务实战本质就两件事——写两个任务函数、用LOS_TaskCreate登记进内核(让LOS_TaskDelay承担"让出 CPU"的节奏,红绿灯才能错相轮班);登记失败拿LOS_TaskCreate返回的LOS_ERRNO_TSK_*错误码去速查表对号入座。本篇先把代码摆全,再定位它在四步法第 3 步,接着给错误码速查表,最后把五个老坑再敲一遍。下面四站全是它的展开。

🗺️ 详细拆解路线(四站,跟着走不迷路):

  • 🚉第一站·完整实战:把app_task.c完整摆出来——两个任务函数(红灯 200ms、绿灯 500ms 错相闪)+AppTaskCreate一气呵成把它们登记进内核。
  • 🚉第二站·四步法里的位置:AppTaskCreate是四步法的第 3 步,先登记、再LOS_Start发令,任务才真正交替跑起来。
  • 🚉第三站·错误码速查表:LOS_TaskCreate返回"招工被拒"的五种理由(NO_MEMORY / 优先级错 / 栈过大 / 栈过小 / 任务数满),照着查(卡住时直接拿错误码去源码 grep,比瞎猜快十倍)。
  • 🚉第四站·实战常踩的坑:栈是 2KB 不是 2MB、TSK_LIMIT=8等五个老坑,再强调一遍。

读完全文,记得回来看末尾的「全文总结」,它会把上面四站串成一句话。

🏠零基础导读(接第 18 篇那两个"错相闪灯"的工人):第 18 篇里,我们把"让红灯和绿灯错相闪烁"比喻成"雇两个工人,一个干得快、一个干得慢,轮班干活"。本篇就是把这件"招工"实事落到纸上——你亲手填好"招工登记表"(LOS_TaskCreate的入参),把两个人登记进公司(内核);再学一份"招聘被拒理由清单"(错误码),万一登记没通过,一眼看出卡在哪。

先把 5 个对应关系记牢,后面就不晕了:

现实比喻RTOS 里的真名
招工登记表LOS_TaskCreate的入参TSK_INIT_PARAM_S
招工被拒:公司没工位了LOS_ERRNO_TSK_NO_MEMORY
招工被拒:给的级别超出编制LOS_ERRNO_TSK_PRIOR_ERROR
招工被拒:操作台太大塞不下(栈超堆)LOS_ERRNO_TSK_STKSZ_TOO_LARGE
招工被拒:编制已满(8 个全招了)LOS_ERRNO_TSK_TCB_UNAVAILABLE

一、第一站·完整实战:把 app_task.c 摆出来

先看成品。很多人怕"业务层到底写啥",其实就两件事——把两个任务函数写出来,再用LOS_TaskCreate填一张"招工登记表"把它们登记进内核。这一站把app_task.c完整摆出来,照着敲就行,别被代码长度吓到。

一句话理解:业务层就两件:定义两个任务函数 + 在 AppTaskCreate 里把它们登记进内核。

#include"board.h"// 业务层唯一硬件接口(只认宏,不碰寄存器)staticVOIDRedLedTask(VOID){while(1){LED_RED_ON();LOS_TaskDelay(200);// 200 tick = 200ms(TICK_PER_SECOND=1000)LED_RED_OFF();LOS_TaskDelay(200);}}staticVOIDGreenLedTask(VOID){while(1){LED_GREEN_ON();LOS_TaskDelay(500);// 500 tick = 500ms,和红灯周期不同 → 错相闪LED_GREEN_OFF();LOS_TaskDelay(500);}}UINT32AppTaskCreate(VOID){UINT32 uwRet;UINT32 taskID1=0,taskID2=0;TSK_INIT_PARAM_S stTask={0};/* 先整体清零,再填字段 *//* 红灯任务(高优先级 6) */stTask.pfnTaskEntry=(TSK_ENTRY_FUNC)RedLedTask;stTask.uwStackSize=0x800;/* 2KB 栈,从内核堆分配 */stTask.pcName="RedLed";stTask.usTaskPrio=6;uwRet=LOS_TaskCreate(&taskID1,&stTask);if(uwRet!=LOS_OK){returnuwRet;/* 失败直接返回错误码 */}/* 绿灯任务(优先级 7) */stTask.pfnTaskEntry=(TSK_ENTRY_FUNC)GreenLedTask;stTask.uwStackSize=0x800;stTask.pcName="GreenLed";stTask.usTaskPrio=7;uwRet=LOS_TaskCreate(&taskID2,&stTask);returnuwRet;}

💡可复用经验:业务层写任务时,硬件操作一律走board.h的宏,不碰寄存器。换块板子只改board.h一行宏,任务文件一行不动。

🚏第一站小结:app_task.c就两块——两个while(1)任务函数(红灯 200ms、绿灯 500ms 错相闪,靠LOS_TaskDelay让出 CPU 轮班)+AppTaskCreate用LOS_TaskCreate把红灯(优先级 6)、绿灯(优先级 7)登记进内核;硬件操作全走board.h宏,不碰寄存器。

📌图注:LOS_TaskDelay(n)不是while空转忙等,而是把当前任务挂起 n 个 tick,内核趁机把 CPU 让给红灯/绿灯里另一个就绪的任务——这正是"轮班"的真相。两个任务能交替闪,靠的就是这一下"让出"。

二、第二站·四步法里的位置:AppTaskCreate 是第 3 步

代码写好了,但它只是"登记",得放进四步法的流水线里才跑得起来。这一站讲清AppTaskCreate站在四步法的第几步,免得你以为"建完任务就立刻闪"。

一句话理解:AppTaskCreate()是四步法的第 3 步,排在LOS_Start()之前——先登记,再发令。

📌图注:你写的代码要走完这四步才在板子上活起来:① 写(改 main.c)→ ② 编(make 出 .elf)→ ③ 下(烧进 SRAM)→ ④ 跑(拨码 001 上电看 LED)。哪一步卡住,就回头看那一步的报错,别一上来就怀疑人生。

BoardInit();// ① 板级硬件初始化(第 08 篇)LOS_KernelInit();// ② 内核初始化(建好内部数据结构,但还没开始调度)AppTaskCreate();// ③ 把 RedLedTask/GreenLedTask 登记进内核(任务建好,但还没跑)LOS_Start();// ④ 启动调度器:从这里开始,两个任务才真正交替跑起来

LOS_TaskCreate只是"把任务对象准备好、放进就绪队列",不会立刻执行任务函数。真正让while(1)转起来的,是LOS_Start之后的调度器。四步法少一步,全盘皆输。

🚏第二站小结:AppTaskCreate是四步法第 3 步(BoardInit ① → LOS_KernelInit ② → AppTaskCreate ③ → LOS_Start ④);LOS_TaskCreate只把任务放进就绪队列、不立刻执行,真正轮转靠LOS_Start之后的调度器,四步少一步全盘皆输。

三、第三站·错误码速查表:招工被拒的五种理由

最容易卡住的就是"任务建不起来"。这一站给你一份"招工被拒理由清单"——LOS_TaskCreate失败时返回的不是LOS_OK,而是一串带原因的错误码,照表对号入座。

一句话理解:出错时返回的不是LOS_OK,而是带错误原因的"负错误码"——下面这张表照着查。

📌白话领路:这一节不用硬啃。把它想成公司前台的一张"招工被拒理由清单"——你填好表递上去(LOS_TaskCreate),前台(内核)不收,就会退给你一个带编号的"拒收条"(LOS_ERRNO_TSK_*)。下面这张表就是"拒收条编号 → 为啥被拒 → 怎么补救"的对照表,建任务卡住时直接查。

📌图注:创建失败返回的那串 LOS_ERRNO_TSK_xxx,别当乱码。它就像招聘被拒的回执:写着一个具体理由(栈不够?优先级超了?名字重了?)。拿着回执去对照本篇错误码表,三秒定位问题。

错误码含义常见根因怎么改
LOS_ERRNO_TSK_NO_MEMORY内存不足任务栈/TCB 从 80KB 堆里分不出来(堆不够或"任务数×栈"太大)调小栈 / 减任务数 / 调大堆(g_liteosHeap/LOSCFG_SYS_HEAP_SIZE,第 16 篇)
LOS_ERRNO_TSK_PRIOR_ERROR优先级非法usTaskPrio超出0 ~ OS_TASK_PRIORITY_LOWEST-1把优先级调到合法范围
LOS_ERRNO_TSK_STKSZ_TOO_LARGE栈过大uwStackSize超过堆总大小LOSCFG_SYS_HEAP_SIZE(0x14000 = 80KB),例如填0x20000(128KB)会直接触发调小栈 / 调大堆(g_liteosHeap/LOSCFG_SYS_HEAP_SIZE,第 16 篇)
LOS_ERRNO_TSK_STKSZ_TOO_SMALL栈过小小于内核最小栈约束(兜不住完整上下文)调大栈(三档栈取值见第 16 篇)
LOS_ERRNO_TSK_TCB_UNAVAILABLE任务数已满创建数量撞上TSK_LIMIT(含 idle = 8)减任务数或调大TSK_LIMIT(第 16 篇)

📌失败会返回哪些"暗号"?LiteOS 用LOS_ERRNO_TSK_*这种带模块标记的数字。最常见的就是上表列出的五种(NO_MEMORY / 优先级错 / 栈过大 / 栈过小 / 任务数满)。养成"创建必查返回值"的习惯,报错时直接拿错误码去源码里grep,比瞎猜快得多。

📌图注:这张图说清了 LOS_TaskCreate 的"两种结局"——成功就进就绪队列等调度,失败就吐一个错误码回来。重点:创建完一定查返回值!很多人任务没跑起来,就是因为没看这个回执。

🚏第三站小结:LOS_TaskCreate失败返回LOS_ERRNO_TSK_*五类拒信——NO_MEMORY(堆不够)/ PRIOR_ERROR(优先级越界)/ STKSZ_TOO_LARGE(栈超堆)/ STKSZ_TOO_SMALL(栈太小)/ TCB_UNAVAILABLE(撞 TSK_LIMIT);养成"创建必查返回值、拿错误码去源码 grep"的习惯。

四、第四站·实战常踩的坑(再强调一遍)

码也写了、错码也查了,最后把五个最容易翻车的老坑再敲一遍——这些坑几乎每个新手都踩过,提前记牢省得调试到半夜。

一句话理解:五个老坑:栈是 2KB 不是 2MB、TSK_LIMIT=8是天花板、delay 的 n 是 tick 不是毫秒、优先级数字越小越高、创建失败必须立即 return。

  • 栈是 2KB 不是 2MB:0x800是十六进制 = 2048。栈太小(函数里定义大数组)会溢出冲掉相邻内存;太大浪费 80KB 堆。
  • TSK_LIMIT=8是天花板:内核最多 8 个任务(含 idle),创建第 9 个直接返回LOS_ERRNO_TSK_TCB_UNAVAILABLE。本工程只建 2 个,远没到上限。
  • LOS_TaskDelay(n)的 n 是 tick 不是毫秒:物理时长 =n / TICK_PER_SECOND。改了节拍率(第 15 篇),所有 delay 实际时长跟着变。
  • 优先级数字越小越高:和"分数越高越好"直觉相反;填错了两个任务谁先跑就反了。
  • 错误要检查:第一个任务创建失败就return,第二个不建了——否则拿着没建成的任务 ID 去操作会踩内存。

💡可复用经验:LOS_TaskCreate必查返回值:第一个失败就立即return,绝不用没建成的任务 ID 去操作(会踩内存)。建多个任务时逐个查,养成「创建即查错」的习惯。

🚏第四站小结:五坑牢记——栈0x800=2KB 别当 2MB、TSK_LIMIT=8第 9 个直接拒、delay 的 n 是 tick(时长随第 15 篇节拍率变)、优先级数字越小越高、LOS_TaskCreate失败立即 return 别拿废 ID 操作。


五、全文总结(回到主线)

本文一句话总结(建议收藏):本篇把第 18 篇的"雇工闪灯"落到实处——业务层只干两件事:写两个任务函数 + 用LOS_TaskCreate填"招工登记表"把它们登记进内核(第 3 步,排在LOS_Start之前)。任务建不起来时,别瞎猜,直接拿LOS_TaskCreate返回的LOS_ERRNO_TSK_*错误码去速查表对号入座(内存不足 / 优先级错 / 栈过大 / 栈过小 / 任务数满五种),再回源码 grep。

用四站回顾一遍:

  • 🚉第一站·完整实战:app_task.c两块——两个while(1)任务函数(红灯 200ms、绿灯 500ms 错相闪)+AppTaskCreate用LOS_TaskCreate把红(优先级6)/绿(优先级7)登记进内核;硬件走board.h宏不碰寄存器。
  • 🚉第二站·四步法里的位置:AppTaskCreate是四步法第 3 步(BoardInit ① → LOS_KernelInit ② → AppTaskCreate ③ → LOS_Start ④);LOS_TaskCreate只进就绪队列、不立刻执行,轮转靠LOS_Start之后的调度器。
  • 🚉第三站·错误码速查表:LOS_TaskCreate失败返回LOS_ERRNO_TSK_*五类拒信(NO_MEMORY / PRIOR_ERROR / STKSZ_TOO_LARGE / STKSZ_TOO_SMALL / TCB_UNAVAILABLE),创建必查返回值。
  • 🚉第四站·实战常踩的坑:栈0x800=2KB 非 2MB、TSK_LIMIT=8天花板、delay 的 n 是 tick(随第 15 篇节拍率变)、优先级数字越小越高、失败立即 return。

🧰本篇可复用经验速记(照着做,别只读):

  • □ 写任务函数:硬件操作一律走board.h宏,换板只改这一行、不碰寄存器。
  • □ 建任务前:TSK_INIT_PARAM_S = {0}先整体清零再填字段,避免脏值带入。
  • □ 写LOS_TaskDelay(n):n 是 tick 不是毫秒,时长随第 15 篇节拍率变。
  • □ 设优先级前:确认内核语义(数字越小越高,与 FreeRTOS 相反)。
  • □LOS_TaskCreate必查返回值;失败拿LOS_ERRNO_TSK_*去速查表对号入座,立即 return。
  • □ 栈大小:从 80KB 堆分,按调用深度实测,受堆 +TSK_LIMIT共同约束。

app/业务层就这么点东西:定义任务(灯怎么闪)+ 创建任务(LOS_TaskCreate)。难点全在前面 08–17 篇的地基,业务反而最轻松——你只要描述"每个任务循环里干什么",把"什么时候让出 CPU"交给LOS_TaskDelay,把"谁先跑"交给优先级,内核全包了。

下一篇(第 20 篇)反过来——当灯不闪、板子死机时,怎么用 OpenOCD + GDB 把现场捞出来,把第 14 篇埋的los_exc.S(HardFault 处理)真正讲透。


术语表(本文出现)

缩写/符号含义
AppTaskCreate业务层函数:把两个 LED 任务"登记"进内核
app_task.c / main.c业务源文件(main 里是四步法骨架)
RedLedTask / GreenLedTask两个任务函数(各自while(1)控制灯)
LED_RED_ON / LED_RED_OFF / LED_GREEN_ON / LED_GREEN_OFF板级宏:控制红/绿灯亮灭
TSK_INIT_PARAM_S任务初始化参数结构体(报名表)
pfnTaskEntry / uwStackSize / pcName / usTaskPrio报名表字段:入口 / 栈大小 / 名字 / 优先级
LOS_TaskCreate内核 API:创建任务
LOS_OK内核返回码:成功
LOS_ERRNO_TSK_NO_MEMORY错误码:堆不够切栈/TCB
LOS_ERRNO_TSK_PRIOR_ERROR错误码:优先级越界
LOS_ERRNO_TSK_STKSZ_TOO_LARGE错误码:栈对齐后超过堆剩余
LOS_ERRNO_TSK_TCB_UNAVAILABLE错误码:任务数撞上 TSK_LIMIT
OS_TASK_PRIORITY_LOWEST宏:最低优先级(优先级合法范围上限)
LOS_KernelInit / LOS_Start内核初始化 / 启动调度器(四步法 ② ④)
BoardInit板级初始化(四步法 ①)
TSK_LIMIT宏:最大任务数(含 idle,本工程 =8)
g_liteosHeap80KB 外部堆(任务栈从这里分)

系列导航
上一篇:第 18 篇 · LED 任务与 LOS_TaskCreate 详解 |本篇:第 19 篇 · LED 任务实战与错误码| 下一篇:第 20 篇 · 死机定位:los_exc 与三破坏现场

板级适配与应用 07–25:07 工程全景 | 08 BSP | 09 SystemInit | 10 lds上 | 11 lds下·解剖 | 12 lds下·照妖镜 | 13 启动文件 | 14 接管中断 | 15 target_config上 | 16 FPU栈档 | 17 改错速查 | 18 LED详解 |19 LED实战| 20 los_exc | 21 FPU栈帧 | 22 八环节 | 23 全文件对账 | 24 六类判据 | 25 经验增删改留

基础 01–06:01 环境准备 | 02 源码下载裁剪 | 03 os_config | 04 Makefile | 05 链接脚本 | 06 烧录

切换汇编 01–06:汇编1 | 汇编2 | 汇编3 | 汇编4 | 汇编5 | 汇编6

代码仓库:stm32mp157-liteos-m @ Gitee · 基线 v0.1-baseline · 全部版本标签
-m @ Gitee](https://gitee.com/tstcoder/stm32mp157-liteos-m) · 基线 v0.1-baseline · 全部版本标签

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进