This module implements the Battery Service with the Battery Level characteristic. During initialization it adds the Battery Service and Battery Level characteristic to the BLE stack database. Optionally隨意地 it can also add a Report Reference descriptor to the Battery Level characteristic (used when including the Battery Service in the HID service).html
If specified, the module will support notification of the Battery Level characteristic through the ble_bas_battery_level_update() function. If an event handler is supplied by the application, the Battery Service will generate Battery Service events to the application.架構
注:app
這個電量服務和心率有點相似,經過定時器產生一個週期性的timeout回調函數battery_level_meas_timeout_handler,在其中調用battery_start()實現making the ADC start a battery level conversion。函數
概述:post
In addition, the use of the ADC to measure the battery level is also demonstrated論證.fetch
When the application starts, two timers are started which control when the heart rate value and battery level should be sentui
Battery level measurement:this
When the peer has enabled notification for Battery Level, the application begins sending the battery level as notifications.atom
Note that the measurement is made every 2 seconds. But as per the Battery Service Specification, the notification of battery level will be sent only if the it has changed.url
The ADC is used to measure the battery level. Every time a new battery measurement has to be sent, a fresh measurement is made.
timers_init產生電量測量定時器
1 // Create timers 2 err_code = app_timer_create(&m_battery_timer_id, 3 APP_TIMER_MODE_REPEATED, 4 battery_level_meas_timeout_handler); 5 APP_ERROR_CHECK(err_code);
上面代碼生成電量超時檢測定時器,每隔必定時期觸發battery_level_meas_timeout_handler:
1 /**@brief Function for handling the Battery measurement timer timeout. 2 * 3 * @details This function will be called each time the battery level measurement timer expires. 4 * This function will start the ADC. 5 * 6 * @param[in] p_context Pointer used for passing some arbitrary information (context) from the 7 * app_start_timer() call to the timeout handler. 8 */ 9 static void battery_level_meas_timeout_handler(void * p_context) 10 { 11 UNUSED_PARAMETER(p_context); 12 battery_start(); 13 }
其中第11行是一個相似於uc-os裏面的東西~宏定義以下:
battery_start()電量測量的AD配置及使能ADC_IRQn外部中斷
1 /**@brief Function for making the ADC start a battery level conversion. 2 */ 3 void battery_start(void) 4 { 5 uint32_t err_code; 6 7 // Configure ADC 8 NRF_ADC->INTENSET = ADC_INTENSET_END_Msk; 9 NRF_ADC->CONFIG = (ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos) |//後一個是位置,前一個是設置,在下面講解中在一塊兒的 10 (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) | 11 (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) | 12 (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) | 13 (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos); 14 NRF_ADC->EVENTS_END = 0; 15 NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled; 16 17 // Enable ADC interrupt 18 err_code = sd_nvic_ClearPendingIRQ(ADC_IRQn); 19 APP_ERROR_CHECK(err_code); 20 21 err_code = sd_nvic_SetPriority(ADC_IRQn, NRF_APP_PRIORITY_LOW); 22 APP_ERROR_CHECK(err_code); 23 24 err_code = sd_nvic_EnableIRQ(ADC_IRQn); 25 APP_ERROR_CHECK(err_code); 26 27 NRF_ADC->EVENTS_END = 0; // Stop any running conversions. 28 NRF_ADC->TASKS_START = 1; 29 }
NRF_ADC的結構體爲:
1 typedef struct { /*!< ADC Structure */ 2 __O uint32_t TASKS_START; /*!< Start an ADC conversion. */ 3 __O uint32_t TASKS_STOP; /*!< Stop ADC. */ 4 __I uint32_t RESERVED0[62]; 5 __IO uint32_t EVENTS_END; /*!< ADC conversion complete. */ 6 __I uint32_t RESERVED1[128]; 7 __IO uint32_t INTENSET; /*!< Interrupt enable set register. */ 8 __IO uint32_t INTENCLR; /*!< Interrupt enable clear register. */ 9 __I uint32_t RESERVED2[61]; 10 __I uint32_t BUSY; /*!< ADC busy register. */ 11 __I uint32_t RESERVED3[63]; 12 __IO uint32_t ENABLE; /*!< ADC enable. */ 13 __IO uint32_t CONFIG; /*!< ADC configuration register. */ 14 __I uint32_t RESULT; /*!< Result of ADC conversion. */ 15 __I uint32_t RESERVED4[700]; 16 __IO uint32_t POWER; /*!< Peripheral power control. */ 17 } NRF_ADC_Type;
黃色部分AD中斷使能的意義:
1 /* Register: ADC_INTENSET */ 2 /* Description: Interrupt enable set register. */ 3 4 /* Bit 0 : Enable interrupt on END event. */ 5 #define ADC_INTENSET_END_Pos (0UL) /*!< Position of END field. */ 6 #define ADC_INTENSET_END_Msk (0x1UL << ADC_INTENSET_END_Pos) /*!< Bit mask of END field. */ 7 #define ADC_INTENSET_END_Disabled (0UL) /*!< Interrupt disabled. */ 8 #define ADC_INTENSET_END_Enabled (1UL) /*!< Interrupt enabled. */ 9 #define ADC_INTENSET_END_Set (1UL) /*!< Enable interrupt on write. */
綠色部分AD初始化的意義:
1 /* Register: ADC_CONFIG */ 2 /* Description: ADC configuration register. */ 3 4 /* Bits 17..16 : ADC external reference pin selection. */ 5 #define ADC_CONFIG_EXTREFSEL_Pos (16UL) /*!< Position of EXTREFSEL field. */ 6 #define ADC_CONFIG_EXTREFSEL_Msk (0x3UL << ADC_CONFIG_EXTREFSEL_Pos) /*!< Bit mask of EXTREFSEL field. */ 7 #define ADC_CONFIG_EXTREFSEL_None (0UL) /*!< Analog external reference inputs disabled. */ 8 #define ADC_CONFIG_EXTREFSEL_AnalogReference0 (1UL) /*!< Use analog reference 0 as reference. */ 9 #define ADC_CONFIG_EXTREFSEL_AnalogReference1 (2UL) /*!< Use analog reference 1 as reference. */ 10 11 /* Bits 15..8 : ADC analog pin selection. */ 12 #define ADC_CONFIG_PSEL_Pos (8UL) /*!< Position of PSEL field. */ 13 #define ADC_CONFIG_PSEL_Msk (0xFFUL << ADC_CONFIG_PSEL_Pos) /*!< Bit mask of PSEL field. */ 14 #define ADC_CONFIG_PSEL_Disabled (0UL) /*!< Analog input pins disabled. */ 15 #define ADC_CONFIG_PSEL_AnalogInput0 (1UL) /*!< Use analog input 0 as analog input. */ 16 #define ADC_CONFIG_PSEL_AnalogInput1 (2UL) /*!< Use analog input 1 as analog input. */ 17 #define ADC_CONFIG_PSEL_AnalogInput2 (4UL) /*!< Use analog input 2 as analog input. */ 18 #define ADC_CONFIG_PSEL_AnalogInput3 (8UL) /*!< Use analog input 3 as analog input. */ 19 #define ADC_CONFIG_PSEL_AnalogInput4 (16UL) /*!< Use analog input 4 as analog input. */ 20 #define ADC_CONFIG_PSEL_AnalogInput5 (32UL) /*!< Use analog input 5 as analog input. */ 21 #define ADC_CONFIG_PSEL_AnalogInput6 (64UL) /*!< Use analog input 6 as analog input. */ 22 #define ADC_CONFIG_PSEL_AnalogInput7 (128UL) /*!< Use analog input 7 as analog input. */ 23 24 /* Bits 6..5 : ADC reference參照 selection. */ 25 #define ADC_CONFIG_REFSEL_Pos (5UL) /*!< Position of REFSEL field. */ 26 #define ADC_CONFIG_REFSEL_Msk (0x3UL << ADC_CONFIG_REFSEL_Pos) /*!< Bit mask of REFSEL field. */ 27 #define ADC_CONFIG_REFSEL_VBG (0x00UL) /*!< Use internal 1.2V bandgap頻帶間隙 voltage as reference for conversion. */ 28 #define ADC_CONFIG_REFSEL_External (0x01UL) /*!< Use external source configured by EXTREFSEL as reference for conversion. */ 29 #define ADC_CONFIG_REFSEL_SupplyOneHalfPrescaling (0x02UL) /*!< Use supply voltage with 1/2 prescaling as reference for conversion. Only usable when supply voltage is between 1.7V and 2.6V. */ 30 #define ADC_CONFIG_REFSEL_SupplyOneThirdPrescaling (0x03UL) /*!< Use supply voltage with 1/3 prescaling as reference for conversion. Only usable when supply voltage is between 2.5V and 3.6V. */ 31 32 /* Bits 4..2 : ADC input selection. */ 33 #define ADC_CONFIG_INPSEL_Pos (2UL) /*!< Position of INPSEL field. */ 34 #define ADC_CONFIG_INPSEL_Msk (0x7UL << ADC_CONFIG_INPSEL_Pos) /*!< Bit mask of INPSEL field. */ 35 #define ADC_CONFIG_INPSEL_AnalogInputNoPrescaling (0x00UL) /*!< Analog input specified by PSEL with no prescaling used as input for the conversion. */ 36 #define ADC_CONFIG_INPSEL_AnalogInputTwoThirdsPrescaling (0x01UL) /*!< Analog input specified by PSEL with 2/3 prescaling used as input for the conversion. */ 37 #define ADC_CONFIG_INPSEL_AnalogInputOneThirdPrescaling (0x02UL) /*!< Analog input specified by PSEL with 1/3 prescaling used as input for the conversion. */ 38 #define ADC_CONFIG_INPSEL_SupplyTwoThirdsPrescaling (0x05UL) /*!< Supply voltage with 2/3 prescaling used as input for the conversion. */ 39 #define ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling (0x06UL) /*!< Supply voltage with 1/3 prescaling 預分頻 used as input for the conversion. */ 40 41 /* Bits 1..0 : ADC resolution. */ 42 #define ADC_CONFIG_RES_Pos (0UL) /*!< Position of RES field. */ 43 #define ADC_CONFIG_RES_Msk (0x3UL << ADC_CONFIG_RES_Pos) /*!< Bit mask of RES field. */ 44 #define ADC_CONFIG_RES_8bit (0x00UL) /*!< 8bit ADC resolution分辨率. */ 45 #define ADC_CONFIG_RES_9bit (0x01UL) /*!< 9bit ADC resolution. */ 46 #define ADC_CONFIG_RES_10bit (0x02UL) /*!< 10bit ADC resolution. */
綜上:AD的配置爲——
44>8bit分辨率+39>1/3預分頻用於轉換輸入電源電壓+27>用內聯1.2V頻帶間隙電壓做爲參照電壓+14>總共有8個模擬輸入通道,測量電量不用選擇任何一個+7>ADC額外輸入參考失能
18 err_code = sd_nvic_ClearPendingIRQ(ADC_IRQn);
19 APP_ERROR_CHECK(err_code);
X清除正在進行的中斷
21 err_code = sd_nvic_SetPriority(ADC_IRQn, NRF_APP_PRIORITY_LOW);
22 APP_ERROR_CHECK(err_code);
X設置中斷優先級
24 err_code = sd_nvic_EnableIRQ(ADC_IRQn);
25 APP_ERROR_CHECK(err_code);
X使能外部中斷ADC
那麼,綜上定時器觸發timeout週期性執行battery_start()。在該函數中對AD進行配置而後使能~
接下來深刻分析這個AD中斷時怎麼執行的!
AD中斷執行,深刻m0內核
順着ADC_IRQn並非一箇中斷回調函數,而是:
1 typedef enum { 2 /* ------------------- Cortex-M0 Processor Exceptions Numbers ------------------- */ 3 Reset_IRQn = -15, /*!< 1 Reset Vector, invoked on Power up and warm reset */ 4 NonMaskableInt_IRQn = -14, /*!< 2 Non maskable Interrupt, cannot be stopped or preempted */ 5 HardFault_IRQn = -13, /*!< 3 Hard Fault, all classes of Fault */ 6 SVCall_IRQn = -5, /*!< 11 System Service Call via SVC instruction */ 7 DebugMonitor_IRQn = -4, /*!< 12 Debug Monitor */ 8 PendSV_IRQn = -2, /*!< 14 Pendable request for system service */ 9 SysTick_IRQn = -1, /*!< 15 System Tick Timer */ 10 /* ---------------------- nRF51 Specific Interrupt Numbers ---------------------- */ 11 POWER_CLOCK_IRQn = 0, /*!< 0 POWER_CLOCK */ 12 RADIO_IRQn = 1, /*!< 1 RADIO */ 13 UART0_IRQn = 2, /*!< 2 UART0 */ 14 SPI0_TWI0_IRQn = 3, /*!< 3 SPI0_TWI0 */ 15 SPI1_TWI1_IRQn = 4, /*!< 4 SPI1_TWI1 */ 16 GPIOTE_IRQn = 6, /*!< 6 GPIOTE */ 17 ADC_IRQn = 7, /*!< 7 ADC */ 18 TIMER0_IRQn = 8, /*!< 8 TIMER0 */ 19 TIMER1_IRQn = 9, /*!< 9 TIMER1 */ 20 TIMER2_IRQn = 10, /*!< 10 TIMER2 */ 21 RTC0_IRQn = 11, /*!< 11 RTC0 */ 22 TEMP_IRQn = 12, /*!< 12 TEMP */ 23 RNG_IRQn = 13, /*!< 13 RNG */ 24 ECB_IRQn = 14, /*!< 14 ECB */ 25 CCM_AAR_IRQn = 15, /*!< 15 CCM_AAR */ 26 WDT_IRQn = 16, /*!< 16 WDT */ 27 RTC1_IRQn = 17, /*!< 17 RTC1 */ 28 QDEC_IRQn = 18, /*!< 18 QDEC */ 29 LPCOMP_COMP_IRQn = 19, /*!< 19 LPCOMP_COMP */ 30 SWI0_IRQn = 20, /*!< 20 SWI0 */ 31 SWI1_IRQn = 21, /*!< 21 SWI1 */ 32 SWI2_IRQn = 22, /*!< 22 SWI2 */ 33 SWI3_IRQn = 23, /*!< 23 SWI3 */ 34 SWI4_IRQn = 24, /*!< 24 SWI4 */ 35 SWI5_IRQn = 25 /*!< 25 SWI5 */ 36 } IRQn_Type;
哪裏會用IRQn_Type呢?
是內核的中斷向量部分——
1 /* ########################## NVIC functions #################################### */ 2 /** \ingroup CMSIS_Core_FunctionInterface 3 \defgroup CMSIS_Core_NVICFunctions NVIC Functions 4 \brief Functions that manage interrupts and exceptions via the NVIC. 5 @{ 6 */ 7 8 /* Interrupt Priorities are WORD accessible only under ARMv6M */ 9 /* The following MACROS handle generation of the register offset and byte masks */ 10 #define _BIT_SHIFT(IRQn) ( (((uint32_t)(IRQn) ) & 0x03) * 8 ) 11 #define _SHP_IDX(IRQn) ( ((((uint32_t)(IRQn) & 0x0F)-8) >> 2) ) 12 #define _IP_IDX(IRQn) ( ((uint32_t)(IRQn) >> 2) ) 13 14 15 /** \brief Enable External Interrupt 16 17 The function enables a device-specific特殊 interrupt in the NVIC interrupt controller中斷向量控制器. 18 19 \param [in] IRQn External interrupt number. Value cannot be negative. 20 */ 21 __STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)//被動函數 22 { 23 NVIC->ISER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); 24 } 25 26 27 /** \brief Disable External Interrupt 28 29 The function disables a device-specific interrupt in the NVIC interrupt controller. 30 31 \param [in] IRQn External interrupt number. Value cannot be negative. 32 */ 33 __STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)//被動函數 34 { 35 NVIC->ICER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); 36 } 37 38 39 /** \brief Get Pending Interrupt 40 41 The function reads the pending register in the NVIC and returns the pending bit 42 for the specified interrupt. 43 44 \param [in] IRQn Interrupt number. 45 46 \return 0 Interrupt status is not pending. 47 \return 1 Interrupt status is pending. 48 */ 49 __STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)//判斷中斷是否正在進行 50 { 51 return((uint32_t) ((NVIC->ISPR[0] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); 52 } 53 54 55 /** \brief Set Pending Interrupt 56 57 The function sets the pending bit of an external interrupt. 58 59 \param [in] IRQn Interrupt number. Value cannot be negative. 60 */ 61 __STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)//設置正在進行中斷爲IRQn 62 { 63 NVIC->ISPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); 64 } 65 66 67 /** \brief Clear Pending Interrupt 68 69 The function clears the pending bit of an external interrupt. 70 71 \param [in] IRQn External interrupt number. Value cannot be negative. 72 */ 73 __STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)//清除正在進行中斷 74 { 75 NVIC->ICPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ 76 } 77 78 79 /** \brief Set Interrupt Priority 80 81 The function sets the priority of an interrupt. 82 83 \note The priority cannot be set for every core interrupt. 84 85 \param [in] IRQn Interrupt number. 86 \param [in] priority Priority to set. 87 */ 88 __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)//設置中斷優先級 89 { 90 if(IRQn < 0) { 91 SCB->SHP[_SHP_IDX(IRQn)] = (SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | 92 (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } 93 else { 94 NVIC->IP[_IP_IDX(IRQn)] = (NVIC->IP[_IP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | 95 (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } 96 } 97 98 99 /** \brief Get Interrupt Priority 100 101 The function reads the priority of an interrupt. The interrupt 102 number can be positive to specify an external (device specific) 103 interrupt, or negative to specify an internal (core) interrupt. 104 105 106 \param [in] IRQn Interrupt number. 107 \return Interrupt Priority. Value is aligned automatically to the implemented 108 priority bits of the microcontroller. 109 */ 110 __STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)//讀取一箇中斷的優先級,若是爲正表示是外部中斷,爲負表示爲內部中斷 111 { 112 113 if(IRQn < 0) { 114 return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & 0xFF) >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M0 system interrupts */ 115 else { 116 return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & 0xFF) >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ 117 } 118 119 120 /** \brief System Reset 121 122 The function initiates發起 a system reset request to reset the MCU. 123 */ 124 __STATIC_INLINE void NVIC_SystemReset(void) 125 { 126 __DSB(); /* Ensure all outstanding memory accesses included 127 buffered write are completed before reset */ 128 SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | 129 SCB_AIRCR_SYSRESETREQ_Msk); 130 __DSB(); /* Ensure completion of memory access */ 131 while(1); /* wait until reset */ 132 } 133 134 /*@} end of CMSIS_Core_NVICFunctions */
繞了一圈最後反推找到ADC中斷回調函數在start上面!(瞎了~)
1 /**@brief Function for handling the ADC interrupt. 2 * @details This function will fetch the conversion result from the ADC, convert the value into 3 * percentage and send it to peer. 4 */ 5 void ADC_IRQHandler(void) 6 { 7 if (NRF_ADC->EVENTS_END != 0) 8 { 9 uint8_t adc_result; 10 uint16_t batt_lvl_in_milli_volts; 11 uint8_t percentage_batt_lvl; 12 uint32_t err_code; 13 14 NRF_ADC->EVENTS_END = 0; 15 adc_result = NRF_ADC->RESULT; 16 NRF_ADC->TASKS_STOP = 1; 17 18 batt_lvl_in_milli_volts = ADC_RESULT_IN_MILLI_VOLTS(adc_result) + 19 DIODE_FWD_VOLT_DROP_MILLIVOLTS; 20 percentage_batt_lvl = battery_level_in_percent(batt_lvl_in_milli_volts); 21 22 err_code = ble_bas_battery_level_update(&bas, percentage_batt_lvl); 23 if ( 24 (err_code != NRF_SUCCESS) 25 && 26 (err_code != NRF_ERROR_INVALID_STATE) 27 && 28 (err_code != BLE_ERROR_NO_TX_BUFFERS) 29 && 30 (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING) 31 ) 32 { 33 APP_ERROR_HANDLER(err_code); 34 } 35 } 36 }
上面函數先從AD中得到讀取值,而後調用updata函數。
其中重點也就是這個ble_bas_battery_level_update函數~
該函數是最上面簡介中談到的一個——
If specified, the module will support notification of the Battery Level characteristic through the ble_bas_battery_level_update() function. If an event handler is supplied by the application, the Battery Service will generate Battery Service events to the application.
該函數首先判斷老的電量值和新的是否同樣,不同則更新老的
1 uint32_t ble_bas_battery_level_update(ble_bas_t * p_bas, uint8_t battery_level) 2 { 3 uint32_t err_code = NRF_SUCCESS; 4 5 if (battery_level != p_bas->battery_level_last) 6 { 7 uint16_t len = sizeof(uint8_t); 8 9 // Save new battery value 10 p_bas->battery_level_last = battery_level; 11 12 // Update database 13 err_code = sd_ble_gatts_value_set(p_bas->battery_level_handles.value_handle, 14 0, 15 &len, 16 &battery_level); 17 if (err_code != NRF_SUCCESS) 18 { 19 return err_code; 20 } 21 22 // Send value if connected and notifying 23 if ((p_bas->conn_handle != BLE_CONN_HANDLE_INVALID) && p_bas->is_notification_supported) 24 { 25 ble_gatts_hvx_params_t hvx_params; 26 27 memset(&hvx_params, 0, sizeof(hvx_params)); 28 len = sizeof(uint8_t); 29 30 hvx_params.handle = p_bas->battery_level_handles.value_handle; 31 hvx_params.type = BLE_GATT_HVX_NOTIFICATION; 32 hvx_params.offset = 0; 33 hvx_params.p_len = &len; 34 hvx_params.p_data = &battery_level; 35 36 err_code = sd_ble_gatts_hvx(p_bas->conn_handle, &hvx_params); 37 } 38 else 39 { 40 err_code = NRF_ERROR_INVALID_STATE; 41 } 42 } 43 44 return err_code; 45 }
而後調用gatts_value_set更新database
其中——
12 // Update database
13 err_code = sd_ble_gatts_value_set(p_bas->battery_level_handles.value_handle,
14 0,
15 &len,
16 &battery_level);
36 err_code = sd_ble_gatts_hvx(p_bas->conn_handle, &hvx_params);
該函數在上一篇中的timer部分的最後已經詳細講過——
This function checks for the relevant相關的 Client Characteristic Configuration descriptor描述符 value to verify斷定 that the relevant operation (notification or indication) has been enabled by the client.
It is also able to update the attribute 屬性 value before issuing發出 the PDU(protocol data unit:https://en.wikipedia.org/wiki/Protocol_data_unit && BLE 包結構及傳輸速率), so that the application can atomically原子級地 perform a value update and a server initiated開始 transaction事務 with a single API call. (僅僅調用一個API就可以將attribute的value有效地發出)If the application chooses to indicate an attribute value, a BLE_GATTS_EVT_HVC will be sent up as soon as the confirmation arrives from the peer.
這樣,終於搞通了AD的測量電量和利用BLE發送的全過程!
具體爲——
一、首先在timer初始化中產生一個電量測量timer,
二、在timeout的回調函數battery_level_meas_timeout_handler中對AD進行配置並啓動AD轉換中斷
三、在AD中斷回調函數ADC_IRQHandler中獲取轉換的AD值,
四、並調用ble_bas_battery_level_update與老的電量值對比更新database,
五、最後調用sd_ble_gatts_hvx系統API在原子級操做將attribute的value有效地發出。
此外~再來補一下少說的services_init部分
services_init
1 // Initialize Battery Service 2 memset(&bas_init, 0, sizeof(bas_init)); 3 4 // Here the sec level for the Battery Service can be changed/increased. 5 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.cccd_write_perm); 6 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_char_attr_md.read_perm); 7 BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&bas_init.battery_level_char_attr_md.write_perm); 8 9 BLE_GAP_CONN_SEC_MODE_SET_OPEN(&bas_init.battery_level_report_read_perm); 10 11 bas_init.evt_handler = NULL; 12 bas_init.support_notification = true; 13 bas_init.p_report_ref = NULL; 14 bas_init.initial_batt_level = 100; 15 16 err_code = ble_bas_init(&bas, &bas_init); 17 APP_ERROR_CHECK(err_code);
同上一篇同樣——
最後一個黃線部分闡明瞭事件如何關聯——
The application must propagate BLE stack events to the Battery Service module by calling ble_bas_on_ble_evt() from the from the ble_stack_handler callback.
應用須要把BLE stack events傳送給BAS,黃線部分說能夠經過在ble_stack_handler的回調函數中調用ble_bas_on_ble_evt來實現~
下面就是初始化並設置ble_stack_handler的回調函數的:ble_evt_dispatch
1 /**@brief Function for initializing the BLE stack. 2 * 3 * @details Initializes the SoftDevice and the BLE event interrupt. 4 */ 5 static void ble_stack_init(void) 6 { 7 uint32_t err_code; 8 9 // Initialize the SoftDevice handler module. 10 SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false); 11 12 // Register with the SoftDevice handler module for BLE events. 13 err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch); 14 APP_ERROR_CHECK(err_code); 15 16 // Register with the SoftDevice handler module for BLE events. 17 err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch); 18 APP_ERROR_CHECK(err_code); 19 }
下面這個函數負責將BLE stack事件分派給全部模塊
該函數是由ble_stack_handler的回調函數調用的,發生在:This function is called from the BLE Stack event interrupt handler after a BLE sta
1 /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler. 2 * 3 * @details This function is called from the BLE Stack event interrupt handler after a BLE stack 4 * event has been received. 5 * 6 * @param[in] p_ble_evt Bluetooth stack event. 7 */ 8 static void ble_evt_dispatch(ble_evt_t * p_ble_evt) 9 { 10 ble_bondmngr_on_ble_evt(p_ble_evt); 11 ble_hrs_on_ble_evt(&m_hrs, p_ble_evt); 12 ble_bas_on_ble_evt(&bas, p_ble_evt); 13 ble_conn_params_on_ble_evt(p_ble_evt); 14 on_ble_evt(p_ble_evt); 15 }
也就是說當應用初始了ble_stack_init以後,一旦有ble協議棧的事件就會經過ble_evt_dispatch分派給每一個模塊
對於電量檢測的ble_bas_on_ble_evt模塊有(其餘也相似)——
其中on_write(p_bas, p_ble_evt)是手機等客戶端向BAS服務寫數據時觸發的操做~這裏先不講!
1 /**@brief Function for handling the Application's BLE Stack events. 2 * 3 * @details Handles all events from the BLE stack of interest to the Battery Service. 4 * 5 * @note For the requirements in the BAS specification to be fulfilled, 6 * ble_bas_battery_level_update() must be called upon reconnection if the 7 * battery level has changed while the service has been disconnected from a bonded 8 * client. 9 * 10 * @param[in] p_bas Battery Service structure. 11 * @param[in] p_ble_evt Event received from the BLE stack. 12 */ 13 void ble_bas_on_ble_evt(ble_bas_t * p_bas, ble_evt_t * p_ble_evt) 14 { 15 switch (p_ble_evt->header.evt_id) 16 { 17 case BLE_GAP_EVT_CONNECTED: 18 on_connect(p_bas, p_ble_evt); 19 break; 20 21 case BLE_GAP_EVT_DISCONNECTED: 22 on_disconnect(p_bas, p_ble_evt); 23 break; 24 25 case BLE_GATTS_EVT_WRITE: 26 on_write(p_bas, p_ble_evt); 27 break; 28 29 default: 30 // No implementation needed. 31 break; 32 } 33 }
1 /**@brief Function for handling the Connect event. 2 * 3 * @param[in] p_bas Battery Service structure. 4 * @param[in] p_ble_evt Event received from the BLE stack. 5 */ 6 static void on_connect(ble_bas_t * p_bas, ble_evt_t * p_ble_evt) 7 { 8 p_bas->conn_handle = p_ble_evt->evt.gap_evt.conn_handle; 9 } 10 11 12 /**@brief Function for handling the Disconnect event. 13 * 14 * @param[in] p_bas Battery Service structure. 15 * @param[in] p_ble_evt Event received from the BLE stack. 16 */ 17 static void on_disconnect(ble_bas_t * p_bas, ble_evt_t * p_ble_evt) 18 { 19 UNUSED_PARAMETER(p_ble_evt); 20 p_bas->conn_handle = BLE_CONN_HANDLE_INVALID; 21 } 22 23 24 /**@brief Function for handling the Write event. 25 * 26 * @param[in] p_bas Battery Service structure. 27 * @param[in] p_ble_evt Event received from the BLE stack. 28 */ 29 static void on_write(ble_bas_t * p_bas, ble_evt_t * p_ble_evt) 30 { 31 if (p_bas->is_notification_supported) 32 { 33 ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; 34 35 if ( 36 (p_evt_write->handle == p_bas->battery_level_handles.cccd_handle) 37 && 38 (p_evt_write->len == 2) 39 ) 40 { 41 // CCCD written, call application event handler 42 if (p_bas->evt_handler != NULL) 43 { 44 ble_bas_evt_t evt; 45 46 if (ble_srv_is_notification_enabled(p_evt_write->data)) 47 { 48 evt.evt_type = BLE_BAS_EVT_NOTIFICATION_ENABLED; 49 } 50 else 51 { 52 evt.evt_type = BLE_BAS_EVT_NOTIFICATION_DISABLED; 53 } 54 55 p_bas->evt_handler(p_bas, &evt); 56 } 57 } 58 } 59 }
注:
本篇講了電量檢測服務的業務流程More:
[藍牙] 一、藍牙核心技術瞭解(藍牙協議、架構、硬件和軟件筆記)
[藍牙] 二、藍牙BLE協議及架構淺析&&基於廣播超時待機說廣播事件
[藍牙] 四、Heart Rate Service module
@beautifulzzzz 2015-12-15 continue~