[蓝牙] 5、Battery Service module

Detailed Description



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).

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.

Note
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.
Attention! To maintain compliance with Nordic Semiconductor ASA Bluetooth profile qualification listings, this section of source code must not be modified. 

注:

这个电量服务和心率有点类似,通过定时器产生一个周期性的timeout回调函数battery_level_meas_timeout_handler,在其中调用battery_start()实现making the ADC start a battery level conversion。

概述:

In addition, the use of the ADC to measure the battery level is also demonstrated论证.

When the application starts, two timers are started which control when the heart rate value and battery level should be sent

Battery level measurement:

When the peer has enabled notification for Battery Level, the application begins sending the battery level as notifications.

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.

The ADC is used to measure the battery level. Every time a new battery measurement has to be sent, a fresh measurement is made.

Note
The application will stop advertising after 180 seconds and go to system-off mode. Push the button 0 or button 1 to restart advertising.

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里面的东西~宏定义如下:

#define UNUSED_VARIABLE(X)  ((void)(X))

#define UNUSED_PARAMETER(X) UNUSED_VARIABLE(X)

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发送的全过程!

具体为——

1、首先在timer初始化中产生一个电量测量timer,

2、在timeout的回调函数battery_level_meas_timeout_handler中对AD进行配置并启动AD转换中断

3、在AD中断回调函数ADC_IRQHandler中获取转换的AD值,

4、并调用ble_bas_battery_level_update与老的电量值对比更新database,

5、最后调用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 }

注:

本篇讲了电量检测服务的业务流程

下一篇将实体运行,利用log分析具体流程

同时,本篇可能部分存在纰漏,希望在log分析中纠正

More:

[蓝牙] 1、蓝牙核心技术了解(蓝牙协议、架构、硬件和软件笔记)

[蓝牙] 2、蓝牙BLE协议及架构浅析&&基于广播超时待机说广播事件

[蓝牙] 3、 剖析BLE心率检测工程

[蓝牙] 4、Heart Rate Service module

@beautifulzzzz 2015-12-15 continue~ 

时间: 2024-08-09 19:28:05

[蓝牙] 5、Battery Service module的相关文章

FreeBSD修改root密码错误passwd: pam_chau(www.111cn.net)thtok(): error in service module from:http://www.111cn.net/sys/freebsd/66713.htm

在FreeBSD中修改帐号密码有时候会出现一些错误,针对passwd: pam_chauthtok(): error in service module这样的错误提示,简单整理了以下解决方案:错误提示: 代码如下 复制代码 kiccleaf# passwd kiccleafChanging local password for kiccleafNew Password:Retype New Password: /: write failed, filesystem is fullpasswd:

[编译] 4、在Linux下搭建nRF51822的开发烧写环境(makefile版)

星期日, 09. 九月 2018 07:51下午 - beautifulzzzz 1.安装步骤 1) 从GNU Arm Embedded Toolchain官网下载最新的gcc-arm工具链,写文章时下载的是: gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2 2) 从NORDIC官网下载相应版本的SDK,我这里选择的是12.3.0版本: Code Name Version nRF5-SDK-v12-zip nRF5 SDK Zip File

[蓝牙] 3、&lt;KEIL path&gt; \ARM\Device\Nordic\nrf51822\Board\pca10001\s110\ble_app_hrs BLE心率检测工程

Heart Rate Example The Heart Rate Application is a firmware example that implements the Heart Rate profile using the hardware delivered in the nRF51822 Development Kit. The source code and project file can be found in the <InstallFolder>\Nordic\nrf5

蓝牙Bluetooth技术手册规范下载【转】

蓝牙Bluetooth技术手册规范下载 http://www.crifan.com/summary_bluetooth_specification_download/ [背景] 之前就已经整理和转帖了和蓝牙技术相关的一些内容: [资源下载]bluetooth 协议 spec specification 蓝牙1.1.蓝牙1.2.蓝牙2.0(蓝牙2.0+EDR)区别 但是发现上述spec下载地址失效了. 所以继续重新找蓝牙的spec. [整理过程] 1.google搜: bluetooth spec

蓝牙4.0BLE抓包(二) – 广播包解析

本文转自:http://www.cnblogs.com/aikm/p/5022502.html 感谢原创作者! SleepingBug评论:这篇文档写的相当好,受教了,多谢了!   作者:强光手电[艾克姆科技-无线事业部] 在使用EN-Dongle捕获和解析广播包之前,我们先了解一下BLE报文的结构,之后,再对捕获的广播包进行分析.在学习BLE的时候,下面两个文档是极其重要的,这是SIG发布的蓝牙的核心协议和核心协议增补. 核心协议Core_v4.2. 核心协议增补CSS v6. 虽然这两个文档

Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08系统:android/android4.1.3.4作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)欢迎指正错误,共同学习.共同进步!!一.Android Bluetooth Architecture蓝牙代码架构部分(google 官方蓝牙框架) Android的蓝牙系统,自下而上包括以下一些

Cisco IOS LAN Base、IP Base 和IP Service的区别

Details: http://www.cisco.com/c/en/us/products/collateral/switches/catalyst-3560-x-series-switches/white_paper_c11-579326.html The LAN Base feature set offers enhanced intelligent services that include comprehensive Layer 2 features, with up-to 255 V

[Angular 2] Share a Service Across Angular 2 Components and Modules

Services are used to share data between components. They follow a module pattern that allows you to use the data throughout your application so that your data is consistent and in sync. Create a service.module.ts: import { NgModule} from '@angular/co

手把手教你做蓝牙聊天应用(六)-界面优化

第6节 应用的美化与完善 现在,我们还可以为聊天应用加上多国语言的支持和关于界面,把使用到的颜色和尺寸定义到资源文件当中,这样一来,安豆的蓝牙聊天应用就算是比较完整的完成了. 这两部分在以前"计算器"章节中,已经介绍过了,大家就自己动手吧. 这一节,我们将重点介绍聊天文字的背景图片是如何制作的. 6.1 9Patch图片的原理 观察一下安卓系统中需要经常用到的图片,可以发现: 很多要使用透明效果的地方在转角处: 很多图片不同的地方只在靠近边缘的地方,内部区域几乎都是一样的: 为此安卓系