时间统计和时间延迟:

VOID ExampleGetTime(VOID)
{
    UINT32 cyclePerTick;
    UINT64 tickCountBefore;
    UINT64 tickCountAfter;

    cyclePerTick  = LOS_CyclePerTickGet();
    if (0 != cyclePerTick) {
        printf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
    }

    tickCountBefore = LOS_TickCountGet();
    LOS_TaskDelay(200);
    tickCountAfter = LOS_TickCountGet();
    printf("LOS_TickCountGet after delay rising = %d \n", (UINT32)(tickCountAfter - tickCountBefore));
}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">

结果验证

编译运行得到的结果为:

时间转换:

tick = 1000
ms = 1000
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

时间统计和时间延迟:

LOS_CyclePerTickGet = 250000 (根据实际运行环境,数据会有差异)
LOS_TickCountGet after delay rising = 200
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

软件定时器

基本概念

软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当经过设定的Tick时钟计数值后会触发用户定义的回调函数。定时精度与系统Tick时钟的周期有关。

硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器,OpenHarmony LiteOS-M内核提供软件定时器功能。软件定时器扩展了定时器的数量,允许创建更多的定时业务。

软件定时器功能上支持:

运行机制

软件定时器是系统资源,在模块初始化的时候已经分配了一块连续的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置,该值按产品实际需要设定。

软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。

软件定时器以Tick为基本计时单位,当用户创建并启动一个软件定时器时,OpenHarmony LiteOS-M内核会根据当前系统Tick时间及用户设置的定时间隔确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。

当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,看是否有定时器超时,若有则将超时的定时器记录下来。

Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用之前记录下来的定时器的超时回调函数。

定时器状态

定时器模式

OpenHarmony LiteOS-M内核的软件定时器提供三类定时器机制:

接口说明

OpenHarmony LiteOS-M内核的软件定时器模块提供下面几种功能,接口详细信息可以查看API参考。

表1 软件定时器接口

class="table-box">
功能分类接口描述
创建、删除定时器- LOS_SwtmrCreate:创建定时器。
- LOS_SwtmrDelete:删除定时器。
启动、停止定时器- LOS_SwtmrStart:启动定时器。
- LOS_SwtmrStop:停止定时器。
获得软件定时器剩余Tick数LOS_SwtmrTimeGet:获得软件定时器剩余Tick数。

开发流程

软件定时器的典型开发流程:

  1. 配置软件定时器。

  2. 创建定时器LOS_SwtmrCreate。

  3. 启动定时器LOS_SwtmrStart。

  4. 获得软件定时器剩余Tick数LOS_SwtmrTimeGet。

  5. 停止定时器LOS_SwtmrStop。

  6. 删除定时器LOS_SwtmrDelete。

说明:

编程实例

实例描述

在下面的例子中,演示如下功能:

  1. 软件定时器创建、启动、删除、暂停、重启操作。

  2. 单次软件定时器,周期软件定时器使用方法。

示例代码

前提条件:

代码实现如下:

本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleSwtmr。

#include "los_swtmr.h"

/* 定时器间隔时间 */
#define SWTMR_INTERVAL_LONG      1000
#define SWTMR_INTERVAL_SHORT     100

/* 定时器触发次数计数 */
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;

/* 回调函数1,单次触发定时器的回调函数 */
void Timer1Callback(UINT32 arg)
{
    g_timerCount1++;
    printf("g_timerCount1=%d\n", g_timerCount1);
}

/* 回调函数2,多次触发定时器的回调函数 */
void Timer2Callback(UINT32 arg)
{
    g_timerCount2++;
    printf("g_timerCount2=%d\n", g_timerCount2);
}

void SwtmrTest(void)
{
    UINT32 ret;
    UINT32 id1; // 定时器id1,单次触发定时器
    UINT32 id2; // 定时器id2,周期触发定时器
    UINT32 tickCount;

#if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
    /* 创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
    LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0,
                    OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);

    /* 创建周期性软件定时器,每100Tick数执行回调函数2 */
    LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0,
                    OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);
#else
    /* 创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
    LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0);

    /* 创建周期性软件定时器,每100Tick数执行回调函数2 */
    LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0);
#endif

    /* 启动单次软件定时器 */
    ret = LOS_SwtmrStart(id1);
    printf("start Timer1 %s\n", (ret == LOS_OK) ? "success" : "failed");

    /* 短时间延时,定时器还未触发 */
    LOS_TaskDelay(SWTMR_INTERVAL_SHORT);

    /* 单次定时器还未到时间触发,此时停止应该成功 */
    ret = LOS_SwtmrStop(id1);
    printf("stop timer1 %s\n", (ret == LOS_OK) ? "success" : "failed");

    LOS_SwtmrStart(id1);

    /* 长时间延时,定时器触发 */
    LOS_TaskDelay(SWTMR_INTERVAL_LONG);

    /* 单次定时器触发后自删除,此时停止失败才是正常 */
    ret = LOS_SwtmrStop(id1);
    printf("timer1 self delete test %s\n", (ret != LOS_OK) ? "success" : "failed");

    /* 启动周期性软件定时器 */
    ret = LOS_SwtmrStart(id2);
    printf("start Timer2 %s\n", (ret == LOS_OK) ? "success" : "failed");

    /* 长时间延时,定时器周期触发 */
    LOS_TaskDelay(SWTMR_INTERVAL_LONG);

    LOS_SwtmrStop(id2);

    ret = LOS_SwtmrDelete(id2);
    if (ret == LOS_OK) {
        printf("delete Timer2 success\n");
    }
}

UINT32 ExampleSwtmr(VOID)
{
    UINT32 ret;
    TSK_INIT_PARAM_S taskParam = { 0 };
    UINT32 taskId;

    /* 锁任务调度 */
    LOS_TaskLock();

    /* 创建任务 */
    taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SwtmrTest;
    taskParam.pcName       = "TimerTsk";
    taskParam.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    taskParam.usTaskPrio   = 5;
    ret = LOS_TaskCreate(&taskId, &taskParam);
    if (ret != LOS_OK) {
        printf("TimerTsk create failed.\n");
        return LOS_NOK;
    }

    /* 解锁任务调度 */
    LOS_TaskUnlock();
    return LOS_OK;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

结果验证

编译烧录运行,输出结果如下:

start Timer1 success
stop timer1 success
g_timerCount1=1
timer1 self delete test success
start Timer2 success
g_timerCount2=1
g_timerCount2=2
g_timerCount2=3
g_timerCount2=4
g_timerCount2=5
g_timerCount2=6
g_timerCount2=7
g_timerCount2=8
g_timerCount2=9
g_timerCount2=10
delete Timer2 success
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

双向链表

基本概念

双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增加一个指向前一个节点的指针。其头指针head是唯一确定的。

从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构形式使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等操作,但需要注意前后方向的操作。

功能说明

双向链表模块为用户提供下面几种功能,接口详细信息可以查看API参考。

class="table-box">
功能分类接口描述
初始化和删除链表LOS_ListInit:将指定双向链表节点初始化为双向链表。
LOS_DL_LIST_HEAD:定义一个双向链表节点并以该节点初始化为双向链表。
LOS_ListDelInit:删除指定的双向链表。
增加节点LOS_ListAdd:将指定节点插入到双向链表头端
LOS_ListTailInsert:将指定节点插入到双向链表尾端。
删除节点LOS_ListDelete:将指定节点从链表中删除。
LOS_ListDelInit:将指定节点从链表中删除,并使用该节点初始化链表。
判断双向链表是否为空LOS_ListEmpty:判断链表是否为空。
获取结构体信息LOS_DL_LIST_ENTRY:获取包含链表的结构体地址,接口的第一个入参表示的是链表中的某个节点,第二个入参是要获取的结构体名称,第三个入参是链表在该结构体中的名称。
LOS_OFF_SET_OF:获取指定结构体内的成员相对于结构体起始地址的偏移量。
遍历双向链表LOS_DL_LIST_FOR_EACH:遍历双向链表。
LOS_DL_LIST_FOR_EACH_SAFE:遍历双向链表,并存储当前节点的后继节点用于安全校验。
遍历包含双向链表的结构体LOS_DL_LIST_FOR_EACH_ENTRY:遍历指定双向链表,获取包含该链表节点的结构体地址。
LOS_DL_LIST_FOR_EACH_ENTRY_SAFE:遍历指定双向链表,获取包含该链表节点的结构体地址,并存储包含当前节点的后继节点的结构体地址。

开发流程

双向链表的典型开发流程:

  1. 调用LOS_ListInit/LOS_DL_LIST_HEAD初始双向链表。

  2. 调用LOS_ListAdd向链表插入节点。

  3. 调用LOS_ListTailInsert向链表尾部插入节点。

  4. 调用LOS_ListDelete删除指定节点。

  5. 调用LOS_ListEmpty判断链表是否为空。

  6. 调用LOS_ListDelInit删除指定节点并以此节点初始化链表。

说明:

编程实例

实例描述

本实例实现如下功能:

  1. 初始化双向链表。

  2. 增加节点。

  3. 删除节点。

  4. 测试操作是否成功。

示例代码

示例代码如下:

本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleList。

#include "stdio.h"
#include "los_list.h"

STATIC UINT32 ExampleList(VOID)
{
    LOS_DL_LIST listHead = {NULL,NULL};
    LOS_DL_LIST listNode1 = {NULL,NULL};
    LOS_DL_LIST listNode2 = {NULL,NULL};

    /* 初始化链表 */
    printf("Initial head\n");
    LOS_ListInit(&listHead);

    /* 添加节点1和节点2,并校验他们的相互关系 */
    LOS_ListAdd(&listHead, &listNode1);
    if (listNode1.pstNext == &listHead && listNode1.pstPrev == &listHead) {
        printf("Add listNode1 success\n");
    }

    LOS_ListTailInsert(&listHead, &listNode2);
    if (listNode2.pstNext == &listHead && listNode2.pstPrev == &listNode1) {
        printf("Tail insert listNode2 success\n");
    }

    /* 删除两个节点 */
    LOS_ListDelete(&listNode1);
    LOS_ListDelete(&listNode2);

    /* 确认链表为空 */
    if (LOS_ListEmpty(&listHead)) {
        printf("Delete success\n");
    }

    return LOS_OK;
}

 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

结果验证

编译运行得到的结果为:

Initial head 
Add listNode1 success 
Tail insert listNode2 success
Delete success 
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

如果大家想更加深入的学习 OpenHarmony(鸿蒙南向) 开发的全栈内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

系统架构分析:https://qr18.cn/CgxrRy

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/140881744","extend1":"pc","ab":"new"}">> id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension"> class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}"> class="blog_extension_card_left"> class="blog_extension_card_cont"> 鸿蒙开发学习资料领取!!! class="blog_extension_card_cont_r"> 微信名片
注:本文转载自blog.csdn.net的沧海一笑-dj的文章"https://blog.csdn.net/dengjin20104042056/article/details/97620868"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!