bluetooth
टीआई के बीएलई स्टैक के साथ शुरुआत करें
खोज…
BLE गुलाम उपकरणों से जुड़ना
परिचय
टेक्सास इंस्ट्रूमेंट्स (TI) CC26XX श्रृंखला SoCs ब्लूटूथ कम ऊर्जा (BLE) अनुप्रयोगों को लक्षित कर आसानी से उपलब्ध वायरलेस MCUs हैं। MCUs के साथ, TI एक पूर्ण विकसित सॉफ्टवेयर स्टैक प्रदान करता है जो टूल चेन के साथ डेवलपर्स को शुरू करने में मदद करने के लिए आवश्यक API और नमूना कोड प्रदान करता है। हालांकि, शुरुआती लोगों के लिए, हमेशा यह सवाल होता है कि संदर्भ दस्तावेज और कोड की लंबी सूची के सामने कहां से शुरू करें। इस नोट का लक्ष्य उन आवश्यक कदमों को दर्ज करना है, जो चल रहे पहले प्रोजेक्ट को किक करने के लिए होता है।
सिंपल पेरिफेरल प्रोफाइल BLE स्टैक का 'हैलो वर्ल्ड' उदाहरण है, जहाँ MCU, PC और स्मार्टफ़ोन जैसे BLE सर्विस क्लाइंट को अपस्ट्रीम होस्ट या BLE सर्विस क्लाइंट की तरह काम कर रहा है। सामान्य वास्तविक विश्व अनुप्रयोगों में शामिल हैं: ब्लूटूथ हेडफ़ोन, ब्लूटूथ तापमान सेंसर, आदि।
शुरू करने से पहले, हमें सबसे पहले प्रोग्रामिंग और डीबगिंग के उद्देश्य के लिए बुनियादी सॉफ्टवेयर और हार्डवेयर टूल इकट्ठा करना होगा।
BLE स्टैक
आधिकारिक वेबसाइट से TI के BLE-STACK-2-2-0 को डाउनलोड और इंस्टॉल करें। मान लें कि यह डिफ़ॉल्ट स्थान 'C: \ ti' में स्थापित है।
आईडीई - दो विकल्प हैं:
एआरएम के लिए IAR एंबेडेड कार्यक्षेत्र। यह 30 दिनों के मुफ्त मूल्यांकन की अवधि के साथ एक वाणिज्यिक उपकरण है।
टीआई का कोड कंपोजर स्टूडियो (CCS)। टीआई की आधिकारिक आईडीई और मुफ्त लाइसेंस प्रदान करता है। इस उदाहरण में हम CCS V6.1.3 का उपयोग करेंगे
हार्डवेयर प्रोग्रामिंग टूल
TI के XDS100 USB-इंटरफ़ेस JTAG डिवाइस की सिफारिश करें।
CCS में आयात उदाहरण परियोजना
सरल परिधीय प्रोफाइल नमूना कोड BLE-Stack इंस्टॉलेशन के साथ आता है। इस उदाहरण प्रोजेक्ट को CCS में आयात करने के लिए नीचे दिए गए चरणों का पालन करें।
- CCS प्रारंभ करें, एक कार्यक्षेत्र फ़ोल्डर बनाएँ। फिर फ़ाइल-> आयात करें। 'एक आयात स्रोत का चयन करें' के तहत, 'कोड कंपोज़ स्टूडियो -> सीसीएस प्रोजेक्ट्स' विकल्प चुनें और 'अगला' पर क्लिक करें।
- 'C: \ ti \ simplelink \ ble_sdk_2_02_00_31' उदाहरण \ cc2650em \ simple_peripheral \ ccs 'पर ब्राउज़ करें। दो परियोजनाओं की खोज की जाएगी। सभी का चयन करें और नीचे दोनों विकल्पों पर टिक करें। इसके बाद 'फिनिश ’पर क्लिक करें। परियोजनाओं को कार्यक्षेत्र में कॉपी करके, आप निम्नलिखित सभी संशोधनों के लिए मूल प्रोजेक्ट सेटिंग को अपरिवर्तित छोड़ देते हैं।
सरल परिधीय प्रोफाइल उदाहरण में दो परियोजनाएं शामिल हैं:
- simple_peripheral_cc2650em_app
- simple_peripheral_cc2650em_stack
'cc2650em' TI के cc2650 मूल्यांकन बोर्ड का कोड नाम है। _Stack परियोजना में TI के BEL-Stack-2-2-0 के कोड और बाइनरी शामिल हैं, जो ब्लूटूथ विज्ञापन, हैंडशेकिंग, आवृत्ति तुल्यकालन आदि को संभालता है। यह कोड का हिस्सा है जो अपेक्षाकृत स्थिर है और बनना नहीं चाहता है ज्यादातर समय डेवलपर्स द्वारा छुआ गया। _App परियोजना वह जगह है जहां डेवलपर्स अपने स्वयं के कार्यों और BLE सेवा को लागू करते हैं।
बनाएँ और डाउनलोड करें
दोनों प्रोजेक्ट बनाने के लिए मेनू 'प्रोजेक्ट-> बिल्ड ऑल' पर क्लिक करें। यदि कंपाइलर लिंकिंग पर किसी प्रकार की आंतरिक त्रुटि की रिपोर्ट करता है, तो लिंकर के लिए 'compress_dwarf' विकल्प को निष्क्रिय करने का प्रयास करें:
- प्रोजेक्ट पर राइट क्लिक करें और 'प्रॉस्पेर्टीज़' चुनें।
- 'बिल्ड-> एआरएम लिंकर' में, 'फ़्लैग्स एडिट' बटन पर क्लिक करें।
- अंतिम विकल्प को '--compress_dwarf = off' में बदलें।
दोनों प्रोजेक्ट्स सफलतापूर्वक बनने के बाद, MCU में स्टैक और ऐप इमेज दोनों को डाउनलोड करने के लिए अलग से 'रन-> डिबग' पर क्लिक करें।
कोड स्पर्श करें
नमूना कोड में आक्रामक संशोधन करने में सक्षम होने के लिए, डेवलपर्स को BLE स्टैक की स्तरित संरचना के बारे में विस्तृत ज्ञान प्राप्त करना होगा। तापमान पढ़ने / अधिसूचना जैसे प्राथमिक कार्यों के लिए, हम केवल दो फाइलों पर ध्यान केंद्रित कर सकते हैं: PROFILES / simple_gatt_profile.c (-h) और अनुप्रयोग / simple_peripheral.c (.h)
simple_gatt_profile.c
सभी ब्लूटूथ एप्लिकेशन एक निश्चित प्रकार की सेवा प्रदान करते हैं, प्रत्येक में विशेषताओं का एक सेट होता है। सरल परिधीय प्रोफ़ाइल 0xFFF0 के UUID के साथ एक सरल सेवा को परिभाषित करती है, जिसमें 5 विशेषताएं होती हैं। यह सेवा simple_gatt_profile.c में निर्दिष्ट है। सरल सेवा का सारांश निम्नानुसार सूचीबद्ध है।
नाम | डेटा का आकार | UUID | विवरण | संपत्ति |
---|---|---|---|---|
simplePeripheralChar1 | 1 | 0xFFF1 | विशेषताएँ 1 | पढ़ना लिखना |
simplePeripheralChar2 | 1 | 0xFFF2 | अभिलक्षण २ | सिफ़ पढ़िये |
simplePeripheralChar3 | 1 | 0xFFF3 | अभिलक्षण ३ | ही लिखो |
simplePeripheralChar4 | 1 | 0xFFF4 | अभिलक्षण ४ | सूचित करें |
simplePeripheralChar5 | 5 | 0xFFF5 | अभिलक्षण ५ | सिफ़ पढ़िये |
पांच विशेषताओं में विभिन्न गुण हैं और विभिन्न उपयोगकर्ता मामलों के लिए उदाहरण के रूप में काम करते हैं। उदाहरण के लिए, एमसीयू सूचना के परिवर्तन के बारे में अपने ग्राहकों, अपस्ट्रीम होस्ट्स को सूचित करने के लिए simplePeripheralChar4 का उपयोग कर सकता है।
ब्लूटूथ सेवा को परिभाषित करने के लिए, एक विशेषता तालिका का निर्माण करना होगा।
/*********************************************************************
* Profile Attributes - Table
*/
static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
{
// 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_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
},
...
};
विशेषता तालिका एक डिफ़ॉल्ट 'प्राथमिक सेवा सेवा' से शुरू होती है, जो सेवा के यूयूआईडी (इस मामले में 0xFFF0) को निर्दिष्ट करती है। इसके बाद सभी विशेषताओं की घोषणा के बाद सेवा शामिल होती है। प्रत्येक विशेषताओं में कई विशेषताएं होती हैं, अर्थात् पहुंच की अनुमति, मूल्य और उपयोगकर्ता विवरण आदि। यह तालिका बाद में बीएलई स्टैक के साथ पंजीकृत है।
// Register GATT attribute list and CBs with GATT Server App
status = GATTServApp_RegisterService( simpleProfileAttrTbl,
GATT_NUM_ATTRS( simpleProfileAttrTbl ),
GATT_MAX_ENCRYPT_KEY_SIZE,
&simpleProfileCBs );
सेवा के पंजीकरण पर, डेवलपर्स को विशेषताओं के 'रीड', 'राइट' और 'प्राधिकरण' के लिए तीन कॉलबैक फ़ंक्शन प्रदान करना होता है। हम नमूना कोड में कॉलबैक फ़ंक्शन की सूची पा सकते हैं।
/*********************************************************************
* PROFILE CALLBACKS
*/
// Simple Profile Service Callbacks
// Note: When an operation on a characteristic requires authorization and
// pfnAuthorizeAttrCB is not defined for that characteristic's service, the
// Stack will report a status of ATT_ERR_UNLIKELY to the client. When an
// operation on a characteristic requires authorization the Stack will call
// pfnAuthorizeAttrCB to check a client's authorization prior to calling
// pfnReadAttrCB or pfnWriteAttrCB, so no checks for authorization need to be
// made within these functions.
CONST gattServiceCBs_t simpleProfileCBs =
{
simpleProfile_ReadAttrCB, // Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL // Authorization callback function pointer
};
एक बार, ग्राहक कनेक्शन को ब्लूटूथ कनेक्शन पर रीड रिक्वेस्ट भेजने के बाद simpleProfile_ReadAttrCB कहा जाएगा। इसी तरह, simpleProfile_WriteAttrCB को लिखित अनुरोध करने पर बुलाया जाएगा। इन दोनों कार्यों को समझना परियोजना अनुकूलन की सफलता की कुंजी है।
नीचे रीड कॉलबैक फ़ंक्शन है।
/*********************************************************************
* @fn simpleProfile_ReadAttrCB
*
* @brief Read an attribute.
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be read
* @param pLen - length of data to be read
* @param offset - offset of the first octet to be read
* @param maxLen - maximum length of data to be read
* @param method - type of read message
*
* @return SUCCESS, blePending or Failure
*/
static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle,
gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t *pLen,
uint16_t offset, uint16_t maxLen,
uint8_t 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 );
}
uint16 uuid = 0;
if ( pAttr->type.len == ATT_UUID_SIZE )
// 128-bit UUID
uuid = BUILD_UINT16( pAttr->type.uuid[12], pAttr->type.uuid[13]);
else
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_CHAR2_UUID:
*pLen = SIMPLEPROFILE_CHAR2_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR2_LEN );
break;
case SIMPLEPROFILE_CHAR1_UUID:
*pLen = SIMPLEPROFILE_CHAR1_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR1_LEN );
break;
case SIMPLEPROFILE_CHAR4_UUID:
*pLen = SIMPLEPROFILE_CHAR4_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR4_LEN );
break;
case SIMPLEPROFILE_CHAR5_UUID:
*pLen = SIMPLEPROFILE_CHAR5_LEN;
VOID 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;
}
return ( status );
}
मैंने इसके मूल संस्करण से कोड को थोड़ा संशोधित किया है। यह फ़ंक्शन 7 पैरामीटर लेता है, जिसे हेडर टिप्पणियों में समझाया गया है। फ़ंक्शन विशेषता की पहुंच अनुमति की जांच करके शुरू होता है, उदाहरण के लिए कि क्या उसने पढ़ने की अनुमति दी है। तब यह जांचता है कि क्या यह एक बड़ा ब्लॉब रीड रिक्वेस्ट है जिसे '' (ऑफसेट> 0) 'स्थिति का परीक्षण करके पढ़ा जा सकता है। जाहिर है, फ़ंक्शन अभी के लिए पढ़े गए बूँद का समर्थन नहीं करता है। अगला, अनुरोधित विशेषता का UUID निकाला जाता है। यूयूआईडी दो प्रकार के होते हैं: 16-बिट और 128-बिट। जबकि नमूना कोड 16-बिट यूयूआईडी का उपयोग करके सभी विशेषताओं को परिभाषित करता है, 128-बिट यूयूआईडी अधिक सार्वभौमिक है और आमतौर पर पीसी और स्मार्टफोन जैसे अपस्ट्रीम होस्ट में उपयोग किया जाता है। इसलिए, 128-बिट UUID को 16-बिट UUID में बदलने के लिए कोड की कई पंक्तियों का उपयोग किया जाता है।
uint16 uuid = 0;
if ( pAttr->type.len == ATT_UUID_SIZE )
// 128-bit UUID
uuid = BUILD_UINT16( pAttr->type.uuid[12], pAttr->type.uuid[13]);
else
uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
अंत में, UUID प्राप्त करने के बाद, हम यह निर्धारित कर सकते हैं कि किस विशेषता का अनुरोध किया गया है। तब डेवलपर्स की ओर से शेष कार्य गंतव्य पॉइंटर 'pValue' के लिए अनुरोधित विशेषता के मूल्य को कॉपी करना है।
switch ( uuid )
{
case SIMPLEPROFILE_CHAR1_UUID:
*pLen = SIMPLEPROFILE_CHAR1_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR1_LEN );
break;
case SIMPLEPROFILE_CHAR2_UUID:
*pLen = SIMPLEPROFILE_CHAR2_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR2_LEN );
break;
case SIMPLEPROFILE_CHAR4_UUID:
*pLen = SIMPLEPROFILE_CHAR4_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR4_LEN );
break;
case SIMPLEPROFILE_CHAR5_UUID:
*pLen = SIMPLEPROFILE_CHAR5_LEN;
VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN );
break;
default:
*pLen = 0;
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
राइटबैक कॉलबैक फ़ंक्शन समान है सिवाय इसके कि GATT_CLIENT_CHAR_CFG_UUID के UUID के साथ एक विशेष प्रकार का लेखन है। यह विशेषता अधिसूचना या संकेत के लिए रजिस्टर करने के लिए अपस्ट्रीम होस्ट का अनुरोध है। BLE स्टैक के अनुरोध को पारित करने के लिए बस API GATTServApp_ProcessCCCWriteReq पर कॉल करें।
case GATT_CLIENT_CHAR_CFG_UUID:
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
offset, GATT_CLIENT_CFG_NOTIFY | GATT_CLIENT_CFG_INDICATE ); // allow client to request notification or indication features
break;
MCU पर कोड का एप्लिकेशन पक्ष लेखन-अनुमत विशेषताओं के लिए किसी भी परिवर्तन के साथ अधिसूचित किया जा सकता है। डेवलपर्स इस अधिसूचना को उस तरह से लागू कर सकते हैं जिस तरह से वे चाहते हैं। नमूना कोड में, कॉलबैक फ़ंक्शन का उपयोग किया जाता है।
// 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 );
}
दूसरी ओर, यदि BLE परिधीय अपनी विशेषता में किसी भी परिवर्तन के अपस्ट्रीम होस्ट को सूचित करना चाहता है, तो वह API GATTServApp_ProcessCharCfg कह सकता है। यह API फ़ंक्शन SimpleProfile_SetParameter में दिखाया गया है।
/*********************************************************************
* @fn SimpleProfile_SetParameter
*
* @brief Set a Simple Profile parameter.
*
* @param param - Profile parameter ID
* @param len - length of data to write
* @param value - pointer to data to write. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16 will be cast to
* uint16 pointer).
*
* @return bStatus_t
*/
bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )
{
bStatus_t ret = SUCCESS;
switch ( param )
{
case SIMPLEPROFILE_CHAR2:
if ( len == SIMPLEPROFILE_CHAR2_LEN )
{
VOID memcpy( simpleProfileChar2, value, SIMPLEPROFILE_CHAR2_LEN );
}
else
{
ret = bleInvalidRange;
}
break;
case SIMPLEPROFILE_CHAR3:
if ( len == sizeof ( uint8 ) )
{
simpleProfileChar3 = *((uint8*)value);
}
else
{
ret = bleInvalidRange;
}
break;
case SIMPLEPROFILE_CHAR1:
if ( len == SIMPLEPROFILE_CHAR1_LEN )
{
VOID memcpy( simpleProfileChar1, value, SIMPLEPROFILE_CHAR1_LEN );
}
else
{
ret = bleInvalidRange;
}
break;
case SIMPLEPROFILE_CHAR4:
if ( len == SIMPLEPROFILE_CHAR4_LEN )
{
//simpleProfileChar4 = *((uint8*)value);
VOID memcpy( simpleProfileChar4, value, SIMPLEPROFILE_CHAR4_LEN );
// See if Notification has been enabled
GATTServApp_ProcessCharCfg( simpleProfileChar4Config, simpleProfileChar4, FALSE,
simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),
INVALID_TASK_ID, simpleProfile_ReadAttrCB );
}
else
{
ret = bleInvalidRange;
}
break;
case SIMPLEPROFILE_CHAR5:
if ( len == SIMPLEPROFILE_CHAR5_LEN )
{
VOID memcpy( simpleProfileChar5, value, SIMPLEPROFILE_CHAR5_LEN );
}
else
{
ret = bleInvalidRange;
}
break;
default:
ret = INVALIDPARAMETER;
break;
}
return ( ret );
}
तो अगर सरल परिधीय अनुप्रयोग सहकर्मी उपकरणों को SIMPLEPROFILE_CHAR4 के वर्तमान मूल्य को सूचित करना चाहता है, तो इसे सिंपलप्रोफाइल_सेटपैरमीटर फ़ंक्शन कह सकते हैं।
सारांश में, PROFILES / simple_gatt_profile.c (.h) उस सेवा की सामग्री को परिभाषित करता है, जिसे BLE परिधीय अपने ग्राहकों को प्रस्तुत करना चाहते हैं, साथ ही साथ उन तरीकों को भी प्रस्तुत करते हैं जो सेवा में उन विशेषताओं तक पहुँचते हैं।
simple_peripheral.c
TI का BLE स्टैक एक लाइट मल्टी थ्रेडेड OS लेयर के ऊपर चल रहा है। MCU में वर्कलोड जोड़ने के लिए, डेवलपर्स को पहले एक कार्य बनाना होता है। simple_peripheral.c एक कस्टम कार्य की मूल संरचना को दर्शाता है, जिसमें कार्य का निर्माण, आरंभ और हाउसकीपिंग शामिल है। तापमान पढ़ने और अधिसूचना जैसे बहुत बुनियादी कार्यों के साथ शुरू करने के लिए, हम नीचे कुछ प्रमुख कार्यों पर ध्यान केंद्रित करेंगे।
फ़ाइल की शुरुआत उन मापदंडों के एक सेट को परिभाषित करती है जो ब्लूटूथ कनेक्शन व्यवहार को प्रभावित कर सकते हैं।
// Advertising interval when device is discoverable (units of 625us, 160=100ms)
#define DEFAULT_ADVERTISING_INTERVAL 160
// Limited discoverable mode advertises for 30.72s, and then stops
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL
// Minimum connection interval (units of 1.25ms, 80=100ms) if automatic
// parameter update request is enabled
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 80
// Maximum connection interval (units of 1.25ms, 800=1000ms) if automatic
// parameter update request is enabled
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 400
// Slave latency to use if automatic parameter update request is enabled
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms, 1000=10s) if automatic parameter
// update request is enabled
#define DEFAULT_DESIRED_CONN_TIMEOUT 1000
// Whether to enable automatic parameter update request when a connection is
// formed
#define DEFAULT_ENABLE_UPDATE_REQUEST TRUE
// Connection Pause Peripheral time value (in seconds)
#define DEFAULT_CONN_PAUSE_PERIPHERAL 6
// How often to perform periodic event (in msec)
#define SBP_PERIODIC_EVT_PERIOD 1000
पैरामीटर DEFAULT_DESIRED_MIN_CONN_INTERVAL, DEFAULT_DESIRED_MAX_CONN_INTERVAL और DEFAULT_DESIRED_SLES_LATENCY एक साथ ब्लूटूथ कनेक्शन के अंतराल को परिभाषित करते हैं, जो कि कितनी बार डिवाइसों की जानकारी का एक जोड़ी है। एक कम कनेक्शन अंतराल का मतलब अधिक संवेदनशील व्यवहार है, लेकिन उच्च बिजली की खपत भी है।
पैरामीटर DEFAULT_DESIRED_CONN_TIME एक कनेक्शन खो जाने से पहले सहकर्मी प्रतिक्रिया प्राप्त करने के लिए कितनी देर परिभाषित करता है। पैरामीटर DEFAULT_ENABLE_UPDATE_REQUEST परिभाषित करता है कि रन-टाइम के दौरान दास डिवाइस को कनेक्शन अंतराल बदलने की अनुमति है या नहीं। यह व्यस्त और निष्क्रिय चरणों के लिए अलग-अलग कनेक्शन मापदंडों के लिए बिजली की बचत के संदर्भ में उपयोगी है।
पैरामीटर SBP_PERIODIC_EVT_PERIOD एक घड़ी की घटना की अवधि को परिभाषित करता है जो कार्य को समय-समय पर फ़ंक्शन को निष्पादित करने की अनुमति देगा। तापमान पढ़ने और सेवा ग्राहकों को सूचित करने के लिए कोड जोड़ने के लिए यह हमारे लिए एकदम सही जगह है।
आवधिक घड़ी SimpleBLEPeripheral_init फ़ंक्शन में आरंभ की गई है।
// Create one-shot clocks for internal periodic events.
Util_constructClock(&periodicClock, SimpleBLEPeripheral_clockHandler,
SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);
यह SBP_PERIODIC_EVT_PERIOD की अवधि के साथ एक घड़ी बनाएगा। और टाइमआउट पर, पैरामीटर SBP_PERIODIC_EVT के साथ SimpleBLEPeripheral_clockHandler के फ़ंक्शन को कॉल करेगा। घड़ी की घटना तब तक चालू हो सकती है
Util_startClock(&periodicClock);
Util_startClock कीवर्ड के लिए खोज करते हुए, हम पा सकते हैं कि यह आवधिक घड़ी सबसे पहले GAPROLE_CONNECTED इवेंट (SimpleBLEPeripheral_processStateChangeCvt फ़ंक्शन के अंदर) पर चालू होती है, जिसका अर्थ है कि यह एक होस्ट के साथ संबंध स्थापित करने के बाद एक आवधिक दिनचर्या शुरू करेगा।
जब समय-समय पर घड़ी निकलती है, तो उसका पंजीकृत कॉलबैक फ़ंक्शन कहा जाएगा।
/*********************************************************************
* @fn SimpleBLEPeripheral_clockHandler
*
* @brief Handler function for clock timeouts.
*
* @param arg - event type
*
* @return None.
*/
static void SimpleBLEPeripheral_clockHandler(UArg arg)
{
// Store the event.
events |= arg;
// Wake up the application.
Semaphore_post(sem);
}
यह फ़ंक्शन इवेंट वेक्टर में एक ध्वज सेट करता है और ओएस कार्य सूची से एप्लिकेशन को सक्रिय करता है। ध्यान दें कि हम इस कॉलबैक फ़ंक्शन में कोई विशिष्ट उपयोगकर्ता कार्यभार नहीं करते हैं, क्योंकि यह अनुशंसित नहीं है। उपयोगकर्ता कार्यभार में अक्सर BLE स्टैक API में कॉल शामिल होते हैं। कॉलबैक फ़ंक्शन के अंदर BLE स्टैक API कॉल करने से अक्सर सिस्टम अपवाद होते हैं। इसके बजाय, हम कार्य के वेक्टर में एक ध्वज सेट करते हैं और इसके लिए आवेदन संदर्भ में बाद में संसाधित होने की प्रतीक्षा करते हैं। उदाहरण कार्य के लिए प्रवेश बिंदु simpleBLEPeripheral_taskFxn () है।
/*********************************************************************
* @fn SimpleBLEPeripheral_taskFxn
*
* @brief Application task entry point for the Simple BLE Peripheral.
*
* @param a0, a1 - not used.
*
* @return None.
*/
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
{
// Initialize application
SimpleBLEPeripheral_init();
// Application main loop
for (;;)
{
// Waits for a signal to the semaphore associated with the calling thread.
// Note that the semaphore associated with a thread is signaled when a
// message is queued to the message receive queue of the thread or when
// ICall_signal() function is called onto the semaphore.
ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
if (errno == ICALL_ERRNO_SUCCESS)
{
ICall_EntityID dest;
ICall_ServiceEnum src;
ICall_HciExtEvt *pMsg = NULL;
if (ICall_fetchServiceMsg(&src, &dest,
(void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
uint8 safeToDealloc = TRUE;
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
// Check for BLE stack events first
if (pEvt->signature == 0xffff)
{
if (pEvt->event_flag & SBP_CONN_EVT_END_EVT)
{
// Try to retransmit pending ATT Response (if any)
SimpleBLEPeripheral_sendAttRsp();
}
}
else
{
// Process inter-task message
safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg);
}
}
if (pMsg && safeToDealloc)
{
ICall_freeMsg(pMsg);
}
}
// If RTOS queue is not empty, process app message.
while (!Queue_empty(appMsgQueue))
{
sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue);
if (pMsg)
{
// Process message.
SimpleBLEPeripheral_processAppMsg(pMsg);
// Free the space from the message.
ICall_free(pMsg);
}
}
}
if (events & SBP_PERIODIC_EVT)
{
events &= ~SBP_PERIODIC_EVT;
Util_startClock(&periodicClock);
// Perform periodic application task
SimpleBLEPeripheral_performPeriodicTask();
}
}
}
यह एक अनंत लूप है जो कार्य के स्टैक और एप्लिकेशन संदेश कतार में मतदान करता रहता है। यह विभिन्न झंडे के लिए अपने घटनाओं वेक्टर की भी जांच करता है। वहीं समय-समय पर नियमित वास्तव में क्रियान्वित किया जाता है। SBP_PERIODIC_EVT की खोज करने पर, कार्य फ़ंक्शन पहले ध्वज को साफ़ करता है, उसी टाइमर को तुरंत शुरू करता है और रूटीन फ़ंक्शन को कॉल करता है SimpleBLEPeripheral_performPeriodicTask ();
/*********************************************************************
* @fn SimpleBLEPeripheral_performPeriodicTask
*
* @brief Perform a periodic application task. This function gets called
* every five seconds (SBP_PERIODIC_EVT_PERIOD). In this example,
* the value of the third characteristic in the SimpleGATTProfile
* service is retrieved from the profile, and then copied into the
* value of the fourth characteristic.
*
* @param None.
*
* @return None.
*/
static void SimpleBLEPeripheral_performPeriodicTask(void)
{
uint8_t newValue[SIMPLEPROFILE_CHAR4_LEN];
// user codes to do specific work like reading the temperature
// .....
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN,
newValue);
}
आवधिक फ़ंक्शन के अंदर, हम अपने रीडिंग तापमान के बहुत विशिष्ट कार्य को चलाते हैं, UART के अनुरोध आदि को उत्पन्न करते हैं। फिर हम ब्लूटूथ कनेक्शन के माध्यम से सेवा ग्राहकों को जानकारी संप्रेषित करने के लिए SimpleProfile_SetParameter () API कहते हैं। BLE स्टैक ब्लूटूथ लिंक पर संदेश प्रसारित करने के लिए वायरलेस कनेक्शन को बनाए रखने से लेकर सभी निम्न स्तर की नौकरियों का ख्याल रखता है। सभी डेवलपर्स को आवेदन विशिष्ट डेटा इकट्ठा करने और उन्हें एक सेवा तालिका में संबंधित विशेषताओं में अपडेट करने की आवश्यकता है।
अंत में, जब लेखन-अनुमत विशेषताओं पर एक लिखित अनुरोध किया जाता है, तो कॉलबैक फ़ंक्शन को रोक दिया जाएगा।
static void SimpleBLEPeripheral_charValueChangeCB(uint8_t paramID)
{
SimpleBLEPeripheral_enqueueMsg(SBP_CHAR_CHANGE_EVT, paramID);
}
फिर से, यह कॉलबैक फ़ंक्शन केवल उपयोगकर्ता कार्य के लिए एक एप्लिकेशन संदेश को शामिल करता है, जिसे बाद में एप्लिकेशन संदर्भ में नियंत्रित किया जाएगा।
static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID)
{
uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
switch(paramID)
{
case SIMPLEPROFILE_CHAR1:
SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue[0]);
ProcessUserCmd(newValue[0], NULL);
break;
case SIMPLEPROFILE_CHAR3:
break;
default:
// should not reach here!
break;
}
}
उपरोक्त उदाहरण में, जब SIMPLEPROFILE_CHAR1 लिखा जाता है, तो उपयोगकर्ता कोड पहले SimpleProfile_GetParameter () को कॉल करके नया मान प्राप्त करेगा, और फिर उपयोगकर्ता परिभाषित आदेशों के लिए डेटा पार्स करेगा।
सारांश में, simple_peripheral.c कस्टम वर्कलोड के लिए उपयोगकर्ता कार्य बनाने के तरीके का एक उदाहरण दिखाता है। एप्लिकेशन वर्कलोड को शेड्यूल करने का एक मूल तरीका आवधिक घड़ी घटना है। डेवलपर्स को केवल सेवा तालिका में विशेषताओं से / से जानकारी संसाधित करने की आवश्यकता होती है, जबकि BLE स्टैक ब्लूटूथ कनेक्शन के माध्यम से सेवा तालिका से सहकर्मी उपकरणों (या इसके विपरीत) तक जानकारी संप्रेषित करने के आराम का ख्याल रखता है।
रियल वर्ल्ड सेंसर कनेक्ट करना
BLE गुलाम उपकरणों के लिए कोई उपयोगी काम करने के लिए, वायरलेस MCU के GPIO लगभग हमेशा शामिल होते हैं। उदाहरण के लिए, बाहरी सेंसर से तापमान को पढ़ने के लिए, GPIO पिन की ADC कार्यक्षमता की आवश्यकता हो सकती है। TI के CC2640 MCU में विभिन्न पैकेजिंग प्रकारों को देखते हुए अधिकतम 31 GPIO की सुविधा है।
हार्डवेयर पक्ष में, CC2640 ADC, UARTS, SPI, SS2, I2C आदि जैसे परिधीय कार्यात्मकताओं का एक समृद्ध सेट प्रदान करता है। सॉफ्टवेयर पक्ष में, TI के BLE स्टैक विभिन्न बाह्य उपकरणों के लिए एक समान डिवाइस-स्वतंत्र ड्राइवर इंटरफ़ेस की पेशकश करने की कोशिश करता है। एक समान ड्राइवर इंटरफ़ेस कोड पुन: प्रयोज्य की संभावना में सुधार कर सकता है, लेकिन दूसरी ओर, यह सीखने की अवस्था के ढलान को भी बढ़ाता है। इस नोट में, हम एक उदाहरण के रूप में SPI कंट्रोलर का उपयोग करते हैं और बताते हैं कि सॉफ्टवेयर ड्राइवर को उपयोगकर्ता अनुप्रयोगों में कैसे एकीकृत किया जाए।
बेसिक SPI ड्राइवर फ्लो
टीआई के बीएलई स्टैक में, एक परिधीय चालक में अक्सर तीन भाग होते हैं: ड्राइवर एपीआई का एक उपकरण स्वतंत्र विनिर्देश; ड्राइवर API का एक उपकरण विशिष्ट कार्यान्वयन और हार्डवेयर संसाधन का मानचित्रण।
SPI नियंत्रक के लिए, इसके चालक कार्यान्वयन में तीन फाइलें शामिल हैं:
- <ti / ड्राइवर / SPI.h> - यह डिवाइस-स्वतंत्र API विनिर्देश है
- <ti / ड्राइवर / spi / SPICC26XXDMA.h> - यह CC2640- विशिष्ट एपीआई कार्यान्वयन है
- <ti / ड्राइवर / dma / UDMACC26XX.h> - यह SPI ड्राइवर द्वारा अपेक्षित uDMA ड्राइवर है
(नोट: TI के BLE स्टैक के परिधीय ड्राइवरों के लिए सबसे अच्छा दस्तावेज़ ज्यादातर उनकी हेडर फ़ाइलों में पाया जा सकता है, जैसे कि SPICC26XXDMA.h इस मामले में)
एसपीआई नियंत्रक का उपयोग शुरू करने के लिए, चलो पहले एक कस्टम सी फ़ाइल बनाते हैं, जिसका नाम है sbp_spi.c, जिसमें ऊपर की तीन हेडर फाइलें शामिल हैं। प्राकृतिक अगला कदम चालक का एक उदाहरण बनाना और उसे आरंभ करना है। ड्राइवर की आवृत्ति डेटा संरचना में संकुचित है - SPI_Handle। एक अन्य डेटा संरचना - SPI_Params का उपयोग SPI नियंत्रक के लिए प्रमुख मापदंडों को निर्दिष्ट करने के लिए किया जाता है, जैसे बिट दर, स्थानांतरण मोड, आदि।
#include <ti/drivers/SPI.h>
#include <ti/drivers/spi/SPICC26XXDMA.h>
#include <ti/drivers/dma/UDMACC26XX.h>
static void sbp_spiInit();
static SPI_Handle spiHandle;
static SPI_Params spiParams;
void sbp_spiInit(){
SPI_init();
SPI_Params_init(&spiParams);
spiParams.mode = SPI_MASTER;
spiParams.transferMode = SPI_MODE_CALLBACK;
spiParams.transferCallbackFxn = sbp_spiCallback;
spiParams.bitRate = 800000;
spiParams.frameFormat = SPI_POL0_PHA0;
spiHandle = SPI_open(CC2650DK_7ID_SPI0, &spiParams);
}
उपरोक्त नमूना कोड यह बताता है कि SPI_Handle उदाहरण को कैसे आरंभ किया जाए। एपीआई SPI_init () को आंतरिक डेटा संरचनाओं को आरंभीकृत करने के लिए पहले बुलाया जाना चाहिए। फ़ंक्शन कॉल SPI_Params_init (और spiParams) डिफ़ॉल्ट मानों के लिए SPI_Params संरचना के सभी फ़ील्ड सेट करता है। फिर डेवलपर्स अपने विशिष्ट मामलों के अनुरूप महत्वपूर्ण मापदंडों को संशोधित कर सकते हैं। उदाहरण के लिए, उपरोक्त कोड 800kbps की बिट दर के साथ मास्टर मोड में संचालित करने के लिए SPI नियंत्रक सेट करता है और प्रत्येक लेनदेन को संसाधित करने के लिए एक गैर-अवरोधक विधि का उपयोग करता है, ताकि जब कोई लेनदेन पूरा हो जाए तो कॉलबैक फ़ंक्शन sbp_spiCallback कहा जाएगा।
अंत में, SPI_open () के लिए एक कॉल हार्डवेयर SPI नियंत्रक को खोलता है और बाद में SPI लेनदेन के लिए एक हैंडल लौटाता है। SPI_open () दो तर्क लेता है, पहला SPI नियंत्रक की आईडी है। CC2640 में दो हार्डवेयर SPI कंट्रोलर ऑन-चिप दिए गए हैं, इस प्रकार यह ID तर्क या तो 0 या 1 होगा जैसा कि नीचे परिभाषित किया गया है। दूसरा तर्क एसपीआई नियंत्रक के लिए वांछित पैरामीटर है।
/*!
* @def CC2650DK_7ID_SPIName
* @brief Enum of SPI names on the CC2650 dev board
*/
typedef enum CC2650DK_7ID_SPIName {
CC2650DK_7ID_SPI0 = 0,
CC2650DK_7ID_SPI1,
CC2650DK_7ID_SPICOUNT
} CC2650DK_7ID_SPIName;
SPI_Handle के सफल उद्घाटन के बाद, डेवलपर्स SPI लेनदेन तुरंत शुरू कर सकते हैं। प्रत्येक SPI लेनदेन डेटा संरचना - SPI_Transaction का उपयोग करके वर्णित है।
/*!
* @brief
* A ::SPI_Transaction data structure is used with SPI_transfer(). It indicates
* how many ::SPI_FrameFormat frames are sent and received from the buffers
* pointed to txBuf and rxBuf.
* The arg variable is an user-definable argument which gets passed to the
* ::SPI_CallbackFxn when the SPI driver is in ::SPI_MODE_CALLBACK.
*/
typedef struct SPI_Transaction {
/* User input (write-only) fields */
size_t count; /*!< Number of frames for this transaction */
void *txBuf; /*!< void * to a buffer with data to be transmitted */
void *rxBuf; /*!< void * to a buffer to receive data */
void *arg; /*!< Argument to be passed to the callback function */
/* User output (read-only) fields */
SPI_Status status; /*!< Status code set by SPI_transfer */
/* Driver-use only fields */
} SPI_Transaction;
उदाहरण के लिए, एसपीआई बस पर एक लिखित लेन-देन शुरू करने के लिए, डेवलपर्स को प्रेषित किए जाने वाले डेटा से भरा 'txBuf' तैयार करना होगा और भेजे जाने वाले डेटा बाइट्स की लंबाई के लिए 'काउंट' वैरिएबल सेट करना होगा। अंत में, SPI_transfer (spiHandle, spiTrans) के लिए एक कॉल लेनदेन शुरू करने के लिए SPI नियंत्रक को इंगित करता है।
static SPI_Transaction spiTrans;
bool sbp_spiTransfer(uint8_t len, uint8_t * txBuf, uint8_t rxBuf, uint8_t * args)
{
spiTrans.count = len;
spiTrans.txBuf = txBuf;
spiTrans.rxBuf = rxBuf;
spiTrans.arg = args;
return SPI_transfer(spiHandle, &spiTrans);
}
क्योंकि एसपीआई एक द्वैध प्रोटोकॉल है जो एक ही समय में ट्रांसमिट और प्राप्त करना दोनों होता है, जब एक लिखित लेन-देन समाप्त हो जाता है, तो इसके संबंधित प्रतिक्रिया डेटा पहले से ही 'rxBuf' पर उपलब्ध है।
चूंकि हम ट्रांसफर मोड को कॉलबैक मोड पर सेट करते हैं, जब भी कोई लेनदेन पूरा होता है, तो पंजीकृत कॉलबैक फ़ंक्शन को कॉल किया जाएगा। यह वह जगह है जहां हम प्रतिक्रिया डेटा को संभालते हैं या अगले लेनदेन की शुरुआत करते हैं। (नोट: हमेशा एक कॉलबैक फ़ंक्शन के अंदर आवश्यक API कॉल से अधिक नहीं करना याद रखें)।
void sbp_spiCallback(SPI_Handle handle, SPI_Transaction * transaction){
uint8_t * args = (uint8_t *)transaction->arg;
// may want to disable the interrupt first
key = Hwi_disable();
if(transaction->status == SPI_TRANSFER_COMPLETED){
// do something here for successful transaction...
}
Hwi_restore(key);
}
I / O पिन कॉन्फ़िगरेशन
अब तक, यह SPI ड्राइवर का उपयोग करने के लिए यथोचित सरल लगता है। लेकिन रुकिए, सॉफ्टवेयर एपीआई कॉल को भौतिक एसपीआई सिग्नल से कैसे जोड़ा जा सकता है? यह तीन डेटा संरचनाओं के माध्यम से किया जाता है: SPICC26XXDMA_Object, SPICC26XXDMA_HWAttrsV1 और SPI_Config। वे आमतौर पर 'board.c' की तरह एक अलग स्थान पर त्वरित हैं।
/* SPI objects */
SPICC26XXDMA_Object spiCC26XXDMAObjects[CC2650DK_7ID_SPICOUNT];
/* SPI configuration structure, describing which pins are to be used */
const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC2650DK_7ID_SPICOUNT] = {
{
.baseAddr = SSI0_BASE,
.intNum = INT_SSI0_COMB,
.intPriority = ~0,
.swiPriority = 0,
.powerMngrId = PowerCC26XX_PERIPH_SSI0,
.defaultTxBufValue = 0,
.rxChannelBitMask = 1<<UDMA_CHAN_SSI0_RX,
.txChannelBitMask = 1<<UDMA_CHAN_SSI0_TX,
.mosiPin = ADC_MOSI_0,
.misoPin = ADC_MISO_0,
.clkPin = ADC_SCK_0,
.csnPin = ADC_CSN_0
},
{
.baseAddr = SSI1_BASE,
.intNum = INT_SSI1_COMB,
.intPriority = ~0,
.swiPriority = 0,
.powerMngrId = PowerCC26XX_PERIPH_SSI1,
.defaultTxBufValue = 0,
.rxChannelBitMask = 1<<UDMA_CHAN_SSI1_RX,
.txChannelBitMask = 1<<UDMA_CHAN_SSI1_TX,
.mosiPin = ADC_MOSI_1,
.misoPin = ADC_MISO_1,
.clkPin = ADC_SCK_1,
.csnPin = ADC_CSN_1
}
};
/* SPI configuration structure */
const SPI_Config SPI_config[] = {
{
.fxnTablePtr = &SPICC26XXDMA_fxnTable,
.object = &spiCC26XXDMAObjects[0],
.hwAttrs = &spiCC26XXDMAHWAttrs[0]
},
{
.fxnTablePtr = &SPICC26XXDMA_fxnTable,
.object = &spiCC26XXDMAObjects[1],
.hwAttrs = &spiCC26XXDMAHWAttrs[1]
},
{NULL, NULL, NULL}
};
SPI_Config सरणी में प्रत्येक हार्डवेयर SPI नियंत्रक के लिए एक अलग प्रविष्टि है। प्रत्येक प्रविष्टि के तीन क्षेत्र हैं: fxnTablePtr, ऑब्जेक्ट और hwAttrs। 'FxnTablePtr' एक पॉइंट टेबल है जो ड्राइवर एपीआई के डिवाइस-विशिष्ट कार्यान्वयन को इंगित करता है।
'ऑब्जेक्ट' ड्राइवर के लिए ड्राइवर की स्थिति, स्थानांतरण मोड, कॉलबैक फ़ंक्शन जैसी जानकारी का ट्रैक रखता है। यह 'ऑब्जेक्ट' ड्राइवर द्वारा स्वचालित रूप से बनाए रखा जाता है।
'HwAttrs' वास्तविक हार्डवेयर संसाधन मैपिंग डेटा संग्रहीत करता है, जैसे SPI संकेतों के लिए IO पिन, हार्डवेयर व्यवधान संख्या, SPI नियंत्रक का आधार पता आदि। 'hwAttrs' के अधिकांश क्षेत्र पूर्व-परिभाषित हैं और इन्हें संशोधित नहीं किया जा सकता है। जबकि इंटरफ़ेस के IO पिन को स्वतंत्र रूप से उपयोगकर्ता आधारित मामलों को सौंपा जा सकता है। नोट: CC26XX MCUs विशिष्ट परिधीय कार्यक्षमता से IO पिनों को अलग करता है जो IO पिनों में से किसी भी परिधीय फ़ंक्शन को सौंपा जा सकता है।
बेशक वास्तविक IO पिन को पहले 'board.h' में परिभाषित किया जाना है।
#define ADC_CSN_1 IOID_1
#define ADC_SCK_1 IOID_2
#define ADC_MISO_1 IOID_3
#define ADC_MOSI_1 IOID_4
#define ADC_CSN_0 IOID_5
#define ADC_SCK_0 IOID_6
#define ADC_MISO_0 IOID_7
#define ADC_MOSI_0 IOID_8
परिणामस्वरूप, हार्डवेयर संसाधन मैपिंग के कॉन्फ़िगरेशन के बाद, डेवलपर्स अंततः SPI इंटरफ़ेस के माध्यम से बाहरी सेंसर चिप्स के साथ संवाद कर सकते हैं।