nrf52 ADC采集

NRF52 ADC使用

一、简介

Analog-to-Digital Converter 模拟信号/数字信号转换器,简称ADC。它是指将连续变化的模拟信号转换为离散的数字信号的模块,如现实生活中的声音、温度、压力等类型的模拟信号,需要ADC转换成处理器可识别的数字信号。在nRF52xx系列处理器中的ADC为一个逐次逼近(successive-approximation)模拟数字转换器,nordic的nrf52xx系列芯片的内部ADC称为SAADC。其具体属性如下:

1、8/10/12分辨率,采用过采样可达到14位分辨率;

2、8输入通道;

3、满量程输入范围位0和VDD;

4、通过软件触发采样任务启动采样,或者RTC定时器或PPI触发采样任务;

5、单次采样只能使用一个采集通道;

6、扫描模式是按照顺序依次采样配置通道,其延迟时间由tack + tconv决定,可配置tack改变采样间隔;

7、通过使用EasyDMA可以直接将采样结果保存到RAM中;

8、采样中断发生在单次采样结束后或者连续采样的数据缓存填满时;

9、无需外部定时器协同就可以实现连续采样。

二、原理

根据下图公式计算出ADC采样结果:

其中,V(P)表示ADC正端输入;V(N)表示负端输入;GAIN表示增益值,通过配置寄存器 CH[n].CONFIG中的GAIN设置;REFERENCE表示参考电压,有内部参考电压和VDD/4参考电压两种,通过寄存器 CH[n].CONFIG中的RESEL位配置;RESOULUTION表示采样精度,由RESOULUTION寄存器配置;m表示采样模式,有单端模式和差分模式,单端模式时PSELN被忽略,负输入短接至地。

SAADC输入管脚和内部结构体

三、工程移植

1、添加驱动文件

并确保添加头文件路径

2、使能saadc

3、应用ADC

driver_adc.h

#ifndef __DRIVER_ADC_H
#define __DRIVER_ADC_H
/*********************************************************************
 * INCLUDES
 *********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "nrf_drv_saadc.h"
#include "ble_bas.h"
​
/*********************************************************************
 * DEFINITIONS
 *********************************************************************/
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
        ((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT) * ADC_PRE_SCALING_COMPENSATION)
​
#define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600 /**< Reference voltage (in milli volts) used by ADC while doing 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 DIODE_FWD_VOLT_DROP_MILLIVOLTS 270 /**< Typical forward voltage drop of the diode . */
#define ADC_RES_12BIT 1024                 /**< Maximum digital value for 10-bit ADC conversion. */
​
//#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE) ((ADC_VALUE * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_12BIT)
​
/*********************************************************************
 * API FUNCTIONS
 *********************************************************************/
void saadc_init(void);                          //初始化ADC
void refresh_ad_value(void *p_context);         //更新AD值
uint8_t battery_percent_get(void);              //获取电池百分比
void refresh_battery_level(void);               //刷新电池ADC数据
void update_battery_service(ble_bas_t *x3_bas); //更新电池服务数据
void adc_disable(void);
void adc_enable(void);
​
#endif

driver_adc.c

/*********************************************************************
 * INCLUDES
 *********************************************************************/
#include "nrf_delay.h"
#include "nrf_log.h"
#include "driver_adc.h"
​
#define ADC2_SAMPLES_BUFFER 16
​
/*********************************************************************
 * LOCAL VARIABLES
 *********************************************************************/
static nrf_saadc_value_t adc_buf[2][ADC2_SAMPLES_BUFFER];
// static uint8_t           percentage_batt_lvl      = 0;                //电池百分比
static uint16_t batt_lvl_in_milli_volts = 0; //电池AD值
​
static void saadc_callback(nrf_drv_saadc_evt_t const *p_event);
static void adc_read(void);
​
/*********************************************************************
 * PUBLIC FUNCTIONS
 *********************************************************************/
/**
 * @brief Function is saadc of init.
 */
void saadc_init(void)
{
    ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_callback); // saadc的初始化
    if (NRF_ERROR_INVALID_STATE == err_code)
    {
        return;
    }
    else
        APP_ERROR_CHECK(err_code);
​
    nrf_saadc_channel_config_t config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); //采集 NRF_SAADC_INPUT_AIN2
    err_code = nrf_drv_saadc_channel_init(0, &config);
    APP_ERROR_CHECK(err_code);
​
    err_code = nrf_drv_saadc_buffer_convert(adc_buf[0], ADC2_SAMPLES_BUFFER);
    APP_ERROR_CHECK(err_code);
​
    err_code = nrf_drv_saadc_buffer_convert(adc_buf[1], ADC2_SAMPLES_BUFFER); //用双缓冲采集
    APP_ERROR_CHECK(err_code);
}
​
/**
 * @brief 刷新AD值
 */
void refresh_ad_value(void *p_context)
{
    UNUSED_PARAMETER(p_context);
​
    adc_read();
}
​
/**
 * @brief 获取电池百分比
 */
uint8_t battery_percent_get()
{
    batt_lvl_in_milli_volts = 0;
​
    while (batt_lvl_in_milli_volts == 0)
    {
        adc_read();
        nrf_delay_ms(10);
    }
​
    return battery_level_in_percent(batt_lvl_in_milli_volts);
}
​
/**
 * @brief 刷新电池ADC数据
 */
void refresh_battery_level(void)
{
    adc_read();
}
​
/**
 * @brief 更新到服务
 *          以及显示(未完成)
 */
void update_battery_service(ble_bas_t *x3_bas)
{
    uint32_t err_code;
    uint8_t percentage_batt_lvl = 0; //电池百分比
​
    batt_lvl_in_milli_volts = 0;
    while (batt_lvl_in_milli_volts == 0)
    {
        adc_read();
        nrf_delay_ms(10);
    }
​
    percentage_batt_lvl = battery_level_in_percent(batt_lvl_in_milli_volts);
    err_code = ble_bas_battery_level_update(x3_bas, percentage_batt_lvl, BLE_CONN_HANDLE_ALL); //更新到电池服务
    if ((err_code != NRF_SUCCESS) &&
        (err_code != NRF_ERROR_INVALID_STATE) &&
        (err_code != NRF_ERROR_RESOURCES) &&
        (err_code != NRF_ERROR_BUSY) &&
        (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING))
    {
        APP_ERROR_HANDLER(err_code);
    }
​
    //      err_code = ble_advertising_advdata_update(&m_advertising, &new_advdata, NULL);    //更新到广播
    //      APP_ERROR_CHECK(err_code);
}
​
/**
 @brief 开启ADC,与初始化没有区别,为了与Disable成对出现
 @param 无
 @return 无
*/
void adc_enable(void)
{
    saadc_init();
}
​
/**
 @brief 禁用ADC
 @param 无
 @return 无
*/
void adc_disable(void)
{
    nrfx_saadc_uninit();
}
​
/*********************************************************************
 * LOCAL FUNCTIONS
 *********************************************************************/
/**
 * @brief saadc_callback adc回调处理函数
 * @param nrf_drv_saadc_evt_t const * p_event 事件参数
 */
static void saadc_callback(nrf_drv_saadc_evt_t const *p_event)
{
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
    {
        KUI_QUEUE_PARAM uiParam;
        //      nrf_saadc_value_t adc_result;
        uint16_t value_t[ADC2_SAMPLES_BUFFER] = {0};
        uint8_t i;
        uint32_t err_code, sum = 0, temp = 0;
​
        //      adc_result = p_event->data.done.p_buffer[ADC2_SAMPLES_BUFFER - 2]; //采集结果
​
        for (i = 0; i < ADC2_SAMPLES_BUFFER; i++)
        {
            value_t[i] = p_event->data.done.p_buffer[i]; //电池电量计算公式--电压值
            sum = sum + value_t[i];
            //          printf("%d\r\n", value_t[i]);
        }
​
        //      printf("%d\r\n", adc_result);
​
        err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, ADC2_SAMPLES_BUFFER);
        APP_ERROR_CHECK(err_code);
​
        temp = (sum / (ADC2_SAMPLES_BUFFER));
​
        batt_lvl_in_milli_volts = ADC_RESULT_IN_MILLI_VOLTS(temp); //电池电量计算公式--电压值
​
        percentage_batt_lvl = battery_level_in_percent(batt_lvl_in_milli_volts);          //配置电池水平
    }
}
​
/**
 @brief ADC读取
 @param 无
 @return 结果在回调函数的缓冲区中
*/
static void adc_read(void)
{
    ret_code_t errCode;
    errCode = nrf_drv_saadc_sample();
    APP_ERROR_CHECK(errCode);
}
​
/****************************************************END OF FILE****************************************************/
​
​

采用双缓冲单通道采集,用算术平均滤波法去滤波(因为有时电压跳动比较大,所以选用滤波去滤掉它),保证电压稳定。如果电源没有很高的跳变,选择单次采集就可以,没必要多缓存去采集,ADC的值更新得没有那么快。

四、参考文章

青风蓝牙

NRF52832学习笔记(7)——ADC接口使用

nRF52832 — 多通道ADC接口的使用

posted @ 2022-04-09 17:10  wfagly  阅读(556)  评论(0编辑  收藏  举报