-
3.3 SortLinkList 排序链表和Tick时间关系任务、定时器加入排序链表后,随时时间推移,一个tick一个tick的逝去,排序链表中的滚动数是如何更新的呢?我们看看Tick中断的处理函数VOID OsTickHandler(VOID),该函数在kernelbaselos_tick.c文件里。当时间每走过一个tick,会调用该中断处理函数,代码片段中的⑴、⑵处的代码分别扫描任务和定时器,检查和更新时间。LITE_OS_SEC_TEXT VOID OsTickHandler(VOID){UINT32 intSave;TICK_LOCK(intSave);g_tickCount[ArchCurrCpuid()]++;TICK_UNLOCK(intSave);......⑴ OsTaskScan(); /* task timeout scan */#if (LOSCFG_BASE_CORE_SWTMR == YES)⑵ OsSwtmrScan();#endif}我们以OsTaskScan()为例,快速了解下排序链表和tick时间的关系。函数在kernelbaselos_task.c文件中,函数代码片段如下:⑴处代码获取任务排序链表的第一个节点,然后执行下一行代码把该节点的滚动数减去1。⑵处代码循环遍历排序链表,如果滚动数为0,即时间到期了,会调用LOS_ListDelete()函数从从排序链表中删除,然后执行⑶处代码,获取对应的taskCB,然后进一步进行业务处理。读者可以自行查看更多代码,后续的文章中也会对任务、定时器进行专题进行讲解。LITE_OS_SEC_TEXT VOID OsTaskScan(VOID){ SortLinkList *sortList = NULL; ...... LOS_DL_LIST *listObject = NULL; SortLinkAttribute *taskSortLink = NULL; taskSortLink = &OsPercpuGet()->taskSortLink; SORTLINK_CURSOR_UPDATE(taskSortLink->cursor); SORTLINK_LISTOBJ_GET(listObject, taskSortLink); ......⑴ sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); ROLLNUM_DEC(sortList->idxRollNum); ⑵ while (ROLLNUM(sortList->idxRollNum) == 0) { LOS_ListDelete(&sortList->sortLinkNode);⑶ taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList); ...... sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); } ......}
-
3、SortLinkList 排序链表SortLinkList是LiteOS另外一个比较重要的数据结构,它在LOS_DL_LIST双向链表结构体的基础上,增加了RollNum滚动数,用于涉及时间到期、超时的业务场景。在阻塞任务是否到期,定时器是否超时场景下,非常依赖SortLinkList排序链表这个数据结构。LiteOS排序链表支持单一链表LOSCFG_BASE_CORE_USE_SINGLE_LIST和多链表LOSCFG_BASE_CORE_USE_MULTI_LIST,可以通过LiteOS的menuconfig工具更改Sortlink Option选项来配置使用单链表还是多链表,我们这里先讲述前者。排序链表SortLinkList接口主要内部使用,用户业务开发时不涉及,不对外提供接口。SortLinkList排序链表的代码都在kernelbaseincludelos_sortlink_pri.h头文件和kernelbaselos_sortlink.c实现文件中。3.1 SortLinkList 排序链表结构体定义在kernelbaseincludelos_sortlink_pri.h文件中定义了两个结构体,如下述源码所示。SortLinkAttribute结构体定义排序链表的头结点LOS_DL_LIST *sortLink,游标UINT16 cursor。SortLinkList结构体定义排序链表的业务节点,除了负责双向链接的成员变量LOS_DL_LIST *sortLink,还包括业务信息,UINT32 idxRollNum,即index索引和rollNum滚动数。在单链表的排序链表中,idxRollNum表示多长时间后会到期。我们举个例子,看下面的示意图。排序链表中,有3个链表节点,分别在25 ticks、35 ticks、50 ticks后到期超时,已经按到期时间进行了先后排序。三个节点的idxRollNum分别等于25 ticks、10ticks、15 ticks。每个节点的idxRollNum保存的不是这个节点的超时时间,而是从链表head节点到该节点的所有节点的idxRollNum的加和,才是该节点的超时时间。这样设计的好处就是,随着Tick时间推移,只需要更新第一个节点的超时时间就好,可以好好体会一下。示意图如下:源码如下:typedef struct { LOS_DL_LIST sortLinkNode; UINT32 idxRollNum;} SortLinkList; typedef struct { LOS_DL_LIST *sortLink; UINT16 cursor; UINT16 reserved;} SortLinkAttribute;下面我们来学习下排序链表支持的那些操作。3.2 SortLinkList 排序链表接口在继续之前我们先看下kernelbaseincludelos_sortlink_pri.h文件中的一些单链表配置LOSCFG_BASE_CORE_USE_SINGLE_LIST下的宏定义,包含滚动数最大值等,对滚动数进行加、减、减少1等操作。源码如下:#define OS_TSK_SORTLINK_LOGLEN 0U#define OS_TSK_SORTLINK_LEN 1U#define OS_TSK_MAX_ROLLNUM 0xFFFFFFFEU#define OS_TSK_LOW_BITS_MASK 0xFFFFFFFFU#define SORTLINK_CURSOR_UPDATE(CURSOR)#define SORTLINK_LISTOBJ_GET(LISTOBJ, SORTLINK) (LISTOBJ = SORTLINK->sortLink)#define ROLLNUM_SUB(NUM1, NUM2) NUM1 = (ROLLNUM(NUM1) - ROLLNUM(NUM2))#define ROLLNUM_ADD(NUM1, NUM2) NUM1 = (ROLLNUM(NUM1) + ROLLNUM(NUM2))#define ROLLNUM_DEC(NUM) NUM = ((NUM) - 1)#define ROLLNUM(NUM) (NUM)#define SET_SORTLIST_VALUE(sortList, value) (((SortLinkList *)(sortList))->idxRollNum = (value))3.2.1 UINT32 OsSortLinkInit() 排序链表初始化在系统启动软件初始化,初始化任务、初始化定时器时,会分别初始化任务的排序链表和定时器的排序链表。kernelbaselos_task.c : UINT32 OsTaskInit(VOID)函数`ret = OsSortLinkInit(&g_percpu[index].taskSortLink);`kernelbaselos_swtmr.c : UINT32 OsSwtmrInit(VOID)函数`ret = OsSortLinkInit(&g_percpu[cpuid].swtmrSortLink);`我们看下排序链表初始化函数的源代码,⑴处代码计算需要申请多少个双向链表的内存大小,对于单链表的排序链表,OS_TSK_SORTLINK_LOGLEN为0,为一个双向链表申请内存大小即可。然后申请内存,初始化申请的内存区域为0等,⑵处把申请的双向链表节点赋值给sortLinkHeader的链表节点,作为排序链表的头节点,然后调用LOS_ListInit()函数初始化为双向循环链表。源码如下:LITE_OS_SEC_TEXT_INIT UINT32 OsSortLinkInit(SortLinkAttribute *sortLinkHeader){UINT32 size;LOS_DL_LIST *listObject = NULL;⑴ size = sizeof(LOS_DL_LIST) << OS_TSK_SORTLINK_LOGLEN;listObject = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, size); /* system resident resource */if (listObject == NULL) {return LOS_NOK;}(VOID)memset_s(listObject, size, 0, size);⑵ sortLinkHeader->sortLink = listObject;LOS_ListInit(listObject);return LOS_OK;}3.2.2 VOID OsAdd2SortLink() 排序链表插入在任务等待互斥锁、信号量等资源阻塞时,定时器启动时,这些需要等待指定时间的任务、定时器等,都会加入对应的排序链表。我们一起看下代码,包含2个参数,第一个参数sortLinkHeader用于指定排序链表的头结点,第二个参数sortList是待插入的链表节点,此时该节点的滚动数等于对应阻塞任务或定时器的超时时间。⑴处代码处理滚动数超大的场景,如果滚动数大于OS_TSK_MAX_ROLLNUM,则设置滚动数等于OS_TSK_MAX_ROLLNUM。⑵处代码,如果排序链表为空, 则把链表节点尾部插入。如果排序链表不为空,则执行⑶处代码,获取排序链表上的下一个节点SortLinkList *listSorted。⑷、⑸ 处代码,如果待插入节点的滚动数大于排序链表的下一个节点的滚动数,则把待插入节点的滚动数减去下一个节点的滚动数,并继续执行⑹处代码,继续与下下一个节点进行比较。否则,如果待插入节点的滚动数小于排序链表的下一个节点的滚动数,则把下一个节点的滚动数减去待插入节点的滚动数,然后跳出循环,继续执行⑺处代码,完成待插入节点的插入。插入过程,可以结合上文的示意图进行理解。源码如下:LITE_OS_SEC_TEXT VOID OsAdd2SortLink(const SortLinkAttribute *sortLinkHeader, SortLinkList *sortList){SortLinkList *listSorted = NULL;LOS_DL_LIST *listObject = NULL;⑴ if (sortList->idxRollNum > OS_TSK_MAX_ROLLNUM) {SET_SORTLIST_VALUE(sortList, OS_TSK_MAX_ROLLNUM);}listObject = sortLinkHeader->sortLink;⑵ if (listObject->pstNext == listObject) {LOS_ListTailInsert(listObject, &sortList->sortLinkNode);} else {⑶ listSorted = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);do {⑷ if (ROLLNUM(listSorted->idxRollNum) <= ROLLNUM(sortList->idxRollNum)) {ROLLNUM_SUB(sortList->idxRollNum, listSorted->idxRollNum);} else {⑸ ROLLNUM_SUB(listSorted->idxRollNum, sortList->idxRollNum);break;}⑹ listSorted = LOS_DL_LIST_ENTRY(listSorted->sortLinkNode.pstNext, SortLinkList, sortLinkNode);} while (&listSorted->sortLinkNode != listObject);⑺ LOS_ListTailInsert(&listSorted->sortLinkNode, &sortList->sortLinkNode);}}3.2.3 VOID OsDeleteSortLink() 排序链表删除当任务恢复、删除,定时器停止的时候,会从对应的排序链表中删除。我们一起阅读下删除函数的源代码,包含2个参数,第一个参数sortLinkHeader用于指定排序链表的头结点,第二个参数sortList是待删除的链表节点。⑴处是获取排序链表的头结点listObject,⑵处代码检查要删除的节点是否在排序链表里,否则输出错误信息和回溯栈信息。⑶处代码判断是否排序链表里只有一个业务节点,如果只有一个节点,直接执行⑸处代码删除该节点即可。如果排序链表里有多个业务节点,则执行⑷处代码获取待删除节点的下一个节点nextSortList,把删除节点的滚动数加到下一个节点的滚动数里,然后执行⑸处代码执行删除操作。源码如下:LITE_OS_SEC_TEXT VOID OsDeleteSortLink(const SortLinkAttribute *sortLinkHeader, SortLinkList *sortList){LOS_DL_LIST *listObject = NULL;SortLinkList *nextSortList = NULL;⑴ listObject = sortLinkHeader->sortLink;⑵ OsCheckSortLink(listObject, &sortList->sortLinkNode);⑶ if (listObject != sortList->sortLinkNode.pstNext) {⑷ nextSortList = LOS_DL_LIST_ENTRY(sortList->sortLinkNode.pstNext, SortLinkList, sortLinkNode);ROLLNUM_ADD(nextSortList->idxRollNum, sortList->idxRollNum);}⑸ LOS_ListDelete(&sortList->sortLinkNode);}3.2.4 UINT32 OsSortLinkGetNextExpireTime() 获取下一个超时到期时间在Tickless特性,会使用此方法获取下一个超时到期时间。我们一起阅读下源代码,包含1个参数,sortLinkHeader用于指定排序链表的头结点。⑴处是获取排序链表的头结点listObject,⑵处代码判断排序链表是否为空,如果排序链表为空,则返回OS_INVALID_VALUE。如果链表不为空,⑶处代码获取排序链表的第一个业务节点,然后获取其滚动数,即过期时间,进行返回。源码如下:LITE_OS_SEC_TEXT UINT32 OsSortLinkGetNextExpireTime(const SortLinkAttribute *sortLinkHeader){UINT32 expireTime = OS_INVALID_VALUE;LOS_DL_LIST *listObject = NULL;SortLinkList *listSorted = NULL;⑴ listObject = sortLinkHeader->sortLink;⑵ if (!LOS_ListEmpty(listObject)) {⑶ listSorted = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);expireTime = listSorted->idxRollNum;}return expireTime;}3.2.5 OsSortLinkGetTargetExpireTime() 获取指定节点的超时时间定时器获取剩余超时时间函数LOS_SwtmrTimeGet()会调用函数OsSortLinkGetTargetExpireTime() 获取指定节点的超时时间。我们一起看下代码,包含2个参数,第一个参数sortLinkHeader用于指定排序链表的头结点,第二个参数targetSortList是待获取超时时间的目标链表节点。⑴处代码获取目标节点的滚动数。⑵处代码获取排序链表的头结点listObject,⑶处代码获取排序链表上的下一个节点SortLinkList *listSorted。⑷处循环代码,当下一个节点不为目标链表节点的时候,依次循环,并执行⑸处代码把循环遍历的各个节点的滚动数相加,最终的计算结果即为目标节点的超时时间。源码如下:LITE_OS_SEC_TEXT_MINOR UINT32 OsSortLinkGetTargetExpireTime(const SortLinkAttribute *sortLinkHeader,const SortLinkList *targetSortList){SortLinkList *listSorted = NULL;LOS_DL_LIST *listObject = NULL;⑴ UINT32 rollNum = targetSortList->idxRollNum;⑵ listObject = sortLinkHeader->sortLink;⑶ listSorted = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);⑷ while (listSorted != targetSortList) {⑸ rollNum += listSorted->idxRollNum;listSorted = LOS_DL_LIST_ENTRY((listSorted->sortLinkNode).pstNext, SortLinkList, sortLinkNode);}return rollNum;}3.2.6 VOID OsSortLinkUpdateExpireTime() 更新超时时间在Tickless特性,会使用此方法更新超时时间。Tickless休眠sleep时,需要把休眠的ticks数目从排序链表里减去。调用此方法的函数会保障减去的ticks数小于节点的滚动数。我们一起阅读下源代码,包含2个参数,第一个参数sleepTicks是休眠的ticks数,第二个参数sortLinkHeader用于指定排序链表的头结点。⑴处获取排序链表的头结点listObject,⑵处代码获取下一个链表节点sortList,这个也是排序链表的第一个业务节点,然后把该节点的滚动数减去sleepTicks - 1完成超时时间更新。源码如下:LITE_OS_SEC_TEXT VOID OsSortLinkUpdateExpireTime(UINT32 sleepTicks, SortLinkAttribute *sortLinkHeader){ SortLinkList *sortList = NULL; LOS_DL_LIST *listObject = NULL; if (sleepTicks == 0) { return; } ⑴ listObject = sortLinkHeader->sortLink;⑵ sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); ROLLNUM_SUB(sortList->idxRollNum, sleepTicks - 1);}
-
1.4 LOS_DL_LIST 双向循环链表遍历LiteOS双向循环链表提供两种遍历双向链表的方法,LOS_DL_LIST_FOR_EACH和LOS_DL_LIST_FOR_EACH_SAFE。1.4.1 LOS_DL_LIST_FOR_EACH(item, list)该宏定义LOS_DL_LIST_FOR_EACH遍历双向链表,接口的第一个入参表示的是双向链表节点的指针变量,在遍历过程中依次指向下一个链表节点。第二个入参是要遍历的双向链表的起始节点。这个宏是个循环条件部分,用户的业务代码写在宏后面的代码块{}内。我们以实际例子来演示这个宏LOS_DL_LIST_FOR_EACH是如何使用的。在kernelbaseschedsched_sqlos_priqueue.c文件中,UINT32 OsPriQueueSize(UINT32 priority)函数的片段如下:&g_priQueueList[priority]是我们要遍历的双向链表,curNode指向遍历过程中的链表节点,见⑴处代码代码。完整代码请访问我们的开源站点。UINT32 OsPriQueueSize(UINT32 priority){UINT32 itemCnt = 0;LOS_DL_LIST *curNode = NULL;......⑴ LOS_DL_LIST_FOR_EACH(curNode, &g_priQueueList[priority]) {......task = OS_TCB_FROM_PENDLIST(curNode);......}return itemCnt;}源码如下:#define LOS_DL_LIST_FOR_EACH(item, list)for (item = (list)->pstNext; (item) != (list); item = (item)->pstNext)1.4.2 LOS_DL_LIST_FOR_EACH_SAFE(item, next, list)该宏定义LOS_DL_LIST_FOR_EACH_SAFE和LOS_DL_LIST_FOR_EACH唯一的区别就是多个入参next, 这个参数表示遍历到的双向链表节点的下一个节点。该宏用于安全删除,如果删除遍历到的item, 不影响继续遍历。源码如下:#define LOS_DL_LIST_FOR_EACH_SAFE(item, next, list)for (item = (list)->pstNext, next = (item)->pstNext; (item) != (list); item = next, next = (item)->pstNext)1.5 LOS_DL_LIST 遍历包含双向链表的结构体LiteOS双向链表提供三个宏定义来遍历包含双向链表成员的结构体,LOS_DL_LIST_FOR_EACH_ENTRY、LOS_DL_LIST_FOR_EACH_ENTRY_SAFE和LOS_DL_LIST_FOR_EACH_ENTRY_HOOK。1.5.1 LOS_DL_LIST_FOR_EACH_ENTRY(item, list, type, member)该宏定义LOS_DL_LIST_FOR_EACH_ENTRY遍历双向链表,接口的第一个入参表示的是包含双向链表成员的结构体的指针变量,第二个入参是要遍历的双向链表的起始节点,第三个入参是要获取的结构体名称,第四个入参是在该结构体中的双向链表的成员变量名称。我们以实际例子来演示这个宏LOS_DL_LIST_FOR_EACH_ENTRY是如何使用的。在kernelbaseschedsched_sqlos_priqueue.c文件中,LosTaskCB *OsGetTopTask(VOID)函数的片段如下。结构体LosTaskCB包含双向链表成员变量pendList,&g_priQueueList[priority] 是对应任务优先级priority的pendList的双向链表。会依次遍历这个双向链表&g_priQueueList[priority],根据遍历到的链表节点,依次获取任务结构体LosTaskCB的指针变量newTask,如⑴处代码所示。LITE_OS_SEC_TEXT_MINOR LosTaskCB *OsGetTopTask(VOID){UINT32 priority;UINT32 bitmap;LosTaskCB *newTask = NULL;......⑴ LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &g_priQueueList[priority], LosTaskCB, pendList) {......OsPriQueueDequeue(&newTask->pendList);......}......}源码如下:源码实现上,for循环的初始化语句item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member)表示包含双向链表成员的结构体的指针变量item,条件测试语句&(item)->member != (list)循环条件表示当双向链表遍历一圈到自身节点的时候,停止循环。循环更新语句item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))中,使用(item)->member.pstNext遍历到下一个链表节点,然后根据这个节点获取对应的下一个结构体的指针变量item,直至遍历完毕。#define LOS_DL_LIST_FOR_EACH_ENTRY(item, list, type, member)for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member); &(item)->member != (list); item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))1.5.2LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, next, list, type, member)该宏定义和LOS_DL_LIST_FOR_EACH_ENTRY唯一的区别就是多个个入参next, 这个参数表示遍历到的结构体的下一个结构体地址的指针变量。该宏用于安全删除,如果删除遍历到的item,不影响继续遍历。源码如下:#define LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, next, list, type, member)for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member),next = LOS_DL_LIST_ENTRY((item)->member->pstNext, type, member); &(item)->member != (list); item = next, next = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member))1.5.3LOS_DL_LIST_FOR_EACH_ENTRY_HOOK(item, list, type, member, hook)该宏定义和LOS_DL_LIST_FOR_EACH_ENTRY的区别就是多了个入参hook个钩子函数。在每次遍历循环中,调用该钩子函数做些用户定制的工作。源码如下:#define LOS_DL_LIST_FOR_EACH_ENTRY_HOOK(item, list, type, member, hook)for (item = LOS_DL_LIST_ENTRY((list)->pstNext, type, member), hook; &(item)->member != (list); item = LOS_DL_LIST_ENTRY((item)->member.pstNext, type, member), hook)2、Priority Queue 优先级队列在任务调度模块,就绪队列是个重要的数据结构,就绪队列需要支持初始化,出入队列,从队列获取最高优先级任务等操作。LiteOS调度模块支持单一就绪队列(Single Ready Queue)和多就绪队列(Multiple Ready Queue),我们这里主要讲述一下单一就绪队列。优先级队列Priority Queue接口主要内部使用,用户业务开发时不涉及,不对外提供接口。优先级队列其实就是个双向循环链表数组,提供更加方便的接口支持任务基于优先级进行调度。优先级队列核心的代码都在kernelbaseincludelos_priqueue_pri.h头文件和kernelbaseschedsched_sqlos_priqueue.c实现文件中。我们来看看优先级队列支持的操作。2.1 Priority Queue 优先级队列变量定义LiteOS支持32个优先级,取值范围0-31,优先级数值越小优先级越大。优先级队列在kernelbaseschedsched_sqlos_priqueue.c文件中定义的几个变量如下,其中⑴表示优先级为0的位,⑵处表示优先级队列的双向链表数组,后文会初始化为数组的长度为32,⑶表示优先级位图,标志哪些优先级就绪队列里有挂载的任务。示意图如下:优先级位图g_priQueueBitmap的bit位和优先级的关系是bits=31-priority,g_priQueueList[priority]优先级数组内容为双向链表,挂载各个优先级的处于就绪状态的任务。源码如下:#define OS_PRIORITY_QUEUE_NUM 32⑴ #define PRIQUEUE_PRIOR0_BIT 0x80000000U⑵ LITE_OS_SEC_BSS LOS_DL_LIST *g_priQueueList = NULL;⑶ STATIC LITE_OS_SEC_BSS UINT32 g_priQueueBitmap;下面我们来学习下优先级队列支持的那些操作。2.2 Priority Queue 优先级队列接口2.2.1 OsPriQueueInit(VOID)初始化优先级队列初始化在系统初始化的时候调用:main.c:main(void)k-->kernelinitlos_init.c:OsMain(VOID)-->kernelbaselos_task.c:OsTaskInit(VOID)-->OsPriQueueInit()。从下面的代码可以看出,⑴处申请长度为32的双向链表数值申请常驻内存,运行期间不会调用Free()接口释放。⑴处代码为数组的每一个双向链表元素都初始化为双向循环链表。源码如下:UINT32 OsPriQueueInit(VOID){UINT32 priority;/* 系统常驻内存,运行期间不会Free释放 */⑴ g_priQueueList = (LOS_DL_LIST *)LOS_MemAlloc(m_aucSysMem0, (OS_PRIORITY_QUEUE_NUM * sizeof(LOS_DL_LIST)));if (g_priQueueList == NULL) {return LOS_NOK;}for (priority = 0; priority < OS_PRIORITY_QUEUE_NUM; ++priority) {⑵ LOS_ListInit(&g_priQueueList[priority]);}return LOS_OK;}2.2.2 OsPriQueueEnqueueHead()插入就绪队列头部OsPriQueueEnqueueHead()从就绪队列的头部进行插入,插入得晚,但在同等优先级的任务中,会第一个调度。一起看下代码,⑴处先判断指定优先级priority的就绪队列是否为空,如果为空,则在⑵处更新优先级位图。⑶处把就绪状态的任务插入就绪队列的头部,以便优先调度。源码如下:VOID OsPriQueueEnqueueHead(LOS_DL_LIST *priqueueItem, UINT32 priority){LOS_ASSERT(priqueueItem->pstNext == NULL);⑴ if (LOS_ListEmpty(&g_priQueueList[priority])) {⑵ g_priQueueBitmap |= PRIQUEUE_PRIOR0_BIT >> priority;}⑶ LOS_ListHeadInsert(&g_priQueueList[priority], priqueueItem);}2.2.3 OsPriQueueEnqueue()插入就绪队列尾部和OsPriQueueEnqueueHead()的区别是,把就绪状态的任务插入就绪队列的尾部,同等优先级的任务中,后插入的后调度。2.2.4 OsPriQueueDequeue()就绪队列中删除在任务被删除、进入suspend状态,优先级调整等场景时,都需要调用接口OsPriQueueEnqueue()把任务从优先级队列中删除。我们来看下代码,⑴把任务从优先级就绪队列中删除。⑵获取删除的任务TCB信息,用来获取任务的优先级。刚从优先级队列中删除了一个任务,⑶处代码判断优先级队列是否为空,如果为空,则需要执行⑷处代码,把优先级位图中对应的优先级bit位置为0。源码如下:VOID OsPriQueueDequeue(LOS_DL_LIST *priqueueItem){LosTaskCB *runTask = NULL;⑴ LOS_ListDelete(priqueueItem);⑵ runTask = LOS_DL_LIST_ENTRY(priqueueItem, LosTaskCB, pendList);⑶ if (LOS_ListEmpty(&g_priQueueList[runTask->priority])) {⑷ g_priQueueBitmap &= ~(PRIQUEUE_PRIOR0_BIT >> runTask->priority);}}2.2.5 LOS_DL_LIST *OsPriQueueTop(VOID)获取就绪的优先级最高的链表节点这个接口可以获取优先级就绪队列中优先级最高的链表节点。⑴处判断优先级位图g_priQueueBitmap是否为0,如果为0,说明没有任何就绪状态的任务,返回NULL。 ⑵处计算g_priQueueBitmap二进制时开头的0的数目,这个数目对应于任务的优先级priority,然后⑶处从&g_priQueueList[priority]优先级队列链表中获取第一个链表节点。源码如下:LOS_DL_LIST *OsPriQueueTop(VOID){UINT32 priority;⑴ if (g_priQueueBitmap != 0) {⑵ priority = CLZ(g_priQueueBitmap);⑶ return LOS_DL_LIST_FIRST(&g_priQueueList[priority]);}return NULL;}2.2.6 UINT32 OsPriQueueSize(UINT32 priority)获取指定优先级的就绪任务的数量这个接口可以获取指定优先级的就绪队列中任务的数量。⑴、⑶处代码表示,在SMP多核模式下,根据获取的当前CPU编号的cpuId,判断任务是否属于当前CPU核,如果不属于,则不计数。⑵处代码使用for循环遍历指定优先级就绪队列中的链表节点,对遍历到新节点则执行⑷处代码,对计数进行进行加1操作。源码如下:UINT32 OsPriQueueSize(UINT32 priority){UINT32 itemCnt = 0;LOS_DL_LIST *curNode = NULL;#ifdef LOSCFG_KERNEL_SMPLosTaskCB *task = NULL;⑴ UINT32 cpuId = ArchCurrCpuid();#endifLOS_ASSERT(ArchIntLocked());LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));⑵ LOS_DL_LIST_FOR_EACH(curNode, &g_priQueueList[priority]) {#ifdef LOSCFG_KERNEL_SMPtask = OS_TCB_FROM_PENDLIST(curNode);⑶ if (!(task->cpuAffiMask & (1U << cpuId))) {continue;}#endif⑷ ++itemCnt;}return itemCnt;}2.2.7 LosTaskCB *OsGetTopTask(VOID)获取就绪的优先级最高的任务这个接口或者就绪任务队列中优先级最高的任务。一起看下代码,⑴、⑷处对SMP多核做特殊处理,如果是多核,只获取指定在当前CPU核运行的优先级最高的任务。⑵处获取g_priQueueBitmap优先级位图的值,赋值给UINT32 bitmap;。不直接操作优先级位图的原因是什么呢?在SMP多核时,在高优先级任务就绪队列里没有找到指定在当前CPU核运行的任务,需要执行⑹处的代码,清零临时优先级位图的bit位,去低一级的优先级就绪队列里去查找。只能改动临时优先级位图,不能改变g_priQueueBitmap。⑶处代码对优先级最高的就绪队列进行遍历,如果遍历到则执行⑸处代码从优先级就绪队列里出队,函数返回对应的LosTaskCB *newTask。源码如下:{ UINT32 priority; UINT32 bitmap; LosTaskCB *newTask = NULL; #ifdef LOSCFG_KERNEL_SMP⑴ UINT32 cpuid = ArchCurrCpuid(); #endif⑵ bitmap = g_priQueueBitmap; while (bitmap) { priority = CLZ(bitmap);⑶ LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &g_priQueueList[priority], LosTaskCB, pendList) { #ifdef LOSCFG_KERNEL_SMP⑷ if (newTask->cpuAffiMask & (1U << cpuid)) { #endif⑸ OsPriQueueDequeue(&newTask->pendList); goto OUT; #ifdef LOSCFG_KERNEL_SMP } #endif }⑹ bitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - priority - 1)); } OUT: return newTask; }
-
1.2 LOS_DL_LIST 双向链表初始化1.2.1 LOS_ListInit(LOS_DL_LIST *list)LOS_DL_LIST的两个成员*pstPrev和*pstNext, 是LOS_DL_LIST结构体类型的指针。需要为双向链表节点申请长度为sizeof(LOS_DL_LIST)的一段内存空间。为链表节点申请完毕内存后,可以调用初始化LOS_ListInit(LOS_DL_LIST *list)方法,把这个节点链接为环状的双向链表。初始化链表的时候,只有一个链表节点,这个节点的前序和后继节点都是自身。链表节点初始化为链表,如图所示:源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list){ list->pstNext = list; list->pstPrev = list;}另外,还提供了一个宏LOS_DL_LIST_HEAD,直接定义一个双向链表节点并以该节点初始化为双向链表。#define LOS_DL_LIST_HEAD(list) LOS_DL_LIST list = { &(list), &(list) }1.2.2 LOS_ListEmpty(LOS_DL_LIST *list)该接口用于判断链表是否为空。如果双向链表的前驱/后继节点均为自身,只有一个链表HEAD头节点,没有挂载业务结构体的链表节点,称该链表为空链表。源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE BOOL LOS_ListEmpty(LOS_DL_LIST *list){ return (BOOL)(list->pstNext == list);}1.3 LOS_DL_LIST 双向链表节点操作LiteOS双向链表提供三种链表节点插入方法,指定链表节点后面插入LOS_ListAdd、尾部插入LOS_ListTailInsert、头部插入LOS_ListHeadInsert。在头部插入的节点,从头部开始遍历时第一个遍历到,从尾部插入的节点,最后一个遍历到。1.3.1 LOS_ListAdd(LOS_DL_LIST list, LOS_DL_LIST node)这个API接口往链表节点*list所在的双向链表中插入一个链表节点*node,插入位置在链表节点*list的后面。如图所示,完成插入后,*node的后继节点是list->pstNext,*node的前序节点是*list。list->pstNext的前序节点是*node,*list的后续是*node节点。图示:源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListAdd(LOS_DL_LIST *list, LOS_DL_LIST *node){ node->pstNext = list->pstNext; node->pstPrev = list; list->pstNext->pstPrev = node; list->pstNext = node;}1.3.2 LOS_ListTailInsert(LOS_DL_LIST list, LOS_DL_LIST node)这个API接口往链表节点*list所在的双向链表中插入一个链表节点*node,插入位置在链表节点*list的前面,在list->pstPrev节点的后面。源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node){LOS_ListAdd(list->pstPrev, node);}1.3.3 LOS_ListHeadInsert(LOS_DL_LIST list, LOS_DL_LIST node)这个API接口和LOS_ListAdd()接口实现同样的功能,往链表节点*list所在的双向链表中插入一个链表节点*node,插入位置在链表节点*list的后面。源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListHeadInsert(LOS_DL_LIST *list, LOS_DL_LIST *node){LOS_ListAdd(list, node);}LiteOS双向链表提供两种链表节点的删除方法,指定节点删除LOS_ListDelete、删除并初始化为一个新链表LOS_ListDelInit。1.3.4 LOS_ListDelete(LOS_DL_LIST *node)这个API接口将链表节点*node从所在的双向链表中删除。节点删除后,可能需要调用Free()函数释放节点所占用的内存。如图所示,*node节点后继节点的前序改为*node的前序,*node节点前序节点的后续改为*node的后续,并把*node节点的前序、后续节点设置为null。图示:源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node){node->pstNext->pstPrev = node->pstPrev;node->pstPrev->pstNext = node->pstNext;node->pstNext = NULL;node->pstPrev = NULL;}1.3.5 LOS_ListDelInit(LOS_DL_LIST *list)这个API接口将链表节点*list从所在的双向链表中删除, 并把删除后的节点重新初始化为一个新的双向链表。*list节点后继节点的前序改为*list的前序,*list节点前序节点的后续改为*list的后续。和LOS_ListDelete()方法不同的是,并不并把*list节点的前序、后续节点设置为null,而是把这个删除的节点重新初始化为一个新的以*list为头结点的双向链表。源码如下:LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelInit(LOS_DL_LIST *list){list->pstNext->pstPrev = list->pstPrev;list->pstPrev->pstNext = list->pstNext;LOS_ListInit(list);}LiteOS双向链表还提供获取链表节点、获取包含链表的结构体地址的操作。1.3.6 LOS_DL_LIST_LAST(object)这个宏定义获取链表的前驱节点。源码如下:#define LOS_DL_LIST_LAST(object) ((object)->pstPrev)1.3.7 LOS_DL_LIST_FIRST(object)这个宏定义获取链表的后继节点。源码如下:#define LOS_DL_LIST_FIRST(object) ((object)->pstNext)1.3.8 LOS_OFF_SET_OF(type, member)这个宏定义根据结构体类型名称type和其中的成员变量名称member,获取member成员变量相对于结构体type的内存地址偏移量。在应用场景上,业务结构体包含双向链表作为成员,当知道双向链表成员变量的内存地址时,和这个偏移量,可以进一步获取业务结构体的内存地址。源码如下:#define LOS_OFF_SET_OF(type, member) ((UINTPTR)&((type *)0)->member)1.3.9 LOS_DL_LIST_ENTRY(item, type, member)根据业务结构体类型名称type、其中的双向链表成员变量名称member,和双向链表的内存指针变量item,使用该宏定义LOS_DL_LIST_ENTRY可以获取业务结构体的内存地址。我们以实际例子演示下这个宏LOS_DL_LIST_ENTRY是如何使用的。互斥锁的control block结构体LosMuxCB在上文已经展示过其代码,有个双向链表的成员变量LOS_DL_LIST muxList。在创建互斥锁的方法LOS_MuxCreate()中,⑴ 处代码从空闲互斥锁链表中获取一个空闲的双向链表节点指针地址LOS_DL_LIST *unusedMux,把这个作为第一个参数,结构体名称LosMuxCB及其成员变量muxList,分别作为第二、第三个参数,使用宏LOS_DL_LIST_ENTRY可以计算出结构体的指针变量地址LosMuxCB *muxCreated,见⑵处代码。LITE_OS_SEC_TEXT UINT32 LOS_MuxCreate(UINT32 *muxHandle){......LosMuxCB *muxCreated = NULL;LOS_DL_LIST *unusedMux = NULL;......⑴ unusedMux = LOS_DL_LIST_FIRST(&g_unusedMuxList);LOS_ListDelete(unusedMux);⑵ muxCreated = LOS_DL_LIST_ENTRY(unusedMux, LosMuxCB, muxList);......}从这个例子上,就比较容易理解,这个宏定义可以用于什么样的场景,读者们可以阅读查看更多使用这个宏的例子,加强理解。源码如下:源码实现上,基于双向链表节点的内存地址,和双向链表成员变量在结构体中的地址偏移量,可以计算出结构体的内存地址。#define LOS_DL_LIST_ENTRY(item, type, member) ((type *)(VOID *)((CHAR *)(item) - LOS_OFF_SET_OF(type, member)))
-
在学习Huawei LiteOS源代码的时候,常常会遇到一些数据结构的使用。如果没有掌握这它们的用法,阅读LiteOS源代码的时候会很费解、很吃力。本文会给读者介绍下LiteOS源码中常用的几个数据结构,包括: 双向循环链表LOS_DL_LIST,优先级队列Priority Queue,排序链表SortLinkList等。在讲解时,会结合相关的绘图,培养数据结构的平面想象能力,帮助更好的学习和理解这些数据结构用法。本文中所涉及的LiteOS源码,均可以在LiteOS开源站点https://gitee.com/LiteOS/LiteOS 获取。我们首先来看看使用最多的双向循环链表Doubly Linked List。1、LOS_DL_LIST 双向循环链表双向链表LOS_DL_LIST核心的代码都在kernelincludelos_list.h头文件中,包含LOS_DL_LIST结构体定义、一些inline内联函数LOS_ListXXX,还有一些双向链表相关的宏定义LOS_DL_LIST_XXXX。双向链表源代码、示例程序代码、开发文档如下:kernelincludelos_list.h 双向链表头文件网页获取源码 https://gitee.com/LiteOS/Lite...。demoskernelapilos_api_list.c 双向链表Demo程序网页获取源码 https://gitee.com/LiteOS/Lite...。开发指南双向链表文档在线文档https://gitee.com/LiteOS/Lite...1.1 LOS_DL_LIST 双向链表结构体双向链表结构体LOS_DL_LIST定义如下。看得出来,双向链表的结构非常简单、通用、抽象,只包含前驱、后继两个节点,负责承上启下的双向链表作用。双向链表不包任何业务数据信息,业务数据信息维护在业务的结构体中。双向链表作为业务结构体的成员使用,使用示例稍后会有讲述。typedef struct LOS_DL_LIST { struct LOS_DL_LIST *pstPrev; /** 当前节点的指向前驱节点的指针 */ struct LOS_DL_LIST *pstNext; /** 当前节点的指向后继节点的指针 */ } LOS_DL_LIST; 从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找、插入、删除等操作,对于非常方便。由于双向链表的环状结构,任何一个节点的地位都是平等的。从业务上,可以创建一个节点作为Head头节点,业务结构体的链表节点从HEAD节点开始挂载。从head节点的依次遍历下一个节点,最后一个不等于Head节点的节点称之为Tail尾节点。这个Tail节点也是Head节点的前驱。从Head向前查找,可以更快的找到Tail节点。 我们看看LiteOS内核代码中如何使用双向链表结构体的。下面是互斥锁结构体LosMuxCB定义,其中包含双向链表LOS_DL_LIST muxList;成员变量: typedef struct { LOS_DL_LIST muxList; /** 互斥锁的双向链表*/ LosTaskCB *owner; /** 当前持有锁的任务TCB */ UINT16 muxCount; /** 持有互斥锁的次数 */ UINT8 muxStat; /** 互斥锁状态OS_MUX_UNUSED, OS_MUX_USED */ UINT32 muxId; /** 互斥锁handler ID*/ } LosMuxCB; LiteOS的双向链表为用户提供下面初始化双向列表,增加、删除链表节点,判断节点是否为空,获取链表节点,获取链表所在的结构体,遍历双向链表,遍历包含双向链表的结构体等功能。我们一一来详细的学习、分析下代码。
-
【功能模块】用IoT Sutdio生产的Demo里,编译的时候Makefile出错【操作步骤&问题现象】1、点击rebuild编译2、然后提示module.mk出错make[3]: Entering directory 'd:/Works/IoT/Codes/LiteOS_test1/components/net/at_device'd:/Works/IoT/Codes/LiteOS_test1/build/mk/module.mk:91: *** multiple target patterns. Stop.make[3]: Leaving directory 'd:/Works/IoT/Codes/LiteOS_test1/components/net/at_device'make[2]: *** [d:/Works/IoT/Codes/LiteOS_test1/build/mk/module.mk:57: SUB_MODULE_BUILD] Error 1make[2]: Leaving directory 'd:/Works/IoT/Codes/LiteOS_test1/components/net'make[1]: *** [d:/Works/IoT/Codes/LiteOS_test1/build/mk/module.mk:57: SUB_MODULE_BUILD] Error 1make[1]: Leaving directory 'd:/Works/IoT/Codes/LiteOS_test1/components'【截图信息】【日志信息】(可选,上传日志内容或者附件)
Ivan5233
发表于2020-12-28 15:59:36
2020-12-28 15:59:36
最后回复
bingbingyouli_2020
2020-12-30 14:11:16
2249 6 -
大家是不是都会遇到这样的场景:出门走了有几分钟,忽然忘记自己是否锁门,对于许多有强迫症的人而言,在这种情况下若是不回去确认一下,一天的心神都不会安宁。而这时,如果你拿出自己的手机查看智能门锁的状态,就能知道门窗是否已经紧锁,就完全不用再揪心一天了。这种场景就是物联网中一个微小的缩影。可能有些人是最近几年才接触到物联网,而刘洪峰却在这个领域走了已有10余年的时间。在环保领域、消防领域、养殖种植领域、以及水处理和军工领域都有不同程度的物联网项目落地。他在早期接触物联网的时候,一直认为和他熟知的工控自动化网没有本质的区别。随着现在实施物联网的项目增多,及相关物联网新技术的产品和平台涌现,刘洪峰对物联网有了新的认知:物联网和工业自动化最大的区别就是,前者的量级非常巨大,对成本也比较敏感,一般没有专门维护的队伍,另外对数据实时分析和深度挖掘也有很高的要求,所以在项目架构设计及实施上,二者就有了很大的区别。物联网老兵结缘华为云正如前面提到的,刘洪峰在物联网领域有着很丰富的经历,也正是在这些领域的项目中与华为云结缘。当时刘洪峰因为一个物联网项目需要在市面上选取一款小型轻量级的IoT嵌入式系统。他调研了国内外好几家的产品,虽然国外uC/OSII、FreeRTOS和Mbed OS等嵌入式操作系统都很优秀,但长期应用就有一种不踏实的感觉。随后,他们挑选国内产品的时候,发现了华为云的这款轻量级物联网操作系统LiteOS。选取产品之后,刘洪峰利用线上和线下各种渠道去了解和学习LiteOS系统。随着学习内容的加深,他对LiteOS越来越充满了兴趣。也正是他对LiteOS系统的这份认真,刘洪峰团队在华为上海研究所还和LiteOS团队人员面对面进行技术交流。在了解他们工作中的困难和需求之后,LiteOS团队为他们专门设计了一款“六方塔”的环境监测仪器,运行支持JS代码的LiteOS系统。这款产品在2018年华为上海全联接大会的LiteOS展台上进行了展示。也正是轻量级,低功耗的特色让开发的产品更有竞争力,刘洪峰公司的智能门锁,智能水表等产品也都采用了LiteOS嵌入式系统。有了这前期的应用经验,在2018年3月份华为举办的《寻找IoT达人—LiteOS对接云平台》竞赛,刘洪峰参加大赛并用大赛中所设计的智慧农业示范应用项目,获得了竞赛的第一名,还成为了本次竞赛最佳解决方案奖的获得者。 从此,刘洪峰与华为云结下了不解之缘。三个因素影响物联网技术落地现在已经进入了5G新时代,刘洪峰表示,未来物联网技术未来的发展分为两个方向,一就是面向具体行业,具体需求的综合型产品将越来越多(工控领域大都是通用产品,比如PLC,通用组态等)。二就是越来越多的厂家将抛弃自己云端软件平台的设计,转为采用大公司的物联网平台。因为物联网项目联接的智能设备越来越多,一般公司很难有实力维护这么多数量设备的接入,更难进一步对采集的海量数据进行深度挖掘和分析。在物联网产品落地应用方面,刘洪峰感慨道,物联网要得以顺利实施必须在三方面下功夫:项目成本要低;项目实施容易;设备少维护甚至免维护。刘洪峰举了一个养鸡项目的案例:在项目实施的过程中,他们重新设计了一款综合传感器采集器,把各种传感器探头直接进行对接,省掉其中的中间环节,不仅成本大大降低,实施成本也大大降低。设备集成度也从最初的三合一到最终的九合一。全球物联网设备联接数目前正处于复合30%左右的速度快速增长,预测在2025 年达到 252 亿。同时,物联网应用在各行各业中快速普及,新应用层出不穷。平台作为海量设备联接到场景应用的“桥梁”,起到了关键作用。新兴的AI、边缘计算和 5G 技术的推广,将在未来持续推动 IoT的发展。刘洪峰表示,相信物联网在5G时代,发展会更为迅捷,正如活动的主题一样,真正达成智简联接,万物互联的愿景。
-
与其说传感器是一个硬件设备,不如说它是我们人体感官的延伸。试想一下,我们被丰富且微小的传感器所包围,它们时刻监测我们健康,通过云端算法,如同加密的 “私人医生”一样,帮助我们预防疾病风险;城市和生活也会被丰富的传感器网络所包围,时刻检测温湿度、空气质量,由此开启和关闭空气净化装置,实时改善城市的环境……这也是刘文龙所构想的物联网世界,他认为未来的世界,传感器将包含人类五感的功能,部分甚至能代替和制造五感的效果。那么,这样一个有些“荒诞”,又有些“理想化”的世界,从哪里开始呢?与IoT结缘,感知世界的另一种方式刘文龙算是物联网行业的老兵了,在嵌入式领域有着10多年的工作经验。他和物联网的渊源最早可以追溯到他上大学那会,回忆起当初做物联网开发的那段经历,刘文龙提到了一个有趣的项目。当时他在学校实验室开发一个精密的形变检测产品,用于测试大坝微小变化。其原理是利用24位AD对一个形变片上的微小电压进行测试,采样后利用Δ-∑滤波器进行滤波,然后通过MCU进行中值处理后得到一个新的数据点,最后将数据发送到中央监控器,监控被测物体微米或是纳米级别的形变,来预判一些事故的发生。可见一个小小的传感器蕴藏的能量是不可估量的,刘文龙迷恋于此。所以,毕业后他一直从事嵌入式开发的工作,尤其以伺服嵌入式软件开发和物联网应用开发为主。10年的行业经验,也让刘文龙对物联网市场的变化感同身受。 单一的传感器能收集到的数据是有限的,以前也没有合适的技术能将这些数据价值最大化。但通过物联网技术,可以把传感器的参数采集后通过安全加密的方式传输到云端,在云端大数据和AI的支撑下,赋能这些传感器。最终,无处不在的IoT设备让我们更好地控制和感知我们的世界。在刘文龙看来,IoT无处不在,用于测量空气的温度、湿度、PM2.5,用于生产安全的燃气浓度、烟感等等,更多智能的传感器和云端算法正融入到我们的生活。LiteOS:速度快、周期短、高可靠工欲善其事必先利其器,万物互联的美好图景始于物联网应用的开发,这也是刘文龙接触华为云后最受益匪浅的地方。他在学习华为云IoT课程时了解到华为云的嵌入式操作系统LiteOS,自此就是一发不可收的项目开发之路。在刘文龙看来,LiteOS可以很好地进行实时线程的控制,首先保证了传感器采样对时间的要求。其次,LiteOS的设计能够加速开发者项目的落地,以它提供的传感器框架为例,由于统一了接口,开发者选择已有的传感器驱动进行开发或是依照框架设计传感器驱动时,能直接调用应用程序,不仅缩短开发周期,也减少不靠谱代码造成的影响。刘文龙举了个开发实操的案例:利用SR04超声波传感器进行距离采样。如果使用原有的裸机开发,需要自己做定时的采样,开启定时器中断,然后检测中断的时间,按照这个时间计算处理,再通过时间扫描将数据发送到云端。但是用LiteOS后,我只需要把SR04的超声波检测模块添加到SensorHub框架,然后利用轮询传感器采样,将数据转成Json格式直接发到云端。这几步操作只需LiteOS创建几个线程,写几行语句就能实现,比原有逻辑的开发思路简单多,再加上华为在安全上做的工作,还能保证运行后的可靠。更优秀的是,华为在保证系统足够安全可靠的情况下,还增加了IoT Studio数据平台的服务接口,这样从数据采集、网络协议(MQTT、CoAP等)传输到IoT Studio也只要几行指令即可完成。软件的便利性让刘文龙颇为感慨,“这在原有的嵌入式编程上是不可想象的,华为云IoT工具的效率和安全性并重,既降低开发者的使用门槛,也给嵌入式设备更好地赋能。”现在手上有新项目,刘文龙第一考虑就是LiteOS,它既有优质的框架能直接选用,也可以更好得完成项目算法的迁移。经过几个项目的历练,刘文龙认为,“未来嵌入式MCU必将从裸机开发到RTOS开发,因为现在的程序可靠性高,编程也简单,相应的项目产出也快了。”不过,LiteOS解决的只是物联网应用的开发,真正让传感器智能的还要靠设备(数据)上云后带来的数据价值。IoT+AI赋能,让设备“活”起来数据上云后,用传统的方式面对这些数据感觉很无力:既没有好的大数据算法,也没有相应的大型服务支持。但现在有了IoT数据接入服务DIS,一切难题便可迎刃而解。DIS可以连接AI开发平台ModelArts实现AI的运算,并把结果反馈回IoT平台到开发板进行命令执行。具体流程如上图所示:边端设备将数据传输到IoT 平台,数据以JSON格式转入到DIS,再通过对象存储服务OBS将边端数据传送到ModelArts,数据经过预先制定的边端模型训练后在AI开发平台上实现功能。这就是从端边的数据采集到云端AI运行,再将结果输出到IoT平台监控和终端设备执行命令的全流程。以此类推,在智慧城市、智慧工厂这样大的场景中,我们也可以将传感器收集到的数据加载到AI模型中,从而预判场景的变化,如城市人口的移动数据、各个景点空气质量数据、工厂的生产进度……这样就能高效地进行城市建设,为工厂设备的使用进行预判维护,提高生产力和效率。可以预见的是,伴随着云服务、AI、边缘计算的深入结合,万物互联,以传感器为代表的物物感知时代,离我们真的不远了。
-
目标是做路灯PWM,但是PB9没有定时器的通道,所以用一个定时器产生50Hz的中断,然后在中断中完成PWM。但是怎么都找不到在LiteOS中使用定时器的教程???求救有个用定时器闪烁LED的教程,但是那是在裸板上用的,LiteOS中不能用
-
现在打算用TIM2设置一个定时中断,按照闪烁LED教程使用STM32CubeMX生成驱动代码,复制进工程初始化后发现无法进入定时器2的中断处理函数TIM2_IRQHandler查了一下结果是LiteOS接管了中断,需要用LiteOS的中断接管模块注册中断,可是这个模块要怎么用啊???找到的帖子都是介绍啥是中断我真是醉了。。。有没有大佬救救孩子啊,为啥教程里用原生的中断管理方式,然后LiteOS里又把中断给接管了呢???就算是接管了你倒是给个教程啊。。。找个教程找的头都炸了
-
参加华为第三阶段第二章实验,编译LiteOS_Lab_STM32_V5工程不通过,编译失败详情如下,求助大神,谢谢![2020-12-08 14:55:05] 开始编译...Makefile:66: 物联终端,数据源头/LiteOS_Lab_STM32_V5/targets/STM32L431_BearPi/GCC/物联终端,数据源头/LiteOS_Lab_STM32_V5/targets/STM32L431_BearPi/GCC/project.mk: No such file or directory make: *** No rule to make target '物联终端,数据源头/LiteOS_Lab_STM32_V5/targets/STM32L431_BearPi/GCC/物联终端,数据源头/LiteOS_Lab_STM32_V5/targets/STM32L431_BearPi/GCC/project.mk'. Stop. 0 Error(s), 0 Warning(s).exit code=2[2020-12-08 14:55:05] 编译失败。
-
摘要:鸿蒙和LiteOS的内核都是一样的名字,可它们究竟有什么不同呢?一起来对比一下文件吧!HarmonyOS系统HarmonyOS是一款“面向未来”、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统。在传统的单设备系统能力的基础上,HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持多种终端设备。鸿蒙系统的特点(硬件互助,资源共享):1)分布式软总线分布式软总线是多种终端设备的统一基座,为设备之间的互联互通提供了统一的分布式通信能力,能够快速发现并连接设备,高效地分发任务和传输数据。2)分布式设备虚拟化分布式设备虚拟化平台可以实现不同设备的资源融合、设备管理、数据处理,多种设备共同形成一个超级虚拟终端。针对不同类型的任务,为用户匹配并选择能力合适的执行硬件,让业务连续地在不同设备间流转,充分发挥不同设备的资源优势。3)分布式数据管理分布式数据管理基于分布式软总线的能力,实现应用程序数据和用户数据的分布式管理。用户数据不再与单一物理设备绑定,业务逻辑与数据存储分离,应用跨设备运行时数据无缝衔接,为打造一致、流畅的用户体验创造了基础条件。4)分布式任务调度分布式任务调度基于分布式软总线、分布式数据管理、分布式Profile等技术特性,构建统一的分布式服务管理(发现、同步、注册、调用)机制,支持对跨设备的应用进行远程启动、远程调用、远程连接以及迁移等操作,能够根据不同设备的能力、位置、业务运行状态、资源使用情况,以及用户的习惯和意图,选择合适的设备运行分布式任务。5)一次开发,多端部署HarmonyOS提供了用户程序框架、Ability框架以及UI框架,支持应用开发过程中多终端的业务逻辑和界面逻辑进行复用,能够实现应用的一次开发、多端部署,提升了跨设备应用的开发效率。一次开发、多端部署。6)统一OS,弹性部署HarmonyOS通过组件化和小型化等设计方法,支持多种终端设备按需弹性部署,能够适配不同类别的硬件资源和功能需求。支撑通过编译链关系去自动生成组件化的依赖关系,形成组件树依赖图,支撑产品系统的便捷开发,降低硬件设备的开发门槛。HarmonyOS系统架构整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 功能/模块”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的子系统或功能/模块。因为系统比较庞大,今天主要是针对其中一个内核进行分析,即LiteOS内核。Huawei LiteOSHuawei LiteOS是华为针对物联网领域推出的轻量级物联网操作系统,是华为物联网战略的重要组成部分,具备轻量级、低功耗、互联互通、组件丰富、快速开发等关键能力,基于物联网领域业务特征打造领域性技术栈,为开发者提供 “一站式” 完整软件平台,有效降低开发门槛、缩短开发周期,可广泛应用于可穿戴设备、智能家居、车联网、LPWA等领域。Huawei LiteOS自开源社区发布以来,围绕物联网市场从技术、生态、解决方案、商用支持等多维度使能合作伙伴,构建开源的物联网生态,目前已经聚合了50+ MCU和解决方案合作伙伴,共同推出一批开源开发套件和行业解决方案,帮助众多行业客户快速的推出物联网终端和服务,客户涵盖抄表、停车、路灯、环保、共享单车、物流等众多行业,加速物联网产业发展和行业数字化转型。关键特性1)低功耗框架:LiteOS是轻量级的物联网操作系统,最小内核尺寸仅为6KB,具备快速启动、低功耗等优势,Tickless机制显著降低传感器数据采集功耗。2)OpenCPU架构:专为LiteOS小内核架构设计,满足硬件资源受限需求,比如LPWA场景下的水表、气表、车检器等,通过MCU和通信模组二合一的OpenCPU架构,显著降低终端体积和终端成本。3)安全性设计:构建低功耗安全传输机制,支持双向认证、FOTA固件差分升级,DTLS/DTLS+等,构建低功耗安全传输机制。4)端云互通组件:LiteOS SDK端云互通组件是终端对接到IoT云平台的重要组件,集成了 LwM2M、CoAP、MQTT、mbed TLS、LwIP等全套IoT互联互通协议栈,大大减少开发周期,快速入云。5)SOTA远程升级:SOTA远程升级,通过差分方式降低升级包的尺寸,更能适应低带宽网络环境和电池供电环境,经过特别优化差分合并算法,对RAM资源要求更少,满足海量低资源终端的升级诉求。系统支持对比从官网的数据来看,HUAWEI LiteOS支持硬件比较鸿蒙还是丰富一些,提供了更多内核案例和移植策略,从代码来看鸿蒙支持ARM类型更多,分别支持Cortex-M内核和Cortex-A内核,内核文件也分别有两个,即liteos-a和liteos-m,例程分别有对应的支持;而HUAWEI LiteOS现阶段仅是对Cortex-M内核的支持,因为也是产品地位不同,估计未来也不会去适配A系列内核的处理吧,因为更好性能的需求,HarmonyOS应该是更好的选择。总结两个系统对硬件内核架构的支持情况。因为产品定位不同,两个系统的对内存的要也是非常大,HUAWEI LiteOS因为其具有很高的剪裁特性,在硬件资源有限的时候的仅保留内核,可以剪裁到6KB的ROM以及消耗2KB的RAM资源,可以说对资源的消耗是低到一定程度,而HarmonyOS为了保证系统性能要求,对硬件的资源则是128K的ROM及2MB的RAM的要求。从资源消耗可以看出,HUAWEI LiteOS更适合资源需求低,应用要求不高,对成本功耗要求较高的应用场合;HarmonyOS更适合多媒体交互,需要Js增加开发效率的复杂大应用的场合使用。现在两个系统都已经开始在市场在开发者在进行学习和开发,那对其硬件的支持又是如何,总结如下表:HarmonyOS的开发板支持还是以华为自研的芯片为主,都是自身主打的IOT及视频处理芯片,这样开发速度快可以更快的应用到自己的设备上,验证系统可靠性;HUAWEI LiteOS的开发板支持更多的是市场上主流的ARM开发学习板(STM原生、小熊派、野火),这样的原因推测是让开发者更好的做芯片切换和容易学些如何进行内核移植的工作内容,以便上手容易快捷。内核文件对比因为两者对Cortex-M的内核名字都是liteos-m,因为我就对比了两者的内核kernel文件夹。首先分别从各自的git库下载master下发布的最新文件文件库地址HarmonyOS-liteos-m:https://gitee.com/openharmony/kernel_liteos_m.git;HUAWEI LiteOS:https://gitee.com/LiteOS/LiteOS.git; 从gitee上的库kernel文件夹下的文件的日期都是两个月前,说明内核的发布时间比较一致,那文件的内容,分别对比文件目录,发现竟然的相似,共有文件目录都是/base;/extended;/include;HUAWEI LiteOS下会有一个Kconfig和Makefile用于文件包含定义和Kconfig文件菜单生成;而HarmonyOS则是BULD.gn和los_init.c分别用于IDE的文件编译及内核文件剪辑初始化文件,文件功能基本移植,只是适配的编译环境不同。说明华为内部对文件命名和分类有着比较一致的监控,同时两班人马的风格比较一致,这样对工作交接表现比较友好。从这里开始就有些不同,base下的文件夹设置两者的区别较大。左边是HarmonyOS,右边是HUAWEI LiteOS的base文件下的文件分部,HarmonyOS吧内核文件封装在IPC和Core两个文件中,而HUAWEI LiteOS则是放在的base文件下,对比文件发现,HarmonyOS内核的文件名字大部分在HUAWEI LiteOS base文件夹下找到,但文件大小都不相同,这次仅对比一个文件,task.c,看看两者的异同,发现基本没有使用过相同的方法实现功能,所以不展示方法内容对比,对比一下各自提供了哪些方法用于task的操作接口。左边是HarmonyOS提供task操作接口,右边是HUAWEI LiteOS提供task操作接口,都提供了task的初始化(建立),文件上下文查询,钩子函数的使用,同时HarmonyOS提供了所有task的信息获取,HUAWEI LiteOS提供了task入口地址的获取的细节功能。今天分享到这里,后面希望可以分享各自系统移植及使用。
-
build.rar: Make构建软件,解压到C:\Users\<UserName>\.huawei-liteos-studio\tools\buildextension-collections.rar: 中文语言扩展extension-collections\1.45.1\MS-CEINTL.vscode-language-pack-zh-hans-1.45.1.vsix C/C++扩展extension-collections\ms-vscode.cpptools-0.29.0.vsixopenocd-0.10.0.rar: 烧录器,解压到C:\Users\<UserName>\.huawei-liteos-studio\tools\openocd-0.10.0arm-none-eabi.rar: 编译器,解压到C:\Users\<UserName>\.huawei-liteos-studio\tools\arm-none-eabi
Sunshine_666
发表于2020-10-10 17:51:17
2020-10-10 17:51:17
最后回复
yd_269179125
2026-03-23 11:25:37
12339 6 -
发文的版块名:IoT发文的标题名:【IoT美学】《物联网操作系统原理(LiteOS)》——任务同步帖子内容链接:【IoT美学】《物联网操作系统原理(LiteOS)》——任务同步_博客_云社区_开发者中心-华为云 (huaweicloud.com)
-
其实,最早被这个学习项目吸引进来是一个很巧合的事情,因为前段时间刚完成了一个类似的物联网项目,系统架构和做软件部分要使用单独的系统,想到这方面以后的系统性和可扩展性其实是有问题的,都要自己的人开发,于是关注到了华为的LiteOS这个项目,居然是免费的推广和培训,于是就义无反顾地投入时间进来参与。 市场上已经有很多诸如Arduino和树莓派等各种派的开发板,这次看到小熊派的开发板结合了华为LiteOS这个生态在配合,也是一件很有意义的事情。其实也很想看到除了小熊派,还有更多的厂家来参与,或者是不是华为提供类似软件开发LiteOS的开发方式的模式,将能兼容LiteOS系统的硬件开发,也搞一种模块化开发的设计路线,只要入门级别的硬件工程师拿到模板化的设计原理图和 Layout,就能自己生成自己要的实际可用的硬件板子,然后配合烧录的固件。 然后,再将云端开发的应用类增加一些培训,在来一个云端开发APP的运作模式,用户还能自己简易开发APP和小程序,那样这个生态系统就完整起来了。 很期待这样的状况出现!
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签