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/O
InitBoard( 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 initialization
InitBoard( OB_READY );
#if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY );
#endif
/* Start OSAL */
osal_start_system(); // No Return from here
return 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 System
osal_mem_init();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
// Initialize the message queue
osal_qHead = NULL;
// Initialize the timers
osalTimerInit();
// Initialize the Power Management System
osal_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 service
osal_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 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
SM_ProcessEvent, // task 6
GATT_ProcessEvent, // task 7
GAPRole_ProcessEvent, // task 8
GAPBondMgr_ProcessEvent, // task 9
GATTServApp_ProcessEvent, // task 10
SimpleBLEPeripheral_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_ICALL
ICall_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_ICALL
uint32 next_timeout_prior = osal_next_timeout();
#else /* USE_ICALL */
#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif
Hal_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 GAP
VOID 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 pressed
uint8 initial_advertising_enable = FALSE;
#else
// For other hardware platforms, device starts advertising upon initialization
uint8 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 TRUE
uint16 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 Parameters
GAPRole_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 Characteristics
GGS_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 attributes
GGS_AddService( GATT_ALL_SERVICES ); // GAP
GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes
DevInfo_AddService(); // Device Information Service
SimpleProfile_AddService( GATT_ALL_SERVICES ); // Simple GATT Profile
#if defined FEATURE_OAD
VOID 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 events
RegisterForKeys( simpleBLEPeripheral_TaskID );
// makes sure LEDs are off
HalLedSet( (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 GPIO
P1SEL = 0; // Configure Port 1 as GPIO
P2SEL = 0; // Configure Port 2 as GPIO
P0DIR = 0xFC; // Port 0 pins P0.0 and P0.1 as input (buttons),
// all others (P0.2-P0.7) as output
P1DIR = 0xFF; // All port 1 pins (P1.0-P1.7) as output
P2DIR = 0x1F; // All port 1 pins (P2.0-P2.4) as output
P0 = 0x03; // All pins on port 0 to low except for P0.0 and P0.1 (buttons)
P1 = 0; // All pins on port 1 to low
P2 = 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 );
#else
HalLcdWriteStringValue( "BLE Peri-B", OAD_VER_NUM( _imgHdr.ver ), 16, HAL_LCD_LINE_1 );
#endif // HAL_IMAGE_A
#else
HalLcdWriteString( "BLE Peripheral", HAL_LCD_LINE_1 );
#endif // FEATURE_OAD
#endif // (defined HAL_LCD) && (HAL_LCD == TRUE)
// Register callback with SimpleGATTprofile
VOID SimpleProfile_RegisterAppCBs( &simpleBLEPeripheral_SimpleProfileCBs );
// Enable clock divide on halt
// This reduces active current while radio is active and CC254x MCU
// is halted
HCI_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 startup
osal_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 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;
}
任务函数首先通过osal_msg_receive 从消息队列中获取OSAL消息,进行处理,例如按键消息等。然后,根据初始化事件的ID,处理初始化任务其中在初始化事件中开起一个系统级的定时器。最后是系统定时器的事件处理。