BLE 技术是 Bluetooth SIG 规定的一套通信协议, 在协议变成具体的代码之前, 都只存在文档中, TI、 Nordic、 CSR 等厂商, 根据 SIG 发布的 BLE 技术协议, 配合自身的芯片开发了一整套源码, 并且这套源码经过了 SIG 的测试, 服务 BLE 协议。 这套源码就叫做协议栈, 协议栈是协议的实现。 不同的芯片厂商都有各自的协议栈, 而这不同的协议栈, Bluetooth SIG 是不允许厂商开源的, 所以, 无论是TI的或者NORDIC的BLE芯片资料中协议栈都是做成库的形式提供, 只开放部分的API层。
  分析的使用 TI 的 BLE 协议栈 1.3.2 版本,其他更新的版本, 也是同样的结构的。

一、BLE 协议栈 OSAL

  BLE 协议栈包含了 BLE 协议所规定的基本功能, 这些功能是以函数的形式实现的,为了便于管理这些函数集, BLE 协议栈内称为 OSAL (操作系统抽象层Operating SystemAbstraction Layer)。
  OSAL 就是一种支持多任务运行的系统资源分配机制。OSAL 建立事件和任务的时间处理的联系方法是: 建议一个事件表, 保存各个任务的对应的时间, 建立另一个函数, 保存每个任务函数的地址, 然后将这两张表建立某种对应关系, 当某一时间发生时, 则查找函数表, 找到对应的任务函数的指针, 然后通过函数指针, 调用该函数即可。
  BLE 协议栈 OSAL主要应用在四个文件内。OSAL_SimpleBLEPeripheral.c , simpleBLEPeripheral.c , simpleBLEPeripheral.h ,
SimpleBLEPeripheral_Main.c 整个程序所实现的功能都包含在这四个源文件中。

二、BLE 协议栈执行流程

  SimpleBLEPeripheral_Main.c 文件, 打开该文件可以找到 main()函数, 这就是整个协议栈的入口点, 即从该函数开始执行。

/*************************************************************************************************** @fn          main** @brief       Start of application.** @param       none** @return      none***************************************************************************************************/
int main(void)
{/* Initialize hardware */HAL_BOARD_INIT();// Initialize board I/OInitBoard( OB_COLD );/* Initialze the HAL driver */HalDriverInit();/* Initialize NV system */osal_snv_init();/* Initialize LL *//* Initialize the operating system */osal_init_system();/* Enable interrupts */HAL_ENABLE_INTERRUPTS();// Final board initializationInitBoard( OB_READY );#if defined ( POWER_SAVING )osal_pwrmgr_device( PWRMGR_BATTERY );#endif/* Start OSAL */osal_start_system(); // No Return from herereturn 0;
}

  函数osal_init_system();执行的是OSAL系统的初始化,
  函数osal_start_system();就是开始OSAL系统

2.1、OSAL系统的初始化

  打开 OSAL.c 文件的uint8 osal_init_system( void )。

/********************************************************************** @fn      osal_init_system** @brief**   This function initializes the "task" system by creating the*   tasks defined in the task table (OSAL_Tasks.h).** @param   void** @return  SUCCESS*/
uint8 osal_init_system( void )
{#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS// Initialize the Memory Allocation Systemosal_mem_init();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */// Initialize the message queueosal_qHead = NULL;// Initialize the timersosalTimerInit();// Initialize the Power Management Systemosal_pwrmgr_init();#ifdef USE_ICALL/* Prepare memory space for service enrollment */osal_prepare_svc_enroll();
#endif /* USE_ICALL */// Initialize the system tasks.osalInitTasks();#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS// Setup efficient search for the first free block of heap.osal_mem_kick();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */#ifdef USE_ICALL// Initialize variables used to track timing and provide OSAL timer serviceosal_last_timestamp = (uint_least32_t) ICall_getTicks();osal_tickperiod = (uint_least32_t) ICall_getTickPeriod();osal_max_msecs = (uint_least32_t) ICall_getMaxMSecs();/* Reduce ceiling considering potential latency */osal_max_msecs -= 2;
#endif /* USE_ICALL */return ( SUCCESS );
}

  其中 osalInitTasks();就是初始化系统任务。打开OSAL_SimpleBLEPeripheral.c文件找到:

/********************************************************************** GLOBAL VARIABLES*/// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] =
{LL_ProcessEvent,                                                  // task 0Hal_ProcessEvent,                                                 // task 1HCI_ProcessEvent,                                                 // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ),           // task 3
#endifL2CAP_ProcessEvent,                                               // task 4GAP_ProcessEvent,                                                 // task 5SM_ProcessEvent,                                                  // task 6GATT_ProcessEvent,                                                // task 7GAPRole_ProcessEvent,                                             // task 8GAPBondMgr_ProcessEvent,                                          // task 9GATTServApp_ProcessEvent,                                         // task 10SimpleBLEPeripheral_ProcessEvent                                  // task 11
};const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
uint16 *tasksEvents;/********************************************************************** FUNCTIONS*********************************************************************//********************************************************************** @fn      osalInitTasks** @brief   This function invokes the initialization function for each task.** @param   void** @return  none*/
void osalInitTasks( void )
{uint8 taskID = 0;tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));/* 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 );
}

  tasksArr数组, 定义了各个人物的处理函数, 这些任何函数, 就是 osal 所谓的“多任务” 处理函数, 其中SimpleBLEPeripheral_ProcessEvent 就是从机的应用任务处理函数。

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

  这里对任务个数经行了赋值, 并且开辟了任务的事件内存。
  接下来是逐个任务的初始化,SimpleBLEPeripheral_Init( taskID )是对应用任务的初始化。

2.2、OSAL系统的执行

  OSAL系统的执行函数 osal_start_system(); 定义在 OSAL.c 文件中

/********************************************************************** @fn      osal_start_system** @brief**   This function is the main loop function of the task system (if*   ZBIT and UBIT are not defined). This Function doesn't return.** @param   void** @return  none*/
void osal_start_system( void )
{#ifdef USE_ICALL/* Kick off timer service in order to allocate resources upfront.* The first timeout is required to schedule next OSAL timer event* as well. */ICall_Errno errno = ICall_setTimer(1, osal_msec_timer_cback,(void *) osal_msec_timer_seq,&osal_timerid_msec_timer);if (errno != ICALL_ERRNO_SUCCESS){ICall_abort();}
#endif /* USE_ICALL */#if !defined ( ZBIT ) && !defined ( UBIT )for(;;)  // Forever Loop
#endif{osal_run_system();#ifdef USE_ICALLICall_wait(ICALL_TIMEOUT_FOREVER);
#endif /* USE_ICALL */}
}

  其中osal_run_system();进入任务函数的循环。

/********************************************************************** @fn      osal_run_system** @brief**   This function will make one pass through the OSAL taskEvents table*   and call the task_event_processor() function for the first task that*   is found with at least one event pending. If there are no pending*   events (all tasks), this function puts the processor into Sleep.** @param   void** @return  none*/
void osal_run_system( void )
{uint8 idx = 0;#ifdef USE_ICALLuint32 next_timeout_prior = osal_next_timeout();
#else /* USE_ICALL */
#ifndef HAL_BOARD_CC2538osalTimeUpdate();
#endifHal_ProcessPoll();
#endif /* USE_ICALL */#ifdef USE_ICALL{/* Update osal timers to the latest before running any OSAL processes* regardless of wakeup callback from ICall because OSAL timers are added* relative to the current time. */unsigned long newtimestamp = ICall_getTicks();uint32 milliseconds;if (osal_tickperiod == 1000){milliseconds = newtimestamp - osal_last_timestamp;osal_last_timestamp = newtimestamp;}else{unsigned long long delta = (unsigned long long)((newtimestamp - osal_last_timestamp) & 0xfffffffful);delta *= osal_tickperiod;delta /= 1000;milliseconds = (uint32) delta;osal_last_timestamp += (uint32) (delta * 1000 / osal_tickperiod);}osalAdjustTimer(milliseconds);/* Set a value that will never match osal_next_timeout()* return value so that the next time can be scheduled.*/next_timeout_prior = 0xfffffffful;}if (osal_eventloop_hook){osal_eventloop_hook();}for (;;){void *msg;ICall_EntityID src, dst;osal_msg_hdr_t *hdr;uint8 dest_id;if (ICall_fetchMsg(&src, &dst, &msg) != ICALL_ERRNO_SUCCESS){break;}hdr = (osal_msg_hdr_t *) msg - 1;dest_id = osal_dispatch2id(dst);if (dest_id == TASK_NO_TASK){/* Something wrong */ICall_abort();}else{/* Message towards one of the tasks *//* Create a proxy task ID if necessary and* queue the message to the OSAL internal queue.*/uint8 proxyid = osal_alien2proxy(hdr->srcentity);if (hdr->format == ICALL_MSG_FORMAT_1ST_CHAR_TASK_ID){uint8 *bytes = msg;*bytes = proxyid;}else if (hdr->format == ICALL_MSG_FORMAT_3RD_CHAR_TASK_ID){uint8 *bytes = msg;bytes[2] = proxyid;}/* now queue the message to the OSAL queue */osal_msg_send(dest_id, msg);}}
#endif /* USE_ICALL */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 ) && !defined(USE_ICALL)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#if defined USE_ICALL/* Note that scheduling wakeup at this point instead of* scheduling it upon ever OSAL start timer request,* would only work if OSAL start timer call is made* from OSAL tasks, but not from either ISR or* non-OSAL application thread.* In case, OSAL start timer is called from non-OSAL* task, the scheduling should be part of OSAL_Timers* module.* Such a change to OSAL_Timers module was not made* in order not to diverge the OSAL implementations* too drastically between pure OSAL solution vs.* OSAL upon service dispatcher (RTOS).* TODO: reconsider the above statement.*/{halIntState_t intState;uint32 next_timeout_post = osal_next_timeout();if (next_timeout_post != next_timeout_prior){/* Next wakeup time has to be scheduled */if (next_timeout_post == 0){/* No timer. Set time to the max */next_timeout_post = OSAL_TIMERS_MAX_TIMEOUT;}if (next_timeout_post > osal_max_msecs){next_timeout_post = osal_max_msecs;}/* Restart timer */HAL_ENTER_CRITICAL_SECTION(intState);ICall_stopTimer(osal_timerid_msec_timer);ICall_setTimerMSecs(next_timeout_post, osal_msec_timer_cback,(void *) (++osal_msec_timer_seq),&osal_timerid_msec_timer);HAL_EXIT_CRITICAL_SECTION(intState);}}
#endif /* USE_ICALL */
}

  在 osal_run_system() 函 数 中 , 每 次 循 环 前 都 会 判 断tasksEvents,代码流程图如下:

三、BLE的 peripheral 从机工程

  在OSAL系统的初始化的时候在tasksArr任务指针数组中定义了一个SimpleBLEPeripheral_ProcessEvent任务,且 调用了SimpleBLEPeripheral_Init( taskID )。SimpleBLEPeripheral_Init 是任务的初始化函数, SimpleBLEPeripheral_ProcessEvent是从机工程的任务函数。

3.1、 peripheral 从机工程的初始化

  SimpleBLEPeripheral_Init 从机工程的初始化函数如下:

/********************************************************************** @fn      SimpleBLEPeripheral_Init** @brief   Initialization function for the Simple BLE Peripheral App Task.*          This is called during initialization and should contain*          any application specific initialization (ie. hardware*          initialization/setup, table initialization, power up*          notificaiton ... ).** @param   task_id - the ID assigned by OSAL.  This ID should be*                    used to send messages and set timers.** @return  none*/
void SimpleBLEPeripheral_Init( uint8 task_id )
{simpleBLEPeripheral_TaskID = task_id;// Setup the GAPVOID GAP_SetParamValue( TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL );// Setup the GAP Peripheral Role Profile{#if defined( CC2540_MINIDK )// For the CC2540DK-MINI keyfob, device doesn't start advertising until button is presseduint8 initial_advertising_enable = FALSE;#else// For other hardware platforms, device starts advertising upon initializationuint8 initial_advertising_enable = TRUE;#endif// By setting this to zero, the device will go into the waiting state after// being discoverable for 30.72 second, and will not being advertising again// until the enabler is set back to TRUEuint16 gapRole_AdvertOffTime = 0;uint8 enable_update_request = DEFAULT_ENABLE_UPDATE_REQUEST;uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;// Set the GAP Role ParametersGAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );GAPRole_SetParameter( GAPROLE_ADVERT_OFF_TIME, sizeof( uint16 ), &gapRole_AdvertOffTime );GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), scanRspData );GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, sizeof( uint8 ), &enable_update_request );GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), &desired_slave_latency );GAPRole_SetParameter( GAPROLE_TIMEOUT_MULTIPLIER, sizeof( uint16 ), &desired_conn_timeout );}// Set the GAP CharacteristicsGGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName );// Set advertising interval{uint16 advInt = DEFAULT_ADVERTISING_INTERVAL;GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, advInt );GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, advInt );GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MIN, advInt );GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MAX, advInt );}// Setup the GAP Bond Manager{uint32 passkey = 0; // passkey "000000"uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;uint8 mitm = TRUE;uint8 ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;uint8 bonding = TRUE;GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof ( uint8 ), &pairMode );GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof ( uint8 ), &mitm );GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof ( uint8 ), &bonding );}// Initialize GATT attributesGGS_AddService( GATT_ALL_SERVICES );            // GAPGATTServApp_AddService( GATT_ALL_SERVICES );    // GATT attributesDevInfo_AddService();                           // Device Information ServiceSimpleProfile_AddService( GATT_ALL_SERVICES );  // Simple GATT Profile
#if defined FEATURE_OADVOID OADTarget_AddService();                    // OAD Profile
#endif// Setup the SimpleProfile Characteristic Values{uint8 charValue1 = 1;uint8 charValue2 = 2;uint8 charValue3 = 3;uint8 charValue4 = 4;uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, sizeof ( uint8 ), &charValue1 );SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, sizeof ( uint8 ), &charValue2 );SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, sizeof ( uint8 ), &charValue3 );SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, sizeof ( uint8 ), &charValue4 );SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );}#if defined( CC2540_MINIDK )SK_AddService( GATT_ALL_SERVICES ); // Simple Keys Profile// Register for all key events - This app will handle all key eventsRegisterForKeys( simpleBLEPeripheral_TaskID );// makes sure LEDs are offHalLedSet( (HAL_LED_1 | HAL_LED_2), HAL_LED_MODE_OFF );// For keyfob board set GPIO pins into a power-optimized state// Note that there is still some leakage current from the buzzer,// accelerometer, LEDs, and buttons on the PCB.P0SEL = 0; // Configure Port 0 as GPIOP1SEL = 0; // Configure Port 1 as GPIOP2SEL = 0; // Configure Port 2 as GPIOP0DIR = 0xFC; // Port 0 pins P0.0 and P0.1 as input (buttons),// all others (P0.2-P0.7) as outputP1DIR = 0xFF; // All port 1 pins (P1.0-P1.7) as outputP2DIR = 0x1F; // All port 1 pins (P2.0-P2.4) as outputP0 = 0x03; // All pins on port 0 to low except for P0.0 and P0.1 (buttons)P1 = 0;   // All pins on port 1 to lowP2 = 0;   // All pins on port 2 to low#endif // #if defined( CC2540_MINIDK )#if (defined HAL_LCD) && (HAL_LCD == TRUE)#if defined FEATURE_OAD#if defined (HAL_IMAGE_A)HalLcdWriteStringValue( "BLE Peri-A", OAD_VER_NUM( _imgHdr.ver ), 16, HAL_LCD_LINE_1 );#elseHalLcdWriteStringValue( "BLE Peri-B", OAD_VER_NUM( _imgHdr.ver ), 16, HAL_LCD_LINE_1 );#endif // HAL_IMAGE_A
#elseHalLcdWriteString( "BLE Peripheral", HAL_LCD_LINE_1 );
#endif // FEATURE_OAD#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)// Register callback with SimpleGATTprofileVOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );// Enable clock divide on halt// This reduces active current while radio is active and CC254x MCU// is haltedHCI_EXT_ClkDivOnHaltCmd( HCI_EXT_ENABLE_CLK_DIVIDE_ON_HALT );#if defined ( DC_DC_P0_7 )// Enable stack to toggle bypass control on TPS62730 (DC/DC converter)HCI_EXT_MapPmIoPortCmd( HCI_EXT_PM_IO_PORT_P0, HCI_EXT_PM_IO_PORT_PIN7 );#endif // defined ( DC_DC_P0_7 )// Setup a delayed profile startuposal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );}

  SimpleBLEPeripheral_Init函数首先设置GAP协议栈,例如设置为从角色,设置GAP层的Characteristics,然后初始化GATT协议栈,例如设置GATT属性,设置GATT的特征值。
  再然后注册从角色的回调函数VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs )。
   最后产生一个启动事件osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT )
  回调函数void simpleProfileChangeCB( uint8 paramID )定义如下:

/********************************************************************** @fn      simpleProfileChangeCB** @brief   Callback from SimpleBLEProfile indicating a value change** @param   paramID - parameter ID of the value that was changed.** @return  none*/
static void simpleProfileChangeCB( uint8 paramID )
{uint8 newValue;switch( paramID ){case SIMPLEPROFILE_CHAR1:SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, &newValue );#if (defined HAL_LCD) && (HAL_LCD == TRUE)HalLcdWriteStringValue( "Char 1:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)break;case SIMPLEPROFILE_CHAR3:SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR3, &newValue );#if (defined HAL_LCD) && (HAL_LCD == TRUE)HalLcdWriteStringValue( "Char 3:", (uint16)(newValue), 10,  HAL_LCD_LINE_3 );#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)break;default:// should not reach here!break;}
}

  该函数就是从机接收数据的入口。

3.2、 peripheral 从机工程的任务函数

  任务函数代码如下:

/********************************************************************** @fn      SimpleBLEPeripheral_ProcessEvent** @brief   Simple BLE Peripheral Application Task event processor.  This function*          is called to process all events for the task.  Events*          include timers, messages and any other user defined events.** @param   task_id  - The OSAL assigned task ID.* @param   events - events to process.  This is a bit map and can*                   contain more than one event.** @return  events not processed*/
uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{VOID task_id; // OSAL required parameter that isn't used in this functionif ( events & SYS_EVENT_MSG ){uint8 *pMsg;if ( (pMsg = osal_msg_receive( simpleBLEPeripheral_TaskID )) != NULL ){simpleBLEPeripheral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );// Release the OSAL messageVOID osal_msg_deallocate( pMsg );}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}if ( events & SBP_START_DEVICE_EVT ){// Start the DeviceVOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs );// Start Bond ManagerVOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs );// Set timer for first periodic eventosal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );return ( events ^ SBP_START_DEVICE_EVT );}if ( events & SBP_PERIODIC_EVT ){// Restart timerif ( SBP_PERIODIC_EVT_PERIOD ){osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );}// Perform periodic application taskperformPeriodicTask();return (events ^ SBP_PERIODIC_EVT);}// Discard unknown eventsreturn 0;
}

  任务函数首先通过osal_msg_receive 从消息队列中获取OSAL消息,进行处理,例如按键消息等。然后,根据初始化事件的ID,处理初始化任务其中在初始化事件中开起一个系统级的定时器。最后是系统定时器的事件处理。

TI-BLE 协议栈(CC2541)peripheral的执行流程分析相关推荐

  1. 蓝牙BLE设备主机重启回连流程分析

    本文出自:<蓝牙BLE设备主机重启回连流程分析> 如果一个BLE设备已经与蓝牙中心设备连接上,那么当中心设备的断电重启,其依然会和配对过的BLE设备连接上,而不需要重新走配对的流程,这个过 ...

  2. 动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler

    在<动态执行流程分析和性能瓶颈分析的利器--valgrind的callgrind>中,我们领略了valgrind对流程和性能瓶颈分析的强大能力.本文将介绍拥有相似能力的gperftools ...

  3. 动态执行流程分析和性能瓶颈分析的利器——valgrind的callgrind

    在<内存.性能问题分析的利器--valgrind>一文中我们简单介绍了下valgrind工具集,本文将使用callgrind工具进行动态执行流程分析和性能瓶颈分析.(转载请指明出于brea ...

  4. Java多线程- 线程池的基本使用和执行流程分析 - ThreadPoolExecutor

    线程池的实现原理 池化技术 一说到线程池自然就会想到池化技术. 其实所谓池化技术,就是把一些能够复用的东西放到池中,避免重复创建.销毁的开销,从而极大提高性能. 常见池化技术的例如: 线程池 内存池 ...

  5. scala语言的底层是java实现的_Scala学习笔记一(与Java、Jvm的关系以及程序执行流程分析)...

    一.Scala语言与Java.Jvm的关系分析 Scala语言是马丁奥德斯基接触Java语言后,针对Java语言的特点,将函数式编程语言的特点融合到Java中,由此发明的.Scala语言和Java语言 ...

  6. 【网络安全】Metasploit生成的Shellcode的导入函数解析以及执行流程分析(2)

    密码破解的利器--彩虹表(rainbow table) 确定 shellcode 依赖于哪些导入将使研究人员进一步了解其其余逻辑.不用动态分析shellcode,并且考虑到研究人员已经弄清楚了上面的哈 ...

  7. Scala 执行流程分析

    Scala 执行流程分析

  8. mysql 8.0 一条insert语句的具体执行流程分析(三)

    代码版本:mysql 8.0.22 编程语言:c++ && c++11 && c++14 && c++17 上一篇文章:mysql 8.0 一条inse ...

  9. mysql 8.0 一条insert语句的具体执行流程分析(二)

    继续上一篇文章:mysql 8.0 一条insert语句的具体执行流程分析(一)_一缕阳光的博客-CSDN博客 由于最近换工作一直在试用期内,在拼命的学习.总结中,因此没有时间写文章,今天转正了腾出来 ...

最新文章

  1. 暂缓上市!小马智行SPAC赴美上市计划推迟,自驾IPO路漫漫
  2. mysql 启动个关闭命定_mysql利用phpmyadmin实现数据库同步更新
  3. 类与接口(四)方法重载解析
  4. 将时间戳转为年月日时分秒格式
  5. 整合tomcat的一些配置
  6. Android 系统(265)----Android进程保活全攻略(上)
  7. mysql5.6.22编译安装教程_Linux CentOS6.0下编译安装MySQL 5.6.22
  8. jsp,jstl checkbox 回显方法
  9. 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,
  10. nginx rewrite重写规则配置详解
  11. ai人工智能的数据服务_可解释的AI-它对数据科学家有何影响?
  12. linux如何打开22端口?如何开启ssh远程链接
  13. 人民币对美元汇率中间价报6.7969元 下调115个基点
  14. Windows版微信3.3.0内测版更新啦,亲测可刷朋友圈(附内测版)
  15. 鬼谷八荒逆天改命修改教程(3月亲测有效)
  16. 手机产品设计之用户引导
  17. spring 定时任务的 执行时间设置规则
  18. 华为云沙箱实验室的相关操作介绍
  19. Linux目录标准FHS介绍
  20. 《涨知识啦30》-太阳能电池基本工作原理

热门文章

  1. 生活必备冷门逆天的黑科技APP,每一款都堪称神器
  2. 通过url地址批量打包zip下载文件
  3. 易人支票打印软件 v8.4 绿色
  4. 通信中隧道技术的解释
  5. ArcSoft4.0_Go
  6. Hive查询报错:selectItem : ( ( exprexxxx ( ( ( KW_AS )......
  7. 下载了最新版本flash player,还是不能安装,还是提示版本低(转)
  8. SQL SERVER存储过程AS和GO的含义
  9. 实现金山快译工具条的自动收缩功能
  10. 安卓手机 python3_python3安卓版下载