HarmonyOS技术社区 · 2020年11月04日

【开发实录】在Hi3861开发板上创建线程(三种方式)

Hi3861上开发应用时推荐使用CMSIS标准的API,详细API查看api/api-LinkIoT/CMSIS.md · OpenHarmony/docs - Gitee.com

技术有限,如有错误还望不吝赐教。

基础:完成官方的快速入门教程

那天在看Hi3861的源码的时候发现在目录:

//project/vendor/hisi/hi3861/hi3861/platform/os/Huawei_LiteOS/components/lib/libc/musl/include

包含了musl的头文件,并且在编译脚本里面有此目录,难道Hi3861支持POSIX标准(Linux的API也遵循POSIX标准)?后来在kernel/标准库-0.md · OpenHarmony/docs - Gitee.com这一章中看到

OpenHarmony内核使用musl libc库,支持标准POSIX接口,开发者可基于POSIX标准接口开发内核之上的组件及应用。

我就以为应该支持的,但弄了挺久之后发现源码中只包含了这些API的头文件,并没有实现源码或者编译后的库,这就有点奇怪了。在gitee逛了挺久的,后来在readme/系统服务框架子系统README.md · OpenHarmony/docs - gitee.com中发现

M核:处理器架构为Cortex-M或同等处理能力的硬件平台,系统内存一般低于512KB,无文件系统或者仅提供一个可有限使用的轻量级文件系统,遵循CMSIS接口规范。

虽然Hi3861使用的是riscv架构(不是m核),但配置方面没达到Liteos-a的要求,所以系统还是liteos-m。那Hi3861应该时只支持CMSIS标准了,但不知道为什么源码里面包含了musl,不知道会不会是做了POSIX适配层,还没释放出来。

那既然是支持CMSIS标准,那就用CMSIS的API来创建一个线程吧,其实例程里面的LED灯demo就是使用CMSIS的。主要的代码:

  osThreadAttr_t attr;

    attr.name = "LedTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = LED_TASK_STACK_SIZE;
    attr.priority = LED_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL) {
        printf("[LedExample] Falied to create LedTask!\n");
    }

创建一个osThreadAttr_t结构体变量,这个结构体主要是包括了线程的各种参数,例如任务名,栈大小、优先级等等都是。然后使用osThreadNew函数创建一个线程。

可以在

//kernel/liteos_m/components/cmsis/2.0

看一下osThreadNew的实现源码

osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
{
    ......
    TSK_INIT_PARAM_S stTskInitParam;
    ......
    stTskInitParam.usTaskPrio = OS_TASK_PRIORITY_LOWEST - ((UINT16)(attr->priority) - LOS_PRIORITY_WIN); /* 0~31 */

    uwRet = LOS_TaskCreate(&uwTid, &stTskInitParam);
    ......
}

可以看到osThreadNew调用的是最原生的Liteos API,从上面的代码可以看到,LiteOS的优先级和CMSIS的优先级不是一一对应的,转换关系看第三条代码,开发的时候需要注意一下。

那如果我们使用原生的代码创建线程(任务)会是怎样的呢:

    TSK_INIT_PARAM_S stInitParam = {0};

    stInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)LedTask;
    stInitParam.usTaskPrio = LED_TASK_PRIO;
    stInitParam.pcName = "LedTask";
    stInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    stInitParam.uwResved  = LOS_TASK_STATUS_DETACHED;
    
    if (LOS_TaskCreate(&s_uwTskID, &stInitParam) != LOS_OK) {
        printf("Example_Task1 create Failed!\r\n");
    }

其实大部分代码都差不多,只是需要注意的只是优先级不同。

LiteOS的原生API一般推荐写内核代码,不推荐用来写应用代码,但如果我们多看一下源码,其实里面很大一部分没使用LiteOS原生API也没使用CMSIS标准的API,而是使用了hi开头的API。我在官方提供的Hi3861的api文档里面发现,里面的就是这一套API。比如,如果用这套API实现创建线程的话会是这样的:

    hi_task_attr attr;

    attr.stack_size = LED_TASK_STACK_SIZE; /* 800 */
    attr.task_name = (hi_char*) "LedTask";
    attr.task_prio = LED_TASK_PRIO; /* 28 */
    hi_task_create(&g_test_flash_tb, &attr, LedTask, 0);

其实各套API代码内容都是差不多的。

我在源码里面并没有发现hi_task_create的实现源码,这个应该是以库文件的形式存在的。有个问题是为什么已经有los API,还弄了一套hi API呢,我找的资料不够多,也可能是之前没有深入地了解过,还没找到这个答案。

那总共三套API,一般是开发应用层面使用CMSIS接口,内核则使用hi接口(其实我挺希望有POSIX的)。

其实还有一个有趣的地方,在文件中

//foundation/distributedschedule/services/samgr_lite/samgr/adapter/posix/thread_adapter.c

//foundation/distributedschedule/services/samgr_lite/samgr/adapter/cmsis/thread_adapter.c

分别对CMSIS和POSIX接口再封装了一层,例如:

ThreadId THREAD_Create(Runnable run, void *argv, const ThreadAttr *attr)
{
    osThreadAttr_t taskAttr = {attr->name, 0, NULL, 0, NULL, attr->stackSize, attr->priority, 0, 0};
    return (ThreadId)osThreadNew((osThreadFunc_t)run, argv, &taskAttr);
}

相当于用一个API接口适配了CMSIS和POSIX接口,那就意味着也同时适配了hi和los接口,那是不是说明以后还想再推一套API呢?但我现在能力有限,也对这部分代码并没有更多的了解,期待以后能知道更多。

获取源码资源包


作者:OSAaaa

想了解更多内容,请访问:
51CTO和华为官方战略合作共建的鸿蒙技术社区
https://harmonyos.51cto.com#jssq

推荐阅读
关注数
3010
内容数
446
华为鸿蒙相关技术,活动及资讯,欢迎关注及加入创作
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息