> 文档中心 > 华为云14天鸿蒙设备开发-Day3内核开发

华为云14天鸿蒙设备开发-Day3内核开发

目录

  • 前言
  • 一、CMSIS-RIOS2接口简介
  • 二、任务管理
    • 基本概念
    • 任务状态迁移
    • API函数
    • 实验案例
  • 三、定时器管理
    • 基本概念
    • 运作机制
    • API函数
    • 实验案例
  • 四、信号量管理
    • 基本概念
    • 运作机制
    • API函数
  • 五、互斥锁管理
    • 基本概念
    • 运行机制
    • API函数
  • 六、事件管理
    • 基本概念
    • 运行机制
    • API函数
    • 实验案例
  • 七、消息队列
    • 基本概念
    • 运行机制
    • API函数
    • 实验案例
  • 总结

前言

之前学STM32时,学习过liteOS,对内核有过简单了解。

LiteOS内核
这次学习鸿蒙,主要了解封装后的接口函数及其如何调用。


一、CMSIS-RIOS2接口简介

CMSIS是Cortex微控制器软件接口标准(Cortex Microcontroller Software Interface Standard)是ARM和一些编译器厂家以及半导体厂家共同遵循的一套标准,是由ARM专门针对Cortex-M系列提出的标准。在该标准的约定下, ARM和芯片厂商会提供一些通用的API接口来访问Cortex内核以及一些专用外设,以减少更换芯片以及开发工具等移植工作所带来的金钱以及时间上的消耗。
CMSIS-RTOS2(CMSIS-RTOS API Version 2)是Arm® Cortex®-M 处理器的通用的RTOS接口。为需要RTOS功能的软件组件提供了标准化的API。
CMSIS-RTOS2是一个通用的API,它与底层的RTOS内核无关,写应用程序的程序员在用户代码中调用CMSIS-RTOS2 API函数,可以更方便地将应用程序从一个RTOS到另一个RTOS,使用CMSIS-RTOS2 API的中间件也可以避免很多不必要的移植工作。
鸿蒙在CMSIS RTOS2 接口中封装了 LiteOS-m 的内核代码
CMSIS RTOS2 实现 :// kernel/liteos_m /kal /cmsis /cmsis_liteos2.c
鸿蒙与CMSIS接口的关系
华为云14天鸿蒙设备开发-Day3内核开发
使用时,包含cmsis_os2.h头文件。

二、任务管理

基本概念

从系统角度看,可把任务等同于线程。可把任务管理等同于多线程管理。Lite OS的任务默认有32个优先级(0-31),最低为0,最高为31。其他的可参考之前文章。

任务状态迁移

就绪态->运行态: 任务创建后进入就绪态,发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态, 但此刻该任务依旧在就绪列表中 。
运行态->阻塞态: 任务运行因挂起 、 读信号量等待等,在就绪列表中被删除进入阻塞 。
阻塞态->就绪态(阻塞态->运行态): 阻塞的任务被恢复后(任务恢复 、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;此时如果被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务由就绪态变成运行态 。
**就绪态->阻塞态:**任务也有可能在就绪态时被阻塞(挂起) 。
运行态->就绪态: 有更高优先级任务创建或者恢复后,发生任务切换而进入就绪列表 。
运行态->退出态: 任务运行结束 内核自动将此任务删除,此时由运行态变为退出态 。
阻塞态->退出态: 阻塞的任务调用删除接口,任务状态由阻塞态变为退出态 。

API函数

接口名 功能描述
osThreadNew 创建任务
osThreadTerminate 删除任务(非自身)
osThreadSuspend 任务挂起
osThreadResume 任务恢复

创建任务:

osThreadNew (osThreadFunc_t func,void * argument,const osThreadAttr_t * attr)
描述:
函数osThreadNew通过将线程添加到活动线程列表并将其设置为就绪状态来启动线程函数。线程函数的参数使用参数指针*argument传递。当创建的thread函数的优先级高于当前运行的线程时,创建的thread函数立即启动并成为新的运行线程。线程属性是用参数指针attr定义的。属性包括线程优先级、堆栈大小或内存分配的设置。可以在RTOS启动(调用osKernelStart)之前安全地调用该函数,但不能在内核初始化 (调用 osKernelInitialize)之前调用该函数。

注意 :不能在中断服务调用该函数

参数:

名字 描述
func 线程函数.
argument 作为启动参数传递给线程函数的指针
attr 线程属性

删除某个任务:
osThreadTerminate( osThreadId_t thread_id)'
任务挂起:
osThreadSuspend(osThreadId_t thread_id)
任务恢复:
osThreadResume(osThreadId_t thread_id)

实验案例

Gitee页面
页面的实验结果截图应该是错的,自己学习操作一下就好。

三、定时器管理

基本概念

软件定时器,是基于系统Tick 时钟中断且由软件来模拟的定时器,当经过设定的 Tick 时钟计数值后会触发用户定义的回调函数。定时精度与系统 Tick 时钟的周期有关。
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器, LiteOS 操作系统提供软件定时器功能。
软件定时器扩展了定时器的数量,允许创建更多的定时业务。
软件定时器功能上支持:

  • 静态裁剪:能通过宏关闭软件定时器功能。
  • 软件定时器创建。
  • 软件定时器启动。
  • 软件定时器停止。
  • 软件定时器删除。
  • 软件定时器剩余 Tick 数获取。

运作机制

软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
软件定时器以Tick 为基本计时单位,当用户创建并启动一个软件定时器时, Huawei LiteOS 会根据当前系统 Tick 时间及用户设置的定时间隔确定该定时器的到期 Tick 时间,并将该定时器控制结构挂入计时全局链表。
当Tick 中断到来时,在 Tick 中断处理函数中扫描软件定时器的计时全局链表,看是否有定时器超时,若有则将超时的定时器记录下来。
Tick中断处理函数结束后,软件定时器任务(优先级为最高)被唤醒,在该任务中调用之前记录下来的定时器的超时回调函数。

API函数

接口名 功能描述
osTimerNew 创建定时器
osTimerStart 启动定时器
osTimerStop 停止定时器
osTimerDelete 删除定时器

创建定时器:
osTimerNew (osTimerFunc_t func, osTimerType_t type, void * argument, const osTimerAttr_t *attr)
描述:
函数osTimerNew创建一个一次性或周期性计时器,并将其与一个带参数的回调函数相关联。计时器在osTimerStart启动之前一直处于停止状态。可以在RTOS启动(调用osKernelStart)之前安全地调用该函数,但不能在内核初始化 (调用 osKernelInitialize)之前调用该函数。

注意 :不能在中断服务调用该函数

参数:

名字 描述
func 函数指针指向回调函数.
type 定时器类型,osTimerOnce表示单次定时器,osTimerPeriodic周期表示周期性定时器.
argument 定时器回调函数的参数
attr 计时器属性

启动定时器:
osTimerStart (osTimerId_t timer_id, uint 32 _t ticks)
描述:
函数osTimerStart启动或重新启动指定参数timer_id的计时器。参数ticks指定计时器的计数值。

注意 :不能在中断服务调用该函数

参数:

名字 描述
timer_id 由osTimerNew获得的计时器ID.
ticks 时间滴答计时器的值.

停止定时器:
osTimerStop (osTimerId_t timer_id)
删除定时器:
osTimerDelete (osTimerId_t timer_id)

实验案例

Gitee页面
在Timer_example函数中,通过osTimerNew()函数创建了回调函数为Timer1_Callback的定时器1,并通过osTimerStart()函数将该定时器设置为100个tick,因为hi3861默认10ms为一个tick,所以100个tick正好为1S钟,1S计时到后会触发Timer1_Callback()函数并打印日志。定时器2也同理为3S触发Timer2_Callback()函数并打印日志.

四、信号量管理

基本概念

参照前言的文章。

运作机制

  1. 信号量初始化 为配置的 N 个信号量申请内存( N 值可以由用户自行配置 ,受内存限制), 并把所有的信号量初始化成未使用 ,并加入到未使用链表中供系统使用 。
  2. 信号量创建 从未使用的信号量链表中获取一个信号量资源 ,并设定初值 。
  3. 信号量申请 若其计数器值大于 0 则直接减 1 返回成功 。 否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾 。
  4. 信号量释放 若没有任务等待该信号量,则直接将计数器加 1 返回。否则唤醒该信号量等待任务队列上的第一个任务 。
  5. 信号量删除 将正在使用的信号量置为未使用信号量,并挂回到未使用链表 。
  6. 信号量允许多个任务在同一时刻访问同一资源 但会限制同一时刻访问此资源的最大任务数目。访问同一资源的任务数达到该资源的最大数量时,会阻塞其他试图获取该资源的任务, 直到有任务释放该信号量 。
    华为云14天鸿蒙设备开发-Day3内核开发
    公共资源有四个任务数,信号量都分别被线程1 、 2 、 3 、 4 获取后,此时此资源就会锁定而不让线程 5 进入 ,线程 5 及后面的线程都进入阻塞模式 ,当线程 1 工作完成而释放出信号量, 线程 5 立即获得信号而得到执行 。 如此往复 。

API函数

接口名 功能描述
osSemaphoreNew 创建信号量
osSemaphoreAcquire 获取信号量
osSemaphoreRelease 释放信号量
osSemaphoreDelete 删除信号量

创建信号量
osSemaphoreId_t osSemaphoreNew(uint32_t max_count,uint32_t initial_count,const osSemaphoreAttr_t *attr)

五、互斥锁管理

基本概念

互斥锁是一种特殊的二值信号量,用于实现对共享资源的独占处理。任意时刻互斥锁的状态只有两种:开锁或闭锁。当有 任务持有时 互斥锁处于闭锁状态,这个任务获得该互斥锁的所有权。当该 任务释放时 该互斥锁被开锁,任务失去该互斥锁的所有权 。互斥锁可以解决信号量存在的优先级翻转问题 。

运行机制

互斥锁运行机制与信号量类似。

API函数

接口名 功能描述
osMutexNew 创建互斥锁
osMutexAcquire 获取互斥锁
osMutexRelease 释放互斥锁
osMutexDelete 删除互斥锁

创建信号量
osMutexId_t osMutexNew(const osMutexAttr_t *attr)

六、事件管理

基本概念

事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输。一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。事件集合用 32 位无符号整型变量来表示,每一位代表一个事件。
多任务环境下,任务之间往往需要同步操作。事件可以提供一对多、多对多的同步操作。一对多同步模型:一个任务等待多个事件的触发;多对多同步模型:多个任务等待多个事件的触发。
任务可以通过创建事件控制块来实现对事件的触发和等待操作。
LiteOS 的事件仅用于任务间的同步,

运行机制

读事件时,可以根据入参事件掩码类型uwEventMask 读取事件的单个或者多个事件类型。事件读取成功后,如果设置 LOS_WAITMODE_CLR 会清除已读取到的事件类型,反之不会清除已读到的事件类型,需显式清除。可以通过入参选择读取模式,读取事件掩码类型中所有事件还是读取事件掩码类型中任意事件。
写事件时,对指定事件写入指定的事件类型,可以一次同时写多个事件类型。写事件会触发任务调度。
清除事件时,根据入参事件和待清除的事件类型,对事件对应位进行清 0 操作。
华为云14天鸿蒙设备开发-Day3内核开发

API函数

接口名 功能描述
osEventFlagsNew 创建事件标记对象
osEventFlagsSet 设置事件标记
osEventFlagsWait 等待事件标记触发
osEventFlagsDelete 删除事件标记对象

创建事件标记对象
osEventFlagsId_t osEventFlagsNew(const osEventFlagsAttr_t *attr)
描述:
osEventFlagsNew函数创建了一个新的事件标志对象,用于跨线程发送事件,并返回事件标志对象标识符的指针,或者在出现错误时返回NULL。可以在RTOS启动(调用osKernelStart)之前安全地调用该函数,但不能在内核初始化 (调用 osKernelInitialize)之前调用该函数。
设置事件标记
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id,uint32_t flags)
描述:
osEventFlagsSet函数在一个由参数ef_id指定的事件标记对象中设置由参数flags指定的事件标记。
参数:

名字 描述
ef_id 事件标志由osEventFlagsNew获得的ID.
flags 指定设置的标志.

等待事件标记触发
uint32_t osEventFlagsWait(osEventFlagsId_t ef_id,uint32_t flags,uint32_t options,uint32_t timeout)
描述:
osEventFlagsWait函数挂起当前运行线程,直到设置了由参数ef_id指定的事件对象中的任何或所有由参数flags指定的事件标志。当这些事件标志被设置,函数立即返回。否则,线程将被置于阻塞状态。
参数:

名字 描述
ef_id 事件标志由osEventFlagsNew获得的ID.
flags 指定要等待的标志.
options 指定标记选项.
timeout 超时时间,0表示不超时

实验案例

Gitee页面
在Event_example函数中,通过osEventFlagsNew()函数创建了事件标记ID,Thread_EventReceiver()函数中通过osEventFlagsWait()函数一直将线程置于阻塞状态,等待事件标记。在Thread_EventSender()函数中通过osEventFlagsSet()函数每隔1S设置的标志,实现任务间的同步。

七、消息队列

基本概念

消息队列
是一种常用于任务间通信的数据结构。实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间 。 任务能够从队列里面读取消息,当队列中的消息是空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息 。
用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用 。
LiteOS中使用队列数据结构实现任务异步通信工作。具有如下特性:

  • 消息以先进先出方式排队,支持异步读写工作方式 。
  • 读队列和写队列都支持超时机制 。
  • 发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消息 。
  • 一个任务能够从任意一个消息队列接收和发送消息 。
  • 多个任务能够从同一个消息队列接收和发送消息 。
  • 当队列使用结束后 如果是动态申请的内存 需要通过释放内存函数回收。

运行机制

华为云14天鸿蒙设备开发-Day3内核开发

创建队列时根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用 返回队列 ID。
在队列控制块中维护一个消息头节点位置Head 和一个消息尾节点位置 Tail 来表示当前队列中消息存储情况。Head 表示队列中被占用消息的起始位置。Tail 表示队列中被空闲消息的起始位置。刚创建时 Head 和 Tail 均指向队列起始位置。
写队列时根据 Tail 找到被占用消息节点末尾的空闲节点作为数据写入对象 。
读队列时根据 Head 找到最先写入队列中的消息节点进行读取 。
删除队列时根据传入的队列 ID 寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。

API函数

接口名 功能描述
osMessageQueueNew 创建消息队列
osMessageQueuePut 发送消息
osMessageQueueGet 获取消息
osMessageQueueDelete 删除消息队列

创建消息队列
osMessageQueueId_t osMessageQueueNew(uint32_t msg_count,uint32_t msg_size,const osMessageQueueAttr_t *attr)
描述:
函数osMessageQueueNew创建并初始化一个消息队列对象。该函数返回消息队列对象标识符,如果出现错误则返回NULL,可以在RTOS启动(调用osKernelStart)之前安全地调用该函数,但不能在内核初始化 (调用 osKernelInitialize)之前调用该函数。
参数:

名字 描述
msg_count 队列中的最大消息数.
msg_size 最大消息大小(以字节为单位).
attr 消息队列属性;空:默认值.

发送消息
osStatus_t osMessageQueuePut(osMessageQueueId_t mq_id,const void *msg_ptr,uint8_t msg_prio,uint32_t timeout)
描述:
函数osMessageQueuePut将msg_ptr指向的消息放入参数mq_id指定的消息队列中。
参数:

名字 描述
mq_id 由osMessageQueueNew获得的消息队列ID.
msg_ptr 要发送的消息.
msg_prio 消息优先级.
timeout 超时值.

获取消息
osStatus_t osMessageQueueGet(osMessageQueueId_t mq_id,void *msg_ptr,uint8_t *msg_prio,uint32_t timeout)
描述:
函数osMessageQueueGet从参数mq_id指定的消息队列中检索消息,并将其保存到参数msg_ptr所指向的缓冲区中。
参数:

名字 描述
mq_id 由osMessageQueueNew获得的消息队列ID.
msg_ptr 要发送的消息.
msg_prio 消息优先级.
timeout 超时值.

实验案例

Gitee页面
在Message_example函数中,通过osMessageQueueNew()函数创建了消息队列ID,Thread_MsgQueue1()函数中通过osMessageQueuePut()函数向消息队列中发送消息。在Thread_MsgQueue2()函数中通过osMessageQueueGet()函数读取消息队列中的消息比打印出来。

总结

之前学习的时候,没有太了解事件和消息队列,这两个应该是OS独有的功能。可做深入了解。