1. 程式人生 > >基於cc2540的電池電量服務。

基於cc2540的電池電量服務。

基於藍芽協議棧的電池電量服務

摘要:在理解協議棧和ADC的基礎上,就可以講講藍芽低功耗裝置如何新增電池服務(這裡的電池指的是:鈕釦電池CR2032, 3V ),讓藍芽主機可以知道藍芽裝置的電池電量。可以設定一個電池低電量的臨界值,當電池的電量低於這個臨界值時,就提醒使用者更換電池。為了實現這個功能,需要用到HAL層的halAdc.c與halAdc.h兩個檔案來配置處理器的ADC模組,然後還需要在此基礎上用到電池電量的配置檔案:Battservice.c與Battservice.h。通過CC2540 的ADC模組來測量外接電池的電量:VADD5/3作為ADC的取樣通道,參考電壓選擇晶片內部1.25V的參考電壓,以10位精度取樣,有效位為9位,最大的AD值為511,也就是說1.25V對應的AD值為511。對於鈕釦電池3V來說,電池的電壓在2V~3V之間可以正常給晶片供電,電池電壓與電池電量之間的關係為:

2V <==> 0%

3V <==> 100%

因為選擇AVDD5/3通道,正常使用的電池電壓輸入到ADC上實際上為2/3V~3/3V,即0.66V~1V,計算得到AD值為273~409(0.66/1.25*511 ~ 1/1.25*511)。最後可以得到取樣到的AD值與電量的關係:

273 <==> 0%

409 <==> 100%

計算公式:Battery Level = (adc - 273) / (409 - 273) * 100

根據上面的公式,就可以計算得到藍芽裝置的電池電量。

檢測到電池電量後,需要通過藍芽上傳。這時則需要向GATT增加電池服務,藍芽裝置會發送電池電量的通告給主機,這樣的話藍芽主機端(如手機等)看到藍芽裝置的電池電量。電池電量的通告只有在下面2種情況下才會發送:1、第一次連線時;2、電池電量較少時。可以設定一個電池電量的臨界提醒值,當電池電量達到這個臨界值時,就會給藍芽主機發送通告,提醒使用者更換藍芽裝置的電池。

下面就正式開始講講藍芽的電池服務。

1、跟ADC相關的幾個變數

(1)電池電壓3V對應的AD值BATT_ADC_LEVEL_3V=409

define BATT_ADC_LEVEL_3V 409

(2)電池電壓2V對應的AD值BATT_ADC_LEVEL_2V=273

define BATT_ADC_LEVEL_2V 273

(c)最低電量所對應的AD值battMinLevel=273

static uint16 battMinLevel = BATT_ADC_LEVEL_2V;

(d)最高電量所對應的AD值battMaxLevel=409

static uint16 battMaxLevel = BATT_ADC_LEVEL_3V;

(e)電池電量的臨界值(提醒換電池的電量)battCriticalLevel

static uint8 battCriticalLevel;

(f)電池電壓的AD值取樣通道設定成VDD/3通道battServiceAdcCh

static uint8 battServiceAdcCh = HAL_ADC_CHANNEL_VDD;

2、跟ADC取樣相關的幾個回撥函式

(1)AD測量設定回撥函式battServiceSetupCB,在AD取樣之前呼叫。

typedef void (*battServiceSetupCB_t)(void);

static battServiceSetupCB_t battServiceSetupCB = NULL;

(2)AD測量結束回撥函式battServiceTeardownCB,在AD測量結束時呼叫。

typedef void (*battServiceTeardownCB_t)(void);

static battServiceTeardownCB_t battServiceTeardownCB = NULL;

(3)AD測量計算回撥函式battServiceCalcCB,在AD測量後呼叫這個函式用來計算電池電量。

typedef uint8 (*battServiceCalcCB_t)(uint16 adcVal);

static battServiceCalcCB_t battServiceCalcCB = NULL;

3、使用者自定義的電池服務應用回撥函式battServiceCB,在操作電池服務寫屬性時呼叫這個函式。

typedef void (*battServiceCB_t)(uint8 event);

static battServiceCB_t battServiceCB;

4、電池服務的UUID定義

(1)電池服務的UUID

define BATT_SERVICE_UUID 0x180F

CONST uint8 battServUUID[ATT_BT_UUID_SIZE] =

{

LO_UINT16(BATT_SERVICE_UUID), HI_UINT16(BATT_SERVICE_UUID)

};

(2)電池電量的UUID

define BATT_LEVEL_UUID 0x2A19

CONST uint8 battLevelUUID[ATT_BT_UUID_SIZE] =

{

LO_UINT16(BATT_LEVEL_UUID), HI_UINT16(BATT_LEVEL_UUID)

};

5、電池電量服務的配置檔案(Profiles)相關的變數

(1)電池服務屬性的GATT屬性型別格式

static CONST gattAttrType_t battService = { ATT_BT_UUID_SIZE, battServUUID };

(2)電池電量特性許可權battLevelProps 設定為可讀、可通告

static uint8 battLevelProps = GATT_PROP_READ | GATT_PROP_NOTIFY;

(3)電池電量值預設設定成battLevel=100

static uint8 battLevel = 100;

(4)儲存電池客戶端連線的屬性配置陣列battLevelClientCharCfg[]

static gattCharCfg_t battLevelClientCharCfg[GATT_MAX_NUM_CONN];

(5)HID報告參考特性描述

static uint8 hidReportRefBattLevel[HID_REPORT_REF_LEN] ={HID_RPT_ID_BATT_LEVEL_IN, HID_REPORT_TYPE_INPUT };

6、電池服務中的幾個變數

(1)電池電量值變數地址BATT_PARAM_LEVEL=0

define BATT_PARAM_LEVEL 0

(2)電池電量臨界值變數地址BATT_PARAM_CRITICAL_LEVEL=1

define BATT_PARAM_CRITICAL_LEVEL 1

(3)電池服務控制代碼變數地址BATT_PARAM_SERVICE_HANDLE = 2

define BATT_PARAM_SERVICE_HANDLE 2

(4)電池電量報告地址BATT_PARAM_BATT_LEVEL_IN_REPORT=3

define BATT_PARAM_BATT_LEVEL_IN_REPORT 3

7、電池電量服務在GATT層上的屬性配置陣列battAttrTbl[]

static gattAttribute_t battAttrTbl[] ={};

這個屬性配置陣列共有5個元素。

(1)電池服務宣告

{

{ ATT_BT_UUID_SIZE, primaryServiceUUID },/* type */

GATT_PERMIT_READ, /* permissions */

0, /* handle */

(uint8 )&battService / pValue */

},

(2)電池電量宣告

{

{ ATT_BT_UUID_SIZE, characterUUID },

GATT_PERMIT_READ,

0,

&battLevelProps

},

(3)電池電量值

{

{ ATT_BT_UUID_SIZE, battLevelUUID },

GATT_PERMIT_READ,

0,

&battLevel

},

(4)電池電量客戶端特徵配置

{

{ ATT_BT_UUID_SIZE, clientCharCfgUUID },

GATT_PERMIT_READ | GATT_PERMIT_WRITE,

0,

(uint8 *) &battLevelClientCharCfg

},

(5)HID報告參考特徵描述

{

ATT_BT_UUID_SIZE, reportRefUUID },

GATT_PERMIT_READ,

0,

hidReportRefBattLevel

}

8、電池服務屬性操作回撥函式,有讀、寫、授權三個回撥函式,這是隻使用讀、寫屬性兩種。

CONST gattServiceCBs_t battCBs =

{

battReadAttrCB, // Read callback function pointer

battWriteAttrCB, // Write callback function pointer

NULL // Authorization callback function pointer

};

Batt_AddService() 向藍芽協議新增電池服務

1、初始化客戶端特徵配置屬性

GATTServApp_InitCharCfg( INVALID_CONNHANDLE, battLevelClientCharCfg );

2、向GATT註冊電池服務的屬性列表和屬性操作回撥函式

status = GATTServApp_RegisterService( battAttrTbl,GATT_NUM_ATTRS( battAttrTbl ),&battCBs );

Batt_SetParameter() 設定電池服務的變數

引數:

param-變數地址

len-資料的長度

*value-指向要設定的資料

在之前定義過4個變數地址BATT_PARAM_LEVEL、BATT_PARAM_CRITICAL_LEVEL、BATT_PARAM_SERVICE_HANDLE、BATT_PARAM_BATT_LEVEL_IN_REPORT,其中只有BATT_PARAM_CRITICAL_LEVEL這個變數的值是可以設定的。

1、設定電池電量臨界值

case BATT_PARAM_CRITICAL_LEVEL:

battCriticalLevel = ((uint8)value);

break;

2、如果當前電池的電量小於這個臨界值,則發出一則通告

if ( battLevel < battCriticalLevel )

{

battNotifyLevel();

}

Batt_GetParameter() 獲取電池服務的變數

引數:

param-引數的地址

*value-用於儲存獲取到變數的值

在之前定義過4個變數地址BATT_PARAM_LEVEL、BATT_PARAM_CRITICAL_LEVEL、BATT_PARAM_SERVICE_HANDLE、BATT_PARAM_BATT_LEVEL_IN_REPORT。這4個變數都可以獲取其值。

1、獲取當前電量變數

case BATT_PARAM_LEVEL:

((uint8)value) = battLevel;

break;

2、獲取電池電量臨界值變數

case BATT_PARAM_CRITICAL_LEVEL:

((uint8)value) = battCriticalLevel;

break;

3、獲取電池服務控制代碼變數

case BATT_PARAM_SERVICE_HANDLE:

((uint16)value) = GATT_SERVICE_HANDLE( battAttrTbl );

break;

4、獲取電池電量服務報告

case BATT_PARAM_BATT_LEVEL_IN_REPORT:

{

hidRptMap_t *pRpt = (hidRptMap_t *)value;

pRpt->id = hidReportRefBattLevel[0];

pRpt->type = hidReportRefBattLevel[1];

pRpt->handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle;

pRpt->cccdHandle=battAttrTbl[BATT_LEVEL_VALUE_CCCD_IDX].handle;

pRpt->mode = HID_PROTOCOL_MODE_REPORT;

}

break;

5、其他非法引數

default:

ret = INVALIDPARAMETER;

break;

battReadAttrCB() 電池服務讀屬性操作的回撥函式

引數:

connHandle-客戶端連線的控制代碼

*pAttr-要操作的屬性

*pValue-儲存屬性值元素的值

pLen-屬性的值元素長度

offset-偏移

maxLen-讀取資料值的最大長度

電池服務屬性陣列battAttrTbl[]共有5種屬性,其中電池電量屬性和HID報告特徵參考屬性可以讀操作,他們對應的UUID分別為BATT_LEVEL_UUID、GATT_REPORT_REF_UUID。

1、獲取屬性對應的UUID

uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1] );

2、如果讀取電池電量值

(1)讀取電量的值

level = battMeasure();

(2)如果電量減少了,則更新當前的電量

battLevel = level;

(3)儲存讀出的資料與長度

*pLen = 1;

pValue[0] = battLevel;

3、如果讀取HID報告特徵參考屬性

*pLen = HID_REPORT_REF_LEN;

osal_memcpy( pValue, pAttr->pValue, HID_REPORT_REF_LEN );

battWriteAttrCB() 電池服務寫屬性回撥函式

引數:

connHandle-客戶端連線的控制代碼

*pAttr-要操作的屬性

*pValue-要寫入的屬性值元素的值

pLen-屬性的值元素長度

offset-偏移

電池服務屬性陣列battAttrTbl[]共有5種屬性,其中只有電池電量客戶端特徵配置屬性可以寫操作,對應的UUID為GATT_CLIENT_CHAR_CFG_UUID。

1、獲取GATT屬性對應的UUID

uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

2、如歸哦是電池電量客戶端特徵配置屬性所對應的UUID。則處理這個寫請求

status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,offset, GATT_CLIENT_CFG_NOTIFY );

3、如果定義了電池服務的應用回撥函式battServiceCB,則執行這個回撥函式

if ( battServiceCB )

{

(*battServiceCB)( (charCfg == GATT_CFG_NO_OPERATION) ?

               BATT_LEVEL_NOTI_DISABLED :

               BATT_LEVEL_NOTI_ENABLED);

}

Batt_HandleConnStatusCB() 電池服務連線狀態改變處理函式

引數:

connHandle-連線的控制代碼

changeType-改變的狀態

當連線斷開時,則復位客戶端特徵配置。

if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) ||

( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&

( !linkDB_Up( connHandle ) ) ) )

{

GATTServApp_InitCharCfg( connHandle, battLevelClientCharCfg );

}

Batt_Register() 註冊電池服務的應用回撥函式

引數:

pfnServiceCB-註冊的函式指標

battServiceCB = pfnServiceCB;

Batt_Setup() 設定ADC模組

引數:

adc_ch-AD的取樣通道

minVal-電池的最低電量AD值

maxVal-電池最高電量的AD值

sCB-ADC取樣前的配置回撥函式

tCB-ADC取樣結束時的回撥函式

cCB-ADC取樣結束後的電量值計算的回撥函式

1、設定ADC的配置

battServiceAdcCh = adc_ch;

battMinLevel = minVal;

battMaxLevel = maxVal;

2、註冊ADC取樣前後的需要的回撥函式

battServiceSetupCB = sCB;/* 測量設定回撥函式 */

battServiceTeardownCB = tCB;/* 測量結束回撥函式 */

battServiceCalcCB = cCB;/* 測量後的計算回撥函式 */

battMeasure() 測量電池電量

1、如果註冊了測量設定回撥函式,則在AD取樣前執行該函式

if (battServiceSetupCB != NULL)

{

battServiceSetupCB();

}

2、設定ADC的參考電壓,並以10位解析度讀取VDD/3通道的AD值

HalAdcSetReference( HAL_ADC_REF_125V );

adc = HalAdcRead( battServiceAdcCh, HAL_ADC_RESOLUTION_10 );

3、AD值讀取完後,如果註冊了測量結束回電函式,則執行該函式。

if (battServiceTeardownCB != NULL)

{

battServiceTeardownCB();

}

4、檢測讀取到的AD值是否有效,無效則設定上下限值

if (adc >= battMaxLevel)

{

percent = 100;

}

else if (adc <= battMinLevel)

{

percent = 0;

}

5、開始計算電池電量

(1)如果註冊了計算回撥函式,則執行這個回撥函式來計算電池電量

if (battServiceCalcCB != NULL)

{

percent = battServiceCalcCB(adc);

}

(2)如果沒有註冊計算回撥函式,則使用預設公式極端電池電量

{

uint16 range = battMaxLevel - battMinLevel + 1;

range >>= 2;

percent = (uint8) ((((adc - battMinLevel) * 25) + (range - 1)) / range);

}

這裡計算公式跟文章最開始將的公式(adc - 273) / (409 - 273) * 100有一點出入。但實際上差別不大。程式碼中只不過把計算好的電量值取頂操作,例如電量計算得為21.1%,則取頂為22%。

Batt_MeasLevel() 測量電池電量

1、測量電池電量

level = battMeasure();

2、如果電量下降了,則更新電量值,併發送一個通告。

if (level < battLevel)

{

battLevel = level;

battNotifyLevel();

}

battNotifyLevel() 註冊通告發送回調函式

linkDB_PerformFunc( battNotifyCB );

battNotifyCB() 傳送通告

引數:

pLinkItem-條目

1、如果連線狀態為連線著,則讀取客戶端的特徵屬性

value = GATTServApp_ReadCharCfg( pLinkItem->connectionHandle, battLevelClientCharCfg );

2、如果讀取到的值為客戶端的屬性配置為通告,則傳送通告

if ( value & GATT_CLIENT_CFG_NOTIFY )

{

attHandleValueNoti_t noti;

noti.handle = battAttrTbl[BATT_LEVEL_VALUE_IDX].handle;

noti.len = 1;

noti.value[0] = battLevel;

GATT_Notification( pLinkItem->connectionHandle, ¬i, FALSE );

}

這裡涉及到很多回調函式的,下面講講它們的關係如下所示:

BLE工程——電池電量服務 - ziye334 - ziye334的部落格