【开发实录】在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呢?但我现在能力有限,也对这部分代码并没有更多的了解,期待以后能知道更多。
👍👍👍
是很有疑问啊
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;
}