前言
简单的运行了下2540的蓝牙Demo,为了应用能顺利跑起来,还需要了解OSAL系统的机制和原理
如有异议,欢迎留言指正
概述
OSAL 操作系统抽象层 (Operating System Abstraction Layer),一种类多任务运行的系统资源分配机制,并不是真正意义上的操作调度系统,但是上层抽象出的API接口对应用开发者比较友好,而且占用资源较少,适用于资源极其有限的硬件平台
框架
OSAL提供调度、内存管理和消息传递功能;HAL提供了对硬件层抽象的访问,将软件层与硬件层进行关联,方便移植
流程
事件可以由中断或其他任务中进行触发,被需要处理事件的任务获取执行;事件触发后可附带消息体进行数据传递交互
代码解读
初始化事件任务
- 在主函数 main() 中会调用
osalInitTasks
进行任务初始化,内部为任务各自维护的初始化函数,自定义新增的任务也可以在此函数内进行初始化
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); //开辟事件缓存2 * tasksCnt
/* The tasksEvents allocated pointer must be valid */
if (tasksEvents != NULL)
{
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));//清除事件缓存
}
else
{
HAL_ASSERT_FORCED();
}
/* LL Task */
LL_Init( taskID++ );
/* Hal Task */
Hal_Init( taskID++ );
/* HCI Task */
HCI_Init( taskID++ );
#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* Profiles */
GAPRole_Init( taskID++ );
GAPBondMgr_Init( taskID++ );
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );
}
启动OSAL
简化了未生效的预编译
- osal_start_system:OSAL启动函数,在for循环中执行
osal_run_system()
void osal_start_system( void )
{
for(;;) // Forever Loop
{
osal_run_system(); //osal运行系统
}
}
任务事件管理
-
根据
tasksEvents
来判断是否有事件,序号idx
从0开始遍历,所以tasksArr函数指针数组中靠前的任务优先级较高;const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
与任务事件表长度对应
-
tasksEvents
开辟的缓存中,为每个任务分配了两个字节(与任务偏移序号一一对应),每个bit代表一个事件(每个任务支持最大16个事件),非0则表示存在需要处理的事件
void osal_run_system( void )
{
uint8 idx = 0;
do {
if (tasksEvents[idx]) // 从高优先级判断是否有事件
{
break;
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)//判断索引是否有效
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // 关中断
events = tasksEvents[idx]; //提取任务事件
tasksEvents[idx] = 0; // 清除当前任务事件
HAL_EXIT_CRITICAL_SECTION(intState);//开中断
activeTaskID = idx; //生效任务标志,在osal内部使用
events = (tasksArr[idx])( idx, events );//执行对应事件的任务函数
activeTaskID = TASK_NO_TASK;//执行完成 失效任务标志
HAL_ENTER_CRITICAL_SECTION(intState);//关中断
tasksEvents[idx] |= events; // 增加任务返回的事件
HAL_EXIT_CRITICAL_SECTION(intState);//开中断
}
}
应用任务
在SimpleBle工程中自定义了两个事件
- 设备启动事件(SBP_START_DEVICE_EVT):用于蓝牙启动想要执行的操作(蓝牙状态回调);
- 周期性定时事件(SBP_PERIODIC_EVT):定时处理事件
// Simple BLE Peripheral Task Events
#define SBP_START_DEVICE_EVT 0x0001 //设备启动事件
#define SBP_PERIODIC_EVT 0x0002 //周期性定时事件
工程协议栈中定义了全局的事件 SYS_EVENT_MSG,固定为0x8000
uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{
VOID task_id; // OSAL required parameter that isn't used in this function
if ( events & SYS_EVENT_MSG ) // 系统自带的全局事件
{
uint8 *pMsg;//接收消息指针
if ( (pMsg = osal_msg_receive( simpleBLEPeripheral_TaskID )) != NULL )//读取缓存数据
{
simpleBLEPeripheral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );//处理数据
// Release the OSAL message
VOID osal_msg_deallocate( pMsg );//释放数据缓存资源
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);//清除当前执行事件,返回未处理事件
}
if ( events & SBP_START_DEVICE_EVT )//启动事件
{
// Start the Device
VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs );//设备启动
// Start Bond Manager
VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs );//注册绑定回调
// Set timer for first periodic event
osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );//启动周期事件
return ( events ^ SBP_START_DEVICE_EVT );//清除当前执行事件,返回未处理事件
}
if ( events & SBP_PERIODIC_EVT )//周期事件
{
// Restart timer
if ( SBP_PERIODIC_EVT_PERIOD )//时间有效
{
osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );//每次生效后需要重新启动
}
// Perform periodic application task
performPeriodicTask();//定时处理的任务
return (events ^ SBP_PERIODIC_EVT);//清除当前执行事件,返回未处理事件
}
// Discard unknown events
return 0; //无事件返回0
}
消息收发
任务间需要有数据交互时,可以使用消息机制进行数据收发
- 系统使用了堆空间分配的方法,在TSK1任务一中开辟缓存存储需要发送的数据,TSK2任务二接收到消息处理完成后进行释放,通过指针地址的方式进行传递
消息发送接口:uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
,内部会自动触发SYS_EVENT_MSG系统事件
destination_task
:任务id,系统初始化时唯一分配msg_ptr
:消息缓存
截取一段按键消息发送的例子
- 消息的数据结构可以参考按键消息的数据结构进行修改,必须指定传递给的任务Id
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 ( SUCCESS );
}
else
return ( FAILURE );
}
消息接收接口:uint8 *osal_msg_receive( uint8 task_id )
匹配任务Id成功后返回接收到的消息缓存地址,数据调用完成后必须释放,否则会出现内存泄漏
if ( events & SYS_EVENT_MSG ) // 系统自带的全局事件
{
uint8 *pMsg;
if ( (pMsg = osal_msg_receive( simpleBLEPeripheral_TaskID )) != NULL )//读取缓存数据
{
simpleBLEPeripheral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );//处理数据
// Release the OSAL message
VOID osal_msg_deallocate( pMsg );//释放数据缓存资源
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);//清除当前执行事件,返回未处理事件
}
事件发送函数:uint8 osal_set_event( uint8 task_id, uint16 event_flag )
可以自定义事件并调取该接口触发事件任务
消息数据传递机制:osal系统通过单链表的方式进行对消息缓存的增删管理
typedef struct
{
void *next;
#ifdef OSAL_PORT2TIRTOS
/* Limited OSAL port to TI-RTOS requires compatibility with ROM
* code compiled with USE_ICALL compile flag. */
uint32 reserved;
#endif /* OSAL_PORT2TIRTOS */
uint16 len;
uint8 dest_id;
} osal_msg_hdr_t;
#endif /* USE_ICALL */
总结
OSAL适用于资源有限的硬件平台,通过遍历事件缓存列表来调度到指定的任务中, 任务间通过可消息来进行通信,对其进行裁剪后可以方便移植到自己的其他应用平台中