SAADC -逐次逼近模拟-数字转换器

ADC是一种差分逐次逼近寄存器(SAR)模数转换器。
以下是SAADC的主要特点:

  • 8/10/12位分辨率,带过采样的14位分辨率
  • 多达8个输入通道
  • 单端输入一个通道,差分输入两个通道
  • 扫描模式可以配置单端通道和差分通道。
  • 满量程输入范围(0到VDD)
  • 通过软件或PPI通道的任务触发采样,以充分灵活的采样频率源,从低功耗32.768kHz RTC或更准确的1/16MHz定时器
  • 一次性转换模式,采样单通道
  • 扫描模式,按顺序采样一系列通道。 信道之间的采样延迟是tack + tconv,根据用户的tack配置,信道之间的延迟会有所不同。 •支持使用EasyDMA直接将样品传输到RAM
  • 在单个样本和满缓冲区事件上中断
  • 对于差分和单端采样,样本存储为16位2的补值
  • 不需要外部定时器的连续采样
  • 内部电阻串
  • 限制匆忙检查

共享资源
ADC可以与COMP和其他使用AIN0-AIN7之一的外围设备共存,只要这些是分配到不同的引脚。
不建议两个模块选择相同的模拟输入引脚。

综述

ADC支持多达8个外部模拟输入通道,取决于封装变体。 它可以
在软件控制下的一次采样模式下操作,或具有可编程采样速率的连续转换模式下操作。

模拟输入可以配置为8个单端输入,4个差分输入或这些的组合。 每个通道可配置为选择AIN0 ~ AIN7引脚或VDD引脚。 通道可以在单次或连续采样模式下单独采样,或者,使用扫描模式,多个通道可以顺序采样。 信道也可以进行过采样以提高噪声性能。

[单片机框架][bsp层][nrf52832][nrf52840][nrf52810][nrf52820][bsp_adc] ADC/SSADC配置和使用_单片机

图98:简化的ADC框图
在内部,ADC始终是一个差分模数转换器,但默认情况下,它在CH[n]的MODE字段中配置为单端输入。 配置寄存器。 在单端模式下,负输入将在内部短路到接地。

单端模式下的假设是ADC的内部接地与测量电压所参考的外部接地相同。 因此,ADC在单端模式下对PCB上的地反弹非常敏感。 如果这是一个问题,我们建议使用差分测量。

EasyDMA
配置后的结果。 PTR和结果。 MAXCNT,通过触发START任务来启动ADC资源。 ADC使用EasyDMA将结果存储在RAM的Result buffer中。

Result缓冲区位于Result中指定的地址。 PTR登记。 结果。 PTR寄存器是双缓冲的,它可以在START事件生成后立即更新并准备下一个START任务。 Result缓冲区的大小在Result中指定。 当MAXCNT寄存器和ADC填满Result缓冲区时,将生成一个END事件,参见第362页的图101:ADC。 结果以小端字节顺序存储在数据RAM中。 每个样本将被符号扩展到16位,然后存储在结果缓冲区中。

通过触发STOP任务来停止ADC。 STOP任务将终止正在进行的采样。 当ADC停止时,将生成一个STOPPED事件。 如果在触发STOP任务时ADC已经停止,则仍然会生成stopped事件。

[单片机框架][bsp层][nrf52832][nrf52840][nrf52810][nrf52820][bsp_adc] ADC/SSADC配置和使用_NRF52820_02


如果 RESULT.PTR没有指向Data RAM区域,一个EasyDMA传输可能会导致HardFault或RAM损坏。 有关不同内存区域的更多信息,请参阅第23页的内存。 当END或STOPPED事件生成时,EasyDMA将完成对RAM的访问。

RESULT.AMOUNT 可以在END事件或STOPPED事件之后读取AMOUNT寄存器,查看自触发START任务以来有多少结果被转移到RAM中的Result缓冲区。 在Scan模式下,Result缓冲区的大小必须足够大,以便有足够的空间容纳来自每个启用通道的至少一个结果。 为了确保这一点,结果。 MAXCNT必须指定为RESULT。 MAXCNT >= “启用的通道数量”。 更多信息请参见第360页的扫描模式关于扫描模式。

/********************************************************************************
* @file bsp_adc.c
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-07-06
* @brief ADC操作 参考:
********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "RTE_Components.h"
#include CMSIS_device_header

#include "nrf_drv_saadc.h"
#include "sdk_errors.h"

#include "bsp_gpio.h"
#include "bsp_exti.h"
#include "bsp_adc.h"

/* Private Includes ----------------------------------------------------------*/
#include "business_gpio.h"
#include "business_function.h"

/* Private Define ------------------------------------------------------------*/
#define ADC0_CH_NUM 8

#define ADC0_CH0 0
#define ADC0_CH1 1
#define ADC0_CH2 2
#define ADC0_CH3 3
#define ADC0_CH4 4
#define ADC0_CH5 5
#define ADC0_CH6 6
#define ADC0_CH7 7

#define CH_NUM (BS_ADC0_CH0 + BS_ADC0_CH1 + BS_ADC0_CH2 + BS_ADC0_CH3 + BS_ADC0_CH4 + BS_ADC0_CH5 + BS_ADC0_CH6 + BS_ADC0_CH7)
/* Private Variables ---------------------------------------------------------*/
// ADC初始化状态(0--deinit 1--init)
static bool g_adc_init = false;
// ADC采集数据存储buff
uint16_t bsp_adc0_val[ADC0_CH_NUM] = {0};
uint16_t bsp_adc0_temp_val[CH_NUM] = {0};
// 定义ADC采集完毕回调函数
typedef void(*bsp_adc0_callback)(void);
static bsp_adc0_callback irq_callback;

/* External Variables --------------------------------------------------------*/
/* Private Function Prototypes -----------------------------------------------*/
#if BS_ADC0_EN
/**
* @brief 将ADC采集的值重新排序到另一个数组
* @note NULL
* @retval None
*/
static void bsp_adc0_val_copy(void)
{
uint8_t num = 0;
bool flag = false;

for (uint8_t i = 0; i < ADC0_CH_NUM; i++)
{
if (i == ADC0_CH0 && BS_ADC0_CH0)
{
flag = true;
}
else if(i == ADC0_CH1 && BS_ADC0_CH1)
{
flag = true;
}
else if(i == ADC0_CH2 && BS_ADC0_CH2)
{
flag = true;
}
else if(i == ADC0_CH3 && BS_ADC0_CH3)
{
flag = true;
}
else if(i == ADC0_CH4 && BS_ADC0_CH4)
{
flag = true;
}
else if(i == ADC0_CH5 && BS_ADC0_CH5)
{
flag = true;
}
else if(i == ADC0_CH6 && BS_ADC0_CH6)
{
flag = true;
}
else if(i == ADC0_CH7 && BS_ADC0_CH7)
{
flag = true;
}
if (flag)
{
flag = false;
bsp_adc0_val[i] = bsp_adc0_temp_val[num];
num++;
if (num == CH_NUM)
{
break;
}
}
}
}

/**
* @brief ADC interrupt handler.
*/
static void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
bsp_adc0_val_copy();
nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, CH_NUM);
if (irq_callback)
{
irq_callback();
}
}
}

/* Public Function Prototypes ------------------------------------------------*/
/**
* @brief ADC0初始化,并使能通道
* @note NULL
* @retval None
*/
void bsp_adc0_init(void)
{
#if CH_NUM > 0
if (g_adc_init)
{
return;
}
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
uint32_t err_code;

err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
APP_ERROR_CHECK(err_code);
#if BS_ADC0_CH0
nrf_saadc_channel_config_t channel_0_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
err_code = nrfx_saadc_channel_init(ADC0_CH0, &channel_0_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH1
nrf_saadc_channel_config_t channel_1_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN1);
err_code = nrfx_saadc_channel_init(ADC0_CH1, &channel_1_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH2
nrf_saadc_channel_config_t channel_2_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
err_code = nrfx_saadc_channel_init(ADC0_CH2, &channel_2_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH3
nrf_saadc_channel_config_t channel_3_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
err_code = nrfx_saadc_channel_init(ADC0_CH3, &channel_3_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH4
nrf_saadc_channel_config_t channel_4_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
err_code = nrfx_saadc_channel_init(ADC0_CH4, &channel_4_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH5
nrf_saadc_channel_config_t channel_5_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN5);
err_code = nrfx_saadc_channel_init(ADC0_CH5, &channel_5_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH6
nrf_saadc_channel_config_t channel_6_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);
err_code = nrfx_saadc_channel_init(ADC0_CH6, &channel_6_config);
APP_ERROR_CHECK(err_code);
#endif
#if BS_ADC0_CH7
nrf_saadc_channel_config_t channel_7_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
err_code = nrfx_saadc_channel_init(ADC0_CH7, &channel_7_config);
APP_ERROR_CHECK(err_code);
#endif
err_code = nrf_drv_saadc_buffer_convert((nrf_saadc_value_t *)(bsp_adc0_temp_val), CH_NUM);
APP_ERROR_CHECK(err_code);
g_adc_init = true;
#endif
}

/**
* @brief ADC0功能关闭,并移除
* @note NULL
* @retval None
*/
void bsp_adc0_deinit(void)
{
#if CH_NUM > 0
if (!g_adc_init)
{
return;
}
nrf_drv_saadc_uninit();
g_adc_init = false;
#endif
}

/**
* @brief ADC0 启动采样功能
* @note NULL
* @retval None
*/
void bsp_adc0_start(void)
{
#if CH_NUM
if (!g_adc_init)
{
return;
}
APP_ERROR_CHECK(nrfx_saadc_sample());
#endif
}

#else
void bsp_adc0_init(void)
{
}

void bsp_adc0_deinit(void)
{
}

void bsp_adc0_start(void)
{
}
#endif

/**
* @brief 得到ADC0采样值
* @note NULL
* @param ch_num: 通道值
* @retval 通道对应的ADC值
*/
uint16_t bsp_adc0_get_ch_val(uint8_t ch_num)
{
if (ch_num >= ADC0_CH_NUM)
{
return 0xFFFF;
}
return bsp_adc0_val[ch_num];
}

/**
* @brief 注册ADC0采样完毕回调函数
* @note NULL
* @param *event: 绑定回调事件
* @retval 0--失败 1--成功
*/
bool bsp_adc0_irq_callback(void *event)
{
if (irq_callback != NULL)
{
return false;
}
else
{
irq_callback = (bsp_adc0_callback)event;
}
return true;
}
/********************************************************************************
* @file bsp_adc.h
* @author jianqiang.xue
* @version V1.0.0
* @date 2021-04-10
* @brief ADC操作
********************************************************************************/

#ifndef __BSP_ADC_H
#define __BSP_ADC_H

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>

/* Public Function Prototypes -----------------------------------------------*/

void bsp_adc0_init(void);
void bsp_adc0_deinit(void);

void bsp_adc0_start(void);

uint16_t bsp_adc0_get_ch_val(uint8_t ch_num);

bool bsp_adc0_irq_callback(void *event);

#endif

官方手册下载:
​​ https://infocenter.nordicsemi.com/pdf/nRF52810_PS_v1.3.pdf​​​ ​

​https://infocenter.nordicsemi.com/pdf/nRF52820_PS_v1.2.pdf​​​

https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.7.pdf
https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.5.pdf