您的位置:首页 > 编程语言

TI蓝牙BLE 协议栈代码学习——OSAL(下)

2015-09-06 15:58 441 查看
接下来我们再看main()函数中另一个跟OSAL相关的函数——osal_start_system(),也位于OSAL.c中。

void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;)  // Forever Loop
#endif
{
osal_run_system();
}
}


一看这是个死循环,相当于单片机程序最后一行while(1);。这个函数最主要的部分还是osal_run_system(),找到它,也在OSAL.c中。

void osal_run_system( void )
{
uint8 idx = 0;

#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif

Hal_ProcessPoll();

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);

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
}


去掉条件编译部分,最核心的是一个do-while循环,一个if判断。

do-while循环:

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);

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);
}


这部分代码应该是OSAL最核心最精髓的部分了。前面的循环中已经确定有事件发生了。HAL_ENTER_CRITICAL_SECTION(intState);和HAL_EXIT_CRITICAL_SECTION(intState);分别是关中断和使能中断,以防止在执行代码时被中断打断。将事件表tasksEvents[]中的事件赋给events,然后该事件清零。接下来events
= (tasksArr[idx])( idx, events );就是去处理事件了,这里的tasksArr[]数组非常重要。下面的tasksEvents[idx] |= events;就是把没有响应的事件再放回到tasksEvents[]中。

我们来看看这个非常重要的数组taskArr[],它被定义在OSAL_KeyFobDemo.c中。
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent,                                          // task 0
Hal_ProcessEvent,                                       // task 1
HCI_ProcessEvent,                                        // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ),     // task 3
#endif
L2CAP_ProcessEvent,                                         // task 4
GAP_ProcessEvent,                                           // task 5
GATT_ProcessEvent,                                          // task 6
SM_ProcessEvent,                                            // task 7
GAPRole_ProcessEvent,                                       // task 8
GAPBondMgr_ProcessEvent,                                    // task 9
GATTServApp_ProcessEvent,                                   // task 10
KeyFobApp_ProcessEvent                                      // task 11
};


数组内的成员看起来很面熟。最上面一行的注释也写得很清楚,表中的顺序要和osalInitTask()中定义的一致。再来看这个数组的类型,是pTaskEventHandlerFn,这是个什么东西?pTaskEventHandlerFn不是东西,是

typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );

这个定义是一个函数指针,看起着很头疼,很蛋疼。如果换一下:

typedef pTaskEventHandlerFn unsigned short (*)( unsigned char task_id, unsigned short event );

这样或许理解起来要好一些。拿KeyFobApp_ProcessEvent的声明来看,uint16 KeyFobApp_ProcessEvent( uint8 task_id, uint16 events ),这是符合pTaskEventHandlerFn的格式的,函数名就是指针,函数的地址。

tasksArr[]是一个函数指针数组,里面保存了所有事件处理函数的地址。当有事件发生时,就执行events = (tasksArr[idx])( idx, events );一句,就是对应的tasksArr[]里相应的那个事件的处理函数。

再看另一个数组,tasksEvents[]。tasksEvents[]声明为全局变量,是在osalInitTasks()中定义和初始化的:

tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);

osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

这个数组的大小跟事件的数量是一致的,而且被osal_memset()初始化为0.

这样OSAL的运行机理基本清晰了,就是在do-while()循环中判断tasksEvents[]中哪个事件非0,即事件被触发了;然后在if中把该事件清0,执行tasksArr[]中定义好的事件处理函数,然后把没有执行的事件再放回到tasksEvents[]中。这也是为什么在osalInitTask()中进行初始化的时候,初始化的顺序要和tasksArr[]一致。
以上是我对OSAL的理解,因为C语言的基本不够瓷实,说得也很大白话。之所以敢这么大胆贴出来,也是请大家多批评指正,让我能得到提高。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: