概述
OSAL
参考: http://blog.csdn.net/itas109/article/details/12952759
OSAL是一种基于事件驱动的轮询式操作系统
OSAL 的主循环
// 程序的主循环
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
// 循环的主体
void osal_run_system( void )
{
uint8 idx = 0;
// ...
// 检查各个任务的状态。说明idx越小的任务,优先级别越高。
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
// 有一个任务发生了
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
/* idx为任务的索引(与任务的taskID相同), 根据索引调用对应的任务处理函数 */
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{ // 没有任务发生则休眠
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
Task : 任务
// The order in this table must be identical to the task initialization calls below in osalInitTask.
// tasksArr 存放的是与某个任务相关的处理函数(使用下标对任务进行索引,及下标为0的任务对应的处理函数为Hal_ProcessEvent)
const pTaskEventHandlerFn tasksArr[] = {
Hal_ProcessEvent,
App_ProcessEvent
};
// 当前任务的个数
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
// 当前任务的状态
uint16 *tasksEvents;
/**
* @fn osalInitTasks
* @brief This function invokes the initialization function for each task.
* @param void
* @return none
* 任务初始化函数,调用所有任务的初始化函数。并为每个任务分配一个taskID。
*/
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
Hal_Init( taskID++ );
App_Init( taskID );
}
事件的捕获
以按键时间为例子进行分析。
首先介绍一个函数:osal_set_event
, 该函数可以修改某任务的事件变量,提示该任务,某个事件发生了。
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
{
...
tasksEvents[task_id] |= event_flag; // Stuff the event bit(s)
...
}
那么找到这个函数在哪里被调用,不就知道事件是在哪里捕获的了吗。经分析,调用过程如下:
main
--> InitBoard( OB_READY );
--> HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback); // 按键发生时的回调函数是OnBoard_KeyCallback,后面会用到。
--> osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
--> osal_start_system(); // 主循环
经过分许发现,在执行osal_start_system
主循环之前,已经注册了一个按键事件 HAL_KEY_EVENT
,因此进入主循环后,Hal_ProcessEvent
被调用。
下面分析一下Hal_ProcessEvent
函数。其中,osal_start_timerEx这是一个很常用的函数,它在这里的功能是经过100毫秒后,向Hal_TaskID这个ID所标示的任务(也就是其本身)发送一个HAL_KEY_EVENT事件。这样以来,每经过100毫秒,Hal_ProcessEvent这个事件处理函数都会至少执行一次来处理HAL_KEY_EVENT事件。也就是说每隔100毫秒都会执行HalKeyPoll()函数。
// 关键代码
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll();
/* if interrupt disabled, do next polling */
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
HalKeyPoll
函数首先将按键信息记录在变量keys中,然后传递给了pHalKeyProcessFunction指向的函数。pHalKeyProcessFunction函数是在通过HalKeyConfig
(上面有提到)设置的,实际指向的函数为: OnBoard_KeyCallback。
/**
* @fn HalKeyPoll
*
* @brief Called by hal_driver to poll the keys
*
* @param None
*
* @return None
*/
void HalKeyPoll (void)
{
//... 获取按键信息,保存在keys中
/* Invoke Callback if new keys were depressed */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); // 即调用OnBoard_KeyCallback
}
}
OnBoard_KeyCallback
函数又做了些什么呢?主要是将按键事件封装成了一个消息(keyChange_t类型),然后调用osal_msg_send
将消息放入到消息队列当中。
/**
* @fn OnBoard_KeyCallback
* @brief Callback service for keys
* @param keys - keys that were pressed
* state - shifted
* @return void
*/
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
uint8 shift;
(void)state;
shift = (keys & HAL_KEY_SW_6) ? true : false;
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
{
// Process SW1 here
if ( keys & HAL_KEY_SW_1 ) // Switch 1
{
}
// Process SW2 here
if ( keys & HAL_KEY_SW_2 ) // Switch 2
{
}
// Process SW3 here
if ( keys & HAL_KEY_SW_3 ) // Switch 3
{
}
// Process SW4 here
if ( keys & HAL_KEY_SW_4 ) // Switch 4
{
}
// Process SW5 here
if ( keys & HAL_KEY_SW_5 ) // Switch 5
{
}
// Process SW6 here
if ( keys & HAL_KEY_SW_6 ) // Switch 6
{
}
}
}
/**
* @fn OnBoard_SendKeys
* @brief Send "Key Pressed" message to application.
* @param keys - keys that were pressed
* state - shifted
* @return status
*/
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
keyChange_t *msgPtr;
if ( registeredKeysTaskID != NO_TASK_ID )
{
// Send the address to the task
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;
msgPtr->state = state;
msgPtr->keys = keys;
osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
}
return ( ZSuccess );
}
else
return ( ZFailure );
}
消息队列
OSAL维护了一个消息队列(链表),当主循环发现一个事件发生时,便会调用相应的事件处理函数,这些处理函数会从消息队列中获取属于自己的消息(使用函数osal_msg_receive(taskID)
来获取某个task的消息),然后调用消息处理函数来处理。
总结
梳理一下整个过程。
- OSAL在开始主循环时,注册了按键事件的回调函数
OnBoard_KeyCallback
、并放入了一个HAL_KEY_EVENT
事件。 - 然后OSAL进入了主循环。不断轮询,查看有没有时间发生。因为第1步中,OSAL自己放入了一个
HAL_KEY_EVENT
事件,所以对应的处理函数Hal_ProcessEvent
被触发。该函数使用osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
让HalKeyPoll
每100毫秒被执行一次。(也就是说,Hal_ProcessEvent第一次被调用是应为OSAL自己放入了一个事件;而之后,是通过timer进行定时调用)
- HalKeyPoll会检查有哪些按键被按下了,然后调用
OnBoard_KeyCallback
, 将该按键事件封装成一个消息,并放入到消息队列当中,同时会给消息加上SYS_EVENT_MSG
。(所以该按键事件会在下次调用Hal_ProcessEvent时被处理)。 - Hal_ProcessEvent函数检测到event中有
SYS_EVENT_MSG
时,会调用osal_msg_receive
从消息队列中取出消息进行处理。
- HalKeyPoll会检查有哪些按键被按下了,然后调用
最后
以上就是无限板凳为你收集整理的OSALOSAL的全部内容,希望文章能够帮你解决OSALOSAL所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复