大家好,小编为大家解答华为python语言通用编程规范的问题。很多人还不知道python云计算有哪些岗位,如今让我们一起来看看吧!
Huawei LiteOS 开发指南
目 录
- 1.知识共享许可协议阐明
- 2.前言
- 3.概述
- 4.底子内核
- 4.1.使命
- 4.2.内存
- 4.3.中断机制
- 4.4.队列
- 4.5.变乱
- 4.6.互斥锁
- 4.7.信号量
- 4.8.时间管理
- 4.9.软件定时器
- 4.10.错误处置惩罚
- 4.11.双向链表
1.知识共享许可协议阐明
您可以自由地:
分享 在任何前言以任何情势复制、发行本文档
演绎 修改、转换或以本文档为底子举行创作
只要你遵守许可协议条款,许可人就无法收回你的这些权利
惟须遵守下列条件:
署名 您必须提供得当的证书,提供一个链接到许可证,并指示是否作出更改。您可以以任何合理的方式这样做,但不是以任何方式表明,许可方附和您或您的使用python简朴代码画皮卡丘。
非商业性使用 您不得将本文档用于商业目的。
相同方式共享 假如您的修改、转换,或以本文档为底子举行创作,仅得依本素材的授权条款来散布您的贡献作品。
没有附加限制 您不能增设法律条款或科技步伐,来限制别人依授权条款本已许可的作为。
声明:
当您使用本素材中属于公众领域的元素,或当法律有破例或限制条款答应您的使用,则您不需要遵守本授权条款。
未提供保证。本授权条款未必能完全提供您预期用途所需要的所有许可。例如:形象权、隐私权、著作人格权等其他权利,大概限制您怎样使用本素材。
须知:
为了方便用户明白,这是协议的概述。可以访问网址CC BY-NC-SA 3.0 Legal Code | Attribution-NonCommercial-ShareAlike 3.0 Unported | Creative Commons了解完整协议内容。
2.前言
目的
本文档介绍Huawei LiteOS的体系结构,并介绍怎样举行开发和调试。
读者对象
本文档主要实用于Huawei LiteOS的开发者,主要实用于以下对象:
符号约定
在本文中大概出现下列标志,它们所代表的寄义如下。
符号阐明用于警示紧急的危险情况,若不避免,将会导致人员死亡或严重的人身伤害用于警示潜在的危险情况,若不避免,大概会导致人员死亡或严重的人身伤害用于警示潜在的危险情况,若不避免,大概会导致中度或轻微的人身伤害用于转达设备或环境安全警示信息,若不避免,大概会导致设备损坏、数据丢失、设备性能降低或别的不可预知的结果,“注意”不涉及人身伤害“阐明”不是安全警示信息,不涉及人身、设备及环境伤害信息 3.概述
Huawei LiteOS 是华为面向物联网领域开发的一个基于实时内核的轻量级使用系统。底子内核支持使命管理、内存管理、时间管理、通讯机制、中断管理、队列管理、变乱管理、定时器等使用系统底子组件,更好地支持低功耗场景,支持 Tickless 机制,支持定时器对齐。
Huawei LiteOS 自开源社区发布以来,围绕 NB-IoT 物联网市场从技能、生态、解决方案、商用支持等多维度使能合作伙伴,构建开源的物联网生态,现在已经聚合了 50+ MCU 和解决方案合作伙伴,共同推出一批开源开发套件和行业解决方案,帮助浩繁行业客户快速的推出物联网终端和服务,客户涵盖抄表、停车、路灯、环保、共享单车、物流等浩繁行业,为开发者提供 “一站式” 完整软件平台,有效降低开发门槛、缩短开发周期。
背景介绍
Huawei LiteOS是轻量级的实时使用系统,是华为IoT OS。
图 1 Huawei LiteOS Kernel的根本框架图
Huawei LiteOS底子内核是最精简的Huawei LiteOS使用系统代码,包括使命管理、内存管理、时间管理、通讯机制、中断管理、队列管理、变乱管理、定时器等使用系统底子组件,可以单独运行。
Huawei LiteOS Kernel的优势
- 高实时性,高稳定性。
- 超小内核,底子内核体积可以裁剪至不到10K。
- 低功耗。
- 支持功能静态裁剪。
各模块简介
使命
提供使命的创建、删除、耽误、挂起、规复等功能,以及锁定和解锁使命调理。支持使命按优先级高低的抢占调理及同优先级时间片轮转调理。
使命同步
- 信号量:支持信号量的创建、删除、申请和释放等功能。
- 互斥锁:支持互斥锁的创建、删除、申请和释放等功能。
硬件相关
提供中断、定时器等功能。
- 中断:提供中断的创建、删除、使能、克制、哀求位的清除等功能。
- 定时器:提供定时器的创建、删除、启动、制止等功能。
IPC通讯
提供变乱、消息队列功能。
- 变乱:支持读变乱和写变乱功能。
- 消息队列:支持消息队列的创建、删除、发送和接收功能。
时间管理
- 系统时间:系统时间是由定时/计数器产生的输出脉冲触发中断而产生的。
- Tick时间:Tick是使用系统调理的根本时间单元,对应的时长由系统主频及每秒Tick数决定,由用户配置。
- 软件定时器:以Tick为单元的定时器功能,软件定时器的超时处置惩罚函数在系统创建的Tick软中断中被调用。
内存管理
- 提供静态内存和动态内存两种算法,支持内存申请、释放。现在支持的内存管理算法有固定大小的BOX算法、动态申请SLAB、DLINK算法。
- 提供内存统计、内存越界检测功能。
支持的核
表 1 Huawei LiteOS开源Kernel支持的核
支持的核
芯片
Cortex-A7
Hi3516A V300
Hi3516C V500
Hi3516E V200
Hi3516E V300
Hi3516D V200
Hi3516D V300
Hi3518E V300
Hi3536
Hi3556 V100
Hi3556 V200
Hi3559 V200
Hi3716M V430
Cortex-A17
Hi3536
Hi3556 V100
Hi3559 V100
Cortex-A53
Hi3519A V100
Hi3556A V100
Hi3559A V100
Hi3796M V200
Hi3798C V200
Hi3798M V200
Hi3798M V300
Hi3798M V310
Cortex-M0
STM32L053R8Tx
ATSAMD21G18A
ATSAMD21J18A
ATSAMR21G18A
EFM32HG322F64
MKL26Z128
MKW41Z512
LPC824M201JHI33
MM32L073PF
nRF51822
NANO130KE3BN
Cortex-M3
K3V3,K3V3+
STM32F103RB
ATSAM4SD32C
EFM32GG990F1024
GD32F103VCT6
GD32150R8
GD32F190R8
GD32F207VC
MM32F103CBT6
MM32L373PS
Cortex-M4
STM32F411RE
STM32F412ZG
STM32F429ZI
STM32F429IG
STM32L476RG
EFM32PG1B200F256GM48
GD32F450IK
CC3220SF
LPC54114j256BD64
nRF52840
nRF52832
NUC472HI8AE
ATSAMG55J19
ADuCM4050LF
Cortex-M7
K3V5
STM32F746ZG
ATSAME70Q21
ARM9
Hi3516C V200
Hi3516C V300
Hi3516E V100
Hi3518E V200
Hi3911
使用约束
- Huawei LiteOS提供一套Huawei LiteOS接口,同时支持CMSIS接口,它们功能一致,但混用CMSIS和Huawei LiteOS接口大概会导致不可预知的错误,例如用CMSIS接口申请信号量,但用Huawei LiteOS接口释放信号量。
- 开发驱动程序只能用Huawei LiteOS的接口,上层APP建议用CMSIS接口。
4.底子内核
4.1 使命
概述
根本概念
从系统的角度看,使命是竞争系统资源的最小运行单元。使命可以使用或等待CPU、使用内存空间等系统资源,并独立于别的使命运行。
Huawei LiteOS的使命模块可以给用户提供多个使命,实现了使命之间的切换和通讯,帮助用户管理业务程序流程。这样用户可以将更多的精神投入到业务功能的实现中。
Huawei LiteOS是一个支持多使命的使用系统。在Huawei LiteOS中,一个使命表示一个线程。
Huawei LiteOS中的使命是抢占式调理机制,同时支持时间片轮转调理方式。
高优先级的使命可打断低优先级使命,低优先级使命必须在高优先级使命阻塞或竣事后才华得到调理。
Huawei LiteOS的使命一共有32个优先级(0-31),最高优先级为0,最低优先级为31。
使命相关概念
使命状态
Huawei LiteOS系统中的每一使命都有多种运行状态。系统初始化完成后,创建的使命就可以在系统中竞争一定的资源,由内核举行调理。
使命状态通常分为以下四种:
- 停当(Ready):该使命在停当列表中,只等待CPU。
- 运行(Running):该使命正在执行。
- 阻塞(Blocked):该使命不在停当列表中。包含使命被挂起、使命被延时、使命正在等待信号量、读写队列或者等待读写变乱等。
- 退出态(Dead):该使命运行竣事,等待系统回收资源。
图 1 使命状态表示图
使命状态迁徙阐明:
使命创建后进入停当态,发生使命切换时,停当列表中最高优先级的使命被执行,从而进入运行态,但现在该使命仍然在停当列表中。
正在运行的使命发生阻塞(挂起、延时、获取互斥锁、读消息、读信号量等待等)时,该使命会从停当列表中删除,使命状态由运行态变成阻塞态,然后发生使命切换,运行停当列表中剩余最高优先级使命。
阻塞的使命被规复后(使命规复、延时时间超时、读信号量超时或读到信号量等),此时被规复的使命会被加入停当列表,从而由阻塞态变成停当态;此时假如被规复使命的优先级高于正在运行使命的优先级,则会发生使命切换,将该使命由停当态变成运行态。
使命也有大概在停当态时被阻塞(挂起),此时使命状态会由停当态变化为阻塞态,该使命从停当列表中删除,不会参与使命调理,直到该使命被规复。
有更高优先级使命创建或者规复后,会发生使命调理,现在停当列表中最高优先级使命变为运行态,那么原先运行的使命由运行态变为停当态,依然在停当列表中。
运行中的使命运行竣事,使命状态由运行态变为退出态。退出态包含使命运行竣事的正常退出以及Invalid状态。例如,未设置分离属性(LOS_TASK_STATUS_DETACHED)的使命,运行竣事后对外呈现的是Invalid状态,即退出态。
阻塞的使命调用删除接口,使命状态由阻塞态变为退出态。
使命ID
使命ID,在使命创建时通过参数返回给用户,作为使命的一个非常重要的标识。用户可以通过使命ID对指定使命举行使命挂起、使命规复、查询使命名等使用。
使命优先级
优先级表示使命执行的优先顺序。使命的优先级决定了在发生使命切换时即将要执行的使命。在停当列表中的最高优先级的使命将得到执行。
使命入口函数
每个新使命得到调理后将执行的函数。该函数由用户实现,在使命创建时,通过使命创建结构体指定。
使命控制块TCB
每一个使命都含有一个使命控制块(TCB)。TCB包含了使命上下文栈指针(stack pointer)、使命状态、使命优先级、使命ID、使命名、使命栈大小等信息。TCB可以反映出每个使命运行情况。
使命栈
每一个使命都拥有一个独立的栈空间,我们称为使命栈。栈空间里保存的信息包含局部变量、寄存器、函数参数、函数返回地点等。使命在使命切换时会将切出使命的上下文信息保存在自身的使命栈空间内里,以便使命规复时还原现场,从而在使命规复后在切出点继承开始执行。
使命上下文
使命在运行过程中使用到的一些资源,如寄存器等,我们称为使命上下文。当这个使命挂起时,其他使命继承执行,在使命规复后,假如没有把使命上下文保存下来,有大概使命切换会修改寄存器中的值,从而导致未知错误。
因此,Huawei LiteOS在使命挂起的时候会将本使命的使命上下文信息,保存在自己的使命栈内里,以便使命规复后,从栈空间中规复挂起时的上下文信息,从而继承执行被挂起时被打断的代码。
使命切换
使命切换包含获取停当列表中最高优先级使命、切出使命上下文保存、切入使命上下文规复等动作。
运作机制
Huawei LiteOS使命管理模块提供使命创建、使命延时、使命挂起和使命规复、锁使命调理和解锁使命调理、根据ID查询使命控制块信息功能。
用户创建使命时,系统会将使命栈举行初始化,预置上下文。别的,系统还会将“使命入口函数”地点放在相应位置。这样在使命第一次启动进入运行态时,将会执行“使命入口函数”。
开发引导
使用场景
使命创建后,内核可以执行锁使命调理,解锁使命调理,挂起,规复,延时等使用,同时也可以设置使命优先级,获取使命优先级。使命竣事的时候,假如使命的状态是自删除状态(LOS_TASK_STATUS_DETACHED),则举行当前使命自删除使用。
功能
Huawei LiteOS 系统中的使命管理模块为用户提供下面几种功能。
功能分类
接口名
形貌
使命的创建和删除
LOS_TaskCreateOnly
创建使命,并使该使命进入suspend状态,并不调理。
LOS_TaskCreate
创建使命,并使该使命进入ready状态,并调理。
LOS_TaskDelete
删除指定的使命。
使命状态控制
LOS_TaskResume
规复挂起的使命。
LOS_TaskSuspend
挂起指定的使命。
LOS_TaskDelay
使命延时等待。
LOS_TaskYield
显式放权,调解指定优先级的使命调理顺序。
使命调理的控制
LOS_TaskLock
锁使命调理。
LOS_TaskUnlock
解锁使命调理。
使命优先级的控制
LOS_CurTaskPriSet
设置当前使命的优先级。
LOS_TaskPriSet
设置指定使命的优先级。
LOS_TaskPriGet
获取指定使命的优先级。
使命信息获取
LOS_CurTaskIDGet
获取当前使命的ID。
LOS_TaskInfoGet
获取指定使命的信息。
LOS_TaskStatusGet
获取指定使命的状态。
LOS_TaskNameGet
获取指定使命的名称。
LOS_TaskInfoMonitor
监控所有使命,获取所有使命的信息。
LOS_NextTaskIDGet
获取即将被调理的使命的ID。
开发流程
以创建使命为例,讲解开发流程。
- 执行make menuconfig配置使命模块。
配置LOSCFG_BASE_CORE_TSK_LIMIT系统支持最大使命数,这个可以根据需求自己配置。
配置LOSCFG_BASE_CORE_TIMESLICE时间片开关为YES。
配置LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT时间片,根据实际情况自己配置。
- 在los_config.h中配置使命栈大小。
配置LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE 空闲(IDLE)使命栈大小,这个默认即可。
配置LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE默认使命栈大小,用户根据自己的需求举行配置,在用户创建使命时,可以举行针对性设置。
- 锁使命LOS_TaskLock,锁住使命,防止高优先级使命调理。
- 创建使命LOS_TaskCreate。
- 解锁使命LOS_TaskUnlock,让使命按照优先级举行调理。
- 延时使命LOS_TaskDelay,使命延时等待。
- 挂起指定的使命LOS_TaskSuspend,使命挂起等待规复使用。
- 规复挂起的使命LOS_TaskResume。
TASK状态
Huawei LiteOS使命的大多数状态由内核维护,唯有自删除状态对用户可见,需要用户在创建使命时传入:
序号
定义
实际数值
形貌
1
LOS_TASK_STATUS_DETACHED
0x0100
使命是自删除的
用户在调用LOS_TaskCreate接口创建使命时,需要将创建使命的TSK_INIT_PARAM_S参数的uwResved域设置为LOS_TASK_STATUS_DETACHED,即自删除状态,设置成自删除状态的使命会在运行完成时举行自删除动作。
须知:
- 在调用内核LOS_TaskCreate接口创建使命时,默认必须要将使命状态设置为LOS_TASK_STATUS_DETACHED。
TASK错误码
对使命存在失败大概性的使用,包括创建使命、删除使命、挂起使命、规复使命、延时使命等等,均需要返回对应的错误码,以便快速定位错误原因。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_TSK_NO_MEMORY
0x03000200
内存空间不敷
分配更大的内存分区
2
LOS_ERRNO_TSK_PTR_NULL
0x02000201
使命参数为空
检查使命参数
3
LOS_ERRNO_TSK_STKSZ_NOT_ALIGN
0x02000202
使命栈大小未对齐
对齐使命栈,暂不使用该错误码
4
LOS_ERRNO_TSK_PRIOR_ERROR
0x02000203
不精确的使命优先级
检查使命优先级
5
LOS_ERRNO_TSK_ENTRY_NULL
0x02000204
使命入口函数为空
定义使命入口函数
6
LOS_ERRNO_TSK_NAME_EMPTY
0x02000205
使命名为空
设置使命名
7
LOS_ERRNO_TSK_STKSZ_TOO_SMALL
0x02000206
使命栈太小
扩大使命栈
8
LOS_ERRNO_TSK_ID_INVALID
0x02000207
无效的使命ID
检查使命ID
9
LOS_ERRNO_TSK_ALREADY_SUSPENDED
0x02000208
使命已经被挂起
等待这个使命被规复后,再去尝试挂起这个使命
10
LOS_ERRNO_TSK_NOT_SUSPENDED
0x02000209
使命未被挂起
挂起这个使命
11
LOS_ERRNO_TSK_NOT_CREATED
0x0200020a
使命未被创建
创建这个使命
12
LOS_ERRNO_TSK_DELETE_LOCKE
0x0300020b
删除使命时,使命处于被锁状态
等待解锁使命之后再举行删除使用
13
LOS_ERRNO_TSK_MSG_NONZERO
0x0200020c
使命信息非零
暂不使用该错误码
14
LOS_ERRNO_TSK_DELAY_IN_INT
0x0300020d
中断期间,举行使命延时
等待退出中断后再举行延时使用
15
LOS_ERRNO_TSK_DELAY_IN_LOCK
0x0200020e
使命被锁的状态下,举行延时
等待解锁使命之后再举行延时使用
16
LOS_ERRNO_TSK_YIELD_IN_LOCK
0x0200020f
使命被锁的状态下,举行Yield使用
等待使命解锁后举行Yield使用
17
LOS_ERRNO_TSK_YIELD_NOT_ENOUGH_TASK
0x02000210
没有或者仅有一个可用使命能举行行程安排
增长使命数
18
LOS_ERRNO_TSK_TCB_UNAVAILABLE
0x02000211
没有空闲的使命控制块可用
增长使命控制块数量
19
LOS_ERRNO_TSK_HOOK_NOT_MATCH
0x02000212
使命的钩子函数不匹配
暂不使用该错误码
20
LOS_ERRNO_TSK_HOOK_IS_FULL
0x02000213
使命的钩子函数数量凌驾界限
暂不使用该错误码
21
LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK
0x02000214
这是个系统级别的使命
检查使命ID,不要试图使用系统使命
22
LOS_ERRNO_TSK_SUSPEND_LOCKED
0x03000215
将被挂起的使命处于被锁状态
等待使命解锁后再尝试挂起使命
23
LOS_ERRNO_TSK_FREE_STACK_FAILED
0x02000217
使命栈free失败
该错误码暂不使用
24
LOS_ERRNO_TSK_STKAREA_TOO_SMALL
0x02000218
使命栈区域太小
该错误码暂不使用
25
LOS_ERRNO_TSK_ACTIVE_FAILED
0x03000219
使命触发失败
创建一个IDLE使命后执行使命转换,暂不使用该错误码
26
LOS_ERRNO_TSK_CONFIG_TOO_MANY
0x0200021a
过多的使命配置项
该错误码暂不使用
27
LOS_ERRNO_TSK_CP_SAVE_AREA_NOT_ALIGN
0x0200021b
暂无
该错误码暂不使用
28
LOS_ERRNO_TSK_MSG_Q_TOO_MANY
0x0200021d
暂无
该错误码暂不使用
29
LOS_ERRNO_TSK_CP_SAVE_AREA_NULL
0x0200021e
暂无
该错误码暂不使用
30
LOS_ERRNO_TSK_SELF_DELETE_ERR
0x0200021f
暂无
该错误码暂不使用
31
LOS_ERRNO_TSK_STKSZ_TOO_LARGE
0x02000220
使命栈大小设置过大
减小使命栈大小
32
LOS_ERRNO_TSK_SUSPEND_SWTMR_NOT_ALLOWED
0x02000221
不答应挂起软件定时器使命
检查使命ID, 不要试图挂起软件定时器使命,暂不使用该错误码
33
LOS_ERRNO_TSK_DELAY_IN_SWTMR_TSK
0x03000222
在软件定时器回调中,调用使命延时
不要在软件定时器中调用使命延时,暂不使用该错误码
34
LOS_ERRNO_TSK_CPU_AFFINITY_MASK_ERR
0x03000223
使命CPU亲和性Mask有误
检查传入的亲和性Mask
35
LOS_ERRNO_TSK_YIELD_IN_INT
0x02000224
使命在中断中举行Yield使用
不要在中断中举行Yield使用
错误码定义: 错误码是一个32位的存储单元,31~24位表示错误品级,23~16位表示错误码标志,15~8位代表错误码所属模块,7~0位表示错误码序号,如下:
- #define LOS_ERRNO_OS_NORMAL(MID,ERRNO) \
- (LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
- LOS_ERRTYPE_NORMAL :Define the error level as critical
- LOS_ERRNO_OS_ID :OS error code flag.
- MID:OS_MOUDLE_ID
- ERRNO:error ID number
复制代码 例如:
- LOS_ERRNO_TSK_NO_MEMORY LOS_ERRNO_OS_FATAL(LOS_MOD_TSK, 0x00)
复制代码 须知:
错误码序号 0x16、0x1c、0x0b,未被定义,不可用。
平台差异性
无。
注意事项
- 创建新使命时,会对之前自删除使命的使命控制块和使命栈举行回收,非自删除使命的控制块和栈在使命删除的时候已经回收。
- 使命名是指针没有分配空间,在设置使命名时,克制将局部变量的地点赋值给使命名指针。
- 若指定的使命栈大小为0,则使用配置项LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE指定默认的使命栈大小。
- 使命栈的大小按8字节大小对齐。确定使命栈大小的原则是,够用就行:多了浪费,少了使命栈溢出。
- 挂起使命的时候若为当前使命且已锁使命,则不能被挂起。
- 空闲(IDLE)使命及软件定时器使命不能被挂起或者删除。
- 在中断处置惩罚函数中或者在锁使命的情况下,执行LOS_TaskDelay使用会失败。
- 锁使命调理,并不关中断,因此使命仍可被中断打断。
- 锁使命调理必须和解锁使命调理配合使用。
- 设置使命优先级的时候大概会发生使命调理。
- 系统可配置的使命资源个数是指:整个系统的使命资源总个数,而非用户能使用的使命资源个数。例如:系统软件定时器多占用一个使命资源数,那么系统可配置的使命资源就会减少一个。
- 不建议使用LOS_CurTaskPriSet或者LOS_TaskPriSet接口来修改软件定时器使命的优先级,否则大概会导致系统出现问题。
- LOS_CurTaskPriSet和LOS_TaskPriSet接口不能在中断中使用。
- LOS_TaskPriGet接口传入的task ID对应的使命未创建或者凌驾最大使命数,同一返回0xffff。
- 在删除使命时要保证使命申请的资源(如互斥锁、信号量等)已被释放。
编程实例
实例形貌
下面的示例介绍使命的根本使用方法,包含使命创建、使命延时、使命锁与解锁调理、挂起和规复、查询当前使命PID、根据PID查询使命信息等使用,阐述使命优先级调理的机制以及各接口的应用。
- 创建了2个使命:TaskHi和TaskLo。
- TaskHi为高优先级使命。
- TaskLo为低优先级使命。
编程示例
结果验证
编译运行得到的结果为:
完整实例代码
sample_task.c
4.2 内存
概述
根本概念
内存管理模块管理系统的内存资源,它是使用系统的核心模块之一。主要包括内存的初始化、分配以及释放。
在系统运行过程中,内存管理模块通过对内存的申请/释放使用,来管理用户和OS对内存的使用,使内存的使用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
Huawei LiteOS的内存管理分为静态内存管理和动态内存管理,提供内存初始化、分配、释放等功能。
动态内存: 在动态内存池中分配用户指定大小的内存块。
静态内存: 在静态内存池中分配用户初始化时预设(固定)大小的内存块。
- 优点:分配和释放效率高,静态内存池中无碎片。
- 缺点:只能申请到初始化预设大小的内存块,不能按需申请。
动态内存运作机制
动态内存管理,即在内存资源充足的情况下,从系统配置的一块比力大的一连内存(内存池),根据用户需求,分配恣意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。
与静态内存相比,动态内存管理的利益是按需分配,缺点是内存池中轻易出现碎片。
LiteOS动态内存支持DLINK和BEST LITTLE两种尺度算法。
1.DLINK
DLINK动态内存管理结构如图1所示:
图 1 DLINK动态内存管理结构图
第一部分:堆内存(也称内存池)的起始地点及堆区域总大小。
第二部分:本身是一个数组,每个元素是一个双向链表,所有free节点的控制头都会被分类挂在这个数组的双向链表中。
假设内存答应的最小节点为2min字节,则数组的第一个双向链表存储的是所有size为2min<size< 2min+1的free节点,第二个双向链表存储的是所有size为2min+1<size< 2min+2的free节点,依次类推第n个双向链表存储的是所有size为2min+n-1<size< 2min+n的free节点。每次申请内存的时候,会从这个数组检索最合适大小的free节点,举行分配内存。每次释放内存时,会将该片内存作为free节点存储至这个数组,以便下次再使用。
第三部分:占用内存池极大部分的空间,是用于存放各节点的实际区域。以下是LOS_MEM_DYN_NODE节点结构体申明以及简朴介绍:
- typedef struct tagLOS_MEM_DYN_NODE
- {
- LOS_DL_LIST stFreeNodeInfo;
- struct tagLOS_MEM_DYN_NODE *pstPreNode;
- UINT32 uwSizeAndFlag;
- }LOS_MEM_DYN_NODE;
复制代码 图 2 LOS_MEM_DYN_NODE节点结构图
2.BEST LITTLE
LiteOS的动态内存分配支持最佳适配算法,即BEST LITTLE,每次分配时选择堆内存(内存池中)最小最得当的内存块举行分配。
图 3 BEST LITTLE动态内存管理结构图
静态内存运作机制
静态内存实质上是一块静态数组,静态内存池内的块大小在初始化时设定,初始化后块大小不可变动。
静态内存池由一个控制块和多少相同大小的内存块构成。控制块位于内存池头部,用于内存块管理。内存块的申请和释放以块大小为粒度。
图 4 静态内存表示图
动态内存
开发引导
使用场景
内存管理的主要工作是动态的分别并管理用户分配好的内存区间。
动态内存管理主要是在用户需要使用大小不等的内存块的场景中使用。
当用户需要分配内存时,可以通过使用系统的动态内存申请函数申请指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
功能
Huawei LiteOS系统中的动态内存管理模块为用户提供下面几种功能,具体的API详见接口手册。
功能分类
接口名
形貌
内存初始化
LOS_MemInit
初始化一块指定的动态内存池,大小为size。
删除内存池
LOS_MemDeInit
删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效。
申请动态内存
LOS_MemAlloc
从指定动态内存池中申请size长度的内存。
释放动态内存
LOS_MemFree
释放已申请的内存。
重新申请内存
LOS_MemRealloc
按size大小重新分配内存块,并保存原内存块内容。
内存对齐分配
LOS_MemAllocAlign
从指定动态内存池中申请长度为size且地点按boundary字节对齐的内存。
获取内存大小
LOS_MemPoolSizeGet
获取指定的动态内存池总大小。
获取内存大小
LOS_MemTotalUsedGet
获取指定动态内存池的总使用量大小。
获取内存块数量
LOS_MemFreeBlksGet
获取指定内存池的空闲内存块数量。
获取内存块数量
LOS_MemUsedBlksGet
获取指定内存池的已使用的内存块数量。
获取分配指定内存区域的使命ID
LOS_MemTaskIdGet
获取分配了指定内存区域的使命ID。
获取内存池中最后一个使用内存块的竣事地点
LOS_MemLastUsedGet
获取内存池最后一个使用内存块的竣事地点。
获取内存结构信息
LOS_MemInfoGet
获取指定内存池的内存结构信息。
对指定内存池做完整性检查
LOS_MemIntegrityCheck
对指定内存池做完整性检查。
获取内存块大小
LOS_MemNodeSizeCheck
获取内存块的总大小和可使用大小。
设定内存检查级别
LOS_MemCheckLevelSet
设定内存检查级别。
获取内存检查级别
LOS_MemCheckLevelGet
获取内存检查级别。
显示系统内存池
LOS_MemPoolList
打印显示系统中已初始化的所有内存池,仅打开LOSCFG_MEM_MUL_POOL时有效
指定模块申请动态内存
LOS_MemMalloc
从指定动态内存池分配内存给指定的模块,并纳入模块统计。
释放指定模块的一个内存块
LOS_MemMfree
释放已经申请的内存块,并纳入模块统计。
指定模块内存对齐分配
LOS_MemMallocAlign
从指定动态内存池中申请长度为size且地点按boundary字节对齐的内存,并纳入模块统计。
指定模块重新分配内存块
LOS_MemMrealloc
按size大小重新分配内存块,并保存原内存块内容,并纳入模块统计。
获取模块内存使用量
LOS_MemMusedGet
获取指定模块当前内存使用量。
显示空闲内存块的规模和数量
LOS_MemFreeNodeShow
显示指定内存池当前空闲内存块的规模及相对应的数量。
DLINK开发流程
- 配置:
OS_SYS_MEM_ADDR:系统动态内存池起始地点,一般不需要修改。
OS_SYS_MEM_SIZE:系统动态内存池大小,以byte为单元,系统默认分配DDR后未使用的空间。
LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:内存越界检测开关,默认关闭。打开后,每次申请动态内存时执举措态内存块越界检查。
- 初始化LOS_MemInit。
初始一个内存池后如图,天生一个 EndNode,而且剩余的内存全部被标记为FreeNode节点。注:EndNode作为内存池末端的节点,size为0。
- 申请恣意大小的动态内存LOS_MemAlloc。
判断动态内存池中是否存在申请量大小的空间,若存在,则划出一块内存块,以指针情势返回,若不存在,返回NULL。
调用三次LOS_MemAlloc函数可以创建三个节点,假设名称分别为UsedA,UsedB,UsedC,大小分别为sizeA,sizeB,sizeC。因为刚初始化内存池的时候只有一个大的FreeNode,以是这些内存块是从这个FreeNode中切割出来的。
当内存池中存在多个FreeNode的时候举行malloc,将会适配最合适大小的FreeNode用来新建内存块,减少内存碎片。若新建的内存块不便是被使用的FreeNode的大小,则在新建内存块后,多余的内存又会被标记为一个新的FreeNode。
- 释放动态内存LOS_MemFree。
回收内存块,供下一次使用。
假设调用LOS_MemFree释放内存块UsedB,则会回收内存块UsedB,而且将其标记为FreeNode。
BEST LITTLE开发流程
- 配置:
OS_SYS_MEM_ADDR:系统动态内存池起始地点,需要用户指定。
OS_SYS_MEM_SIZE:系统动态内存池大小,以byte为单元,需要用户精确计算。
LOSCFG_MEMORY_BESTFIT:置为YES,选择内存管理算法中的BESTFIT算法。
- 初始化:
调用LOS_MemInit函数初始化用户指定的动态内存池。
- 申请恣意大小的动态内存:
调用LOS_MemAlloc函数从指定的内存池中申请指定大小的内存块,申请时内存管理向堆内存空间申请,最后将申请结果返回给用户。在向堆内存空间申请时,会存在内存块的切分。
- 释放动态内存:
调用LOS_MemFree函数向指定的动态内存池释放指定的内存块,向堆内存空间释放内存块。在向堆内存空间释放时,会存在内存块的合并。
平台差异性
无。
注意事项
- 由于系统中动态内存管理需要消耗管理控制块结构的内存,故实际用户可使用空间总量小于在配置文件los_config.h中配置项OS_SYS_MEM_SIZE的大小。
- 系统中地点对齐申请内存分配LOS_MemAllocAlign大概会消耗部分对齐导致的空间,故存在一些内存碎片,当系统释放该对齐内存时,同时回收由于对齐导致的内存碎片。
- 系统中重新分配内存LOS_MemRealloc函数假如分配成功,系统会自己判断是否需要释放原来申请的空间,返回重新分配的空间。假如重新分配失败,原来的空间保持不变,并返回NULL。克制使用pPtr = LOS_MemRealloc(pool, pPtr, uwSize); 即:不能使用本来的pPtr变量直接去接收返回值。
- 系统中多次调用LOS_MemFree时,第一次会返回成功,但对同一块内存举行多次重复释放会导致非法指针使用,导致结果不可预知。
- 由于系统动态内存管理内存节点控制块结构体中,节点size数据类型为UINT32,高两位为标志位,因此用户初始化内存池的大小不能凌驾1G,否则会出现不可预知结果。
- 分模块内存统计依靠于LOSCFG_MEM_MUL_MODULE,使用时需要在配置文件中定义此宏。
编程实例
实例形貌
Huawei LiteOS运行期间,用户需要频繁的使用内存资源,而内存资源有限,必须确保将有限的内存资源分配给急需的程序,同时释放不用的内存。
通过Huawei LiteOS内存管理模块可以保证高效、精确的申请、释放内存。
本实例执行以下步骤:
- 初始化一个动态内存池。
- 在动态内存池中申请一个内存块。
- 使用这块内存块存放一个数据。
- 打印出存放在内存块中的数据。
- 释放掉这块内存。
编程实例
- UINT8 * m_aucSysMem_Tmp;
- VOID los_memory_test() {
- UINT32 *p_num = NULL;
- UINT32 uwRet;
- uwRet = LOS_MemInit(m_aucSysMem_Tmp, 32);
- if (LOS_OK == uwRet) {
- dprintf("内存池初始化成功!\n");
- }
- else {
- dprintf("内存池初始化失败!\n");
- return;
- }
- /*分配内存*/
- p_num = (int*)LOS_MemAlloc(m_aucSysMem_Tmp, 4);
- if (NULL == p_num) {
- dprintf("内存分配失败!\n");
- return;
- }
- dprintf("内存分配成功\n");
- /*赋值*/
- *p_num = 828;
- dprintf("*p_num = %d\n", *p_num);
- /*释放内存*/
- uwRet = LOS_MemFree(m_aucSysMem_Tmp, p_num);
- if (LOS_OK == uwRet) {
- dprintf("内存释放成功!\n");
- }
- else {
- dprintf("内存释放失败!\n");
- }
- return;
- }
复制代码 结果验证
图 1 结果显示
完整实例代码
sample_mem.c
静态内存
开发引导
使用场景
当用户需要使用固定长度的内存时,可以使用静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
功能
Huawei LiteOS的静态内存管理主要为用户提供以下功能。
功能分类
接口名
形貌
初始化静态内存
LOS_MemboxInit
初始化一个静态内存池,设定其起始地点、总大小及每个块大小。
清除静态内存内容
LOS_MemboxClr
清零静态内存块。
申请一块静态内存
LOS_MemboxAlloc
申请一块静态内存块。
释放内存
LOS_MemboxFree
释放一个静态内存块。
分析静态内存池状态
LOS_MemboxStatisticsGet
获取静态内存池的统计信息。
打印静态内存池内容
LOS_ShowBox
打印指定静态内存池所有节点信息(打印品级是LOS_INFO_LEVEL)
开发流程
本节介绍使用静态内存的典范场景开发流程。
- 规划一片内存区域作为静态内存池。
- 调用LOS_MemboxInit接口。
系统内部将会初始化静态内存池。将入参指定的内存区域分割为N块(N值取决于静态内存总大小和块大小),将所有内存块挂到空闲链表,在内存起始处放置控制头。
- 调用LOS_MemboxAlloc接口。
系统内部将会从空闲链表中获取第一个空闲块,并返回该块的用户空间地点。
- 调用LOS_MemboxFree接口。
将该块内存加入空闲块链表。
- 调用LOS_MemboxClr接口。
系统内部清零静态内存块,将入参地点对应的内存块清零。
平台差异性
无。
注意事项
- 静态内存池区域,可以通过定义全局数组或调用动态内存分配接口方式获取。假如使用动态内存分配方式,在不需要静态内存池时,注意要释放该段内存,避免内存走漏。
编程实例
实例形貌
Huawei LiteOS运行期间,用户需要频繁的使用内存资源,而内存资源有限,必须确保将有限的内存资源分配给急需的程序,同时释放不用的内存。
通过内存管理模块可以保证精确且高效的申请释放内存。
本实例执行以下步骤:
- 初始化一个静态内存池。
- 从静态内存池中申请一块静态内存。
- 使用这块内存块存放一个数据。
- 打印出存放在内存块中的数据。
- 清除内存块中的数据。
- 释放掉这块内存。
编程实例
- VOID los_membox_test(void) {
- UINT32 *p_num = NULL;
- UINT32 uwBlkSize = 10, uwBoxSize = 100;
- UINT32 uwRet;
- UINT32 pBoxMem[1000];
- uwRet = LOS_MemboxInit(&pBoxMem[0], uwBoxSize, uwBlkSize);
- if (uwRet != LOS_OK)
- {
- dprintf("内存池初始化失败!\n");
- return;
- }
- else {
- dprintf("内存池初始化成功!\n");
- }
-
- /*申请内存块*/
- p_num = (int*)LOS_MemboxAlloc(pBoxMem);
- if (NULL == p_num) {
- dprintf("内存分配失败!\n");
- return;
- }
- dprintf("内存分配成功\n");
- /*赋值*/
- *p_num = 828;
- dprintf("*p_num = %d\n", *p_num);
- /*清除内存内容*/
- LOS_MemboxClr(pBoxMem, p_num);
- dprintf("清除内存内容成功\n *p_num = %d\n", *p_num);
- /*释放内存*/
- uwRet = LOS_MemboxFree(pBoxMem, p_num);
- if (LOS_OK == uwRet) {
- dprintf("内存释放成功!\n");
- }
- else{
- dprintf("内存释放失败!\n");
- }
- return;
- }
复制代码 结果验证
图 1 结果显示
完整实例代码
sample_membox.c
4.3 中断机制
概述
中断是指出现需要时,CPU停息执行当前程序,转而执行新程序的过程。即在程序运行过程中,系统出现了一个必须由CPU立即处置惩罚的事务。此时,CPU暂时中断当前程序的执行转而处置惩罚这个事务,这个过程就叫做中断。
众所周知,CPU的处置惩罚速度比外设的运行速度快很多,外设可以在没有CPU介入的情况下完成一定的工作,但某些情况下需要CPU为其做一定的工作。
通过中断机制,在外设不需要CPU介入时,CPU可以执行别的使命,而当外设需要CPU时通过产生中断信号使CPU立即中断当前使命来响应中断哀求。这样可以使CPU避免把大量时间泯灭在等待、查询外设状态的使用上,因此将大大进步系统实时性以及执行效率。
Huawei LiteOS的中断支持:
- 中断初始化
- 中断创建
- 开/关中断
- 规复中断
- 中断使能
- 中断屏蔽
中断的介绍
与中断相关的硬件可以分别为三类:设备、中断控制器、CPU本身。
设备:发起中断的源,当设备需要哀求CPU时,产生一个中断信号,该信号连接至中断控制器。
中断控制器:中断控制器是CPU浩繁外设中的一个,它一方面接收别的外设中断引脚的输入,另一方面,它会发出中断信号给CPU。可以通过对中断控制器编程实现对中断源的优先级、触发方式、打开和关闭源等设置使用。常用的中断控制器有VIC(Vector Interrupt Controller)和GIC(General Interrupt Controller),在ARM Cortex-M系列中使用的中断控制器是NVIC(Nested Vector Interrupt Controller)。在ARM Cortex-A7中使用的中断控制器是GIC。
CPU:CPU会响应中断源的哀求,中断当前正在执行的使命,转而执行中断处置惩罚程序。
和中断相关的名词表明
中断号:每个中断哀求信号都会有特定的标志,使得计算性可以或许判断是哪个设备提出的中断哀求,这个标志就是中断号。
中断哀求:“紧急变乱”需向CPU提出申请(发一个电脉冲信号),要求中断,及要求CPU停息当前执行的使命,转而处置惩罚该“紧急变乱”,这一申请过程称为中断申请。
中断优先级:为使系统可以或许实时响应并处置惩罚所有中断,系统根据中断时间的重要性和紧迫程度,将中断源分为多少个级别,称作中断优先级。Huawei LiteOS支持中断控制器的中断优先级及中断嵌套,同时中断管理未对优先级和嵌套举行限制。Huawei LiteOS中所有的中断源优先级相同,不支持中断嵌套或抢占。
中断处置惩罚程序:当外设产生中断哀求后,CPU停息当前的使命,转而响应中断申请,即执行中断处置惩罚程序。
中断触发:中断源发出并送给CPU控制信号,将接口卡上的中断触发器置“1”,表明该中断源产生了中断,要求CPU去响应该中断,CPU停息当前使命,执行相应的中断处置惩罚程序。
中断触发类型:外部中断申请通过一个物理信号发送到NVIC或GIC,可以是电平触发或边沿触发。
中断向量:中断服务程序的入口地点。
中断向量表:存储中断向量的存储区,中断向量与中断号对应,中断向量在中断向量表中按照中断号顺序存储。
中断共享:当外设较少时,可以实现一个外设对应一个中断号,但为了支持更多的硬件设备,可以让多个设备共享一个中断号,共享同一个中断的中断处置惩罚程序形成一个链表,当外部设备产生中断申请时,系统会遍历中断号对应的中断处置惩罚程序链表。
中断底半部:中断处置惩罚程序耗时应尽大概短,以满足中断的快速响应,为了均衡中断处置惩罚程序的性能与工作量,将中断处置惩罚程序分解为两部分:顶半部和底半部。
顶半部完成尽大概少的比力紧急的使命,它通常只是简朴地读取寄存器中的中断状态并清除中断标志位即举行“登记工作”,将耗时的底半部处置惩罚程序挂到系统的底半部执行队列中去。
开发引导
使用场景
当有中断哀求产生时,CPU停息当前的使命,转而去响应外设哀求。根据需要,用户通过中断申请,注册中断处置惩罚程序,可以指定CPU响应中断哀求时所执行的具体使用。
功能
Huawei LiteOS 系统中的中断模块为用户提供下面几种功能。
接口名
形貌
LOS_HwiCreate
硬中断创建,注册硬中断处置惩罚程序
LOS_IntUnLock
开中断
LOS_IntRestore
规复到关中断之前的状态
LOS_IntLock
关中断
HalIrqMask
中断屏蔽(通过设置寄存器,克制CPU响应该中断)
HalIrqUnmask
中断使能(通过设置寄存器,答应CPU响应该中断)
HalIrqSetAffinity
设置中断的亲和性,即设置中断在固定核响应(该函数仅在SMP模式下支持)
开发流程
- 修改配置项
- 打开硬中断裁剪开关:LOSCFG_PLATFORM_HWI定义为YES。
- 配置硬中断使用最大数:LOSCFG_PLATFORM_HWI_LIMIT。
- 调用中断创建接口LOS_HwiCreate创建中断。
- 调用HalIrqUnmask接口使能指定中断。
- 调用HalIrqMask接口屏蔽指定中断。
注意事项
- 根据具体硬件,配置支持的最大中断数及中断初始化使用的寄存器地点。
- 中断共享机制,支持同一中断处置惩罚程序的重复挂载,但中断处置惩罚程序的入参dev必须唯一,即同一中断号,同一dev只能挂载一次;但同一中断号,同一中断处置惩罚程序,dev不同则可以重复挂载。
- 中断处置惩罚程序耗时不能过长,影响CPU对中断的实时响应。
- Cortex-M系列处置惩罚器中1-15中断为内部使用,因此不建议用户去申请和创建。
- 中断响应过程中不能执行引起调理的函数。
- 中断规复LOS_IntRestore()的入参必须是与之对应的LOS_IntLock()保存的关中断之前的CPSR的值。
- Cortex-A7中0-31中断为内部使用,因此不建议用户去申请和创建。
- 一般不直接调用LOS_HwiCreate()创建中断;建议使用系统compat中的linux适配接口request_irq创建中断。
编程实例
实例形貌
本实例实现如下功能:
编程示例
前提条件:
- 在los_config.h中,将LOSCFG_PLATFORM_HWI定义为YES。
- 在los_config.h中,设置最大硬中断个数LOSCFG_PLATFORM_HWI_LIMIT
阐明: 现在的中断测试代码提供了根本框架,中断硬件初始化代码请用户根据开发板硬件情况在Example_Exti0_Init()函数中自行实现。
代码实现如下:
- #include "los_hwi.h"
- #include "los_typedef.h"
- #define HWI_NUM_INT50 50
- void uart_irqhandle(int irq,void *dev)
- {
- printf("\n int the func uart_irqhandle \n");
- }
- void hwi_test()
- {
- int a = 1;
- UINTPTR uvIntSave;
- uvIntSave = LOS_IntLock();
- LOS_HwiCreate(HWI_NUM_INT50, 0,0,uart_irqhandle,NULL);//创建中断
- HalIrqUnmask(HWI_NUM_INT50);
- LOS_IntRestore(uvIntSave);
- HalIrqMask(HWI_NUM_INT50);
- }
复制代码 完整实例
sample_hwi.c
4.4 队列
概述
根本概念
队列又称消息队列,是一种常用于使命间通讯的数据结构,实现了接收来自使命或中断的不固定长度的消息,并根据不同的接口选择转达消息是否存放在自己空间。使命可以或许从队列内里读取消息,当队列中的消息是空时,挂起读取使命;当队列中有新消息时,挂起的读取使命被唤醒并处置惩罚新消息。
用户在处置惩罚业务时,消息队列提供了异步处置惩罚机制,答应将一个消息放入队列,但并不立即处置惩罚它,同时队列还能起到缓冲消息作用。
Huawei LiteOS中使用队列数据结构实现使命异步通讯工作,具有如下特性:
- 消息以先辈先出方式排队,支持异步读写工作方式。
- 读队列和写队列都支持超时机制。
- 发送消息类型由通讯双方约定,可以答应不同长度(不凌驾队列节点最大值)消息。
- 一个使命可以或许从恣意一个消息队列接收和发送消息。
- 多个使命可以或许从同一个消息队列接收和发送消息。
- 当队列使用竣事后,假如是动态申请的内存,需要通过释放内存函数回收。
运作机制
队列控制块
- /**
- * @ingroup los_queue
- * Queue information block structure
- */
- typedef struct
- {
- UINT8 *queueHandle; /**< 队列指针 */
- UINT16 queueState; /**< 队列状态 */
- UINT16 queueLen; /**< 队列中消息个数 */
- UINT16 queueSize; /**< 消息节点大小 */
- UINT16 queueID; /**< 队列ID */
- UINT16 queueHead; /**< 消息头节点位置(数组下标)*/
- UINT16 queueTail; /**< 消息尾节点位置(数组下标)*/
- UINT16 readWriteableCnt[2]; /**< 队列中可读或可写消息数,
- 0:可读,1:可写 */
- LOS_DL_LIST readWriteList[2]; /**< 读取或写入消息任务等待链表,
- 0:读取链表,1:写入链表 */
- LOS_DL_LIST memList; /**< MailBox模块使用 */
- } LosQueueCB;
复制代码 每个队列控制块中都含有队列状态,表示该队列的使用情况:
- OS_QUEUE_UNUSED:队列没有使用。
- OS_QUEUE_INUSED:队列被使用。
队列运作原理
创建队列时,根据用户传入队列长度和消息节点大小来开发相应的内存空间以供该队列使用,返回队列ID。
在队列控制块中维护一个消息头节点位置Head和一个消息尾节点位置Tail来表示当前队列中消息存储情况。Head表示队列中被占用消息的起始位置。Tail表示队列中被空闲消息的起始位置。刚创建时Head和Tail均指向队列起始位置。
写队列时,根据Tail找到被占用消息节点末端的空闲节点作为数据写入对象。假如Tail已经指向队列尾则采取回卷方式。根据readWriteableCnt[1]判断队列是否可以写入,不能对已满(readWriteableCnt[1]为0)队列举行写队列使用。
读队列时,根据Head找到最先写入队列中的消息节点举行读取。假如Head已经指向队列尾则采取回卷方式。根据readWriteableCnt[0]判断队列是否有消息读取,对全部空闲(readWriteableCnt[0]为0)队列举行读队列使用会引起使命挂起。
删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,释放原队列所占的空间,对应的队列控制头置为初始状态。
图 1 队列读写数据使用表示图
开发引导
功能
Huawei LiteOS中Message消息处置惩罚模块提供了以下功能。
功能分类
接口名
形貌
创建消息队列
LOS_QueueCreate
创建一个消息队列。
读队列(不带拷贝)
LOS_QueueRead
读取指定队列头部中的数据(队列节点中的数据实际上是一个地点)。
写队列(不带拷贝)
LOS_QueueWrite
向指定队列尾部写入用户Buffer的地点。
读队列(带拷贝)
LOS_QueueReadCopy
读取指定队列头部中的数据。
写队列(带拷贝)
LOS_QueueWriteCopy
向指定队列尾部写入用户Buffer的数据。
写队列(不带拷贝)
LOS_QueueWriteHead
向指定队列的头部写入用户Buffer的地点。
写队列(带拷贝)
LOS_QueueWriteHeadCopy
向指定队列的头部写入用户Buffer的数据。
删除队列
LOS_QueueDelete
删除一个指定的队列。
获取队列信息
LOS_QueueInfoGet
获取指定队列信息。
开发流程
使用队列模块的典范流程如下:
- 创建消息队列LOS_QueueCreate。
创建成功后,可以得到消息队列的ID值。
- 写队列使用函数LOS_QueueWriteCopy。
- 读队列使用函数LOS_QueueReadCopy。
- 获取队列信息函数LOS_QueueInfoGet。
- 删除队列LOS_QueueDelete。
QUEUE错误码
对队列存在失败大概性的使用,包括创建队列、删除队列等等,均需要返回对应的错误码,以便快速定位错误原因。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_QUEUE_MAXNUM_ZERO
0x02000600
队列资源的最大数目配置为0。
配置要大于0的队列资源的最大数量。假如不使用队列模块,则将配置项设置为将队列资源的最大数量的剪裁设置为NO。
2
LOS_ERRNO_QUEUE_NO_MEMORY
0x02000601
队列块内存无法初始化。
为队列块分配更大的内存分区,或减少队列资源的最大数量。
3
LOS_ERRNO_QUEUE_CREATE_NO_MEMORY
0x02000602
队列创建的内存未能被哀求。
为队列分配更多的内存,或减少要创建的队列中的队列长度和节点的数目。
4
LOS_ERRNO_QUEUE_SIZE_TOO_BIG
0x02000603
队列创建时消息长度凌驾上限。
更改创建队列中最大消息的大小至不凌驾上限。
5
LOS_ERRNO_QUEUE_CB_UNAVAILABLE
0x02000604
已凌驾创建的队列的数量的上限。
增长队列的配置资源数量。
6
LOS_ERRNO_QUEUE_NOT_FOUND
0x02000605
无效的队列。
确保队列ID有效。
7
LOS_ERRNO_QUEUE_PEND_IN_LOCK
0x02000606
当使命被锁定时,克制在队列中被阻塞。
使用队列前解锁使命。
8
LOS_ERRNO_QUEUE_TIMEOUT
0x02000607
等待处置惩罚队列的时间超时。
检查设置的超时时间是否合适。
9
LOS_ERRNO_QUEUE_IN_TSKUSE
0x02000608
阻塞使命的队列不能被删除。
使使命可以或许获得资源而不是在队列中被阻塞。
10
LOS_ERRNO_QUEUE_WRITE_IN_INTERRUPT
0x02000609
在中断处置惩罚程序中不能写队列。
将写队列设为非阻塞模式。
11
LOS_ERRNO_QUEUE_NOT_CREATE
0x0200060a
队列未创建。
检查队列中转达的句柄是否有效。
12
LOS_ERRNO_QUEUE_IN_TSKWRITE
0x0200060b
队列读写不同步。
同队伍列的读写。
13
LOS_ERRNO_QUEUE_CREAT_PTR_NULL
0x0200060c
队列创建过程中转达的参数为空指针。
确保转达的参数不为空指针。
14
LOS_ERRNO_QUEUE_PARA_ISZERO
0x0200060d
队列创建过程中转达的队列长度或消息节点大小为0。
传入精确的队列长度和消息节点大小。
15
LOS_ERRNO_QUEUE_INVALID
0x0200060e
读取队列、写入队列的handle无效。
检查队列中转达的handle是否有效
16
LOS_ERRNO_QUEUE_READ_PTR_NULL
0x0200060f
队列读取过程中转达的指针为空。
检查指针中转达的是否为空。
17
LOS_ERRNO_QUEUE_READSIZE_ISZERO
0x02000610
队列读取过程中转达的缓冲区大小为0。
通过一个精确的缓冲区大小。
18
LOS_ERRNO_QUEUE_WRITE_PTR_NULL
0x02000612
队列写入过程中转达的指针为空。
检查指针中转达的是否为空。
19
LOS_ERRNO_QUEUE_WRITESIZE_ISZERO
0x02000613
队列写入过程中转达的缓冲区大小为0。
通过一个精确的缓冲区大小。
20
LOS_ERRNO_QUEUE_WRITE_SIZE_TOO_BIG
0x02000615
队列写入过程中转达的缓冲区大小比队列大小要大。
减少缓冲区大小,或增大队列节点。
21
LOS_ERRNO_QUEUE_ISFULL
0x02000616
在队列写入过程中没有可用的空闲节点。
确保在队列写入之前,可以使用空闲的节点。
22
LOS_ERRNO_QUEUE_PTR_NULL
0x02000617
正在获取队列信息时转达的指针为空。
检查指针中转达的是否为空。
23
LOS_ERRNO_QUEUE_READ_IN_INTERRUPT
0x02000618
在中断处置惩罚程序中不能读队列。
将读队列设为非阻塞模式。
24
LOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID
0x02000619
正在释放队列的内存时转达的队列的handle无效。
检查队列中转达的handle是否有效。
25
LOS_ERRNO_QUEUE_MAIL_PTR_INVALID
0x0200061a
传入的消息内存池指针为空。
检查指针是否为空。
26
LOS_ERRNO_QUEUE_MAIL_FREE_ERROR
0x0200061b
membox内存释放失败。
传入非空membox内存指针。
27
LOS_ERRNO_QUEUE_ISEMPTY
0x0200061d
队列已空。
确保在读取队列时包含消息。
28
LOS_ERRNO_QUEUE_READ_SIZE_TOO_SMALL
0x0200061f
读缓冲区大小小于队列大小。
增长缓冲区大小,或减小队列节点大小。
平台差异性
无。
注意事项
- 系统可配置的队列资源个数是指:整个系统的队列资源总个数,而非用户能使用的个数。例如:系统软件定时器多占用一个队列资源,那么系统可配置的队列资源就会减少一个。
- 调用 LOS_QueueCreate 函数时所传入的队列名暂时未使用,作为以后的预留参数。
- 队列接口函数中的入参uwTimeOut是指相对时间。
- LOS_QueueReadCopy和LOS_QueueWriteCopy及LOS_QueueWriteHeadCopy是一组接口,LOS_QueueRead和LOS_QueueWrite及LOS_QueueWriteHead是一组接口,两组接口需要配套使用。
- 鉴于LOS_QueueWriteHead和LOS_QueueRead这组接口实际使用的是数据地点,用户必须保证调用LOS_QueueRead获取到的指针所指向内存区域在读队列期间没有被非常修改或释放,否则大概会导致不可预知的后果。
- 鉴于LOS_QueueWrite和LOS_QueueWriteHead和LOS_QueueRead这组接口实际使用的是数据地点,也就意味着实际Write和Read的长度仅仅是一个指针数据,因此用户使用这组接口之前,需确保相关队列在创建时的消息长度为一个指针的长度,避免不必要的浪费和读取失败。
- LOS_QueueWrite和LOS_QueueWriteHead和LOS_QueueRead这组接口正在被逐步废弃,建议使用LOS_QueueWriteCopy和LOS_QueueWriteHeadCopy和LOS_QueueReadCopy做为替换。
编程实例
实例形貌
创建一个队列,两个使命。使命1调用发送接口发送消息;使命2通过接收接口接收消息。
- 通过LOS_TaskCreate创建使命1和使命2。
- 通过LOS_QueueCreate创建一个消息队列。
- 在使命1 send_Entry中发送消息。
- 在使命2 recv_Entry中接收消息。
- 通过LOS_QueueDelete删除队列。
编程示例
- #include "los_task.h"
- #include "los_queue.h"
- static UINT32 g_uwQueue;
- CHAR abuf[] = "test is message x";
-
- /*任务1发送数据*/
- void *send_Entry(void *arg)
- {
- UINT32 i = 0,uwRet = 0;
- UINT32 uwlen = sizeof(abuf);
-
- while (i <5)
- {
- abuf[uwlen -2] = '0' + i;
- i++;
-
- /*将abuf里的数据写入队列*/
- uwRet = LOS_QueueWrite(g_uwQueue, abuf, uwlen, 0);
- if(uwRet != LOS_OK)
- {
- dprintf("send message failure,error:%x\n",uwRet);
- }
-
- LOS_TaskDelay(5);
- }
- }
-
- /*任务2接收数据*/
- void *recv_Entry(void *arg)
- {
- UINT32 uwReadbuf;
- UINT32 uwRet = 0;
-
- while (1)
- {
-
- /*读取队列里的数据存入uwReadbuf里*/
- uwRet = LOS_QueueRead(g_uwQueue, &uwReadbuf, 50, 0);
- if(uwRet != LOS_OK)
- {
- dprintf("recv message failure,error:%x\n",uwRet);
- break;
- }
-
- dprintf("recv message:%s\n", (char *)uwReadbuf);
- LOS_TaskDelay(5);
- }
- /*删除队列*/
- while (LOS_OK != LOS_QueueDelete(g_uwQueue))
- {
- LOS_TaskDelay(1);
- }
-
- dprintf("delete the queue success!\n");
- }
-
- int Example_creat_task(void)
- {
- UINT32 uwRet = 0;
- UINT32 uwTask1, uwTask2;
- TSK_INIT_PARAM_S stInitParam1;
-
- /*创建任务1*/
- stInitParam1.pfnTaskEntry = send_Entry;
- stInitParam1.usTaskPrio = 9;
- stInitParam1.uwStackSize = 0x400;
- stInitParam1.pcName = "sendQueue";
- stInitParam1.uwResved = LOS_TASK_STATUS_DETACHED;
- LOS_TaskLock();//锁住任务,防止新创建的任务比本任务高而发生调度
- uwRet = LOS_TaskCreate(&uwTask1, &stInitParam1);
- if(uwRet != LOS_OK)
- {
- dprintf("create task1 failed!,error:%x\n",uwRet);
- return uwRet;
- }
-
- /*创建任务2*/
- stInitParam1.pfnTaskEntry = recv_Entry;
- uwRet = LOS_TaskCreate(&uwTask2, &stInitParam1);
- if(uwRet != LOS_OK)
- {
- dprintf("create task2 failed!,error:%x\n",uwRet);
- return uwRet;
- }
-
- /*创建队列*/
- uwRet = LOS_QueueCreate("queue", 5, &g_uwQueue, 0, 50);
- if(uwRet != LOS_OK)
- {
- dprintf("create queue failure!,error:%x\n",uwRet);
- }
-
- dprintf("create the queue success!\n");
- LOS_TaskUnlock();//解锁任务,只有队列创建后才开始任务调度
- }
复制代码 结果验证
完整实例代码
sample_queue.c
4.5 变乱
概述
根本概念
变乱是一种实现使命间通讯的机制,可用于实现使命间的同步。一个使命可以等待多个变乱的发生:可以是恣意一个变乱发生时唤醒使命举行变乱处置惩罚;也可以是几个变乱都发生后才唤醒使命举行变乱处置惩罚。
多使命环境下,使命之间通常需要同步使用,一个等待便是一个同步。变乱可以提供一对多、多对多的同步使用。一对多同步模子:一个使命等待多个变乱的触发;多对多同步模子:多个使命等待多个变乱的触发。
使命可以通过创建变乱控制块来实现对变乱的触发和等待使用。
Huawei LiteOS提供的变乱具有如下特点:
- 变乱不与使命相关联,变乱相互独立,一个32位的无符号整型变量,用于标识该使命发生的变乱类型,此中每一位表示一种变乱类型(0表示该变乱类型未发生、1表示该变乱类型已经发生)。
- 变乱仅用于使命间的同步,不提供数据传输功能。
- 多次向使命发送同一变乱类型,等效于只发送一次。
- 答应多个使命对同一变乱举行读写使用。
- 支持变乱读写超时机制。
变乱控制块
- /**
- * @ingroup los_event
- * Event control structure
- */
- typedef struct tagEvent
- {
- UINT32 uwEventID; /**标识发生的事件类型位*/
- LOS_DL_LIST stEventList; /**读取事件任务链表*/
- } EVENT_CB_S, *PEVENT_CB_S;
复制代码 uwEventID:用于标识该使命发生的变乱类型,此中每一位表示一种变乱类型(0表示该变乱类型未发生、1表示该变乱类型已经发生),一共31种变乱类型,第25位系统保存。
变乱读取模式
在读变乱时,可以选择读取模式。读取模式如下:
所有变乱(LOS_WAITMODE_AND):读取掩码中所有变乱类型,只有读取的所有变乱类型都发生了,才华读取成功。
任一变乱(LOS_WAITMODE_OR): 读取掩码中任一变乱类型,读取的变乱中恣意一种变乱类型发生了,就可以读取成功。
清除变乱(LOS_WAITMODE_CLR):这是一种附加读取模式,可以与LOS_WAITMODE_AND和LOS_WAITMODE_OR结合使用。(LOS_WAITMODE_AND| LOS_WAITMODE_CLR或 LOS_WAITMODE_OR| LOS_WAITMODE_CLR),设置该模式读取成功后,对应变乱类型位会自动清除。
运作机制
读变乱时,可以根据入参变乱掩码类型uwEventMask读取变乱的单个或者多个变乱类型。变乱读取成功后,假如设置LOS_WAITMODE_CLR会清除已读取到的变乱类型,反之不会清除已读到的变乱类型,需显式清除。可以通过入参选择读取模式,读取变乱掩码类型中所有变乱还是读取变乱掩码类型中恣意变乱。
写变乱时,对指定变乱写入指定的变乱类型,可以一次同时写多个变乱类型。写变乱会触发使命调理。
清除变乱时,根据入参变乱和待清除的变乱类型,对变乱对应位举行清0使用。
图 1 变乱唤醒使命表示图
开发引导
使用场景
变乱可应用于多种使命同步场合,可以或许一定程度替换信号量。
功能
Huawei LiteOS系统中的变乱模块为用户提供下面几个接口。
功能分类
接口名
形貌
变乱初始化
LOS_EventInit
初始化一个变乱控制块。
读变乱
LOS_EventRead
读取指定变乱类型,超时时间为相对时间:单元为Tick。
写变乱
LOS_EventWrite
写指定的变乱类型。
清除变乱
LOS_EventClear
清除指定的变乱类型。
校验变乱掩码
LOS_EventPoll
根据用户传入的变乱值、变乱掩码及校验模式,返回用户传入的变乱是否符合预期。
烧毁变乱
LOS_EventDestroy
烧毁指定的变乱控制块。
开发流程
使用变乱模块的典范流程如下:
- 调用变乱初始化LOS_EventInit接口,初始化变乱等待队列。
- 写变乱LOS_EventWrite,配置变乱掩码类型。
- 读变乱LOS_EventRead,可以选择读取模式。
- 清除变乱LOS_EventClear,清除指定的变乱类型。
Event错误码
对变乱存在失败的大概性使用,包括变乱初始化,变乱烧毁,变乱读写,变乱清除。
序号
定义
实际值
形貌
参考解决方案
1
LOS_ERRNO_EVENT_SETBIT_INVALID
0x02001c00
变乱ID的第25个bit不能设置为1,因为该位已经作为错误码使用
变乱ID的第25bit置为0
2
LOS_ERRNO_EVENT_READ_TIMEOUT
0x02001c01
读超时
增长等待时间或者重新读取
3
LOS_ERRNO_EVENT_EVENTMASK_INVALID
0x02001c02
入参的变乱ID是无效的
传入有效的变乱ID参数
4
LOS_ERRNO_EVENT_READ_IN_INTERRUPT
0x02001c03
在中断中读取变乱
启动新的使命来获取变乱
5
LOS_ERRNO_EVENT_FLAGS_INVALID
0x02001c04
读取变乱的mode无效
传入有效的mode参数
6
LOS_ERRNO_EVENT_READ_IN_LOCK
0x02001c05
使命锁住,不能读取变乱
解锁使命,再读取变乱
7
LOS_ERRNO_EVENT_PTR_NULL
0x02001c06
传入的参数为空指针
传入非空入参
8
LOS_ERRNO_EVENT_READ_IN_SYSTEM_TASK
0x02001c07
在系统使命中读取变乱,如idle和软件定时器
启动新的使命来获取变乱
9
LOS_ERRNO_EVENT_SHOULD_NOT_DESTORY
0x02001c08
变乱链表上仍有使命,无法被烧毁
先检查变乱链表是否为空
错误码定义: 错误码是一个32位的无符号整型数存储单元,31~24位表示错误品级,23~16位表示错误码标志,15~8位代表错误码所属模块,7~0位表示错误码序号,如下所示:
- #define LOS_ERRNO_OS_ERROR(MID, ERRNO) \
- (LOS_ERRTYPE_ERROR | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
- LOS_ERRTYPE_ERROR:Define critical OS errors
- LOS_ERRNO_OS_ID:OS error code flag
- MID:OS_MOUDLE_ID
- LOS_MOD_EVENT:Event module ID
- ERRNO:error ID number
复制代码 例如:
- #define LOS_ERRNO_EVENT_READ_IN_LOCK LOS_ERRNO_OS_ERROR(LOS_MOD_EVENT, 0x05)
复制代码 平台差异性
无。
注意事项
- 在系统初始化之前不能调用读写变乱接口。假如调用,则系统运行会不正常。
- 在中断中,可以对变乱对象举行写使用,但不能读使用。
- 在锁使命调理状态下,克制使命阻塞与读变乱。
- LOS_EventClear 入参值是:要清除的指定变乱类型的反码(~uwEvents)。
- 变乱掩码的第25位不能使用,原因是为了区别LOS_EventRead接口返回的是变乱还是错误码。
编程实例
实例形貌
示例中,使命Example_TaskEntry创建一个使命Example_Event,Example_Event读变乱阻塞,Example_TaskEntry向该使命写变乱。
- 在使命Example_TaskEntry创建使命Example_Event,此中使命Example_Event优先级高于Example_TaskEntry。
- 在使命Example_Event中读变乱0x00000001,阻塞,发生使命切换,执行使命Example_TaskEntry。
- 在使命Example_TaskEntry向使命Example_Event写变乱0x00000001,发生使命切换,执行使命Example_Event。
- Example_Event得以执行,直到使命竣事。
- Example_TaskEntry得以执行,直到使命竣事。
编程示例
可以通过打印的先后顺序明白变乱使用时伴随的使命切换。
代码实现如下:
- #include "los_event.h"
- #include "los_task.h"
-
- /*任务PID*/
- UINT32 g_TestTaskID01;
-
- /*事件控制结构体*/
- EVENT_CB_S example_event;
-
- /*等待的事件类型*/
- #define event_wait 0x00000001
-
- /*用例任务入口函数*/
- VOID Example_Event()
- {
- UINT32 uwRet;
- UINT32 uwEvent;
-
- /*超时等待方式读事件,超时时间为100 Tick
- 若100 Tick 后未读取到指定事件,读事件超时,任务直接唤醒*/
- printf("Example_Event wait event 0x%x \n",event_wait);
-
- uwEvent = LOS_EventRead(&example_event, event_wait, LOS_WAITMODE_AND, 100);
- if(uwEvent == event_wait)
- {
- printf("Example_Event,read event :0x%x\n",uwEvent);
- }
- else
- printf("Example_Event,read event timeout\n");
- return;
- }
-
- UINT32 Example_TaskEntry()
- {
- UINT32 uwRet;
- TSK_INIT_PARAM_S stTask1;
-
- /*事件初始化*/
- uwRet = LOS_EventInit(&example_event);
- if(uwRet != LOS_OK)
- {
- printf("init event failed .\n");
- return -1;
- }
-
- /*创建任务*/
- memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
- stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event;
- stTask1.pcName = "EventTsk1";
- stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
- stTask1.usTaskPrio = 5;
- uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
- if(uwRet != LOS_OK)
- {
- printf("task create failed .\n");
- return LOS_NOK;
- }
-
- /*写用例任务等待的事件类型*/
- printf("Example_TaskEntry write event .\n");
-
- uwRet = LOS_EventWrite(&example_event, event_wait);
- if(uwRet != LOS_OK)
- {
- printf("event write failed .\n");
- return LOS_NOK;
- }
-
- /*清标志位*/
- printf("EventMask:%d\n",example_event.uwEventID);
- LOS_EventClear(&example_event, ~example_event.uwEventID);
- printf("EventMask:%d\n",example_event.uwEventID);
-
- /*删除任务*/
- uwRet = LOS_TaskDelete(g_TestTaskID01);
- if(uwRet != LOS_OK)
- {
- printf("task delete failed .\n");
- return LOS_NOK;
- }
-
- return LOS_OK;
- }
复制代码 结果验证
编译运行得到的结果为:
- Example_Event wait event 0x1
- Example_TaskEntry write event .
- Example_Event,read event :0x1
- EventMask:1
- EventMask:0
复制代码 完整实例代码
sample_event.c
4.6 互斥锁
概述
根本概念
互斥锁又称互斥型信号量,是一种特别的二值性信号量,用于实现对共享资源的独占式处置惩罚。
恣意时候互斥锁的状态只有两种,开锁或闭锁。当有使命持有时,互斥锁处于闭锁状态,这个使命获得该互斥锁的所有权。当该使命释放它时,该互斥锁被开锁,使命失去该互斥锁的所有权。当一个使命持有互斥锁时,其他使命将不能再对该互斥锁举行开锁或持有。
多使命环境下通常存在多个使命竞争同一共享资源的应用场景,互斥锁可被用于对共享资源的保护从而实现独占式访问。另外,互斥锁可以解决信号量存在的优先级翻转问题。
Huawei LiteOS提供的互斥锁具有如下特点:
运作机制
互斥锁运作原理
多使命环境下会存在多个使命访问同一公共资源的场景,而有些公共资源黑白共享的,需要使命举行独占式处置惩罚。互斥锁怎样来避免这种冲突呢?
用互斥锁处置惩罚非共享资源的同步访问时,假如有使命访问该资源,则互斥锁为加锁状态。此时其他使命假如想访问这个公共资源则会被阻塞,直到互斥锁被持有该锁的使命释放后,其他使命才华重新访问该公共资源,此时互斥锁再次上锁,云云确保同一时候只有一个使命正在访问这个公共资源,保证了公共资源使用的完整性。
图 1 互斥锁运作表示图
开发引导
使用场景
互斥锁可以提供使命之间的互斥机制,用来防止两个使命在同一时候访问相同的共享资源。
功能
Huawei LiteOS 系统中的互斥锁模块为用户提供下面几种功能。
功能分类
接口名
形貌
互斥锁的创建和删除
LOS_MuxCreate
创建互斥锁
LOS_MuxDelete
删除指定的互斥锁
互斥锁的申请和释放
LOS_MuxPend
申请指定的互斥锁
LOS_MuxPost
释放指定的互斥锁
开发流程
互斥锁典范场景的开发流程:
- 创建互斥锁LOS_MuxCreate。
- 申请互斥锁LOS_MuxPend。
申请模式有三种:无阻塞模式、永久阻塞模式、定时阻塞模式。
- 无阻塞模式:使命需要申请互斥锁,若该互斥锁当前没有使命持有,或者持有该互斥锁的使命和申请该互斥锁的使命为同一个使命,则申请成功。
- 永久阻塞模式:使命需要申请互斥锁,若该互斥锁当前没有被占用,则申请成功。否则,该使命进入阻塞态,系统切换到停当使命中优先级最高者继承执行。使命进入阻塞态后,直到有其他使命释放该互斥锁,阻塞使命才会重新得以执行。
- 定时阻塞模式:使命需要申请互斥锁,若该互斥锁当前没有被占用,则申请成功。否则该使命进入阻塞态,系统切换到停当使命中优先级最高者继承执行。使命进入阻塞态后,指定时间超时前有其他使命释放该互斥锁,或者用户指定时间超时后,阻塞使命才会重新得以执行。
- 释放互斥锁LOS_MuxPost。
- 假如有使命阻塞于指定互斥锁,则唤醒最早被阻塞的使命中优先级最高的,该使命进入停当态,并举行使命调理。
- 假如没有使命阻塞于指定互斥锁,则互斥锁释放成功。
- 删除互斥锁LOS_MuxDelete。
互斥锁错误码
对互斥锁存在失败的大概性使用,包括互斥锁创建,互斥锁删除,互斥锁申请,互斥锁释放。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_MUX_NO_MEMORY
0x02001d00
内存哀求失败
减少互斥锁限制数量的上限
2
LOS_ERRNO_MUX_INVALID
0x02001d01
互斥锁不可用
传入有效的互斥锁的ID
3
LOS_ERRNO_MUX_PTR_NULL
0x02001d02
入参为空
确保入参可用
4
LOS_ERRNO_MUX_ALL_BUSY
0x02001d03
没有互斥锁可用
增长互斥锁限制数量的上限
5
LOS_ERRNO_MUX_UNAVAILABLE
0x02001d04
锁失败,因为锁被其他线程使用
等待其他线程解锁或者设置等待时间
6
LOS_ERRNO_MUX_PEND_INTERR
0x02001d05
在中断中使用互斥锁
在中断中克制调用此接口
7
LOS_ERRNO_MUX_PEND_IN_LOCK
0x02001d06
使命调理没有使能,线程等待另一个线程释放锁。
锁使命调理情况下,不答应阻塞调理
设置PEND为非阻塞模式或者使能使命调理
8
LOS_ERRNO_MUX_TIMEOUT
0x02001d07
互斥锁PEND超时
增长等待时间或者设置不绝等待模式
9
LOS_ERRNO_MUX_OVERFLOW
0x02001d08
暂未使用,待扩展
无
10
LOS_ERRNO_MUX_PENDED
0x02001d09
删除正在使用的锁
等待解锁再删除锁
11
LOS_ERRNO_MUX_GET_COUNT_ERR
0x02001d0a
暂未使用,待扩展
无
12
LOS_ERRNO_MUX_REG_ERROR
0x02001d0b
暂未使用,待扩展
无
13
LOS_ERRNO_MUX_PEND_IN_SYSTEM_TASK
0x02001d0c
系统使命中获取mutex,如idle和软件定时器
不要在系统使命中获取mutex
错误码定义: 错误码是一个32位的存储单元,31~24位表示错误品级,23~16位表示错误码标志,15~8位代表错误码所属模块,7~0位表示错误码序号,如下所示:
- #define LOS_ERRNO_OS_ERROR(MID, ERRNO) \
- (LOS_ERRTYPE_ERROR | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
- LOS_ERRTYPE_ERROR:Define critical OS errors
- LOS_ERRNO_OS_ID:OS error code flag
- LOS_MOD_MUX:Mutex module ID
- MID:OS_MOUDLE_ID
- ERRNO:error ID number
复制代码 例如:
- LOS_ERRNO_MUX_TIMEOUT LOS_ERRNO_OS_ERROR(LOS_MOD_MUX, 0x07)
复制代码 平台差异性
无。
注意事项
- 两个使命不能对同一把互斥锁加锁。假如某使命对已被持有的互斥锁加锁,则该使命会被挂起,直到持有该锁的使命对互斥锁解锁,才华执行对这把互斥锁的加锁使用。
- 互斥锁不能在中断服务程序中使用。
- Huawei LiteOS作为实时使用系统需要保证使命调理的实时性,尽量避免使命的长时间阻塞,因此在获得互斥锁之后,应该尽快释放互斥锁。
- 持有互斥锁的过程中,不得再调用LOS_TaskPriSet等接口更改持有互斥锁使命的优先级。
编程实例
实例形貌
本实例实现如下游程。
- 使命Example_TaskEntry创建一个互斥锁,锁使命调理,创建两个使命Example_MutexTask1、Example_MutexTask2,Example_MutexTask2优先级高于Example_MutexTask1,解锁使命调理。
- Example_MutexTask2被调理,永久申请互斥锁,然后使命休眠100Tick,Example_MutexTask2挂起,Example_MutexTask1被唤醒。
- Example_MutexTask1申请互斥锁,等待时间为10Tick,因互斥锁仍被Example_MutexTask2持有,Example_MutexTask1挂起,10Tick后未拿到互斥锁,Example_MutexTask1被唤醒,试图以永久等待申请互斥锁,Example_MutexTask1挂起。
- 100Tick后Example_MutexTask2唤醒, 释放互斥锁后,Example_MutexTask1被调理运行,最后释放互斥锁。
- Example_MutexTask1执行完,300Tick后使命Example_TaskEntry被调理运行,删除互斥锁。
编程示例
前提条件:
- 在los_config.h中,将LOSCFG_BASE_IPC_MUX配置项打开。
- 配好LOSCFG_BASE_IPC_MUX_LIMIT最大的互斥锁个数。
代码实现如下:
- #include "los_mux.h"
- #include "los_task.h"
-
- /*互斥锁句柄ID*/
- MUX_HANDLE_T g_Testmux01;
- /*任务PID*/
- UINT32 g_TestTaskID01;
- UINT32 g_TestTaskID02;
-
- VOID Example_MutexTask1()
- {
- UINT32 uwRet;
-
- printf("task1 try to get mutex, wait 10 Tick.\n");
- /*申请互斥锁*/
- uwRet=LOS_MuxPend(g_Testmux01, 10);
-
- if(uwRet == LOS_OK)
- {
- printf("task1 get mutex g_Testmux01.\n");
- /*释放互斥锁*/
- LOS_MuxPost(g_Testmux01);
- return;
- }
- else if(uwRet == LOS_ERRNO_MUX_TIMEOUT )
- {
- printf("task1 timeout and try to get mutex, wait forever.\n");
- /*申请互斥锁*/
- uwRet = LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
- if(uwRet == LOS_OK)
- {
- printf("task1 wait forever,get mutex g_Testmux01.\n");
- /*释放互斥锁*/
- LOS_MuxPost(g_Testmux01);
- return;
- }
- }
- return;
- }
-
- VOID Example_MutexTask2()
- {
- UINT32 uwRet;
-
- printf("task2 try to get mutex, wait forever.\n");
- /*申请互斥锁*/
- uwRet=LOS_MuxPend(g_Testmux01, LOS_WAIT_FOREVER);
-
- printf("task2 get mutex g_Testmux01 and suspend 100 Tick.\n");
-
- /*任务休眠100 Tick*/
- LOS_TaskDelay(100);
-
- printf("task2 resumed and post the g_Testmux01\n");
- /*释放互斥锁*/
- LOS_MuxPost(g_Testmux01);
- return;
-
- }
-
- UINT32 Example_TaskEntry()
- {
- UINT32 uwRet;
- TSK_INIT_PARAM_S stTask1;
- TSK_INIT_PARAM_S stTask2;
-
- /*创建互斥锁*/
- LOS_MuxCreate(&g_Testmux01);
-
- /*锁任务调度*/
- LOS_TaskLock();
-
- /*创建任务1*/
- memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
- stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
- stTask1.pcName = "MutexTsk1";
- stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
- stTask1.usTaskPrio = 5;
- uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
- if(uwRet != LOS_OK)
- {
- printf("task1 create failed .\n");
- return LOS_NOK;
- }
-
- /*创建任务2*/
- memset(&stTask2, 0, sizeof(TSK_INIT_PARAM_S));
- stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
- stTask2.pcName = "MutexTsk2";
- stTask2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
- stTask2.usTaskPrio = 4;
- uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
- if(uwRet != LOS_OK)
- {
- printf("task2 create failed .\n");
- return LOS_NOK;
- }
-
- /*解锁任务调度*/
- LOS_TaskUnlock();
- /*任务休眠300 Tick*/
- LOS_TaskDelay(300);
-
- /*删除互斥锁*/
- LOS_MuxDelete(g_Testmux01);
-
- /*删除任务1*/
- uwRet = LOS_TaskDelete(g_TestTaskID01);
- if(uwRet != LOS_OK)
- {
- printf("task1 delete failed .\n");
- return LOS_NOK;
- }
- /*删除任务2*/
- uwRet = LOS_TaskDelete(g_TestTaskID02);
- if(uwRet != LOS_OK)
- {
- printf("task2 delete failed .\n");
- return LOS_NOK;
- }
-
- return LOS_OK;
- }
复制代码 结果验证
编译运行得到的结果为:
- task2 try to get mutex, wait forever.
- task2 get mutex g_Testmux01 and suspend 100 ticks.
- task1 try to get mutex, wait 10 ticks.
- task1 timeout and try to get mutex, wait forever.
- task2 resumed and post the g_Testmux01
- task1 wait forever,get mutex g_Testmux01.
复制代码 完整实例代码
sample_mutex.c
4.7 信号量
概述
根本概念
信号量(Semaphore)是一种实现使命间通讯的机制,实现使命之间同步或临界资源的互斥访问。常用于协助一组相互竞争的使命来访问临界资源。
在多使命系统中,各使命之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的寄义分两种情况:
- 0,表示没有积累下来的Post使用,且有大概有在此信号量上阻塞的使命。
- 正值,表示有一个或多个Post下来的释放使用。
以同步为目的的信号量和以互斥为目的的信号量在使用有如下不同:
- 用作互斥时,信号量创建跋文数是满的,在需要使用临界资源时,先取信号量,使其变空,这样其他使命需要使用临界资源时就会因为无法取到信号量而阻塞,从而保证了临界资源的安全。
- 用作同步时,信号量在创建后被置为空,使命1取信号量而阻塞,使命2在某种条件发生后,释放信号量,于是使命1得以进入READY或RUNNING态,从而达到了两个使命间的同步。
运作机制
信号量控制块
- /**
- * @ingroup los_sem
- * Semaphore control structure.
- */
- typedef struct
- {
- UINT8 semStat; /**是否使用标志位*/
- UINT16 semCount; /**信号量计数*/
- UINT32 semID; /**信号量索引号*/
- LOS_DL_LIST semList; /**挂接阻塞于该信号量的任务*/
- }LosSemCB;
复制代码 信号量运作原理
信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,受内存限制),并把所有的信号量初始化成未使用,并加入到未使用链表中供系统使用。
信号量创建,从未使用的信号量链表中获取一个信号量资源,并设定初值。
信号量申请,若其计数器值大于0,则直接减1返回成功。否则使命阻塞,等待别的使命释放该信号量,等待的超时时间可设定。当使命被一个信号量阻塞时,将该使命挂到信号量等待使命队列的队尾。
信号量释放,若没有使命等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待使命队列上的第一个使命。
信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
信号量答应多个使命在同一时候访问同一资源,但会限制同一时候访问此资源的最大使命数目。访问同一资源的使命数达到该资源的最大数量时,会阻塞其他试图获取该资源的使命,直到有使命释放该信号量。
图 1 信号量运作表示图
开发引导
使用场景
信号量是一种非常机动的同步方式,可以运用在多种场合中,实现锁、同步、资源计数等功能,也能方便的用于使命与使命,中断与使命的同步中。
功能
Huawei LiteOS 系统中的信号量模块为用户提供下面几种功能。
功能分类
接口名
形貌
信号量的创建和删除
LOS_SemCreate
创建信号量
LOS_BinarySemCreate
创建二进制信号量
LOS_SemDelete
删除指定的信号量
信号量的申请和释放
LOS_SemPend
申请指定的信号量
LOS_SemPost
释放指定的信号量
开发流程
信号量的开发典范流程:
- 创建信号量LOS_SemCreate。
- 申请信号量LOS_SemPend。
信号量有三种申请模式:无阻塞模式、永久阻塞模式、定时阻塞模式。
- 无阻塞模式:使命需要申请信号量,若当前信号量的使命数没有到信号量设定的上限,则申请成功。否则,立即返回申请失败。
- 永久阻塞模式:使命需要申请信号量,若当前信号量的使命数没有到信号量设定的上限,则申请成功。否则,该使命进入阻塞态,系统切换到停当使命中优先级最高者继承执行。使命进入阻塞态后,直到有其他使命释放该信号量,阻塞使命才会重新得以执行。
- 定时阻塞模式:使命需要申请信号量,若当前信号量的使命数没有到信号量设定的上限,则申请成功。否则,该使命进入阻塞态,系统切换到停当使命中优先级最高者继承执行。使命进入阻塞态后,指定时间超时前有其他使命释放该信号量,或者用户指定时间超时后,阻塞使命才会重新得以执行。
- 释放信号量LOS_SemPost。
- 假如有使命阻塞于指定信号量,则唤醒该信号量阻塞队列上的第一个使命。该使命进入停当态,并举行调理。
- 假如没有使命阻塞于指定信号量,释放信号量成功。
- 删除信号量LOS_SemDelete。
信号量错误码
对大概导致信号量使用失败的情况,包括创建信号量、申请信号量、释放信号量、删除信号量等,均需要返回对应的错误码,以便快速定位错误原因。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_SEM_NO_MEMORY
0x02000700
内存空间不敷。
分配更大的内存分区。
2
LOS_ERRNO_SEM_INVALID
0x02000701
非法传参。
改变传数为合法值。
3
LOS_ERRNO_SEM_PTR_NULL
0x02000702
传入空指针。
传入合法指针。
4
LOS_ERRNO_SEM_ALL_BUSY
0x02000703
信号量控制块不可用。
释放资源信号量资源。
5
LOS_ERRNO_SEM_UNAVAILABLE
0x02000704
定时时间非法。
传入精确的定时时间。
6
LOS_ERRNO_SEM_PEND_INTERR
0x02000705
中断期间非法调用LOS_SemPend。
中断期间克制调用LOS_SemPend。
7
LOS_ERRNO_SEM_PEND_IN_LOCK
0x02000706
使命被锁,无法获得信号量。
在使命被锁时,不能调用LOS_SemPend。
8
LOS_ERRNO_SEM_TIMEOUT
0x02000707
获取信号量时间超时。
将时间设置在合理范围内。
9
LOS_ERRNO_SEM_OVERFLOW
0x02000708
信号量答应pend次数凌驾最大值。
传入合法的值。
10
LOS_ERRNO_SEM_PENDED
0x02000709
等待信号量的使命队列不为空。
唤醒所有等待该型号量的使命后删除该信号量。
11
LOS_ERRNO_SEM_PEND_IN_SYSTEM_TASK
0x0200070a
系统使命中获取sem,如idle和软件定时器
不要在系统使命中获取sem
错误码定义: 错误码是一个32位的存储单元,31~24位表示错误品级,23~16位表示错误码标志,15~8位代表错误码所属模块,7~0位表示错误码序号,如下所示:
- #define LOS_ERRNO_OS_NORMAL(MID,ERRNO) \
- (LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
- LOS_ERRTYPE_NORMAL :Define the error level as critical
- LOS_ERRNO_OS_ID :OS error code flag.
- MID:OS_MOUDLE_ID
- ERRNO:error ID number
复制代码 例如:
- LOS_ERRNO_SEM_NO_MEMORY LOS_ERRNO_OS_ERROR(LOS_MOD_SEM, 0x00))
复制代码 平台差异性
无。
注意事项
- 由于中断不能被阻塞,因此在申请信号量时,阻塞模式不能在中断中使用。
编程实例
实例形貌
本实例实现如下功能:
- 测试使命Example_TaskEntry创建一个信号量,锁使命调理,创建两个使命Example_SemTask1、Example_SemTask2,Example_SemTask2优先级高于Example_SemTask1,两个使命中申请同一信号量,解锁使命调理后两使命阻塞,测试使命Example_TaskEntry释放信号量。
- Example_SemTask2得到信号量,被调理,然后使命休眠20Tick,Example_SemTask2耽误,Example_SemTask1被唤醒。
- Example_SemTask1定时阻塞模式申请信号量,等待时间为10Tick,因信号量仍被Example_SemTask2持有,Example_SemTask1挂起,10Tick后仍未得到信号量,Example_SemTask1被唤醒,试图以永久阻塞模式申请信号量,Example_SemTask1挂起。
- 20Tick后Example_SemTask2唤醒, 释放信号量后,Example_SemTask1得到信号量被调理运行,最后释放信号量。
- Example_SemTask1执行完,40Tick后使命Example_TaskEntry被唤醒。执行删除信号量,删除两个使命。
编程示例
前提条件:
- 在los_config.h中,将LOSCFG_BASE_IPC_SEM配置为YES。
- 配置用户定义的LOSCFG_BASE_IPC_SEM_LIMIT最大的信号量数,如1024。
代码实现如下:
- #include "los_sem.h"
-
- /*任务PID*/
- static UINT32 g_TestTaskID01,g_TestTaskID02;
- /*测试任务优先级*/
- #define TASK_PRIO_TEST 5
- /*信号量结构体ID*/
- static SEM_HANDLE_T g_usSemID;
-
-
- VOID Example_SemTask1(void)
- {
- UINT32 uwRet;
-
- printf("Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.\n");
- /*定时阻塞模式申请信号量,定时时间为10Tick*/
- uwRet = LOS_SemPend(g_usSemID, 10);
-
- /*申请到信号量*/
- if(LOS_OK == uwRet)
- {
- LOS_SemPost(g_usSemID);
- return;
- }
- /*定时时间到,未申请到信号量*/
- if(LOS_ERRNO_SEM_TIMEOUT == uwRet)
- {
- printf("Example_SemTask1 timeout and try get sem g_usSemID wait forever.\n");
- /*永久阻塞模式申请信号量*/
- uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
- printf("Example_SemTask1 wait_forever and get sem g_usSemID .\n");
- if(LOS_OK == uwRet)
- {
- LOS_SemPost(g_usSemID);
- return;
- }
- }
- return;
-
- }
- VOID Example_SemTask2(void)
- {
- UINT32 uwRet;
- printf("Example_SemTask2 try get sem g_usSemID wait forever.\n");
- /*永久阻塞模式申请信号量*/
- uwRet = LOS_SemPend(g_usSemID, LOS_WAIT_FOREVER);
-
- if(LOS_OK == uwRet)
- printf("Example_SemTask2 get sem g_usSemID and then delay 20ticks .\n");
-
- /*任务休眠20 Tick*/
- LOS_TaskDelay(20);
-
- printf("Example_SemTask2 post sem g_usSemID .\n");
- /*释放信号量*/
- LOS_SemPost(g_usSemID);
-
- return;
-
- }
- UINT32 Example_TaskEntry()
- {
- UINT32 uwRet;
- TSK_INIT_PARAM_S stTask1;
- TSK_INIT_PARAM_S stTask2;
-
- /*创建信号量*/
- LOS_SemCreate(0,&g_usSemID);
-
- /*锁任务调度*/
- LOS_TaskLock();
-
- /*创建任务1*/
- memset(&stTask1, 0, sizeof(TSK_INIT_PARAM_S));
- stTask1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask1;
- stTask1.pcName = "MutexTsk1";
- stTask1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
- stTask1.usTaskPrio = TASK_PRIO_TEST;
- uwRet = LOS_TaskCreate(&g_TestTaskID01, &stTask1);
- if(uwRet != LOS_OK)
- {
- printf("task1 create failed .\n");
- return LOS_NOK;
- }
-
- /*创建任务2*/
- memset(&stTask2, 0, sizeof(TSK_INIT_PARAM_S));
- stTask2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_SemTask2;
- stTask2.pcName = "MutexTsk2";
- stTask2.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
- stTask2.usTaskPrio = (TASK_PRIO_TEST - 1);
- uwRet = LOS_TaskCreate(&g_TestTaskID02, &stTask2);
- if(uwRet != LOS_OK)
- {
- printf("task2 create failed .\n");
- return LOS_NOK;
- }
-
- /*解锁任务调度*/
- LOS_TaskUnlock();
-
- uwRet = LOS_SemPost(g_usSemID);
-
- /*任务休眠40 Tick*/
- LOS_TaskDelay(40);
-
- /*删除信号量*/
- LOS_SemDelete(g_usSemID);
-
- /*删除任务1*/
- uwRet = LOS_TaskDelete(g_TestTaskID01);
- if(uwRet != LOS_OK)
- {
- printf("task1 delete failed .\n");
- return LOS_NOK;
- }
- /*删除任务2*/
- uwRet = LOS_TaskDelete(g_TestTaskID02);
- if(uwRet != LOS_OK)
- {
- printf("task2 delete failed .\n");
- return LOS_NOK;
- }
-
- return LOS_OK;
- }
复制代码 结果验证
编译运行得到的结果为:
- Example_SemTask2 try get sem g_usSemID wait forever.
- Example_SemTask1 try get sem g_usSemID ,timeout 10 ticks.
- Example_SemTask2 get sem g_usSemID and then delay 20ticks .
- Example_SemTask1 timeout and try get sem g_usSemID wait forever.
- Example_SemTask2 post sem g_usSemID .
- Example_SemTask1 wait_forever and get sem g_usSemID .
复制代码 完整实例代码
sample_sem.c
4.8 时间管理
概述
根本概念
时间管理以系统时钟为底子。时间管理提供给应用程序所有和时间有关的服务。
系统时钟是由定时/计数器产生的输出脉冲触发中断而产生的,一般定义为整数或长整数。输出脉冲的周期叫做一个“时钟滴答”。系统时钟也称为时标或者Tick。一个Tick的时长可以静态配置。
用户是以秒、毫秒为单元计时,而使用系统时钟芯片CPU的计时是以Tick为单元的,当用户需要对系统使用时,例如使命挂起、延时等,输入秒为单元的数值,此时需要时间管理模块对二者举行转换。
Tick与秒之间的对应关系可以配置。
Huawei LiteOS的时间管理模块提供时间转换、统计、耽误功能以满足用户对时间相关需求的实现。
相关概念
系统最小的计时单元。Cycle的时长由系统主频决定,系统主频就是每秒钟的Cycle数。
Tick是使用系统的根本时间单元,对应的时长由系统主频及每秒Tick数决定,由用户配置。
开发引导
使用场景
用户需要了解当前系统运行的时间以及Tick与秒、毫秒之间的转换关系等。
功能
Huawei LiteOS系统中的时间管理主要提供以下两种功能:
- 时间转换:根据主频实现CPU Tick数到毫秒、微秒的转换。
- 时间统计:获取系统Tick数。
功能分类
接口名
形貌
时间转换
LOS_MS2Tick
毫秒转换成Tick。
LOS_Tick2MS
Tick转化为毫秒。
时间统计
LOS_CyclePerTickGet
每个Tick多少Cycle数。
LOS_TickCountGet
获取当前的Tick数。
时间管理错误码
时间转换存在出错的大概性,需要返回对应的错误码,以便快速定位错误原因。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_SYS_PTR_NULL
0x02000010
入参指针为空
检查入参,传入非空入参
2
LOS_ERRNO_SYS_CLOCK_INVALID
0x02000011
无效的系统时钟配置
在los_config.h配置有效的时钟
3
LOS_ERRNO_SYS_MAXNUMOFCORES_IS_INVALID
0x02000012
错误码暂时没有使用
错误码暂时没有使用
4
LOS_ERRNO_SYS_PERIERRCOREID_IS_INVALID
0x02000013
错误码暂时没有使用
错误码暂时没有使用
5
LOS_ERRNO_SYS_HOOK_IS_FULL
0x02000014
错误码暂时没有使用
错误码暂时没有使用
开发流程
时间管理的典范开发流程:
- 确认配置项LOSCFG_BASE_CORE_TICK_HW_TIME为YES开启状态。
- 在los_config.h中配置每秒的Tick数LOSCFG_BASE_CORE_TICK_PER_SECOND。
- 调用时钟转换接口。
- 获取系统Tick数完成时间统计。
- 通过LOS_TickCountGet获取系统当前Tick值。
注意事项
- 获取系统Tick数需要在系统时钟使能之后。
- 时间管理不是单独的功能模块,依靠于los_config.h中的OS_SYS_CLOCK和LOSCFG_BASE_CORE_TICK_PER_SECOND两个配置选项。
- 系统的Tick数在关中断的情况下不举行计数,故系统Tick数不能作为准确时间计算。
编程实例
实例形貌
在下面的例子中,介绍了时间管理的根本方法,包括:
- 时间转换:将毫秒数转换为Tick数,或将Tick数转换为毫秒数。
- 时间统计和时间耽误:统计每秒的Cycle数、Tick数和耽误后的Tick数。
阐明:
示例中系统时钟频率为80MHZ。
编程示例
前提条件:
- 配好LOSCFG_BASE_CORE_TICK_PER_SECOND每秒的Tick数。
- 配好OS_SYS_CLOCK 系统时钟,单元: Hz。
时间转换:
- VOID Example_TransformTime(VOID)
- {
- UINT32 uwMs;
- UINT32 uwTick;
- uwTick = LOS_MS2Tick(10000);//10000 ms数转换为Tick数
- printf("uwTick = %d \n",uwTick);
- uwMs= LOS_Tick2MS(100);//100 Tick数转换为ms数
- printf("uwMs = %d \n",uwMs);
- }
复制代码 时间统计和时间耽误:
- VOID Example_GetTime(VOID)
- {
- UINT32 uwcyclePerTick;
- UINT64 uwTickCount;
-
- uwcyclePerTick = LOS_CyclePerTickGet();//每个Tick多少Cycle数
- if(0 != uwcyclePerTick)
- {
- dprintf("LOS_CyclePerTickGet = %d \n", uwcyclePerTick);
- }
-
- uwTickCount = LOS_TickCountGet();//获取Tick数
- if(0 != uwTickCount)
- {
- dprintf("LOS_TickCountGet = %d \n", (UINT32)uwTickCount);
- }
- LOS_TaskDelay(200);//延迟200 Tick
- uwTickCount = LOS_TickCountGet();
- if(0 != uwTickCount)
- {
- dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)uwTickCount);
- }
- }
复制代码 结果验证
编译运行得到的结果为:
时间转换:
- uwTick = 1000
- uwMs = 1000
复制代码 时间统计和时间耽误:
- LOS_CyclePerTickGet = 495000
- LOS_TickCountGet = 1
- LOS_TickCountGet after delay = 201
复制代码 完整实例代码
sample_time.c
4.9 软件定时器
概述
根本概念
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器,当颠末设定的Tick时钟计数值后会触发用户定义的回调函数。定时精度与系统Tick时钟的周期有关。
硬件定时器受硬件的限制,数量上不敷以满足用户的实际需求,因此为了满足用户需求,提供更多的定时器,Huawei LiteOS使用系统提供软件定时器功能。
软件定时器扩展了定时器的数量,答应创建更多的定时业务。
软件定时器功能上支持:
- 静态裁剪:能通过宏关闭软件定时器功能。
- 软件定时器创建。
- 软件定时器启动。
- 软件定时器制止。
- 软件定时器删除。
- 软件定时器剩余Tick数获取。
运作机制
软件定时器是系统资源,在模块初始化的时候已经分配了一块一连的内存,系统支持的最大定时器个数由los_config.h中的LOSCFG_BASE_CORE_SWTMR_LIMIT宏配置。
软件定时器使用了系统的一个队列和一个使命资源,软件定时器的触发遵循队列规则,先辈先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
软件定时器以Tick为根本计时单元,当用户创建并启动一个软件定时器时,Huawei LiteOS会根据当前系统Tick时间及用户设置的定时间隔确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
当Tick中断到来时,在Tick中断处置惩罚函数中扫描软件定时器的计时全局链表,看是否有定时器超时,若有则将超时的定时器记载下来。
Tick中断处置惩罚函数竣事后,软件定时器使命(优先级为最高)被唤醒,在该使命中调用之前记载下来的定时器的超时回调函数。
定时器状态
- OS_SWTMR_STATUS_UNUSED(未使用)
系统在定时器模块初始化的时候将系统中所有定时器资源初始化成该状态。
- OS_SWTMR_STATUS_CREATED(创建未启动/制止)
在未使用状态下调用LOS_SwtmrCreate接口或者启动后调用LOS_SwtmrStop接口后,定时器将变成该状态。
- OS_SWTMR_STATUS_TICKING(计数)
在定时器创建后调用LOS_SwtmrStart接口,定时器将变成该状态,表示定时器运行时的状态。
定时器模式
Huawei LiteOS的软件定时器提供二类定时器机制:
- 第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器变乱,然后定时器自动删除。
- 第二类是周期触发定时器,这类定时器会周期性的触发定时器变乱,直到用户手动地制止定时器,否则将永远持续执行下去。
- 第三类也是单次触发定时器,但与第一类不同之处在于这类定时器超时后不会自动删除,需要调用定时器删除接口删除定时器。
开发引导
使用场景
- 创建一个单次触发的定时器,超时后执行用户自定义的回调函数。
- 创建一个周期性触发的定时器,超时后执行用户自定义的回调函数。
功能
Huawei LiteOS系统中的软件定时器模块为用户提供下面几种功能,下面具体的API详见软件定时器对外接口手册。
表 1
功能分类
接口名
形貌
创建、删除定时器
LOS_SwtmrCreate
创建定时器
LOS_SwtmrDelete
删除定时器
启动、制止定时器
LOS_SwtmrStart
启动定时器
LOS_SwtmrStop
制止定时器
获得软件定时器剩余Tick数
LOS_SwtmrTimeGet
获得软件定时器剩余Tick数
开发流程
软件定时器的典范开发流程:
- 配置软件定时器。
- 确认配置项LOSCFG_BASE_CORE_SWTMR和LOSCFG_BASE_IPC_QUEUE为YES打开状态。
- 配置LOSCFG_BASE_CORE_SWTMR_LIMIT最大支持的软件定时器数。
- 配置OS_SWTMR_HANDLE_QUEUE_SIZE软件定时器队列最大长度。
- 创建定时器LOS_SwtmrCreate。
- 创建一个指定计时时长、指定超时处置惩罚函数、指定触发模式的软件定时器。
- 返回函数运行结果,成功或失败。
- 启动定时器LOS_SwtmrStart。
- 获得软件定时器剩余Tick数LOS_SwtmrTimeGet。
- 制止定时器LOS_SwtmrStop。
- 删除定时器LOS_SwtmrDelete。
软件定时器错误码
对软件定时器存在失败大概性的使用,包括创建、删除、停息、重启定时器等等,均需要返回对应的错误码,以便快速定位错误原因。
序号
定义
实际数值
形貌
参考解决方案
1
LOS_ERRNO_SWTMR_PTR_NULL
0x02000300
软件定时器回调函数为空。
定义软件定时器回调函数。
2
LOS_ERRNO_SWTMR_INTERVAL_NOT_SUITED
0x02000301
软件定时器间隔时间为0。
重新定义间隔时间。
3
LOS_ERRNO_SWTMR_MODE_INVALID
0x02000302
不精确的软件定时器模式。
确认软件定时器模式,范围为[0,2]。
4
LOS_ERRNO_SWTMR_RET_PTR_NULL
0x02000303
软件定时器ID指针入参为NULL。
定义ID变量,传入指针。
5
LOS_ERRNO_SWTMR_MAXSIZE
0x02000304
软件定时器个数凌驾最大值。
重新定义软件定时器最大个数,或者等待一个软件定时器释放资源。
6
LOS_ERRNO_SWTMR_ID_INVALID
0x02000305
不精确的软件定时器ID入参。
确保入参合法。
7
LOS_ERRNO_SWTMR_NOT_CREATED
0x02000306
软件定时器未创建。
创建软件定时器。
8
LOS_ERRNO_SWTMR_NO_MEMORY
0x02000307
软件定时器链表创建内存不敷。
申请一块足够大的内存供软件定时器使用。
9
LOS_ERRNO_SWTMR_MAXSIZE_INVALID
0x02000308
不精确的软件定时器个数最大值。
重新定义该值。
10
LOS_ERRNO_SWTMR_HWI_ACTIVE
0x02000309
在中断中使用定时器。
修改源代码确保不在中断中使用。
11
LOS_ERRNO_SWTMR_HANDLER_POOL_NO_MEM
0x0200030a
membox内存不敷。
扩大内存。
12
LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED
0x0200030b
软件定时器队列创建失败。
检查用以创建队列的内存是否足够。
13
LOS_ERRNO_SWTMR_TASK_CREATE_FAILED
0x0200030c
软件定时器使命创建失败。
检查用以创建软件定时器使命的内存是否足够并重新创建。
14
LOS_ERRNO_SWTMR_NOT_STARTED
0x0200030d
未启动软件定时器。
启动软件定时器。
15
LOS_ERRNO_SWTMR_STATUS_INVALID
0x0200030e
不精确的软件定时器状态。
检查确认软件定时器状态。
16
LOS_ERRNO_SWTMR_SORTLIST_NULL
null
暂无。
该错误码暂不使用。
17
LOS_ERRNO_SWTMR_TICK_PTR_NULL
0x02000310
用以获取软件定时器超时tick数的入参指针为NULL。
创建一个有效的变量。
18
LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED
0x02000311
软件定时器链表创建失败
检查内存是否够用,然后重新创建链表
错误码定义: 错误码是一个32位的存储单元,31~24位表示错误品级,23~16位表示错误码标志,15~8位代表错误码所属模块,7~0位表示错误码序号,如下所示:
- #define LOS_ERRNO_OS_NORMAL(MID,ERRNO) \
- (LOS_ERRTYPE_NORMAL | LOS_ERRNO_OS_ID | ((UINT32)(MID) << 8) | (ERRNO))
- LOS_ERRTYPE_NORMAL :Define the error level as critical
- LOS_ERRNO_OS_ID :OS error code flag.
- MID:OS_MOUDLE_ID
- ERRNO:error ID number
复制代码 例如:
- #define LOS_ERRNO_SWTMR_PTR_NULL \
- LOS_ERRNO_OS_ERROR(LOS_MOD_SWTMR, 0x00)
复制代码 注意事项
- 软件定时器的回调函数中不要做过多使用,不要使用大概引起使命挂起或者阻塞的接口或使用。
- 软件定时器使用了系统的一个队列和一个使命资源,软件定时器使命的优先级设定为0,且不答应修改 。
- 系统可配置的软件定时器资源个数是指:整个系统可使用的软件定时器资源总个数,而并非是用户可使用的软件定时器资源个数。例如:系统软件定时器多占用一个软件定时器资源数,那么用户能使用的软件定时器资源就会减少一个。
- 创建单次软件定时器,该定时器超时执行完回调函数后,系统会自动删除该软件定时器,并回收资源。
- 创建单次不自删除属性的定时器,用户需要调用定时器删除接口删除定时器,回收定时器资源,避免资源走漏。
编程实例
实例形貌
在下面的例子中,演示如下功能:
- 软件定时器创建、启动、删除、停息、重启使用。
- 单次软件定时器,周期软件定时器使用方法。
编程示例
前提条件:
- 在los_config.h中,将LOSCFG_BASE_CORE_SWTMR配置项打开。
- 配置好LOSCFG_BASE_CORE_SWTMR_LIMIT最大支持的软件定时器数。
- 配置好OS_SWTMR_HANDLE_QUEUE_SIZE软件定时器队列最大长度。
代码实现如下:
- void Timer1_Callback(uint32_t arg); // callback fuction
-
- void Timer2_Callback(uint32_t arg);
-
- UINT32 g_timercount1 = 0;
- UINT32 g_timercount2 = 0;
-
- void Timer1_Callback(uint32_t arg)//回调函数1
- {
- unsigned long tick_last1;
- g_timercount1++;
- tick_last1=(UINT32)LOS_TickCountGet();//获取当前Tick数
- dprintf("g_timercount1=%d\n",g_timercount1);
- dprintf("tick_last1=%d\n",tick_last1);
-
- }
-
- void Timer2_Callback(uint32_t arg)//回调函数2
- {
- unsigned long tick_last2;
- tick_last2=(UINT32)LOS_TickCountGet();
- g_timercount2 ++;
- dprintf("g_timercount2=%d\n",g_timercount2);
- dprintf("tick_last2=%d\n",tick_last2);
- }
-
- void Timer_example (void) {
- UINT16 id1;
- UINT16 id2;// timer id
- UINT32 uwTick;
- /*创建单次软件定时器,Tick数为1000,启动到1000Tick数时执行回调函数1 */
- LOS_SwtmrCreate (1000, LOS_SWTMR_MODE_ONCE,Timer1_Callback,&id1,1);
- /*创建周期性软件定时器,每100Tick数执行回调函数2 */
- LOS_SwtmrCreate(100,LOS_SWTMR_MODE_PERIOD,Timer2_Callback,&id2,1);
- dprintf("create Timer1 success\n");
-
- LOS_SwtmrStart (id1); //启动单次软件定时器
- dprintf("start Timer1 sucess\n");
-
- LOS_TaskDelay(200);//延时200Tick数
- LOS_SwtmrTimeGet(id1,&uwTick);//获得单次软件定时器剩余Tick数
- dprintf("uwTick =%d\n",uwTick);
-
- LOS_SwtmrStop(id1);//停止软件定时器
- dprintf("stop Timer1 sucess\n");
-
- LOS_SwtmrStart(id1);
- LOS_TaskDelay(1000);
- LOS_SwtmrDelete(id1);//删除软件定时器
- dprintf("delete Timer1 sucess\n");
-
- LOS_SwtmrStart(id2);//启动周期性软件定时器
- dprintf("start Timer2\n");
-
- LOS_TaskDelay(1000);
- LOS_SwtmrStop(id2);
- LOS_SwtmrDelete(id2);
- }
复制代码 结果验证
得到的结果为:
完整实例代码
sample_Timer.c
4.10 错误处置惩罚
概述
根本概念
错误处置惩罚指用户代码发生错误时,系统调用错误处置惩罚模块的接口函数,完成上报错误信息,并调用用户自己的钩子函数,举行特定的处置惩罚。
错误处置惩罚模块实现OS内部错误码记载功能。OS内部错误码无法通过接口返回,通常会上报错误处置惩罚模块举行记载。用户可以通过挂接错误处置惩罚的钩子函数,举行特定的处置惩罚。假如OS上报的错误是致命错误,系统会举行非常流程接管,从而可以保存现场以便定位问题。
通过错误处置惩罚,我们可以把用户在程序中的非法输入举行控制和提示,以防程序崩溃。
运作机制
错误处置惩罚是一种机制,用于处置惩罚非常状况。当程序出现非常错误的时候,会显示相应的错误码用于提示用户。别的,假如有相应的错误处置惩罚程序,则会执行这个程序。
图 1 错误处置惩罚表示图
开发引导
功能
错误处置惩罚模块为用户提供下面几种功能。
功能分类
接口名
形貌
错误处置惩罚
LOS_ErrHandle
根据错误处置惩罚函数来对错误举行处置惩罚
注意事项
无。
编程实例
实例形貌
在下面的例子中,演示如下功能:
执行错误处置惩罚函数。
编程示例
代码实现如下:
- extern USER_ERR_FUNC_S g_stUserErrFunc;
- void err_handler(CHAR *pcFileName,UINT32 uwLineNo,
- UINT32 uwErrorNo,UINT32 uwParaLen,VOID *pPara)
- {
- printf("err handle ok\n");
- }
- UINT32 Example_ErrCaseEntry(VOID)
- {
- g_stUserErrFunc.pfnHook = err_handler;
- /*执行错误处理函数*/
- LOS_ErrHandle(NULL, 0,0,0, NULL);
- return LOS_OK;
- }
复制代码 结果验证
编译运行得到的结果为:
- Huawei LiteOS # err handle ok
复制代码 完整实例代码
sample_err.c
4.11 双向链表
概述
根本概念
双向链表是指含有往前和往后两个方向的链表,即每个结点中除存放下一个节点指针外,还增长一个指向其前一个节点的指针。其头指针head是唯一确定的。
从双向链表中的恣意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这种数据结构情势使得双向链表在查找时更加方便,特别是大量数据的遍历。由于双向链表具有对称性,能方便地完成各种插入、删除等使用,但需要注意前后方向的使用。
开发引导
功能
Huawei LiteOS系统中的双向链表模块为用户提供下面几个接口。
功能分类
接口名
形貌
初始化链表
LOS_ListInit
对链表举行初始化。
增长节点
LOS_ListAdd
将新节点添加到链表中。
在链表尾端插入节点
LOS_ListTailInsert
将节点插入到双向链表尾端。
在链表头端插入节点
LOS_ListHeadInsert
将节点插入到双向链表头端
删除节点
LOS_ListDelete
将指定的节点从链表中删除。
判断双向链表是否为空
LOS_ListEmpty
判断链表是否为空。
删除节点并初始化链表
LOS_ListDelInit
将指定的节点从链表中删除,使用该节点初始化链表。
开发流程
双向链表的典范开发流程:
- 调用LOS_ListInit初始双向链表。
- 调用LOS_ListAdd向链表中增长节点。
- 调用LOS_ListTailInsert向链表尾部插入节点。
- 调用LOS_ListDelete删除指定节点。
- 调用LOS_ListEmpty判断链表是否为空。
- 调用LOS_ListDelInit删除指定节点并以此节点初始化链表。
注意事项
编程实例
实例形貌
使用双向链表,首先要申请内存,删除节点的时候要注意释放掉内存。
本实例实现如下功能:
- 调用函数举行初始化双向链表。
- 增长节点。
- 删除节点。
- 测试使用是否成功。
编程示例
代码实现如下:
- #include "stdio.h"
- #include "los_list.h"
- #ifdef __cplusplus
- #if __cplusplus
- extern "C" {
- #endif /* __cpluscplus */
- #endif /* __cpluscplus */
- static UINT32 DLlist_sample(VOID)
- {
- LOS_DL_LIST DLlist = {NULL,NULL};
- LOS_DL_LIST DLlistNode01 = {NULL,NULL};
- LOS_DL_LIST DLlistNode02 = {NULL,NULL};
- LOS_DL_LIST DLlistNode03 = {NULL,NULL};
- PRINTK("Initial head\n");
- LOS_ListInit(&DLlist);
- LOS_ListAdd(&DLlist,&DLlistNode01);
- if (DLlistNode01.pstNext == &DLlist && DLlistNode01.pstPrev == &DLlist)
- {
- PRINTK("Add DLlistNode01 success \n");
- }
- LOS_ListTailInsert(&DLlist,&DLlistNode02);
- if (DLlistNode02.pstNext == &DLlist && DLlistNode02.pstPrev == &DLlistNode01)
- {
- PRINTK("Tail insert DLlistNode02 success \n");
- }
- LOS_ListHeadInsert(&DLlistNode02,&DLlistNode03);
- if (DLlistNode03.pstNext == &DLlist && DLlistNode03.pstPrev == &DLlistNode02)
- {
- PRINTK("Head insert DLlistNode03 success \n");
- }
- LOS_ListDelInit(&DLlistNode03);
- LOS_ListDelete(&DLlistNode01);
- LOS_ListDelete(&DLlistNode02);
- if (LOS_ListEmpty(&DLlist))
- {
- PRINTK("Delete success \n");
- }
- return LOS_OK;
- }
- #ifdef __cplusplus
- #if __cplusplus
- }
- #endif /* __cpluscplus */
- #endif /* __cpluscplus */
复制代码 结果验证
编译运行得到的结果为:
- Initial head
- Add DLlistNode01 success
- Tail insert DLlistNode02 success
- Head insert DLlistNode03 success
- Delete success
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |