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采样结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7gt6k12R-1649495927398)(E:\知识体系\240 - Learning - 写作\241 - image\nordic的adc公式.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OCoSFc20-1649495927399)(E:\知识体系\240 - Learning - 写作\241 - image\nordic内部ADC结构体.png)]

SAADC输入管脚和内部结构体

三、工程移植

1、添加驱动文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTqeBZ1o-1649495927399)(E:\知识体系\240 - Learning - 写作\241 - image\saadc-添加驱动文件.png)]

并确保添加头文件路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RRy7MN5T-1649495927399)(E:\知识体系\240 - Learning - 写作\241 - image\saadc-头文件路径.png)]

2、使能saadc

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gojwfBUw-1649495927400)(E:\知识体系\240 - Learning - 写作\241 - image\saadc-使能配置.png)]

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. */

/*********************************************************************
 * 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接口的使用

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值