OSAL概述
OSAL为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,其中BLE协议栈、配置文件以及所有的应用程序(app)都在其上运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能,为了方便,下面简称OSAL系统。
1、OSAL简要流程
初始化完成后,在app的init最后一般会启动一个定时器或者直接set一个任务事件START_DEVICE_EVT,超时后进入app回调函数START_DEVICE_EVT事件分支处理,在该分支中启动具体的信息采集、数据处理、数据显示或发送等定时任务。
OSAL系统工作,判断当前是否存在待处理的任务事件,有,则调用对应的回调函数进行处理,并在结束后重新设定定时器等待超时,若没有任务事件,则进入休眠状态,等待唤醒。
2、OSAL的main函数
任何一个程序都由main函数起,单片机程序中,一般顺序为:
时钟初始化——>相应外设初始化——>系统关键参数初始化——>进入死循环处理;
在OSAL的main函数中,同样也是这些流程:
1 2 3 /************************************************************************************************** 4 5 * @fn main 6 7 * 8 9 * @brief Start of application. 10 11 * 12 13 * @param none 14 15 * 16 17 * @return none 18 19 ************************************************************************************************** 20 21 */ 22 23 int main(void) 24 25 { 26 27 /* Initialize hardware */ 28 HAL_BOARD_INIT(); 29 30 // Initialize board I/O 31 InitBoard( OB_COLD ); 32 33 /* Initialze the HAL driver */ 34 HalDriverInit(); 35 36 /* Initialize NV system */ 37 osal_snv_init(); 38 39 /* Initialize LL */ 40 41 /* Initialize the operating system */ 42 osal_init_system(); 43 44 /* Enable interrupts */ 45 HAL_ENABLE_INTERRUPTS(); 46 47 // Final board initialization 48 InitBoard( OB_READY ); 49 50 #if defined ( POWER_SAVING ) 51 52 osal_pwrmgr_device( PWRMGR_BATTERY ); 53 54 #endif 55 56 /* Start OSAL */ 57 osal_start_system(); // No Return from here 58 59 return 0; 60 61 } 62 63 64 65
解析:
HAL_BOARD_INIT(); //初始化晶振及预读取程序
InitBoard( OB_COLD ); //在main函数中有两处InitBoard();此为第一次初始化,此时OSAL 还未启动,初始化IO及一些系统级寄存器,类似电脑上的BIOS 为系统的启动做准备,个人认识,欢迎指正。
HalDriverInit(); //此处进行硬件初始化,定时器、AD转换、LCD、LED、KEY等
osal_snv_init(); //初始化存储区域
osal_init_system(); //初始化OSAL,此处系统初始化,内存初始化、消息队列初始 化、定时器初始化、电源管理初始化及应用程序初始化均在该 函数中进行,在OSAL上编程,我们的任务app的初始化程序 在此处的应用程序初始化中进行,每一个应用程序在OSAL中 有且只有一个全局唯一标识,即本任务的taskID,在这里进行 分配,在之后的主循环中进行任务分配与事件处理时,使用的
即为这里分配的taskID
InitBoard( OB_READY ); //第二次调用该函数初始化,此时系统已启动,在此处可将硬件
回调函数注册至系统~~
osal_pwrmgr_device( PWRMGR_BATTERY ); //如果定义了 POWER_SAVING,电源管理启动
osal_start_system(); //进入系统主循环
以上,各种初始化与设置,然后进入loop,,,,,,,,,,
for( ; ; )
{
//loop
}
3、OSAL的主循环
在osal_start_system() 中调用函数osal_run_system():
1 /********************************************************************* 2 * @fn osal_run_system 3 * 4 * @brief 5 * 6 * This function will make one pass through the OSAL taskEvents table 7 * and call the task_event_processor() function for the first task that 8 * is found with at least one event pending. If there are no pending 9 * events (all tasks), this function puts the processor into Sleep. 10 * 11 * @param void 12 * 13 * @return none 14 */ 15 void osal_run_system( void ) 16 { 17 uint8 idx = 0; 18 19 #ifndef HAL_BOARD_CC2538 20 osalTimeUpdate(); 21 #endif 22 23 Hal_ProcessPoll(); 24 25 do { 26 if (tasksEvents[idx]) // Task is highest priority that is ready. 27 { 28 break; 29 } 30 } while (++idx < tasksCnt); 31 32 if (idx < tasksCnt) 33 { 34 uint16 events; 35 halIntState_t intState; 36 37 HAL_ENTER_CRITICAL_SECTION(intState); 38 events = tasksEvents[idx]; 39 tasksEvents[idx] = 0; // Clear the Events for this task. 40 HAL_EXIT_CRITICAL_SECTION(intState); 41 42 activeTaskID = idx; 43 events = (tasksArr[idx])( idx, events ); 44 activeTaskID = TASK_NO_TASK; 45 46 HAL_ENTER_CRITICAL_SECTION(intState); 47 tasksEvents[idx] |= events; // Add back unprocessed events to the current task. 48 HAL_EXIT_CRITICAL_SECTION(intState); 49 } 50 #if defined( POWER_SAVING ) 51 else // Complete pass through all task events with no activity? 52 { 53 osal_pwrmgr_powerconserve(); // Put the processor/system into sleep 54 } 55 #endif 56 57 /* Yield in case cooperative scheduling is being used. */ 58 #if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0) 59 { 60 osal_task_yield(); 61 } 62 #endif 63 }
分析这个函数之前先搞明白几个主要的变量,
uint16 *tasksEvents;
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
const pTaskEventHandlerFn tasksArr[] =
{
//........
};
uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
(1) tasksCnt,即OSAL内任务个数;
(2) tasksEvents,从定义上看,short类型指针,值域0x0000 ~ 0xFFFF,事实上,每个task只允许存在16个事件类型,在使用宏定义事件类型时,采用bit map方式进行定义,如0x0001、0x0002、0x0004……这样做可以使 存在多个数据同时需要处理时,在处理完某一事件后,返回主循环,判断继续处理下一事件。每一个任务都包含一个系统事件SYS_EVENT_MSG,因此每个任务最多可以有15个自有事件,其在init时初始化为0;
(3) tasksArr[],函数指针数组,该指针数组元素为函数指针,具体为各模块事件回调函数,编写完本模块事件回调函数之后,按照本模块ID,将回调注册至此处,在主循环中准备调用。
在本函数中,do{...}while();循环判断是否存在待处理的任务事件,即tasksEvents[idx]是否为空,若存在则break,并进入后续if语句,先保存当前事件,并清空事件标志,调用(tasksArr[idx])( idx, events );处理任务事件并返回,此时event使用位图方法定义的好处就体现了一点,一般在回调函数返回时,会return event ^ currntEVENT; 异或操作只处理当前位,表明当前事件处理完毕,若其他位不为0,则表示该任务还有其他事件待处理;回调函数返回后,保存事件标志位,进入下一次轮询。