打印
[蓝牙芯片]

沁恒蓝牙芯片CH57x系列学习与应用

[复制链接]
5554|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2021-9-2 12:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沁恒的蓝牙芯片例程已经将各个函数集合的很高,不同主从机例程的主函数中,往往只有一至两个的函数调用的区别,但是在这一到两个函数中,差别却是很大的。

以沁恒的外设从机例程Peripheral为例,进行学习与应用。
但是不懂的地方还有很多,本次学习只是大概梳理了下整体程序的顺序,很多地方的了解只局限于程序本身的注释,也希望能够和大家讨论学习。
注:我是用的是沁恒的CH573F芯片,用的软件是Mounriver Studio。

Peripheral例程的主函数如下所示:

int main( void )
{
  SetSysClock( CLK_SOURCE_PLL_32MHz );//时钟初始化
#ifdef DEBUG
  GPIOA_SetBits(bTXD1);
  GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
  UART1_DefInit( );//串口初始化和引脚配置
#endif   
  PRINT("%s\n",VER_LIB);
  CH57X_BLEInit( );//BLE库初始化
        HAL_Init( );//硬件初始化
        GAPRole_PeripheralInit( );//外围设备配置
        Peripheral_Init( ); //外围应用设备初始化
        while(1){
                TMOS_SystemProcess( );//执行系统处理
        }
}


其中,和沁恒的主机例程Central的主函数相比只有两个函数的区别:

Peripheral例程中,对从机和外设的配置的主要函数如下所示:

GAPRole_PeripheralInit( );//外围设备配置
Peripheral_Init( ); //外围应用设备初始化


Central例程中,对主机和外设的配置的主要函数如下所示:

GAPRole_CentralInit( );
Central_Init( );



使用特权

评论回复
沙发
wiba|  楼主 | 2021-9-2 12:05 | 只看该作者
1.CH57X_BLEInit
首先来看蓝牙的库初始化配置:

void CH57X_BLEInit( void )
{
  uint8 i;
  bleConfig_t cfg;
  if ( tmos_memcmp( VER_LIB, VER_FILE, strlen( VER_FILE ) ) == FALSE )
  {
    PRINT( "head file error...\n" );
    while( 1 )
      ;
  }
  SysTick_Config( SysTick_LOAD_RELOAD_Msk );
  PFIC_DisableIRQ( SysTick_IRQn );

  tmos_memset( &cfg, 0, sizeof(bleConfig_t) );
  cfg.MEMAddr = ( u32 ) MEM_BUF;
  cfg.MEMLen = ( u32 ) BLE_MEMHEAP_SIZE;
  cfg.BufMaxLen = ( u32 ) BLE_BUFF_MAX_LEN;
  cfg.BufNumber = ( u32 ) BLE_BUFF_NUM;
  cfg.TxNumEvent = ( u32 ) BLE_TX_NUM_EVENT;
  cfg.TxPower = ( u32 ) BLE_TX_POWER;
#if (defined (BLE_SNV)) && (BLE_SNV == TRUE)
  cfg.SNVAddr = ( u32 ) BLE_SNV_ADDR;
  cfg.readFlashCB = Lib_Read_Flash;
  cfg.writeFlashCB = Lib_Write_Flash;
#endif
#if( CLK_OSC32K )       
  cfg.SelRTCClock = (u32)CLK_OSC32K;
#endif
  cfg.ConnectNumber = ( PERIPHERAL_MAX_CONNECTION & 3 ) | ( CENTRAL_MAX_CONNECTION << 2 );
  cfg.srandCB = SYS_GetSysTickCnt;
#if (defined TEM_SAMPLE)  && (TEM_SAMPLE == TRUE)
  cfg.tsCB = HAL_GetInterTempValue;    // 根据温度变化校准RF和内部RC( 大于7摄氏度 )
#if( CLK_OSC32K )
  cfg.rcCB = Lib_Calibration_LSI;    // 内部32K时钟校准
#endif
#endif
#if (defined (HAL_SLEEP)) && (HAL_SLEEP == TRUE)
  cfg.WakeUpTime = WAKE_UP_RTC_MAX_TIME;
  cfg.sleepCB = CH57X_LowPower;    // 启用睡眠
#endif
#if (defined (BLE_MAC)) && (BLE_MAC == TRUE)
  for(i=0;i<6;i++) cfg.MacAddr[i] = MacAddr[5-i];
#else
  {
    uint8 MacAddr[6];
    GetMACAddress( MacAddr );
    for(i=0;i<6;i++) cfg.MacAddr[i] = MacAddr[i];   // 使用芯片mac地址
  }
#endif
  if ( !cfg.MEMAddr || cfg.MEMLen < 4 * 1024 )
    while( 1 )
      ;
  i = BLE_LibInit( &cfg );
  if ( i )
  {
    PRINT( "LIB init error code: %x ...\n", i );
    while( 1 )
      ;
  }
}


其中函数是对利用变量实现对当前芯片与库文件的是否匹配的检查,如果不匹配,则执行空循环,并打印“head file error…”,通过串口进行通报:

tmos_memcmp( VER_LIB, VER_FILE, strlen( VER_FILE ) )


后续两个函数分别是对系统时钟的配置和中断的不使能:

SysTick_Config( SysTick_LOAD_RELOAD_Msk );
PFIC_DisableIRQ( SysTick_IRQn );


之后再对BLE的库进行配置。


使用特权

评论回复
板凳
wiba|  楼主 | 2021-9-2 12:08 | 只看该作者
本帖最后由 wiba 于 2021-9-2 12:09 编辑

先是对所有变量进行清零,然后重新配置:

  tmos_memset( &cfg, 0, sizeof(bleConfig_t) );//对cfg结构体中的变量进行清零操作
  cfg.MEMAddr = ( u32 ) MEM_BUF;
  cfg.MEMLen = ( u32 ) BLE_MEMHEAP_SIZE;
  cfg.BufMaxLen = ( u32 ) BLE_BUFF_MAX_LEN;
  cfg.BufNumber = ( u32 ) BLE_BUFF_NUM;
  cfg.TxNumEvent = ( u32 ) BLE_TX_NUM_EVENT;
  cfg.TxPower = ( u32 ) BLE_TX_POWER;
#if (defined (BLE_SNV)) && (BLE_SNV == TRUE)
  cfg.SNVAddr = ( u32 ) BLE_SNV_ADDR;
  cfg.readFlashCB = Lib_Read_Flash;
  cfg.writeFlashCB = Lib_Write_Flash;
#endif
  cfg.ConnectNumber = ( PERIPHERAL_MAX_CONNECTION & 3 ) | ( CENTRAL_MAX_CONNECTION << 2 );
  cfg.srandCB = SYS_GetSysTickCnt;
#if (defined TEM_SAMPLE)  && (TEM_SAMPLE == TRUE)
  cfg.tsCB = HAL_GetInterTempValue;    // 根据温度变化校准RF和内部RC( 大于7摄氏度 )





其中,对芯片的蓝牙地址的定义有两种方法,一个是自己设置,另一个是使用芯片的mac地址,具体函数如下所示:

#if (defined (BLE_MAC)) && (BLE_MAC == TRUE)
  for(i=0;i<6;i++) cfg.MacAddr = MacAddr[5-i];
#else
  {
    uint8 MacAddr[6];
    GetMACAddress( MacAddr );
    for(i=0;i<6;i++) cfg.MacAddr = MacAddr;   // 使用芯片mac地址
  }
#endif
  if ( !cfg.MEMAddr || cfg.MEMLen < 4 * 1024 )
    while( 1 )
      ;


通过对变量BLE_MAC的值来进行选择蓝牙模块使用的地址是自己设置的地址还是芯片的地址。同时,最后还有对蓝牙地址是否成功配置结束的判断,如果未成功,则陷入空循环之中。

同样的,在配置完BLE的库之后,也要对总体的库配置是否完成进行判断,这里是利用了BLE_LibInit()函数,当配置成功,则返回0值。反之则返回1,通过串口报错,并陷入空循环之中。

  i = BLE_LibInit( &cfg );
  if ( i )
  {
    PRINT( "LIB init error code: %x ...\n", i );
    while( 1 )
      ;
  }


使用特权

评论回复
地板
wiba|  楼主 | 2021-9-2 12:10 | 只看该作者
2.HAL_Init
HAL_Init()函数是硬件初始化函数。

函数主体如下所示:

void HAL_Init()
{
  halTaskID = TMOS_ProcessEventRegister( HAL_ProcessEvent );
  HAL_TimeInit();
#if (defined HAL_SLEEP) && (HAL_SLEEP == TRUE)//不执行
  HAL_SleepInit( );
#endif
#if (defined HAL_LED) && (HAL_LED == TRUE)//不执行
  HAL_LedInit( );
#endif
#if (defined HAL_KEY) && (HAL_KEY == TRUE)//不执行
  HAL_KeyInit( );
#endif
#if ( defined BLE_CALIBRATION_ENABLE ) && ( BLE_CALIBRATION_ENABLE == TRUE )
  tmos_start_task( halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME( BLE_CALIBRATION_PERIOD ) );    // 添加校准任务,单次校准耗时小于10ms
#endif
//  tmos_start_task( halTaskID , HAL_TEST_EVENT ,1000 ); // 添加一个测试任务
}


由于Peripheral例程并未用到诸如蓝牙休眠,LED和Key等功能,所以HAL_Init()函数主要只执行了系统时间配置和校准任务的添加。

HAL_TimeInit()为系统定时器初始化,函数如下所示:

void HAL_TimeInit( void )
{
#if( CLK_OSC32K )
  Calibration_LSI();
#else
  R8_SAFE_ACCESS_SIG = 0x57;
  R8_SAFE_ACCESS_SIG = 0xa8;
  R8_CK32K_CONFIG |= RB_CLK_OSC32K_XT | RB_CLK_INT32K_PON | RB_CLK_XT32K_PON;
  R8_SAFE_ACCESS_SIG = 0;
#endif
  RTC_InitTime( 23, 59, 58 );    //RTC时钟初始化当前时间
  TMOS_TimerInit( 0 );
}


剩下还有两个函数,主要如下:

halTaskID = TMOS_ProcessEventRegister( HAL_ProcessEvent );
#if ( defined BLE_CALIBRATION_ENABLE ) && ( BLE_CALIBRATION_ENABLE == TRUE )
tmos_start_task( halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME( BLE_CALIBRATION_PERIOD ) );    // 添加校准任务,单次校准耗时小于10ms
#endif


通过利用TMOS_ProcessEventRegister()函数来获取目标时间内的任务ID。同时

tmos_start_task()函数通过配置当前任务ID,校准任务事件以及事件的执行时间间隔来完成当前事件的开始。具体函数如下所示:

tmosEvents HAL_ProcessEvent( tmosTaskID task_id, tmosEvents events )
{
  uint8 * msgPtr;

  if ( events & SYS_EVENT_MSG )
  {    // 处理HAL层消息,调用tmos_msg_receive读取消息,处理完成后删除消息。
    msgPtr = tmos_msg_receive( task_id );
    if ( msgPtr )
    {
      /* De-allocate */
      tmos_msg_deallocate( msgPtr );
    }
    return events ^ SYS_EVENT_MSG;
  }
  if ( events & LED_BLINK_EVENT )
  {
#if (defined HAL_LED) && (HAL_LED == TRUE)
    HalLedUpdate( );
#endif // HAL_LED
    return events ^ LED_BLINK_EVENT;
  }
  if ( events & HAL_KEY_EVENT )
  {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    HAL_KeyPoll(); /* Check for keys */
    if (!Hal_KeyIntEnable)
    {
      tmos_start_task( halTaskID, HAL_KEY_EVENT, MS1_TO_SYSTEM_TIME(100) );
    }
    return events ^ HAL_KEY_EVENT;
#endif
  }
  if ( events & HAL_REG_INIT_EVENT )
  {
#if (defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)        // 校准任务,单次校准耗时小于10ms
    BLE_RegInit();    // 校准RF
#if( CLK_OSC32K )       
    Lib_Calibration_LSI();    // 校准内部RC
#endif
    tmos_start_task( halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME( BLE_CALIBRATION_PERIOD ) );
    return events ^ HAL_REG_INIT_EVENT;
#endif
  }
  if ( events & HAL_TEST_EVENT )
  {
    PRINT( "* " );
    tmos_start_task( halTaskID, HAL_TEST_EVENT, MS1_TO_SYSTEM_TIME( 10000 ) );
    return events ^ HAL_TEST_EVENT;
  }
  return 0;
}


每个task 下拥有的event,16bit,每bit代表一个event,对于同一个task,所以一共会有16个事件。


使用特权

评论回复
5
wiba|  楼主 | 2021-9-2 12:11 | 只看该作者
①SYS_EVENT_MSG

  if ( events & SYS_EVENT_MSG )
  {    // 处理HAL层消息,调用tmos_msg_receive读取消息,处理完成后删除消息。
    msgPtr = tmos_msg_receive( task_id );
    if ( msgPtr )
    {
      /* De-allocate */
      tmos_msg_deallocate( msgPtr );
    }
    return events ^ SYS_EVENT_MSG;
  }


其中分别利用tmos_msg_receive()函数来获取消息,当读取到消息时,再利用tmos_msg_deallocate()来对信息进行删除。

同时也可以发现,在整个函数中,判断条件是events与SYS_EVENT_MSG,对于SYS_EVENT_MSG这些事件而言,只有一位是1,其他位都为0。而最后又与该变量进行异或,则会对该位进行清零,达到只运行一次该事件的处理与配置。

②LED_BLINK_EVENT

  if ( events & LED_BLINK_EVENT )
  {
#if (defined HAL_LED) && (HAL_LED == TRUE)
    HalLedUpdate( );
#endif // HAL_LED
    return events ^ LED_BLINK_EVENT;
  }


HalLedUpdate()是对LED设备时间的更新。

但是本例程中,由于HAL_LED的宏定义为FALSE,使得该事件中并没有执行该函数。

③HAL_KEY_EVENT

  if ( events & HAL_KEY_EVENT )
  {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    HAL_KeyPoll(); /* Check for keys */
    if (!Hal_KeyIntEnable)
    {
      tmos_start_task( halTaskID, HAL_KEY_EVENT, MS1_TO_SYSTEM_TIME(100) );
    }
    return events ^ HAL_KEY_EVENT;
#endif
  }


HAL_KeyPoll()是按键检测。

tmos_start_task()是在TMOS系统中很重要的函数,他共有三个参数,分别是taskID,event和time,也就是当前任务的ID,当前任务的事件和事件执行间隔时间。而本例中,就是执行硬件任务中的按键事件。

MS1_TO_SYSTEM_TIME()函数则是一个时间计算函数,具体定义如下所示

#define SYSTEM_TIME_MICROSEN     625                               // unit of process event timer is 625us
#define MS1_TO_SYSTEM_TIME(x)    ((x)*1000/SYSTEM_TIME_MICROSEN)   // transform unit in ms to unit in 625us ( attentional bias )


按照计算,则可以计算出按键事件的间隔时间时160us。


使用特权

评论回复
6
wiba|  楼主 | 2021-9-2 12:12 | 只看该作者
④HAL_REG_INIT_EVENT

  if ( events & HAL_REG_INIT_EVENT )
  {
#if (defined BLE_CALIBRATION_ENABLE) && (BLE_CALIBRATION_ENABLE == TRUE)        // 校准任务,单次校准耗时小于10ms
    BLE_RegInit();    // 校准RF
#if( CLK_OSC32K )       
    Lib_Calibration_LSI();    // 校准内部RC
#endif
    tmos_start_task( halTaskID, HAL_REG_INIT_EVENT, MS1_TO_SYSTEM_TIME( BLE_CALIBRATION_PERIOD ) );
    return events ^ HAL_REG_INIT_EVENT;
#endif
  }


BLE_RegInit()函数和Lib_Calibration_LSI()函数分别是BLE寄存器复位,射频校准和校准内部RC。

不过由于CLK_OSC32K的宏定义,所以并没有执行内部校准函数。

同时执行了tmos_start_task()函数,使得HAL_REG_INIT_EVENT时间执行的间隔时间为0.192s。

⑤HAL_TEST_EVENT

  if ( events & HAL_TEST_EVENT )
  {
    PRINT( "* " );
    tmos_start_task( halTaskID, HAL_TEST_EVENT, MS1_TO_SYSTEM_TIME( 10000 ) );
    return events ^ HAL_TEST_EVENT;
  }


TEST事件只是通过串口打印“*”作为通报,同时开始测试事件,并设置间隔时间为16ms。


使用特权

评论回复
7
wiba|  楼主 | 2021-9-2 12:13 | 只看该作者
3.GAPRole_PeripheralInit
GAPRole_PeripheralInit()函数是应用于外围设备配置,也就是说,对于不同的应用需要配置不同的外围设备函数。

4.Peripheral_Init
Peripheral_Init()函数是针对Peripheral工程的外围应用程序功能的初始化。函数主体如下所示:

void Peripheral_Init( )
{
  Peripheral_TaskID = TMOS_ProcessEventRegister( Peripheral_ProcessEvent );

  // Setup the GAP Peripheral Role Profile
  {
    uint8 initial_advertising_enable = TRUE;
    uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
    uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;


    // Set the GAP Role Parameters
    GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
    GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), scanRspData );
    GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );
    GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );
    GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );

  }

  // 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_DISC_ADV_INT_MIN, advInt );
    GAP_SetParamValue( TGAP_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 bonding = TRUE;
    uint8 ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
    GAPBondMgr_SetParameter( GAPBOND_PERI_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );
    GAPBondMgr_SetParameter( GAPBOND_PERI_PAIRING_MODE, sizeof ( uint8 ), &pairMode );
    GAPBondMgr_SetParameter( GAPBOND_PERI_MITM_PROTECTION, sizeof ( uint8 ), &mitm );
    GAPBondMgr_SetParameter( GAPBOND_PERI_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );
    GAPBondMgr_SetParameter( GAPBOND_PERI_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

  // Setup the SimpleProfile Characteristic Values
  {
    uint8 charValue1[SIMPLEPROFILE_CHAR1_LEN] = { 1 };
    uint8 charValue2[SIMPLEPROFILE_CHAR2_LEN] = { 2 };
    uint8 charValue3[SIMPLEPROFILE_CHAR3_LEN] = { 3 };
    uint8 charValue4[SIMPLEPROFILE_CHAR4_LEN] = { 4 };
    uint8 charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };
               

    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1 );
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2 );
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3 );
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4 );
    SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5 );

  }

  // Init Connection Item
  peripheralInitConnItem( &peripheralConnList );

  // Register callback with SimpleGATTprofile
  SimpleProfile_RegisterAppCBs( &Peripheral_SimpleProfileCBs );

  // Register receive scan request callback
        GAPRole_BroadcasterSetCB( &Broadcaster_BroadcasterCBs );
       

        // Setup a delayed profile startup

  tmos_set_event( Peripheral_TaskID, SBP_START_DEVICE_EVT );
}


与硬件初始化类似,先是利用TMOS_ProcessEventRegister()函数来获取当前任务的ID。


使用特权

评论回复
8
wiba|  楼主 | 2021-9-2 12:14 | 只看该作者
其中任务函数Peripheral_ProcessEvent()主要内容如下所示:

uint16 Peripheral_ProcessEvent( uint8 task_id, uint16 events )
{

//  VOID task_id; // TMOS required parameter that isn't used in this function

  if ( events & SYS_EVENT_MSG ){
    uint8 *pMsg;

    if ( (pMsg = tmos_msg_receive( Peripheral_TaskID )) != NULL ){
      Peripheral_ProcessTMOSMsg( (tmos_event_hdr_t *)pMsg );
      // Release the TMOS message
      tmos_msg_deallocate( pMsg );
    }
    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);

  }

  if ( events & SBP_START_DEVICE_EVT ){
        // Start the Device
    GAPRole_PeripheralStartDevice( Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs );
    return ( events ^ SBP_START_DEVICE_EVT );
  }

  if ( events & SBP_PERIODIC_EVT )
  {
   // Restart timer
    if ( SBP_PERIODIC_EVT_PERIOD ){
      tmos_start_task( Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );
    }
    // Perform periodic application task
    performPeriodicTask();
    return (events ^ SBP_PERIODIC_EVT);
  }

  if ( events & SBP_PARAM_UPDATE_EVT )
  {
    // Send connect param update request
    GAPRole_PeripheralConnParamUpdateReq( peripheralConnList.connHandle,
                                          DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                          DEFAULT_DESIRED_MAX_CONN_INTERVAL,
                                          DEFAULT_DESIRED_SLAVE_LATENCY,
                                          DEFAULT_DESIRED_CONN_TIMEOUT,
                                          Peripheral_TaskID);


    return (events ^ SBP_PARAM_UPDATE_EVT);

  }

  if ( events & SBP_READ_RSSI_EVT )
  {
    GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
    tmos_start_task( Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD );      
    return (events ^ SBP_READ_RSSI_EVT);
  }   

  // Discard unknown events
  return 0;
}

使用特权

评论回复
9
wiba|  楼主 | 2021-9-2 12:15 | 只看该作者
①SYS_EVENT_MSG

SYS_EVENT_MSG也是处理相应消息,读取消息和删除消息。

  if ( events & SYS_EVENT_MSG ){
    uint8 *pMsg;

    if ( (pMsg = tmos_msg_receive( Peripheral_TaskID )) != NULL ){
      Peripheral_ProcessTMOSMsg( (tmos_event_hdr_t *)pMsg );
      // Release the TMOS message
      tmos_msg_deallocate( pMsg );
    }
    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);

  }


②SBP_START_DEVICE_EVT

本个事件看似很简单,主要只有一个主要的函数,就是GAPRole_PeripheralStartDevice()函数,他的作用是判断是否对设备进行了初始化,同时GAPRole_PeripheralStartDevice()函数也调用了其他的函数来进行初始化,也就是说初始化完成之后,返回值SUCESS。

  if ( events & SBP_START_DEVICE_EVT ){
        // Start the Device
    GAPRole_PeripheralStartDevice( Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs );
    return ( events ^ SBP_START_DEVICE_EVT );
  }


GAPRole_PeripheralStartDevice函数声明如下所示:

extern bStatus_t GAPRole_PeripheralStartDevice( uint8 taskid, gapBondCBs_t *pCB,gapRolesCBs_t *pAppCallbacks );
1
函数总共三个参数。其中第一个是当前任务的ID,另外两个则是两个结构体。

gapBondCBs_t结构体如下所示:

typedef struct
{
  pfnPasscodeCB_t     passcodeCB;       //!< 密码回调
  pfnPairStateCB_t    pairStateCB;      //!< 配对状态回调
} gapBondCBs_t;


该结构体中又包含了两个结构体,分别用作密码回调和配对状态回调,分别如下所示:

typedef void (*pfnPasscodeCB_t)
(
  uint8  *deviceAddr,                   //!< address of device to pair with, and could be either public or random.
  uint16 connectionHandle,              //!< Connection handle
  uint8  uiInputs,                      //!< Pairing User Interface Inputs - Ask user to input passcode
  uint8  uiOutputs                      //!< Pairing User Interface Outputs - Display passcode
);


deviceAddr是要配对。设备的地址,通过指针运算符,取得该地址下的设备地址;

connectionHandle是连接处理;

uiInputs是配对用户界面输入-要求用户输入密码;

uiOutputs是配对用户界面输出-显示密码。

typedef void (*pfnPairStateCB_t)
(
  uint16 connectionHandle,              //!< Connection handle
  uint8  state,                         //!< Pairing state @ref GAPBOND_PAIRING_STATE_DEFINES
  uint8  status                         //!< Pairing statusc
);




而调用的Peripheral_BondMgrCBs结构体,则是把所有的结构体中的变量都置零,相当于初始化。

static gapBondCBs_t Peripheral_BondMgrCBs =
{
  NULL,                     // Passcode callback (not used by application)
  NULL                      // Pairing / Bonding state Callback (not used by application)
};


而Peripheral_PeripheralCBs结构体中包含了三个函数,

static gapRolesCBs_t Peripheral_PeripheralCBs =
{
  peripheralStateNotificationCB,  // Profile State Change Callbacks
  peripheralRssiCB,  // When a valid RSSI is read from controller (not used by application)
        peripheralParamUpdateCB
};


使用特权

评论回复
10
wiba|  楼主 | 2021-9-2 12:16 | 只看该作者
首先来看peripheralStateNotificationCB()函数:

static void peripheralStateNotificationCB( gapRole_States_t newState, gapRoleEvent_t * pEvent )
{
  switch ( newState )
  {
    case GAPROLE_STARTED:
      PRINT( "Initialized..\n" );
      break;

    case GAPROLE_ADVERTISING:
      if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT )
      {
        Peripheral_LinkTerminated( pEvent );
        PRINT( "Disconnected.. Reason:%x\n",pEvent->linkTerminate.reason );
      }
      PRINT( "Advertising..\n" );
      break;

    case GAPROLE_CONNECTED:
      if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT )
      {
        Peripheral_LinkEstablished( pEvent );
      }
      PRINT( "Connected..\n" );
      break;

    case GAPROLE_CONNECTED_ADV:
      PRINT( "Connected Advertising..\n" );
      break;      

    case GAPROLE_WAITING:
      if( pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT )
      {
        PRINT( "Waiting for advertising..\n" );
      }
      else if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT )
      {
        Peripheral_LinkTerminated( pEvent );
        PRINT( "Disconnected.. Reason:%x\n",pEvent->linkTerminate.reason );
      }
      else if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT )
                    {
                            if( pEvent->gap.hdr.status != SUCCESS )
                            {
                                    PRINT( "Waiting for advertising..\n" );
                            }
                            else
                            {
                                    PRINT( "Error..\n" );
                            }
                    }
                    else
                    {
                            PRINT( "Error..%x\n",pEvent->gap.opcode );
                    }
      break;

    case GAPROLE_ERROR:
                    PRINT( "Error..\n" );
      break;

    default:
      break;

  }
}


peripheralStateNotificationCB()函数共有两个参数,分别是当前状态newState和中心时间结构pEvent。


使用特权

评论回复
11
wiba|  楼主 | 2021-9-2 12:34 | 只看该作者
首先来看第一种状态:

case GAPROLE_STARTED:
  PRINT( "Initialized..\n" );
  break;


GAPROLE_STARTED状态表示的是已经开始但是没有进行广播,通过串口打印表示已经初始化结束。

第二种状态:

case GAPROLE_ADVERTISING:
  if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT )
  {
    Peripheral_LinkTerminated( pEvent );
    PRINT( "Disconnected.. Reason:%x\n",pEvent->linkTerminate.reason );
  }
  PRINT( "Advertising..\n" );
  break;


GAPROLE_ADVERTISING状态表示的是正在进行广播,同时如果 pEvent结构中的gap.opcode变量与表示连接终止标志GAP_LINK_TERMINATED_EVENT相等的时候,则执行Peripheral_LinkTerminated()函数,并通过串口打印通报无法连接和无法连接的原因。其中pEvent结构体中的linkTerminate结构体中的reason寄存的是连接终止的原因。

其中Peripheral_LinkTerminated()函数是当进程终止时,进行处理,具体函数如下所示:

static void Peripheral_LinkTerminated( gapRoleEvent_t * pEvent )
{
  gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *) pEvent;

  if( event->connectionHandle == peripheralConnList.connHandle )
  {
    peripheralConnList.connHandle = GAP_CONNHANDLE_INIT;
    peripheralConnList.connInterval = 0;
    peripheralConnList.connSlaveLatency = 0;
    peripheralConnList.connTimeout = 0;
    tmos_stop_task( Peripheral_TaskID, SBP_PERIODIC_EVT );
    tmos_stop_task( Peripheral_TaskID, SBP_READ_RSSI_EVT );


    // Restart advertising
    {
      uint8 advertising_enable = TRUE;
      GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &advertising_enable );
    }

  }
  else
  {
    PRINT("ERR..\n");
  }      
}


如果没有出现连接终止的标志,则通过串口进行打印正在广播进行通报。


使用特权

评论回复
12
wiba|  楼主 | 2021-9-2 12:37 | 只看该作者
第三种状态:

case GAPROLE_CONNECTED:
  if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT )
  {
    Peripheral_LinkEstablished( pEvent );
  }
  PRINT( "Connected..\n" );
  break;


GAPROLE_CONNECTED状态表示的是在连接中。

当pEvent结构体中的gap结构体中的opcode变量与表示建立连接请求发送完成的标志位GAP_LINK_ESTABLISHED_EVENT相等的时候,则执行Peripheral_LinkEstablished()函数,从而对主从机进行连接操作。

其中Peripheral_LinkEstablished()函数具体如下所示:

static void Peripheral_LinkEstablished( gapRoleEvent_t * pEvent )
{
  gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *) pEvent;

  // See if already connected
  if( peripheralConnList.connHandle != GAP_CONNHANDLE_INIT )
  {
    GAPRole_TerminateLink( pEvent->linkCmpl.connectionHandle );
    PRINT( "Connection max...\n" );
  }
  else
  {
    peripheralConnList.connHandle = event->connectionHandle;
    peripheralConnList.connInterval = event->connInterval;
    peripheralConnList.connSlaveLatency = event->connLatency;
    peripheralConnList.connTimeout = event->connTimeout;


    // Set timer for periodic event
    tmos_start_task( Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );

    // Set timer for param update event
    tmos_start_task( Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY );

    // Start read rssi
    tmos_start_task( Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD );

    PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval);                  

  }        
}


在Peripheral_LinkEstablished()函数中启动了三个事件,分别是SBP_PERIODIC_EVT、

SBP_PARAM_UPDATE_EVT和SBP_READ_RSSI_EVT。三个事件的功能分别是为定期事件设置定时器、设置参数更新事件的计时器和开始读蓝牙强度RSSI。


使用特权

评论回复
13
wiba|  楼主 | 2021-9-2 12:40 | 只看该作者
第四种状态

GAPROLE_CONNECTED_ADV表示的是正在连接和广播,通过串口打印进行通报。

case GAPROLE_CONNECTED_ADV:
  PRINT( "Connected Advertising..\n" );
  break;  


第五种状态

GAPROLE_WAITING表示的是设备已经启动,处于等待广播的状态。

case GAPROLE_WAITING:
  if( pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT )
  {
    PRINT( "Waiting for advertising..\n" );
  }
  else if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT )
  {
    Peripheral_LinkTerminated( pEvent );
    PRINT( "Disconnected.. Reason:%x\n",pEvent->linkTerminate.reason );
  }
  else if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT )
                {
                        if( pEvent->gap.hdr.status != SUCCESS )
                        {
                                PRINT( "Waiting for advertising..\n" );
                        }
                        else
                        {
                                PRINT( "Error..\n" );
                        }
                }
                else
                {
                        PRINT( "Error..%x\n",pEvent->gap.opcode );
                }
  break;


当gap.opcode标志位与表示广播结束的GAP_END_DISCOVERABLE_DONE_EVENT相等时,通过串口打印表示广播结束,等待下次广播。

当gap.opcode标志位与表示连接终止的GAP_LINK_TERMINATED_EVENT相等时,执行Peripheral_LinkTerminated()函数,并通过串口通报无法连接以及无法连接的原因。

当gap.opcode标志位与表示连接请求完成时的GAP_LINK_ESTABLISHED_EVENT相等时,再对status变量进行判断,如果是SUCESS,则打印表示等待下次广播,否则通过串口进行通报错误。


使用特权

评论回复
14
wiba|  楼主 | 2021-9-2 12:41 | 只看该作者
第六种状态

GAPROLE_ERROR表示发生错误,通过串口打印进行通报。

case GAPROLE_ERROR:
                PRINT( "Error..\n" );
  break;


其中peripheralRssiCB()函数具体如下所示,主要功能是从控制器中读取有效的蓝牙强度值RSSI。

static void peripheralRssiCB( uint16 connHandle, int8 rssi )
{
  PRINT( "RSSI -%d dB Conn  %x \n", -rssi, connHandle);
}


其中peripheralParamUpdateCB()函数具体如下所示,主要功能是更新外设参数,分别是connInterval-连接间隔,connSlaveLatency-连接从属延迟和connTimeout-连接超时。

static void peripheralParamUpdateCB( uint16 connHandle, uint16 connInterval,
                                      uint16 connSlaveLatency, uint16 connTimeout )
{
  if( connHandle == peripheralConnList.connHandle )
  {
    peripheralConnList.connInterval = connInterval;
    peripheralConnList.connSlaveLatency = connSlaveLatency;
    peripheralConnList.connTimeout = connTimeout;

    PRINT("Update %x - Int %x \n", connHandle, connInterval);

  }
  else
  {
    PRINT("ERR..\n");
  }
}


使用特权

评论回复
15
wiba|  楼主 | 2021-9-2 12:44 | 只看该作者
③SBP_PERIODIC_EVT

先是利用tmos_start_task()函数来实现本个事件的时间配置。

  if ( events & SBP_PERIODIC_EVT )
  {
   // Restart timer
    if ( SBP_PERIODIC_EVT_PERIOD ){
      tmos_start_task( Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );
    }
    // Perform periodic application task
    performPeriodicTask();
    return (events ^ SBP_PERIODIC_EVT);
  }


Perform periodic application task()函数主要功能是执行申请任务,具体函数如下所示:

static void performPeriodicTask( void )
{
  uint8 notiData[SIMPLEPROFILE_CHAR4_LEN] = { 0x99 };
  peripheralChar4Notify( notiData, SIMPLEPROFILE_CHAR4_LEN );
}


通过设置notiData[]数组中的值,可以实现从机向主机传送数据的改变。

其中peripheralChar4Notify()函数具体如下所示:

static void peripheralChar4Notify( uint8 *pValue, uint16 len )
{
  attHandleValueNoti_t noti;
  noti.len = len;
  noti.pValue = GATT_bm_alloc( peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0 );
  tmos_memcpy( noti.pValue, pValue, noti.len );
  if( simpleProfile_Notify( peripheralConnList.connHandle, &noti ) != SUCCESS )
  {
    GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );
  }
}



得到的变量先通过tmos_memcpy()函数进行内存复制,再通过simpleProfile_Notify()函数,simpleProfile_Notify()函数如下所示:

bStatus_t simpleProfile_Notify( uint16 connHandle, attHandleValueNoti_t *pNoti )
{
  uint16 value = GATTServApp_ReadCharCfg( connHandle, simpleProfileChar4Config );

  // If notifications enabled
  if ( value & GATT_CLIENT_CFG_NOTIFY )
  {
    // Set the handle
    pNoti->handle = simpleProfileAttrTbl[SIMPLEPROFILE_CHAR4_VALUE_POS].handle;

    // Send the notification
    return GATT_Notification( connHandle, pNoti, FALSE );

  }
  return bleIncorrectMode;
}



首先simpleProfile_Notify()函数执行了一个GATTServApp_ReadCharCfg()函数。之后由分别执行了simpleProfileAttrTbl()函数和GATT_Notification()函数。

其中利用simpleProfileAttrTbl()实现配置文件属性表的查询:

static gattAttribute_t simpleProfileAttrTbl[] =
{
  // Simple Profile Service
  {
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&simpleProfileService            /* pValue */
  },

    // Characteristic 1 Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &simpleProfileChar1Props
    },

      // Characteristic Value 1
      {
        { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        simpleProfileChar1
      },

      // Characteristic 1 User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar1UserDesp
      },      

    // Characteristic 2 Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &simpleProfileChar2Props
    },

      // Characteristic Value 2
      {
        { ATT_BT_UUID_SIZE, simpleProfilechar2UUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar2
      },

      // Characteristic 2 User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar2UserDesp
      },           

    // Characteristic 3 Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &simpleProfileChar3Props
    },

      // Characteristic Value 3
      {
        { ATT_BT_UUID_SIZE, simpleProfilechar3UUID },
        GATT_PERMIT_WRITE,
        0,
        simpleProfileChar3
      },

      // Characteristic 3 User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar3UserDesp
      },

    // Characteristic 4 Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &simpleProfileChar4Props
    },

      // Characteristic Value 4
      {
        { ATT_BT_UUID_SIZE, simpleProfilechar4UUID },
        0,
        0,
        simpleProfileChar4
      },

      // Characteristic 4 configuration
      {
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        (uint8 *)simpleProfileChar4Config
      },

      // Characteristic 4 User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar4UserDesp
      },

    // Characteristic 5 Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &simpleProfileChar5Props
    },

      // Characteristic Value 5
      {
        { ATT_BT_UUID_SIZE, simpleProfilechar5UUID },
        GATT_PERMIT_AUTHEN_READ,
        0,
        simpleProfileChar5
      },

      // Characteristic 5 User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        simpleProfileChar5UserDesp
      },

};



而GATT_Notification()函数则是发送数据的函数,将之前peripheralChar4Notify()函数存储的数据发送出去。


使用特权

评论回复
16
wiba|  楼主 | 2021-9-2 12:47 | 只看该作者
③SBP_PARAM_UPDATE_EVT

SBP_PARAM_UPDATE_EVT事件是发送连接参数更新请求

  if ( events & SBP_PARAM_UPDATE_EVT )
  {
    // Send connect param update request
    GAPRole_PeripheralConnParamUpdateReq( peripheralConnList.connHandle,
                                          DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                          DEFAULT_DESIRED_MAX_CONN_INTERVAL,
                                          DEFAULT_DESIRED_SLAVE_LATENCY,
                                          DEFAULT_DESIRED_CONN_TIMEOUT,
                                          Peripheral_TaskID);


    return (events ^ SBP_PARAM_UPDATE_EVT);

  }


其中,GAPRole_PeripheralConnParamUpdateReq()函数需要更新的主要有连接处理,新的连接间隔,新的从属延迟。监控超时值等

④SBP_READ_RSSI_EVT

SBP_READ_RSSI_EVT事件是最后一个事件,他的功能很明显,是读蓝牙强度的这么一个事件任务。

  if ( events & SBP_READ_RSSI_EVT )
  {
    GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
    tmos_start_task( Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD );      
    return (events ^ SBP_READ_RSSI_EVT);
  }


主要执行函数就是tmos_start_task()函数,通过设置SBP_READ_RSSI_EVT事件和间隔时间完成配置。

后续Peripheral_Init()函数的内容主要是进行初始化和配置,像外设角色配置,主要配置使能广播,扫描响应,广播数据,连接间隔最大值和最小值和设置密码等。

最后再Peripheral_Init()函数中,还有以下几个初始化。

  // 初始化连接项
  peripheralInitConnItem( &peripheralConnList );

  // 使用SimpleGATTprofile注册回调
  SimpleProfile_RegisterAppCBs( &Peripheral_SimpleProfileCBs );

  // 注册接收扫描请求回调
        GAPRole_BroadcasterSetCB( &Broadcaster_BroadcasterCBs );
       

        // 设置延迟的配置文件启动

  tmos_set_event( Peripheral_TaskID, SBP_START_DEVICE_EVT );


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

3305

帖子

3

粉丝