Event Driven Class OSAL 基于事件驱动的模拟操作系统

本文介绍了EventDrivenClassOSAL,一个轻量级的、基于事件驱动的模拟操作系统,适用于低端MCU。该系统采用C语言编写,具有小内存占用和简洁代码的特点。OSAL提供了任务、事件、定时器和消息队列等核心功能,通过事件调度实现任务的执行。此外,还介绍了OSAL与RTOS的区别,并强调了其在资源有限的设备上的适用性。文章还涵盖了系统移植、硬件抽象层(HAL)和内存管理等内容。
摘要由CSDN通过智能技术生成

EventDrivenClassOSAL详解

Event Driven Class OSAL
基于事件驱动的模拟操作系统

##前言

什么是OSAL

  • OSAL为:Operating System Abstraction Layer,即“操作系统抽象层”,支持多任务运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能。
  • 以上原文来自于网络
  • OSAL概念是由TI公司在ZIGBEE协议栈引入,他的意思是"操作系统抽象层",我认为叫做"模拟操作系统"更为合适,它并非一个真正的OS,而是模拟OS的一些方法为广大编程者提供一种编写MCU程序的方法,是一个架构,一种思维模式;当有一个事件发生的时候,OSAL负责将此事件分配给能够处理此事件的任务,然后此任务判断事件的类型,调用相应的事件处理程序进行处理。
  • OSAL_EventDrivenClass借鉴了TI OSAL/NXP RTOS/FreeRTOS/RT-Thread等操作系统优势,兼顾了代码小/逻辑简单/等优势,适用于低端MCU开发使用.
  • 网络上有大量把OSAL叫做“操作系统抽象层”,而我认为“操作系统抽象层”这个词应该说是为了抽象不同操作系统的API,将系统API统一,然后我们所看到OASL并非此功能,这里不做过多讨论。
    OSAL和RTOS的区别
  • 本人理解OSAL只是一个裸机编码框架,并非操作系统,然后OSAL实现了让裸机写程序,就像在操作系统上写程序一样简单,所以OSAL是一个适用于低端MCU的编程框架;为了简化后面文档,后面把OSAL当做操作系统处理。
  • RTOS实时操作系统,开源的RTOS主要包括RT-Thread、Huawei LiteOS、AliOS Things、TencentOS-tiny、FreeRTOS、Arm Mbed OS、MS-RTOS、Zephyr、Contiki-NG、NuttX、RIOT、Apache Mynewt、Drone、eCos、F9 Microkernel、Tock、Mark3、Atomthreads、Trampoline等。
  • RTOS和OSAL本质上的区别:
    • RTOS具备任务(线程)调度和切换功能,具备任务优先级和任务抢占等功能,把一个CPU划分的弱干个片运行,让开发者把一个CPU看成多个CPU来处理。
    • OSAL不具备多任务(线程)调度和切换功能,任务无优先级和抢占功能,采用轮询方式调用函数,利用TICK或TIME定时,实现了部分OS的功能。
      EventDrivenClassOSAL:
  • 是一个由事件驱动类的OSAL,顾名思义此OSAL完全由事件进行驱动,没有事件任务就没有事干,那么OSAL就会调度空闲任务,等待事件的发生.
    EventDrivenClassOSAL特点:
  • 此系统完全由C语言编写,不会涉及汇编,且代码量非常少,整个系统不足1000行;适合初学者学习和使用。
  • 内存占用小,特别是对栈的占用级低,完成可以做到同裸机编程占用一样的栈空间;适用于各类低端MCU。
  • OSAL实现了类似RTOS的编程思路,使代码更利于模块化设计,一个应用程序有多个独立的小功能模块组成,可使模块代码偶合度极低,便于多人协作开发,模块的重复使用。
    EventDrivenClassOSAL适用性:
  • 依赖于一个定时器,可适用于各种MCU,包含传统8051。
  • 此OSAL适用于对实时性不严格,对产品成本严格控制,使用较低端的MCU的场景.可适于以上场景的用绝大部分应用.
    关于内存占用情况(V1.x版本):
  • 基于新唐NUVOTON M0-58MCU硬件平台,使用官方标准库,一个示例代码(代码包括4个按键驱动示例程序,一个软件BUZZ驱动程序,2个任务,用来作按键消息分发和按键消息处理,可以说一个简单的应用已经完成80%.)内存使用情况如下:
  • Program Size: Code=6132 RO-data=472 RW-data=64 ZI-data=672
  • 可以看出OSAL内存是占用远远于一般的OS,并且OSAL已经实现任务的消息队,列把任务/事件/定时器的堆空间已经包括,在写应用代码时不会在重复创建.
    可移植性:
  • 目前我公司将OSAL成功的在GD/STM32/HC32等众多MCU上应用。
    陷阱:
  • 由于OSAL只是一个简单的编程架构,为了节能代码量和资源的占用,提供代码运行效率,没有过多考虑安全性,请不要用于对安全性要求较高的产品。
  • OSAL非多线程多任务,实际上还一个单任务在死循环,所以要理解系统的工作原理,以免因不明白运行原理给自己挖些陷阱。
    鸣谢:
  • 特别感谢我的同事“罗天浩”提供了OSAL的部分框架,特别是万能的消息队列,使OS部得更简洁.
  • 感谢业界各位朋友提供宝贵的意见和建议,如“ Seven Pounds 发现队列未进入临界保护等”.
    声明:
  • 文档中部分见解属于个人见解,未经过验证(如:OSAL定义),如果错误敬请谅解,欢迎批评指导.
    愿景:
  • 帮助使用低端MCU的嵌入式开始从业者,不断提关工作效率,实现代码的复用性。
    源代码下载:
  • 最新的版本,请加企鹅群413012273,在共享文件中获得。

软件整体架构

在这里插入图片描述

OASL采用3层架构:硬件层,OASL+HAL层,应用层

第一层

LIB层:为芯片原创提供的库文件

第二层

  • OSAL:为系统层,为用户提供编程框架.
  • HAL:为驱动抽象层,抽象标准驱动库,让应用与硬件隔离.
  • HAL不依赖于OSAL,是一个独立的框架,用户在不需要OSAL的时候也可单独使用HAL
  • OSAL不依赖于HAL,用户可以跳过HAL支持操作LIB层,实现一些特定的功能.

第三层

应用层,用户自己应用程序.

OSAL详解
概要
OSAL框架
在这里插入图片描述

系统硬件层(sHardware):

TICK(心跳):为系统时间节拍使用;osTimer(软件定时器)、osEvent(事件)、osDelay(系统延时)均基于此定时器来运行;故此定时器是整个OSAL的心脏。

  • WDT(看门狗):使用芯片主看门口狗,用户根据不同的芯片移植看门狗程序。
  • 硬件层也是OS适配不同处理器需要移植的一层,默认使用的ARM处理器,目前发现ARM的M0、M23、M3、M4都可以兼容;其它处理器需要自行移植。
  • 系统移植方法在后续章节中详细讲解。
    系统调度器(osScheduler):
  • 系统调度器主要用于调度event(事件)、task(任务),是OSAL的大脑,应用程序的运行就是这里来调度的。

任务(osTask):

任务是一个程序的入口(这里说的程序是指一个较小的功能模块),任务通常指所接受的工作,所担负的职责,是指为了完成某个有方向性的目的而产生的活动。

  • 一个较大型的程序可以划分为弱干的小功能模块,比如我们开发一个由按键和数码管组成的产品,我们可以用一个任务专门负责按键的扫描;另外一个任务处理数码管的显示;两个任务不直接关联,而是采用消息队列来通讯,那么这两个代码模块就是相对独立的程序,不用某个模块是,直接把相应的文件删除,而不影响其它模块和整个代码的运行。
  • 任务一旦被创建就会一直存在,因为考虑系统设计的原因,没有删除任务,只要没有消息触发任务,那么任务就不会运行.

事件(osEvent):

osEvent是思路来源于NXP的BLE模块思路,使用他可以即时使或者延时回调指定的函数。

  • osEvent引入极大的方便我们处理一些需要延时做的事情,或者需要在中断中较长时间处理的事情。
  • 举个列子,我们驱动一个I2C触摸按键,当有按键按下时,触摸芯片将INT脚拉低,MCU中入中断,无需在中断中调用I2C去获取按键,而是创建一个即时事件后,就退出中断;退出中断后,系统会调度创业的时间函数,在事件中在调用I2C读取按键值;这样开发的好处避免了中断的长期时间占用。
  • 再举个例子,我们需要控制一个LED的亮和灭,传统的做法是打开LED后,写一个死延时,等延时到后再去关闭LED,那么在这个延时期间,MCU就无法处理其它事务;而引入了osEvent后,打开LED时,只需要创建一个延时的Event,当时间后到,自动回调关闭LED的函数;应用程序就不需要关心LED关闭的事情了。
  • 事件一般是临时突发的,不可预期的,需要快速响应处理的一类活动,事件与项目,任务的显著区别就是事件是没有明确的目的的,完全不可预期。
  • 事件的显著特性就是其临时性和突发性,可能并不会经常发生,只是偶然性,以致不可预期。
  • 事件可以是即时事件,也可以是延时事件,任务创建后只会运行一次,如果希望事件能各周期运行,那么只需要在事件运行时,重新创建一次就行.
  • 事件在未执行时再次被创建时,系统不会再次创建一个新的事件,而是将已经创建但未执行的事件延时重新设置.
  • 事件在未执行前可以被清除.

软件定时器(osTimer):

定时器属于用软件实现了多个硬件定时器;

  • 定时器主要是用来定期产生中断,方便应用程序开发需要定时完成的工作。
  • 定时器基于系统定时器(TICK)来运行的.
  • 定时器一旦被创建将会周期性自动运行,不需要重装初值,直到被删除.
  • 如果需要一次性定时器,推荐使用事件来完成.
  • 由于定时器是在中断里面完成,所有定时器处理的事件不能太久,以免影响系统的正常运行.
    队列(osQueue):
  • 任务创建时已经为任务创建一个队列来传递消息给任务.
  • 用于如果需要自定义不各种格式的队列,可以自行创建.
    内存管理(heap_4):
  • 这里直接使用了FreeRTOS的内存管理,当然也可以用C库或者其它的内存管理。
    硬件抽象层(HAL):
  • V2.1版本引入了HAL层,将硬件进行抽象设计,应用不再需要直接调用芯片的库或者寄存器。
  • 本HAL不同于其它操作系统的HAL的地方是,HAL是独立于OS,可以直接用于裸机的编程;如果其它朋友有需要也可以直接使用。

osTask任务:

任务配置:

  • 在osConfig.h文件定义了任务最大数 #define MAX_TASK_NUM 8u,
  • 在osConfig.h文件定义了任务消息的最大条数#define MAX_TASK_MSG_BUF_NUM (MAX_TASK_NUM*10),
  • 需要开发者根据情况需求调整大小,此值越大所占内成RAM就越大,
/**  define Max Task Num
*        定义最大任务数 MAX<0xFFFF ,用户根据项目实际大小修改,
*        任务数越多,所占用的RAM就越多. 
**/
#define MAX_TASK_NUM        (IDLE_TASK_NUM+8u)


/** define Max Task Message Buffer Num
* 定义最大任务消息缓冲数,
* 建议与MAX_TASK_NUM相等或者更大
**/
#define MAX_TASK_MSG_BUF_NUM         (MAX_TASK_NUM*3)

任务函数原型:

  • 在osTadk.h中定义了任务函数的原型
/* Defining task function pointer array types   */
/* 定义任务函数指针数组类型
* uint8_t id:任务的ID,向任务发送消息时,需要用到此ID
* uint8_t msg: 消息号
* void *pData: 消息所携带的数据
* uint16_t dataSize: 数据大小
*/
typedef void(*tpfTaskFunc)(uint8_t id, uint8_t msg, void *pData, uint16_t dataSize);

void idleTask(uint8_t taskId, uint8_t msg, void *pData, uint16_t dataSize)

任务创建:

调用函数createTask创建一个任务,函数原型uint8_t createTask(tpfTaskFunc pfFunction), pfFunction是任务的函数指针;创建成功返回任务的ID,创建失败返回0;
uint8_t createTask(tpfTaskFunc pfFunction)

任务删除

有时创建的任务工作任务已经完成,可以被删除,调用uint8_t delTask(tpfTaskFunc pfFunction)可以删除任务.
uint8_t delTask(tpfTaskFunc pfFunction)

注意被删除后,应用层无法对应用是否删除进行判断,此时用户要避免还要此任务发送消息.

任务初始化:

任务创建时系统会自动发一个初始化消息给任务,任务接是到此消息时可以初始化各变量,或者硬件资源.

/* 给当前任务发送初始化消息 */
sendMsgToTask(gtTask.taskIdMax, TASK_INIT, NULL, 0);
 
/* 系统初始化 */
case TASK_INIT:        
{
                   
    adcInit(&gtCfgAdcVol);         
    createTimer(1000,secTimer);
}break;

任务调度:

当有消息发给任务时,任务调度器(osScheduler)会从任务消息取出消息,并回调任务函数。

任务消息发送:

调用sendMsgToTask可以发送消息给指定的任务.

/**
*        function:         
*        作用:        创建一个任务 
*        parame 1:        
*        参数1:        taskId为接收消息的任务ID
*        parame 2:        
*        参数2:        msg为发送的消息内容
*        parame 3:        
*        参数3:        pData传递的数据, 
*        parame 4:        
*        参数4:        dataSize传递数据的大小
*        return :        
*        返回:        消息发送成功返回true,否则返回false.
**/
bool sendMsgToTask(uint8_t taskId, 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuleying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值