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

OSAaaa
发布于 2020-11-3 23:30
浏览
4收藏

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呢?但我现在能力有限,也对这部分代码并没有更多的了解,期待以后能知道更多。

分类
代码.rar 4.45K 49次下载
已于2020-11-3 23:30:11修改
5
收藏 4
回复
举报
2条回复
按时间正序
/
按时间倒序
鸿蒙张荣超
鸿蒙张荣超

👍👍👍

回复
2021-3-31 17:19:32
鸿蒙开发
鸿蒙开发

是很有疑问啊

hi_u32 hi_task_create(hi_u32 *taskid, const hi_task_attr *attr, hi_void *(*task_route)(hi_void *), hi_void *arg)

{

TSK_INIT_PARAM_S my_task = { 0, };

hi_u32 ret;

 

/* 内核接口对输入参数有判断,此处省略 */

if (taskid == HI_NULL || task_route == HI_NULL) {

return HI_ERR_TASK_INVALID_PARAM;

}

 

if (attr != NULL) {

my_task.pcName = attr->task_name;

my_task.uwStackSize = attr->stack_size;

my_task.usTaskPrio = attr->task_prio;

} else {

my_task.pcName = HI_DEFAULT_TSKNAME;

my_task.uwStackSize = HI_DEFAULT_STACKSIZE;

my_task.usTaskPrio = HI_DEFAULT_TSKPRIO;

}

/* user task priority is limit bteween 1 and 30. */

if (my_task.usTaskPrio == OS_TASK_PRIORITY_HIGHEST || my_task.usTaskPrio == OS_TASK_PRIORITY_LOWEST) {

my_task.usTaskPrio = 20; /* normal, usTaskPrio should be [20-30]. */

}

 

if (my_task.uwStackSize == 0) {

my_task.uwStackSize = HI_DEFAULT_STACKSIZE;

}

 

my_task.uwResved = LOS_TASK_STATUS_DETACHED;

my_task.pfnTaskEntry = (TSK_ENTRY_FUNC)task_route;

my_task.auwArgs[0] = (hi_u32)(uintptr_t)arg;

#ifdef LOSCFG_KERNEL_SMP

my_task.usCpuAffiMask = CPUID_TO_AFFI_MASK(0);

#endif

ret = LOS_TaskCreate(taskid, &my_task);

if (ret != LOS_OK) {

return HI_ERR_TASK_CREATE_FAIL;

}

 

return HI_ERR_SUCCESS;

}

回复
2021-9-1 09:32:44
回复
    相关推荐