nRF52实践:BLE下wdt和saadc驱动

282 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

本文介绍在 BLE 协议栈下如何使用看门狗wdt和读取电压值(使用saadc接口)。

概述

硬件产品中看门狗有非常重要的作用,在程序跑飞时能够复位,防止程序进入死循环状态。

对于 BLE 产品,电池的检测也十分重要,SDK 就提供了电池服务。

文中代码仅对外设的底层做封装,不涉及到 BLE 服务。由于两者的接口较简单,因此一并介绍。

操作看门狗和读取电压值,SDK均有相关代码,本文即使用这些代码进行二次封装。总体看需要做的事:

  • 在sdk_config.h头文件开启相关的宏。
  • 添加相关的文件。
  • 封装接口(可选)。

看门狗WDT

对外提供如下:

void dev_wdt_init(void);
void dev_wdt_feed(void);

接口较简单,初始化后,在较耗时的地方调用喂狗接口dev_wdt_feed即可,也可以单独开一定时器,定时喂狗。

实现如下:

#if NRFX_WDT_ENABLED == 1#include "nrf_drv_wdt.h"
​
static nrf_drv_wdt_channel_id m_channel_id;
​
void wdt_event_handler(void)
{
    bsp_board_leds_off();
​
    //NOTE: The max amount of time we can spend in WDT interrupt is two cycles of 32768[Hz] clock - after that, reset occurs
}
​
void dev_wdt_init()
{
    uint32_t err_code = NRF_SUCCESS;
    //Configure WDT.
    nrf_drv_wdt_config_t config = NRF_DRV_WDT_DEAFULT_CONFIG;
    config.reload_value = 3000; // 超时时间 默认2s
​
    err_code = nrf_drv_wdt_init(&config, NULL); // wdt_event_handler
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_wdt_channel_alloc(&m_channel_id);
    APP_ERROR_CHECK(err_code);
    nrf_drv_wdt_enable();
}
​
void dev_wdt_feed()
{
    nrf_drv_wdt_channel_feed(m_channel_id);
}
​
#endif

config.reload_value用于设置超时时间,单位为 ms,代码中设置为 3 秒。默认为 2 秒,由如下宏确定:

// <o> WDT_CONFIG_RELOAD_VALUE - Reload value  <15-4294967295> #ifndef WDT_CONFIG_RELOAD_VALUE
#define WDT_CONFIG_RELOAD_VALUE 2000
#endif

读取电压

对外提供接口如下:

void dev_voltage_init(void);
void dev_voltage_get(uint16_t * p_vbatt);

接口较简单,初始化后,直接调用dev_voltage_get即可获取电压值。

代码如下:

#if NRFX_SAADC_ENABLED == 1#include "nrf_drv_saadc.h"
#include "sdk_macros.h"
​
#define ADC_REF_VOLTAGE_IN_MILLIVOLTS  600  //!< Reference voltage (in milli volts) used by ADC while doing conversion.
#define DIODE_FWD_VOLT_DROP_MILLIVOLTS 270  //!< Typical forward voltage drop of the diode (Part no: SD103ATW-7-F) that is connected in series with the voltage supply. This is the voltage drop when the forward current is 1mA. Source: Data sheet of 'SURFACE MOUNT SCHOTTKY BARRIER DIODE ARRAY' available at www.diodes.com.
#define ADC_RES_10BIT                  1024 //!< Maximum digital value for 10-bit ADC conversion.
#define ADC_PRE_SCALING_COMPENSATION   6    //!< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE) \
    ((((ADC_VALUE) *ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)
​
static nrf_saadc_value_t adc_buf;                   //!< Buffer used for storing ADC value.
static uint16_t          m_batt_lvl_in_milli_volts; //!< Current battery level./**@brief Function handling events from 'nrf_drv_saadc.c'.
 *
 * @param[in] p_evt SAADC event.
 */
static void saadc_event_handler(nrf_drv_saadc_evt_t const * p_evt)
{
    if (p_evt->type == NRF_DRV_SAADC_EVT_DONE)
    {
        nrf_saadc_value_t adc_result;
​
        adc_result = p_evt->data.done.p_buffer[0];
​
        m_batt_lvl_in_milli_volts =
            ADC_RESULT_IN_MILLI_VOLTS(adc_result) + DIODE_FWD_VOLT_DROP_MILLIVOLTS;
​
            // NRF_LOG_INFO("--- vol: %d", m_batt_lvl_in_milli_volts);
    }
}
​
​
void dev_voltage_init(void)
{
    ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
​
    APP_ERROR_CHECK(err_code);
​
    nrf_saadc_channel_config_t config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
    err_code = nrf_drv_saadc_channel_init(0, &config);
    APP_ERROR_CHECK(err_code);
​
    err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
    APP_ERROR_CHECK(err_code);
​
    err_code = nrf_drv_saadc_sample();
    APP_ERROR_CHECK(err_code);
}
​
void dev_voltage_get(uint16_t * p_vbatt)
{
    VERIFY_PARAM_NOT_NULL_VOID(p_vbatt);
​
    *p_vbatt = m_batt_lvl_in_milli_volts;
    if (!nrf_drv_saadc_is_busy())
    {
        ret_code_t err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
        APP_ERROR_CHECK(err_code);
​
        err_code = nrf_drv_saadc_sample();
        APP_ERROR_CHECK(err_code);
    }
}
​
#endif

dev_voltage_get获取的值是电压数值,但实际中,需要提供电池百分比,SDK 中实现了battery_level_in_percent,用于将电压值转换成百分比。实现代码如下:

​
/** @brief Function for converting the input voltage (in milli volts) into percentage of 3.0 Volts.
 *
 *  @details The calculation is based on a linearized version of the battery's discharge
 *           curve. 3.0V returns 100% battery level. The limit for power failure is 2.1V and
 *           is considered to be the lower boundary.
 *
 *           The discharge curve for CR2032 is non-linear. In this model it is split into
 *           4 linear sections:
 *           - Section 1: 3.0V - 2.9V = 100% - 42% (58% drop on 100 mV)
 *           - Section 2: 2.9V - 2.74V = 42% - 18% (24% drop on 160 mV)
 *           - Section 3: 2.74V - 2.44V = 18% - 6% (12% drop on 300 mV)
 *           - Section 4: 2.44V - 2.1V = 6% - 0% (6% drop on 340 mV)
 *
 *           These numbers are by no means accurate. Temperature and
 *           load in the actual application is not accounted for!
 *
 *  @param[in] mvolts The voltage in mV
 *
 *  @return    Battery level in percent.
*/
static __INLINE uint8_t battery_level_in_percent(const uint16_t mvolts)
{
    uint8_t battery_level;
​
    if (mvolts >= 3000)
    {
        battery_level = 100;
    }
    else if (mvolts > 2900)
    {
        battery_level = 100 - ((3000 - mvolts) * 58) / 100;
    }
    else if (mvolts > 2740)
    {
        battery_level = 42 - ((2900 - mvolts) * 24) / 160;
    }
    else if (mvolts > 2440)
    {
        battery_level = 18 - ((2740 - mvolts) * 12) / 300;
    }
    else if (mvolts > 2100)
    {
        battery_level = 6 - ((2440 - mvolts) * 6) / 340;
    }
    else
    {
        battery_level = 0;
    }
​
    return battery_level;
}

根据注释,是针对CR2032纽扣电池的,电压和百分比的转换并非线性关系。

小结

nRF52 的 SDK 例程中,很多函数调用都会使用断言判断返回值,当出错时,会调用到 assert_nrf_callback函数。笔者调试过程中,有时会出现莫名的错误,也没有过多的日志输出。此时使能看门狗板子会复位,但往往还是继续出现相同的错误。其实这种还好解决,毕竟是在调试中发现的,而在实际应用时,可能没有串口或 JTAG ,因此比较难分析,因此需要充分的测试。无论如何,看门狗还是建议使用上的。

使用串口线或 JTAG 线向板子供电,得到的电池百分比一直是100%。为了测试实际的百分比,笔者在某宝上买了块CR2032电池,放到板子上,但发现驱动不了板子,因此无法测试,想到只花了几毛钱,也就作罢了。