简介:ADC在实际使用的时候都要进行误差校准,那Nordic的nrf52系列如何进行校准,如果不校准又有什么影响尼,接下来我将通过实验进行测试,验证不校准和校准的影响(本测试的基础是,默认输入阻抗和采样时间都是合理范围的,没有超标)。

测试环境:

硬件:nrf52DK(nrf52832)

软件:基于nRF5_SDK_17.1.0_ddde560中的SAADC例子进行修改

一、误差确定

  在数据手册ADC的电器章节有这样一个数据表,这个表中对应不同的增益模式有不同的误差范围,如我本次测试采用的就是1/6增益,那么对于芯片来说,误差范围在3%以内都是正常的

上面提到了误差,那这个误差影响什么值尼,主要影响了采样精度,如配置一个1/6增益,然后12bit分辨率的ADC然后进行采样,2^12=4096,也就是说在满量程也就是采样电压等于VCC的时候,理论值的采样值应该为4096,也就是说如果我给芯片供电为3.3V,那么我去采样一个3.3V的电源(公地)是采样值应该是4096,采样GND时应该是0,这都是理论值,实际情况是肯定有偏差的,而且没一个芯片的偏差还不一样,但是对于正常的芯片这个偏差都在一个范围,也就是我上表截图的范围。根据表格有这样一个计算,4096*3%=90 ,也就是说在采集3.3V时,值可以为4006~4096都是正常的,因为有90的偏差。

而我们进行校准就是减小这个误差,为什么是减小,是因为误差是不可能消除的。

二、代码

1、测试方式

  添加校准代码,在校准ADC后启动单次采样,单次采样使用定时器触发,每采样10次就进行一次校准,校准期间不进行采样,如果有转换也先进行停止。保证结果的准确性。

2、代码添加

由于是基于历程:SDK\examples\peripheral\saadc 进行测试,已经有想过的timer源文件加入,所以不用再加入timer相关的源文件了,如果你不是用这个例子,是使用自己的工程代码进行添加,那么应该注意timer的选择,如果是ble的项目,ble默认使用的timer0,timer0就不能用了,需要使用timer1,需要启动sdk_config中的关于timer1的所有宏定义,不然就会编译报错。

在下面这份代码中通过设置宏 Y_and_N_calibrate_change 来确定是否启用校准功能。

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_error.h"
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h" #include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h" /*是否启用校准代码*/
#define Y_and_N_calibrate_change 0 nrf_saadc_value_t test_value; //ADC原始采样值 float V_test=0; //ADC转换后的采样值 static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0); typedef struct{
#if Y_and_N_calibrate_change
bool offset_calibrate_flag; //校准标志
#endif
bool adc_sample_flag; //采样标志
uint32_t adc_sample_timer; //采样时间
}ADC_sample_t; ADC_sample_t m_ADC; #if Y_and_N_calibrate_change
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
switch(p_event->type)
{
case NRF_DRV_SAADC_EVT_CALIBRATEDONE:
m_ADC.offset_calibrate_flag = true;//校准完成回调
break; default:
break;
}
} /*确保要在ADC模块初始化完成后调用校准函数*/
void adc_offset_calibrate(void)
{
ret_code_t err_code; /*检查是否使能的ADC,如果没有就直接返回,不能进行校准*/
if(!nrf_saadc_enable_check())
{
NRF_LOG_INFO("Cannot be calibrated without ADC enabled");
return;
}
err_code = nrf_drv_saadc_calibrate_offset();
APP_ERROR_CHECK(err_code); while(!m_ADC.offset_calibrate_flag)
{
__WFE();
};
NRF_LOG_INFO("End of calibration");
} #endif void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
#if Y_and_N_calibrate_change
static uint8_t counter=0;
#endif
switch(event_type)
{
case NRF_TIMER_EVENT_COMPARE0:
#if Y_and_N_calibrate_change
if(counter<10)
{
counter++;
#endif
m_ADC.adc_sample_flag = true;
NRF_LOG_INFO("ADC sample start");
#if Y_and_N_calibrate_change
}
else
{
counter=0;
/*定时时间到开始校验*/
NRF_LOG_INFO("offset starting....");
nrf_drv_timer_disable(&m_timer);
/*如果有转化,终止转化准备开始校验*/
nrfx_saadc_abort();
/*校准标志位*/
m_ADC.offset_calibrate_flag =false;
}
#endif
break;
default:
break;
} } void saadc_init(void)
{
/*本次配置为使用了单端模式,使用了AIN0通道*/
ret_code_t err_code;
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); /*ADC采样配置,配置为12bit,不使用过采样,中断优先级,低功耗模式*/
nrf_drv_saadc_config_t config;
config.resolution = NRF_SAADC_RESOLUTION_12BIT;
config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
config.low_power_mode = NRFX_SAADC_CONFIG_IRQ_PRIORITY;
config.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE;
#if Y_and_N_calibrate_change
/*nrf_drv_saadc_init的第一个参数为NULL的话将使用默认配置(NRFX_SAADC_DEFAULT_CONFIG),这将使用10bit的分辨率*/
err_code = nrf_drv_saadc_init(&config, saadc_callback);
APP_ERROR_CHECK(err_code);
#else
/*nrf_drv_saadc_init的第一个参数为NULL的话将使用默认配置(NRFX_SAADC_DEFAULT_CONFIG),这将使用10bit的分辨率*/
err_code = nrf_drv_saadc_init(&config, NULL);
APP_ERROR_CHECK(err_code);
#endif
/*配置AIN0通道*/
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code); } void offset_timer_init(void)
{
ret_code_t err_code; nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32; err_code = nrf_drv_timer_init(&m_timer, &timer_cfg, timer_handler);
APP_ERROR_CHECK(err_code); /* 通道0作为采样定时*/
uint32_t adc_sample_ticks = nrf_drv_timer_ms_to_ticks(&m_timer, m_ADC.adc_sample_timer);
nrf_drv_timer_extended_compare(&m_timer,
NRF_TIMER_CC_CHANNEL0,
adc_sample_ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true); nrf_drv_timer_enable(&m_timer);
} void adc_sample(void)
{
m_ADC.adc_sample_flag = false;
/*采样通道1的值并给到 test_value*/
nrfx_saadc_sample_convert(0,&test_value);
/*
根据数据手册有如下的转换公式: V =[V(P)-V(N)]* GAIN / reference *2^(resolution - m)
V : 采样的实际电压
V(P): 使用采用函数获取到的值
V(N): 使用采用函数获取到的值(只在差分采样才有,如果是单端采样,这为0)
GAIN: 增益值,本例程看channel_config,配置为1/6
reference:参考电压,可以为0.6V的内部电压,或者为VCC/4(四分之一的VCC),
本例程看channel_config(NRF_SAADC_REFERENCE_INTERNAL),配置为0.6V
resolution:采样精度,
m: 单端输入为0,差分输入为1
*/ V_test=test_value* 3.6/4096;
//串口查看打印值
NRF_LOG_INFO("%d",test_value);
NRF_LOG_INFO("V=" NRF_LOG_FLOAT_MARKER "\r\n", NRF_LOG_FLOAT(V_test));
NRF_LOG_FLUSH();
} /**
* @brief Function for main application entry.
*/
int main(void)
{
ret_code_t err_code; /*添加了log*/
err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT(); /*清零实体*/
memset(&m_ADC,0,sizeof(m_ADC));
/*设置轮训采样时间,当前为1s*/
m_ADC.adc_sample_timer = 1000; saadc_init(); /*开启定时器,启动采样*/
offset_timer_init(); NRF_LOG_INFO("adc test");
while (1)
{
if(m_ADC.adc_sample_flag)
{
adc_sample();
}
#if Y_and_N_calibrate_change
if(!m_ADC.offset_calibrate_flag)
{
adc_offset_calibrate();
/* 校准过程中停止采样,校准完毕后开始采样 */
nrf_drv_timer_enable(&m_timer);
}
#endif
NRF_LOG_FLUSH();
}
}

三、对比

直接对GND进行采样,对比采样偏差的大小。

1、不添加校准:

Y_and_N_calibrate_change 为 0时:

可以看到对于我的板载芯片偏差基本都在-10以上,部分芯片偏差可能更大如-15,-20或者以上,如果在大批量时出现有部分芯片采样值不对,那么可以添加校准,说不定问题就解决了。

2、添加校准:

Y_and_N_calibrate_change 为 1 时:

可以看到,误差减小了,经过上面测试在nrf52系列使用ADC时校准是很有必要的。

结论:

1、因为校准只会减少误差,并不是把误差消除,所以对于在实际应用中如果有些芯片增益误差达到了3%的临界值,那么就算添加了校准,可能也只校准到了2%,比如12bit的分辨率,满值是4096,那么极限误差是±90,校准后误差还可能是±60,这个时候只能说软件处理了。

2、校准只能在初始化ADC模块,但是在采样开始前,或者结束后进行,所以在代码中进行了是否采样和是否使能了ADC模块的判断。

3、ADC精度还可能和晶振的精度有关,在有些时候晶振偏差过大,也会都在ADC采样偏差大

nordic的nrf52系列——ADC在使用时如何校准增益误差(基于SDK)的更多相关文章

  1. Nordic nRF52系列/nRF5340硬件设计(一)选型及原理图设计

    Nordic 的BLE系列芯片从第一代的nRF51系列,到第二代的nRF52系列,发展到目前最新的第三代的nRF5340.目前市场中使用最多的nRF52系列一共有七款芯片,它们是:nRF52805.n ...

  2. nRF52系列来袭,Nordic的低功耗蓝牙方案大有可为

      坐落在北欧的挪威不像他的邻居芬兰那样,可以先后依靠NOKIA和愤怒的小鸟在世界科技界享有盛名.在一般人看来,挪威除了一个逐渐式微的Opera浏览器以外,并没有更多拿得出手的科技企业.而事实证明这只 ...

  3. nordic——nrf52系列SWD设置回读保护

    在开发时可能需要回读保护功能,在产品出厂后这个功能可以让你的代码更加安全,无法用SEGGER或者其余方式读取你的代码HEX文件,也就是禁用SWD下载接口.但是SWD锁住了,还想使用(从新下载代码)也是 ...

  4. nRF52系列——nRF52832来袭

    nRF52系列——nRF52832来袭 Nordic凭借着在无线技术的数十年深耕,推出第一个μBlue芯片-- nRF8001.其低功耗等特性在当时吸引了无数厂商的目光,并将这产品应用到多个领域,再之 ...

  5. NRF52840相对于之前的NRF52系列、NRF51系列增加了什么功能

    现在广大客户的蓝牙采用NORDIC越来越多了,NORDIC一直在不断进行技术改进更好的满足市场需求 推出了新款NRF52840.NRF52840更为先进些,支持的功能也多点,比如IEEE802.15. ...

  6. Nordic nRF51/nRF52开发环境搭建

    本文将详述Nordic nRF51系列(包括nRF51822/nRF51802/nRF51422等)和nRF52系列(包括nRF52832/nRF52810/nRF52840)开发环境搭建. 1. 强 ...

  7. Nordic nRF51/nRF52开发流程说明

    Nordic nRF51系列包括nRF51822/nRF51422/nRF51802等芯片,nRF52系列包括nRF52832/nRF52840/nRF52810等芯片,硬件工程师可以按照如下流程去评 ...

  8. Cookie使用时需要注意个数及大小限制

    各浏览器对Cookie有一定的限制,在使用时需要格外注意. 各浏览器之间对cookie的不同限制:   IE6.0 IE7.0/8.0/9.0+ Opera FF Safari Chrome cook ...

  9. EntityFrameWork 使用时碰到的小问题

    EntityFrameWork 使用时碰到的小问题 1,在使用orm访问数据库的相目里,也要引用EntityFrameWork.dll,否则无法使用orm 否则,编译错误 错误 5 "Sys ...

  10. MySQL 安装和启动服务,“本地计算机 上的 MySQL 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。”

    MySQL 安装和启动服务,以及遇到的问题 MySQL版本: mysql-5.7.13-winx64.zip (免安装,解压放到程序文件夹即可,比如 C:\Program Files\mysql-5. ...

随机推荐

  1. 如何使用 PTS 快速发起微服务压测

    ​简介:本文讲阐述什么是微服务架构.微服务架构对系统稳定性带来的影响,以及用性能测试验证稳定性的必要性.用户进行微服务压测的痛点和 PTS 的独特优势.云上使用 PTS 快速发起微服务压测的步骤,以及 ...

  2. 探秘RocketMQ源码——Series1:Producer视角看事务消息

    简介: 探秘RocketMQ源码--Series1:Producer视角看事务消息 1. 前言 Apache RocketMQ作为广为人知的开源消息中间件,诞生于阿里巴巴,于2016年捐赠给了Apac ...

  3. Quick BI电子表格: 新手亦可表格自由

    ​简介: 随着企业业务快速增长,单纯的表或交叉表展现的数据模式相对固定,已不能满足企业中不同角色用户.不同业务场景数据可视化分析展现的诉求.在满足业务人员可视化需求层面,Quick BI不仅提供了丰富 ...

  4. [MongoDB] aggregate 查询的优化思路

    首先从业务角度出发,不必要的筛选条件和粗略的筛选条件会严重影响查询速度,比如 $or 查询和 $in 查询,视情况尽可能去掉. 程序中打印出查询条件的各部分,有 $match.$group.比如 PH ...

  5. WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住

    本文记录一个 WPF 已知问题,此问题已经被我修复.传入错误的数据给到 WriteableBitmap 对象,比如调用 WritePixels 时传入错误的 stride 数值,将可能导致渲染线程进入 ...

  6. dotnet 使用 CsWin32 库简化 Win32 函数调用逻辑

    很多开发者,包括开发老司机们,在碰到需要调用 Win32 函数时,都有一个困扰,那就是我应该如何去调用.有两个主要的选项,第一就是自己写 PInvoke 代码,第二就是使用其他大佬给许多 Win32 ...

  7. 2019-10-31-win10-uwp-录制任意应用屏幕

    title author date CreateTime categories win10 uwp 录制任意应用屏幕 lindexi 2019-10-31 09:10:38 +0800 2019-10 ...

  8. Raft 共识算法1-Raft基础

    Raft 共识算法1-Raft基础 Raft算法中译版地址:http://www.redisant.cn/etcd/contact 英原论文地址:https://raft.github.io/raft ...

  9. 4.prometheus监控--监控linux服务器

    一.监控linux服务器 1.1 二进制安装 # 客户端操作wget https://github.com/prometheus/node_exporter/releases/download/v1. ...

  10. 【爬虫实战】用python爬今日头条热榜TOP50榜单!

    目录 一.爬取目标 二.爬取结果 三.代码讲解 四.技术总结 五.演示视频 六.附完整源码 一.爬取目标 您好!我是@马哥python说,一名10年程序猿. 今天分享一期爬虫案例,爬取的目标是:今日头 ...