CH583 是集成 BLE 无线通讯的 32 位 RISC 微控制器。片上集成 2Mbps 低功耗蓝牙 BLE 通讯模块、2 个全速 USB 主机和设备控制器及收发器、2 个 SPI、4 个串口、ADC、触摸按键检测模块、RTC 等丰富的外设资源。外设资源相比国外的蓝牙IOT芯片来说丰富得多,而且价格上也很有优势,系列芯片还基本额外赠送USB外设(毕竟是USB串口接口转换芯片起家的)。编译器使用的也是国产免费编译器,不用再为KEIL IAR破解犯愁,配套使用的国产山河MounRiverStudio编译器是基于eclipse开源编译器进行二次开发的,目前国外不少芯片厂的自家编译器也大部分都是基于eclipse开发而来,如siliconlabs的ide也是。MounRiverStudio使用下来整体体验还不错。
有TI CC2530等相关开发经验的话上手这蓝牙芯片会感觉轻车熟路,开发起来快很多。
蓝牙从机的功能比较简单,基本就广播,连接,配对,绑定,GATT注册读写这几个。
1、广播
// 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;
//设置广播使能,广播扫描回应内容以及广播数据内容。如果做beacon应用的话只用关注广播内容。
// 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 );
{
uint16 advInt = DEFAULT_ADVERTISING_INTERVAL;
// Set advertising interval
GAP_SetParamValue( TGAP_DISC_ADV_INT_MIN, advInt );
GAP_SetParamValue( TGAP_DISC_ADV_INT_MAX, advInt );
// Enable scan req notify
GAP_SetParamValue( TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE );
}
2、绑定配对相关
// Setup the GAP Bond Manager
//绑定配对相关的配置,需要要开发设备的安全需求,IO功能进行设定
{
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 );
}
3、GATT注册读写相关
//GATT注册表,主要是配置service和char。不同service需要用不同的gattAttribute_t来定义
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表注册到BLE STACK里,以及设置读写回调
bStatus_t SimpleProfile_AddService( uint32 services )
{
uint8 status = SUCCESS;
// Initialize Client Characteristic Configuration attributes
GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );
// Register with Link DB to receive link status change callback
linkDB_Register( simpleProfile_HandleConnStatusCB );
if ( services & SIMPLEPROFILE_SERVICE )
{
// Register GATT attribute list and CBs with GATT Server App
status = GATTServApp_RegisterService( simpleProfileAttrTbl,
GATT_NUM_ATTRS( simpleProfileAttrTbl ),
GATT_MAX_ENCRYPT_KEY_SIZE,
&simpleProfileCBs );
}
return ( status );
}
//读写回调设置
// Simple Profile Service Callbacks
gattServiceCBs_t simpleProfileCBs =
{
simpleProfile_ReadAttrCB, // Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL // Authorization callback function pointer
};
//实际的读写回调
static bStatus_t simpleProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint16 *pLen, uint16 offset, uint16 maxLen,uint8 method )
{
bStatus_t status = SUCCESS;
// If attribute permissions require authorization to read, return error
if ( gattPermitAuthorRead( pAttr->permissions ) )
{
// Insufficient authorization
return ( ATT_ERR_INSUFFICIENT_AUTHOR );
}
// Make sure it's not a blob operation (no attributes in the profile are long)
if ( offset > 0 )
{
return ( ATT_ERR_ATTR_NOT_LONG );
}
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch ( uuid )
{
// No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;
// gattserverapp handles those reads
// characteristics 1 and 2 have read permissions
// characteritisc 3 does not have read permissions; therefore it is not
// included here
// characteristic 4 does not have read permissions, but because it
// can be sent as a notification, it is included here
case SIMPLEPROFILE_CHAR1_UUID:
*pLen = SIMPLEPROFILE_CHAR1_LEN;
tmos_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR1_LEN );
break;
case SIMPLEPROFILE_CHAR2_UUID:
*pLen = SIMPLEPROFILE_CHAR2_LEN;
tmos_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR2_LEN );
break;
case SIMPLEPROFILE_CHAR4_UUID:
*pLen = SIMPLEPROFILE_CHAR4_LEN;
tmos_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR4_LEN );
break;
case SIMPLEPROFILE_CHAR5_UUID:
*pLen = SIMPLEPROFILE_CHAR5_LEN;
tmos_memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN );
break;
default:
// Should never get here! (characteristics 3 and 4 do not have read permissions)
*pLen = 0;
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
}
else
{
// 128-bit UUID
*pLen = 0;
status = ATT_ERR_INVALID_HANDLE;
}
return ( status );
}
static bStatus_t simpleProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint16 len, uint16 offset,uint8 method )
{
bStatus_t status = SUCCESS;
uint8 notifyApp = 0xFF;
// If attribute permissions require authorization to write, return error
if ( gattPermitAuthorWrite( pAttr->permissions ) )
{
// Insufficient authorization
return ( ATT_ERR_INSUFFICIENT_AUTHOR );
}
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch ( uuid )
{
case SIMPLEPROFILE_CHAR1_UUID:
//Validate the value
// Make sure it's not a blob oper
if ( offset == 0 )
{
if ( len > SIMPLEPROFILE_CHAR1_LEN )
{
status = ATT_ERR_INVALID_VALUE_SIZE;
}
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;
}
//Write the value
if ( status == SUCCESS )
{
tmos_memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR1_LEN );
notifyApp = SIMPLEPROFILE_CHAR1;
}
break;
case SIMPLEPROFILE_CHAR3_UUID:
//Validate the value
// Make sure it's not a blob oper
if ( offset == 0 )
{
if ( len > SIMPLEPROFILE_CHAR3_LEN )
{
status = ATT_ERR_INVALID_VALUE_SIZE;
}
}
else
{
status = ATT_ERR_ATTR_NOT_LONG;
}
//Write the value
if ( status == SUCCESS )
{
tmos_memcpy( pAttr->pValue, pValue, SIMPLEPROFILE_CHAR3_LEN );
notifyApp = SIMPLEPROFILE_CHAR3;
}
break;
case GATT_CLIENT_CHAR_CFG_UUID:
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
offset, GATT_CLIENT_CFG_NOTIFY );
break;
default:
// Should never get here! (characteristics 2 and 4 do not have write permissions)
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
}
else
{
// 128-bit UUID
status = ATT_ERR_INVALID_HANDLE;
}
// If a charactersitic value changed then callback function to notify application of change
if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange )
{
simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp, pValue, len );
}
return ( status );
}
4、设备状态变化回调相关
// GAP Role Callbacks
static gapRolesCBs_t Peripheral_PeripheralCBs =
{
peripheralStateNotificationCB, // Profile State Change Callbacks
peripheralRssiCB, // When a valid RSSI is read from controller (not used by application)
peripheralParamUpdateCB
};
static void peripheralStateNotificationCB( gapRole_States_t newState, gapRoleEvent_t * pEvent )
{
switch ( newState&GAPROLE_STATE_ADV_MASK )
{
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" );
}
else if( pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
{
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:
if( pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
{
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;
}
}
5、对于需要主动上报的char
//要能notify前提需要先配置2902为notify enable。BLE规范默认notify disable,需要每次连接上主机去是能,或者绑定的话就不用每次连接去使能
static void peripheralChar4Notify( uint8 *pValue, uint16 len )
{
attHandleValueNoti_t noti;
if( len > (peripheralMTU-3) )
{
printf("Too large noti\n");
return;
}
noti.len = len;
noti.pValue = GATT_bm_alloc( peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0 );
if( noti.pValue )
{
tmos_memcpy( noti.pValue, pValue, noti.len );
if( simpleProfile_Notify( peripheralConnList.connHandle, ¬i ) != SUCCESS )
{
GATT_bm_free( (gattMsg_t *)¬i, ATT_HANDLE_VALUE_NOTI );
}
}
}