打印
[DemoCode下载]

操作系统抽像层,基于事件驱动的OSAL_EventDrivenClass,M058MDK工程

[复制链接]
1241|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yuleying|  楼主 | 2017-3-15 22:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前段时间在论坛中发布了EventDrivenClassOSAL得到一些朋友支持和帮助,现在OSAL已经升级到1.0版本.
      
版本说明

OSAL_EventDrivenClass_V0.5_ARM-M0:发布的第一版.
OSAL_EventDrivenClass_V0.6_ARM-M0:增加消息队列中断保护.
OSAL_EventDrivenClass_V0.7_ARM-M0:修改看门狗监控SYS_TICK.
OSAL_EventDrivenClass_V1.0_ARM-M0:增加动态内存管理(移植FreeRTOS的Heap4),任务函数增加一个指针参数,利用指针变量和动态内存在任务间传输大数据.




OSAL_EventDrivenClass_V1.0_ARM-M0.rar

359.57 KB

EventDrivenClassOSAL详解-作者-余乐瀛(1.0).rar

24.88 KB

沙发
捉虫天师| | 2017-3-15 22:44 | 只看该作者
楼主自己编写的OSAL吗

使用特权

评论回复
板凳
捉虫天师| | 2017-3-15 22:45 | 只看该作者
给力了,下载学习一下思路。

使用特权

评论回复
地板
捉虫天师| | 2017-3-15 22:46 | 只看该作者
帮你贴出来文字
EventDrivenClassOSAL详解
前言
什么是OSAL:
目前本人已知OSAL概念是由TI公司在ZIGBEE协议栈引入,他的意思是”操作系统抽象层”,我认为叫做”模拟操作系统”更为合适,它并非一个真正的OS,而是模拟OS的一些方法为广大编程者提供一种写MCU程序的方法.
EventDrivenClassOSAL:
是一个由事件驱动类的OSAL,顾名思义此OSAL完全由事件进行驱动,没有事件任务就没有事干,那么OSAL就会调度空闲任务,等待事件的发生.
EventDrivenClassOSAL特点:
此系统完全由C语言编写,不会涉及汇编,摒弃了内存管理,链表等复杂的方法,且代码量非常少,整个系统不足1000行;适合初学者使用,内存占用小,也适用于各类MCU,不挑剔硬件平台,依耐于硬件的资源只需要一个定时器.
EventDrivenClassOSAL适用性:
此OSAL适用于对实时性不严格,对产品成本严格控制,使用较低端的MCU的场景.可适于以上场景的用绝大部分应用.
关于内存占用情况:
基于新唐NUVOTON M0-58MCU硬件平台,使用官方标准库,一个示例代码(代码包括4个按键驱动示例程序,一个软件BUZZ驱动程序,2个任务,用来作按键消息分发和按键消息处理,可以说一个简单的应用已经完成80%.)内存使用情况如下:
Program Size: Code=6132 RO-data=472RW-data=64 ZI-data=672  
可以看出OSAL内存是占用远远于一般的OS,并且OSAL已经实现任务的消息队,列把任务/事件/定时器的堆空间已经包括,在写应用代码时不会在重复创建.
后续会补充一个基于8051的示例代码内存使用情况.
鸣谢:
特别感谢我的同事“罗天浩,LTH”提供了OSAL的部分框架,特别是万能的消息队列,使OS部得更简洁.
     感谢业界各位朋友提供宝贵的意见和建议,如“ SevenPounds 发现队列未进入临界保护等”.
声明:
     文档中部分见解属于个人见解,未经过验证(如:OSAL定义),如果错误敬请谅解,欢迎批评指导.
源代码下载:
作者:余乐瀛
QQ:470284225
讨论群: 413012273
EMAIL:yuleying@126.com
时间:20160926

版本说明
OSAL_EventDrivenClass_V0.5_ARM-M0:发布的第一版.
OSAL_EventDrivenClass_V0.6_ARM-M0:增加消息队列中断保护.
OSAL_EventDrivenClass_V0.7_ARM-M0:修改看门狗监控SYS_TICK.
OSAL_EventDrivenClass_V1.0_ARM-M0:增加动态内存管理(移植FreeRTOS的Heap4),任务函数增加一个指针参数,利用指针变量和动态内存在任务间传输大数据.
OSAL的使用方法
概要:
     OSAL由”任务Task”,”事件Event”,”定时器Timer”,”队列Queue”组成.
     任务:
任务通常指所接受的工作,所担负的职责,是指为了完成某个有方向性的目的而产生的活动。
任务一旦被创建就会一直存在,因为考虑系统设计的原因,没有删除任务,只要没有消息触发任务,那么任务就不会运行.
     事件:
事件一般是临时突发的,不可预期的,需要快速响应处理的一类活动,事件与项目,任务的显著区别就是事件是没有明确的目的的,完全不可预期。
事件的显著特性就是其临时性和突发性,可能并不会经常发生,只是偶然性,以致不可预期。
事件可以是即时事件,也可以是延时事件,任务创建后只会运行一次,如果希望事件能各周期运行,那么只需要在事件运行时,重新创建一次就行.
事件在未执行时再次被创建时,系统不会再次创建一个新的事件,而是将已经创建但未执行的事件延时重新设置.
事件在未执行前可以被清除.
     定时器:
     定时器属于用软件实现了不同的硬件定时器;定时器基于系统定时器(TICK)来运行的.
     定时器一旦被创建将会周期性自动运行,不需要重装初值,直到被删除.
     如果需要一次性定时器,推荐使用事件来完成.
     由于定时器是在中断里面完成,所有定时器处理的事件不能太久,以免影响系统的正常运行.
     队列:
     任务创建时已经为任务创建一个队列来传递消息给任务.
     用于如果需要自定义不各种格式的队列,可以自行创建.
详解:
     任务:
任务配置:
在”osConfig.h”文件定义了任务最大数#define MAX_TASK_NUM     8u,需要开发者根据情况需求调整大小,此值越大所占内成RAM就越大,
任务创建:
调用函数usCreateTask创建一个任务,函数原型uint8_t usCreateTask(pvTaskFuncxfunction), xfunction是任务的函数指针;创建成功返回任务的ID,创建失败返回0;
任务函数的原型为: void Func(uint8_t ucId,uint8_t ucMsg); ucId为运行时OS传入的任务的ID, ucMsg发给任务的消息.
         任务初始化:
任务创建时系统会自动发一个初始化消息给任务,任务接是到此消息时可以初始化各变量,或者硬件资源.
         任务调度:
当有消息发给任务时,任务调度器()会从任务消息取出消息,并回调任务函数.
         
         任务消息发送:
              调用bSendMsgToTask可以发送消息给指定的任务.
原型:bSendMsgToTask(uint8_t ucTaskId, uint8_t usMsg), ucTaskId为接收消息的任务ID, usMsg为发送的消息内容.消息发送成功返回true,否则返回false.
         消息处理:
任务被调度时,可以从形参ucMsg接收到消息,任务根据消息内存做出相应的处理,处理完成后返回,系统再次调度其它任务.
当任务在处理消息发生新的消息,任务可以向自己发送另外一个消息.
任务中通常不允许Delay,大多Delay可以由延时事件来完成,如果必需用到Delay,那么可以使用OS提供的vOsDelayMs函数;当任务调用此函数Delay时系统会调用其它任务;当然如果本任务收到新的消息时,还在会被调用那么就变成了递归调用,这时需要保护好所使用的全局变量或静态变量.
         空闲任务:
当所有任务都处于空闲时,如果在使能空闲任务的情况下,系统会调度空闲任务,用户可以在空闲任务处理扫描事件,或者让CPU休眠.
     事件:
         事件配置:
在”osConfig.h”文件定义了任务最大数#define EVNT_CONCURRENT     16u,需要开发者根据情况需求调整大小,此值越大所占内成RAM就越大. 注意:此值并不是可以创建事件的数量,而是事件同时运行的并发数量.事件一旦调度运行后,就会释放出内存来给其它事件使用.
         事件创建:
调用函数bCreateEvnt可以创建一个事件,
函数原型bool bCreateEvnt(uint16_t usDelayMs, pvEvntFunc pxFunction);第一个参数是事件延时时间,以mS为单位,传0为即时事件,第二个参数为事件回调函数.返回创建成功与失败.
        事件调度:
事件创建后,延时时间到时,系统会自动调度任务,任务只会运行一次,如果需要周期运行,需要在任务函数中再次创建.
事件删除:
事件创建后可调用bCleanEvnt,删除已经创建的事件;
函数原型bool bCleanEvnt(pvEvntFunc pxFunction),参数为事件的回调函数指针,返回成功或失败.
     定时器:
         定时器配置:
在”osConfig.h”文件定义了任务最大数#define MAX_TIMER_NUM     3u,需要开发者根据情况需求调整大小,此值越大所占内成RAM就越大.
         定时器创建:
调用函数bCreateTimer可以创建一个事件,
函数原型bool bCreateTimer(uint32_t uiTimeMs, pvTimerFunc pxFunction);第一个参数设定定时器的时间,以mS为单位 ,第二个参数为定时器回调函数.返回创建成功与失败.
        定时器调度:
定时器设置定时器到后,会在定时器中断中直接回调函数处理定时器,这样达到抢占CPU的目的,但正由于在中断中处理,定时器中不能写太多程序,以免影响定时器正常运行.
定时器删除:
事件创建后可调用bDelTimer,删除已经创建的事件;
函数原型bool bDelTimer(pvTimerFunc pxFunction),参数为定时器的回调函数指针,返回成功或失败.
     队列:
         用户根据需要可自由创建各种类型的队列.
         队列创建方法:
创建消息结构
根据用户根据需求创建一个队列缓冲区,队列可以是单个字节的数据类型,可以是一个复杂的结构体类型,可参考任务部分创建的消息类型.
file:///C:/Users/gaoya/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg file:///C:/Users/gaoya/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg
创建队列类型变量
使用结构体tsQueue,创建一个变量.以后读队列和取队列均通过此变量操作.
              创建队列
                   使用B_CREATE_QUEUE创建队列
原型#define B_CREATE_QUEUE(T_QUEUE, P_BUF) bCreateQueue(&T_QUEUE,(void *)P_BUF, sizeof(P_BUF)/sizeof(P_BUF[0]), sizeof(P_BUF[0])),使用了宏定义将队列进行了简化, xOsCreateQueue(queue,buf),第一个参数传队列的变量名,第二个参数传缓冲区的指针:
发送消息
使用B_PUSH_QUEUE_BUF来发数多个消息,使用B_PUSH_QUEUE_ONE发送一个消息.
原型#define B_PUSH_QUEUE_BUF(T_QUEUE, P_BUF, UC_LEN)  bPushQueue(&T_QUEUE, (void *)P_BUF,UC_LEN)
第一个参数为创建的队列类型变量指针,可以看作的队列的名字;第二个参数是发送数据的指针,第三个参数是发送数据的长度.
原型#define B_PUSH_QUEUE_ONE(T_QUEUE, T_BUF)  bPushQueue(&T_QUEUE, (void *)&T_BUF,1)
第一个参数为创建的队列类型变量名称,第二个参数是发送数据的指针.
     接收消息
使用uint8_t UC_GET_QUEUE_DATA_NUM可以判断队列中是否有消息和消息的条数.
原型#define UC_GET_QUEUE_DATA_NUM(T_QUEUE)ucGetQueueDataNum(&T_QUEUE),使用宏定义简化参数,简化后参数为队列的名称.
使用UC_PULL_QUEUE可以取出消息.
#define UC_PULL_QUEUE(T_QUEUE, P_BUF, UC_LEN)  ucPullQueue(&T_QUEUE, (void *)P_BUF,UC_LEN)
第一个参数为队列名称,第二个参数为消息缓冲区,第三个参数要取的消息长度.
     系统的移植:
         系统的移植较为简单, 只需要定义全局中断开关、创建一个定时器,并移植看门狗程序即可运行.
         在osHardware.c 中可以看到voidvSysTickInit(void)定时器初始化函数,根据不同的平台就行修改并初始化定时器.
         修改中断入口void SYSTEM_TIME_ISR(void).
osHardware.h中规定了#define TICK_RATE_HZ   1000u定时器的运行频率,尽量使用此参数来自动计算定时器的初值,改部此参数可以达到修改定时器的目的;同时定时器和事件的事件参数均为自然时间,系统通过此参数转变为相对时间.故此参数需要准确.
osHardware.h中定义了#define DISABLE_IRQ()  __disable_irq()和#defineENABLE_IRQ()  __enable_irq(),打开全局中断和关闭全局中断,使部分程序进入临界段进行保护,避免被中断服务打断。
系统目前是在ARM_M0的平台上开的,如果移植到其它平台时编译器未提供     #include <stdint.h> #include<stdbool.h>这两个库,需要自己定义,定义文件见”osTypedef.h”.
     任务延时和多任务
OSAL非抢占式,不带任务切换,故在任务中不能写死循环,任务执行某事件完成后必需立即返回,以便其它使用使用CPU,这样就大大提高了系统实时性,感觉系统像一个多任务在运行.
当某任务实际情况确实需要延时,可调用系统提供的bOsDelayMs,在当前任务延时的时候,系统会调度其它任务,达到多任务的目的,但由于bOsDelayMs可能会再次调度被延时的任务,造成递归调度,而使栈空间资源过多的消耗,故不建议bOsDelayMs同时被多个任务使用.在万不得以的情况下需要使用,需要适当增加栈区大小,并设置bOsDelayMs的调用限制.
                                      

使用特权

评论回复
5
yuleying|  楼主 | 2017-3-17 19:42 | 只看该作者
源码链接有点问题修改一下
pan.baidu.com/s/1dFbzQy1

使用特权

评论回复
6
wahahaheihei| | 2017-3-17 20:14 | 只看该作者
支持楼主的内容,写的很不错。

使用特权

评论回复
7
谧芸| | 2017-3-26 11:11 | 只看该作者
去年接触TI的CC2541时看到OSAL,就想将它抽出来在别的单片机上用,后面搞得乱七八糟的,就放弃了。今天就看到了楼主的力作………………

PS:楼主不妨将这源码放到github上

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

14

帖子

1

粉丝