🎯 本文主线(先吃颗定心丸):放心,本篇是把第 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_liteosHeap | 80KB 外部堆(任务栈从这里分) |
系列导航
上一篇:第 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 · 全部版本标签