当前位置:   article > 正文

LiteOS移植_truncate liteos

truncate liteos

                                          LiteOS移植

                                                                           象棋小子    1048272975

操作系统是用来管理系统硬件、软件及数据资源,控制程序运行,并为其它应用软件提供支持的一种系统软件。根据不同的种类,又可分为实时操作系统、桌面操作系统、服务器操作系统等。对于一些小型的应用,对系统实时性要求高,硬件资源有限等的情况下,应尽量避免使用复杂庞大的操作系统,使用小型的实时操作系统更能满足应用的需求。笔者此处就华为LiteOS物联网操作系统的移植作一个简单的介绍。

1. LiteOS概述

LiteOS是华为针对物联网领域推出的轻量级物联网操作系统,具有轻量级、低功耗、互联互通等特点,广泛应用于可穿戴设备、智能家居、车联网等领域。

LiteOS源码下载地址,https://github.com/LiteOS/LiteOS。源码主要有以下几个目录:

LiteOS源码目录

arch目录,该目录包含跟CPU体系结构相关的代码,LiteOS专为小内核架构设计,可以满足硬件资源受限的应用。当前的源码仅支持功耗、成本敏感的ARM Cortex-M以及MSP430这两种架构CPU。本文档以ARM7、ARM9、ARM11、Cortex-A的CPU架构为例,说明arch目录下CPU架构相关的接口移植。

components目录,该目录包含了一系列的组件。端云互通组件connectivity集成了LwM2M、nb_iot、mqtt等IoT互联互通协议栈。文件系统组件fs集成了vfs虚拟文件系统、fatfs、devfs等适合嵌入式使用的文件系统。net组件集成了lwip TCP/IP协议栈、at指令设备等。ota组件支持设备远程下载升级等等。

demos目录,该目录包含了LiteOS、components组件Demo代码。

doc目录,该目录包含了相应的文档说明。

include目录,该目录包含了components组件使用的头文件。

kennel目录,该目录包含LiteOS内核头文件和源码。

osdepends目录,该目录实现LiteOS封装CMSIS-RTOS接口标准,兼容CMSIS-RTOS应用程序。

targets目录,该目录包含了已经实现运行的相关板级工程。

tests目录,该目录包含了相应的测试代码。

2. 代码准备

Bootloader工程,Bootloader是s3c2416/50/51这系列arm9芯片在运行c代码main函数之前必须先运行的代码,启动代码支持sd、Nand启动,设置系统时钟,初始化内存,自动识别启动设备并搬移代码到RAM,MMU映射,中断管理等,只需专注于用c开发其它功能函数即可。关于Bootloader以及Bootloader的实现过程,笔者前面章节有非常详细的介绍。此处以MDK下移植LiteOS为讲解。

应用代码,用c开发的所有功能代码,其中,应用代码入口为main()函数,在这里实现LiteOS多任务运行代码。

3. LiteOS移植

3.1. LiteOS内核代码

把kernel目录拷贝到MDK工程,添加该目录如下C文件。

los_init.c文件。

base/core目录下所有C文件。

base/ipc目录下所有C文件。

base/mem/bestfit_little目录下所有C文件。

base/mem/common目录下所有C文件。

base/mem/membox目录下所有C文件。

base/misc目录下所有C文件

base/om目录下所有C文件

extended/tickless目录下所有C文件

内核文件

配置工程内核头文件路径。

内核头文件路径

3.2. OS_CONFIG目录

OS_CONFIG目录包含三个文件los_builddef.h、los_printf.h、target_config.h,其中los_builddef.h定义了内核编译的数据结构布局,相应的内核数据结构放在内核空间等等,无特殊需求可直接采用其它板级实现的该文件。los_printf.h定义了内核警告打印、报警输出等内核调试宏实现,可直接采用其它板级实现的文件。target_config.h为目标板级配置内核文件,该文件可以配置内核Tick、任务数、内存地址及大小等等,LiteOS是可裁减实时操作系统,可以根据实际的应用对内核未使用到的功能进行裁减以及配置,以进一步节省系统宝贵的硬件资源。

OS_CONFIG目录

3.3. arch目录

LiteOS内核管理需要切换任务上下文,开关中断禁止任务抢占保护临界区,内核超时、计时、延时等时间处理需要硬件定时器实现系统Tick。这些都是跟CPU架构相关的,需要进行移植,移植文件如下:

arch目录

3.3.1. los_hwi.h/los_hwi.c

los_hwi.h/los_hwi.c,该文件处理硬件中断相关的接口实现,如果配置LiteOS接管硬件中断,内核提供一套标准的中断管理API,实现中断初始化、中断注册、中断注销等操作。如果没有配置LiteOS接管硬件中断,或者应用层不使用这一套标准的中断管理接口,则该文件接口并不是必须实现的。

3.3.2. los_hw_tick.h/los_hw_tick.c

内核需要一个周期性硬件定时中断作为LiteOS的运行Tick,每个Tick需调用osTickHandler()来让内核管理时钟节拍。如果使能了内核Tickless功能,还需要实现重配置硬件定时器的接口,跟硬件定时器相关的接口在los_hw_tick.h/los_hw_tick.c文件实现。

3.3.2.1. osTickStart()接口

内核硬件定时器开启接口为osTickStart(),在接口中配置Timer4每秒钟产生100次(LOSCFG_BASE_CORE_TICK_PER_SECOND)系统Tick。

3.3.3. los_hw.h/los_hw.c

内核需要实现任务调度、任务上下文切换、任务栈初始化、低功耗待机睡眠实现、开关中断,这些都是跟具体的硬件息息相关的。在los_hw.h/los_hw.c中实现相应的接口,由于任务上下文切换、开关中断,需要操作CPU内部寄存器,使用汇编语言才能处理,在los_dispatch_keil.S文件中实现这些汇编接口。

3.3.3.1. LOS_Schedule()接口

该接口决定任务是否进行调度,任务同步事件、IPC通信事件等可能会使更高优先级的任务就绪,从而需要调度。如果在中断执行过程中使高优先级任务就绪,此时只有中断上下文,不允许进行任务的调度。其他情况,只要有更高优先级的任务就绪,调度未上锁,则进行任务调度。

3.3.3.2. osTskStackInit()接口

该接口用来初始化任务栈,使创建的任务具有可运行的任务上下文,任务上下文入栈与出栈必须一一对应。

3.3.3.3. osEnterSleep()接口

LiteOS作为一款物联网操作系统,可以满足不同设备的低功耗需求。如果内核配置了低功耗选项,需要实现osEnterSleep()接口,内核在Idle任务将调用osEnterSleep()进入低功耗模式,等待中断、外部唤醒事件使CPU再次进入运行状态。

3.3.4. los_dispatch_keil.S

任务上下文切换、中断上下文切换、开关中断,需要操作CPU内部寄存器,使用汇编语言才能处理,在los_dispatch_keil.S文件中实现这些汇编接口。

3.3.4.1. LOS_IntLock()和LOS_IntRestore()接口

对于可抢占式操作系统,有一小段关键代码必须独占访问,如果有一个任务正在访问临界代码,则其它任务不能再进入该段代码,直到占有访问权的任务退出这个临界区。LiteOS在访问内核临界区时是通过LOS_IntLock()/LOS_IntRestore()这两个接口来确保临界区不被破坏。

3.3.4.2. LOS_StartToRun()接口

当用户通过LOS_Start()启动LiteOS内核进行管理时,LOS_Start ()会首先调用LOS_StartToRun ()来运行已创建任务中优先级最高的任务,LOS_StartToRun ()需完成以下工作:

(1) 禁止中断切换到管理模式,所有任务均工作在管理模式

(2) 标记LiteOS内核已启动运行,g_bTaskScheduled = 1

(3) 获取全局任务结构,把最新任务TCB设置为当前运行TCB

(4) 设置新的运行任务为运行状态

(5) 获取新运行任务栈指针,SP切换到任务栈

(6) 出栈SP中的任务栈,包括任务状态寄存器CPSR,R0-R12,LR,继续执行任务

3.3.4.3. osSchedule()接口

LiteOS通过osSchedule ()函数进行任务的调度,任务切换函数osSchedule ()需完成以下的工作:

(1) 保存当前任务的上下文(R0-R12,LR,任务打断的PC地址,状态寄存器CPSR)到当前任务栈中

(2) 如果任务切换钩子函数不为空,先调用g_pfnTskSwitchHook()钩子函数

(3) 由全局任务结构,获得运行任务栈指针,并把运行任务SP栈保存进栈指针

(4) 清除当前运行任务的运行状态

(5) 把新任务TCB设置为当前运行TCB

(6) 设置新的运行任务为运行状态

(7) 获取新运行任务栈指针,SP切换到任务栈

(8) 出栈新任务的上下文,执行新任务

3.3.4.4. IRQ_SaveContext()接口

任何异常发生时,均会打断任务,进入异常应先保存当前任务的上下文到当前任务栈中,之后再执行异常处理。IRQ异常也不例外,因为LiteOS需要一个定时器中断Tick,因此IRQ处理也是移植的一部分,IRQ_SaveContext()需完成以下工作:

(1) 临时性使用到一些寄存器,对用到的寄存器压栈到IRQ栈上

(2) 切换到管理模式,禁止中断,任务运行在管理模式,这步将切换SP到被中断打断的任务栈上

(3) 把被打断任务的上下文压入任务的栈

(4) 跟踪中断嵌套计数,判断是任务被中断还是中断嵌套,中断嵌套不用更新任务栈

(5) 非中断嵌套,根据当前任务TCB(任务控制块)获得栈指针,并把打断任务SP栈保存进栈指针

(6) 中断嵌套计数加1

(7) 切换到系统模式,并压栈LR,这步是为了使用系统模式栈来处理中断函数,减轻任务栈的使用

(8) 调用IRQ_Handler()函数实质处理IRQ中断服务,在中断服务中可再打开IRQ中断,支持中断嵌套

(9) 中断服务执行完后,出栈LR,并切换到管理模式,禁止中断,此时SP将切换到被打断任务的任务栈上

(10) 中断嵌套计数减1,如果中断嵌套计数OSIntNesting为0,则说明所有中断退出,将调用LOS_Schedule()进行新任务切换,继续执行任务

(11) 如果中断嵌套计数OSIntNesting不为0,中断未全部退出,则出栈上一个中断的上下文,执行被嵌套的上一级中断

4. 用户代码

在main()函数中需调用LOS_KernelInit()初始化内核,创建任务后,再调用LOS_Start()把CPU管理权交给内核。内核即可正确地管理用户的任务。

  1. #include "ProjectConfig.h"
  2. #include "Speed.h"
  3. #include "Uart.h"
  4. #include "Timer.h"
  5. #include "Gpio.h"
  6. #include "math.h"
  7. #include "los_base.h"
  8. #include "los_task.ph"
  9. #include "los_typedef.h"
  10. #include "los_sys.h"
  11. static UINT32 g_start_tskHandle;
  12. static UINT32 g_task1_tskHandle;
  13. static UINT32 g_task2_tskHandle;
  14. static UINT32 g_task3_tskHandle;
  15. void Task3(void *pdata)
  16. {
  17.       (void)pdata;
  18.       while (1) {
  19.             Gpio_LED3(1);
  20.             LOS_TaskDelay(100); // LED3 1000ms闪烁
  21.             Gpio_LED3(0);
  22.             LOS_TaskDelay(100); // LED3 1000ms闪烁      
  23.       }
  24. }
  25. void Task2(void *pdata)
  26. {
  27.       int n;    
  28.       (void)pdata;
  29.       n = 1;
  30.       while (1) {
  31.             printf("Task2: n=%d, square root %f\n", n, sqrt(n));
  32.             n += 1;
  33.             LOS_TaskDelay(700);
  34.       }
  35. }
  36. void Task1(void *pdata)
  37. {
  38. #define PI 3.14159f  
  39.       float r;  
  40.       (void)pdata;
  41.       r = 1.0f;
  42.       while (1) {
  43.             printf("Task1: r=%.1f, s=%.2f\n", r, PI*r*r);
  44.             r += 1.0f;
  45.             LOS_TaskDelay(200);
  46.       }
  47. }
  48. void TaskStart(void)
  49. {
  50. UINT32 uwRet;
  51.   TSK_INIT_PARAM_S task_init_param;
  52.       Gpio_Init();
  53.      
  54. task_init_param.usTaskPrio = 1;
  55. task_init_param.pcName = "task1";
  56. task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Task1;
  57. task_init_param.uwStackSize = 0x1000;
  58. uwRet = LOS_TaskCreate(&g_task1_tskHandle, &task_init_param);
  59. if (uwRet != LOS_OK) {
  60. printf("task1 failed\n");
  61. }
  62.      
  63. task_init_param.usTaskPrio = 1;
  64. task_init_param.pcName = "task2";
  65. task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Task2;
  66. task_init_param.uwStackSize = 0x1000;
  67. uwRet = LOS_TaskCreate(&g_task2_tskHandle, &task_init_param);
  68. if (uwRet != LOS_OK) {
  69. printf("task2 failed\n");
  70. }
  71.      
  72. task_init_param.usTaskPrio = 1;
  73. task_init_param.pcName = "task3";
  74. task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Task3;
  75. task_init_param.uwStackSize = 0x1000;
  76. uwRet = LOS_TaskCreate(&g_task3_tskHandle, &task_init_param);
  77. if (uwRet != LOS_OK) {
  78. printf("task3 failed\n");
  79. }
  80.      
  81. while (1) {
  82. Gpio_LED2(1);
  83. LOS_TaskDelay(50); // LED2 500ms闪烁
  84. Gpio_LED2(0);
  85. LOS_TaskDelay(50); // LED2 500ms闪烁
  86. }
  87. }
  88. int  main (void)
  89. {   
  90. UINT32 uwRet;
  91. TSK_INIT_PARAM_S task_init_param;
  92.      
  93. Uart_Init();
  94. printf("CPU:   S3C2416@%dMHz\n", get_ARMCLK()/1000000);
  95.       printf("       Fclk = %dMHz, Hclk = %dMHz, Pclk = %dMHz\n",
  96.                   get_FCLK()/1000000, get_HCLK()/1000000, get_PCLK()/1000000);
  97.      
  98. uwRet = LOS_KernelInit();
  99. if (uwRet != LOS_OK) {
  100. return LOS_NOK;
  101. }
  102.      
  103. task_init_param.usTaskPrio = 0;
  104. task_init_param.pcName = "start";
  105. task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskStart;
  106. task_init_param.uwStackSize = 0x1000;
  107. uwRet = LOS_TaskCreate(&g_start_tskHandle, &task_init_param);
  108. if(LOS_OK != uwRet) {
  109. return uwRet;
  110. }
  111. LOS_Start();
  112. return 0;
  113. }

运行LiteOS

5. 附录

本篇LiteOS接口部分的移植对于ARM7、ARM9、ARM11、Cortex-A都是适用的,不同型号的CPU只需加入定时器产生系统Tick,通过调用osTickHandler()来让内核管理时钟节拍。总的来说,熟悉一款操作系统的工作原理,了解其任务调度、信号量同步、临界区访问等概念,对学习其它操作系统、多线程编程等均是有很较大的帮助的。

https://pan.baidu.com/s/19Xv3VESKKF88RAJJ6LSqkw

提取码:wvy8

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/226299
推荐阅读
相关标签
  

闽ICP备14008679号