一、簡介
1.1 主要特點
NRF52832 中 ADC 爲一個 逐次逼近(SAADC) 模擬數字轉換器
- 8/10/12 位分辨率,採用過採樣可以達到 14 位分辨率。
- 多達 8 個輸入通道:
單端輸入時有 1 個通道,2 個通道組成差分輸入。
單端和差分輸入時可以配置成掃描模式。 - 滿量程輸入範圍爲 0 和 VDD
- 可以通過軟件觸發採樣任務啓動採樣,也可以使用低功耗 32.768KHz 的 RTC 定時器或更精確的 1/16MHz 定時器通過 PPI 來觸發採樣任務。
- NRF52832 的 SAADC 支持 單次模式和掃描模式:
單次的採集模式只使用一個採集通道。
掃描模式是按照順序採樣一系列通道。Sample delay between channels is tack + tconv
which may vary between channels according to user configuration of tack. - 通過 EasyDMA 可以直接將採樣結果保存到 RAM。
- 無需外部定時器即可實現連續採樣。
- 可配置通道輸入負載電阻。
- 具備採樣值門限檢測功能。
1.2 採樣模式
NRF52832 的 ADC 有 16 個通道(其中 8個正端輸入通道N ,和 8個負端輸入P),因此信號的採樣模式可分爲單端輸入和差分輸入。
默認狀態下,ADC 的配置模式爲 單端輸入 (即 CH[n].CONFIG 寄存器中的 MODE = 0),此時芯片內部將 ADC 負極輸入短接到地。
而 差分輸入 (即 CH[n].CONFIG 寄存器中的 MODE = 1),則是把負極通過負向端輸入,通過計算兩端的差值來換算出採樣結果。
使用單端模式時,是將內部地和外部待測電壓的參考地假設爲一樣來考慮的,但是地彈噪聲會導致 ADC 產生誤差,如果這個誤差超過我們能接受的範圍,建議使用差分輸入。
在參考源上,相對於 nRF51822,nRF52832 取消了外部參考源,只能使用內部參考源。
ADC 輸出的採樣取決與 CH[n].CONFIG 和 RESOLUTION 寄存器配置的參數,採樣結果計算公式如下:
其中:
- V§:ADC 輸入正極。
- V(N):ADC 輸入負極。
- GAIN:CH[n].CONFIG 寄存器中設置的增益 GAIN(1/6、1/5、1/4、1/3、1/2、1、2、4)。
- REFERENCE:參考電壓(兩種方式:一個是內部參考電壓0.6V,另一個是VDD/4爲參考電壓)。
- RESOLUTION:採樣精度(8/10/12,採用過採樣可以達到 14 位)。
- m:如果 ADC 配置爲單端模式,m = 0,如果 ADC 配置爲差分模式,m = 1。
1.3 工作模式
SAADC 有三種工作模式:單次轉換、連續轉換和掃描模式。
1.3.1 單次轉換
配置ADC的寄存器CH[n].PSELP、CH[n].PSELN 和 CH[n].CONFIG 使得ADC工作於單次模式。觸發採樣任務後,ADC 開始採樣輸入電壓,採樣時間通過 CH[n].CONFIG.TACQ 配置。EVENTS_DONE 事件表明了一次採樣的完成。
在沒有過採樣發生的情況下 EVENTS_RESULTDONE 事件等同於 EVENTS_DONE 事件,注意在實際採樣數據通過EasyDMA 被保存到 RAM 之前,這兩個事件都會產生。
1.3.2 連續轉換
可以通過下面兩種方式實現連續模式:
- 使用 ADC 內部定時器實現定時採樣,ADC 有一個 SAMPLERATE 寄存器,該寄存器可以配置爲 Timer,配置 SAMPLERATE 的比較值 SAMPLERATE.CC 即可實現定時採樣。這種方式下,觸發一次採樣任務即可對所有使能的通道進行採樣。
- 使用 nRF52832 的通用定時器定時通過 PPI 觸發採樣,實現連續採樣。這種方式不能算是 ADC 本身的連續採樣功能,因爲它是藉助了其他外設實現的連續採樣。
採樣速率由 SAMPLERATE.CC 控制,即定時時間長,採樣速率慢,定時時間短,採樣速率快,需要注意的是,使用連續採樣模式的時候,採樣速率應符合下面的公式:
使用 SAMPLERATE 的 Timer 實現的連續採樣不能和掃描模式結合使用,連續採樣模式下只能使用一個通道。
EVENTS_DONE 事件表明了一次採樣的完成,連續採樣模式下,在沒有過採樣發生的情況下 EVENTS_RESULTDONE 事件等同於 EVENTS_DONE 事件,注意在實際採樣數據通過 EasyDMA 被保存到 RAM 之前,這兩個事件都會產生。
1.3.3 連續轉換
當我們使能一個 ADC 通道,ADC 工作於單次模式,當使能的通道數量大於 1 個,ADC進入掃描模式。
掃描模式下,採樣所有通道花費的總時間如下式:
EVENTS_DONE 事件表明了一次採樣的完成,掃描模式下,在沒有過採樣發生的情況下EVENTS_RESULTDONE 事件等同於 EVENTS_DONE 事件,注意在實際採樣數據通過EasyDMA 被保存到 RAM 之前,這兩個事件都會產生。
二、硬件連接
功能口 | 引腳 |
---|---|
AIN0 | 0 |
AIN1 | 2 |
AIN2 | 3 |
AIN3 | 4 |
AIN4 | 5 |
三、移植文件(單次採樣)
注意:以下出現缺失common.h文件錯誤,去除即可。uint8改爲uint8_t或unsigned char或自己宏定義
鏈接:https://pan.baidu.com/s/1rsa6nkGuU1QgEsQ0i1PGZQ 提取碼:vpyt
將 board_adc.c 和 board_adc.h 兩個文件加入工程的Application文件夾下
3.1board_adc.c
/*********************************************************************
* INCLUDES
*/
#include "nrfx_saadc.h"
#include "nrf_drv_saadc.h"
#include "app_error.h"
#include "board_adc.h"
#include "User/user_battery.h"
#include "nrf_log.h"
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent);
/*********************************************************************
* LOCAL VARIABLES
*/
static nrf_saadc_value_t s_bufferPool[SAMPLES_IN_BUFFER];
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief ADC的初始化函數
@param 無
@return 無
*/
void ADC_Init(void)
{
ret_code_t errCode;
// ADC初始化
errCode = nrf_drv_saadc_init(NULL, adcCallbackFunc);
APP_ERROR_CHECK(errCode);
// ADC通道配置
nrf_saadc_channel_config_t channelConfig = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0); // 單端輸入
// ADC通道初始化
errCode = nrf_drv_saadc_channel_init(0, &channelConfig);
APP_ERROR_CHECK(errCode);
// 緩衝配置
errCode = nrf_drv_saadc_buffer_convert(s_bufferPool, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
}
/**
@brief ADC讀取
@param 無
@return 結果在回調函數的緩衝區中
*/
void ADC_Read(void)
{
ret_code_t errCode;
errCode = nrf_drv_saadc_sample();
APP_ERROR_CHECK(errCode);
}
/**
@brief 開啓ADC,與初始化沒有區別,爲了與Disable成對出現
@param 無
@return 無
*/
void ADC_Enable(void)
{
ADC_Init();
}
/**
@brief 禁用ADC
@param 無
@return 無
*/
void ADC_Disable(void)
{
nrfx_saadc_uninit();
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief ADC中斷處理回調函數
@param 無
@return 無
*/
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent)
{
if(pEvent->type == NRF_DRV_SAADC_EVT_DONE) // 採樣完成
{
nrf_saadc_value_t adcResult;
uint16 batteryVoltage;
uint8 batteryPercentage;
ret_code_t errCode;
// 設置好緩存,爲下次轉換緩衝做準備,並且把導入到緩衝的值提取出來
errCode = nrf_drv_saadc_buffer_convert(pEvent->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
adcResult = pEvent->data.done.p_buffer[0];
// 電池電壓轉換計算
batteryVoltage = ADC_RESULT_IN_MILLI_VOLTS(adcResult);
}
}
/****************************************************END OF FILE****************************************************/
3.2 board_adc.h
#ifndef _BOARD_ADC_H_
#define _BOARD_ADC_H_
/*********************************************************************
* INCLUDES
*/
#include "common.h"
/*********************************************************************
* DEFINITIONS
*/
#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 1000 /**< Typical forward voltage drop of the diode . */
#define ADC_RES_10BIT 1024 /**< Maximum digital value for 10-bit ADC conversion. */
// VP = (RESULT * REFERENCE / 2^10) * 6
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)
#define SAMPLES_IN_BUFFER 1
/*********************************************************************
* API FUNCTIONS
*/
void ADC_Init(void);
void ADC_Read(void);
void ADC_Enable(void);
void ADC_Disable(void);
#endif /* _BOARD_ADC_H_ */
四、API調用
需包含頭文件 board_adc.h
ADC_Init
功能 | 初始化ADC驅動 |
---|---|
函數定義 | void ADC_Init(void) |
參數 | 無 |
返回 | 無 |
ADC_Read
功能 | ADC讀取函數 |
---|---|
函數定義 | 無 |
參數 | 無 |
返回 | 無 |
ADC_Enable
功能 | 使能ADC,內部重新初始化ADC驅動,在ADC_Disable之後使用 |
---|---|
函數定義 | void ADC_Enable(void) |
參數 | 無 |
返回 | 無 |
ADC_Disable
功能 | 禁用ADC,以進入低功耗 |
---|---|
函數定義 | void ADC_Disable(void) |
參數 | 無 |
返回 | 無 |
五、SDK配置
點擊 sdk_config.h 文件
選擇 Configuration Wizard
nRF_Drivers 中勾選SAADC相關選項
六、使用例子
1)添加頭文件
#include "board_adc.h"
2)添加初始化代碼(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函數中)
加入 ADC_Init() 並在初始化後調用 ADC_Disable 進入低功耗,在需要用ADC時調用 ADC_Enable 開啓ADC
int main(void)
{
bool erase_bonds;
/*-------------------------- 外設驅動初始化 ---------------------------*/
// Initialize.
log_init(); // 日誌驅動初始化
timers_init(); // 定時器驅動初始化(在此加入自定義定時器)
ADC_Init(); // ADC驅動初始化
/*-------------------------- 藍牙協議棧初始化 ---------------------------*/
power_management_init();
ble_stack_init(); // 協議棧初始化
gap_params_init();
gatt_init();
advertising_init(); // 廣播初始化
services_init(); // 服務初始化
conn_params_init(); // 連接參數初始化
peer_manager_init();
/*-------------------------- 開啓應用 ---------------------------*/
// Start execution.
NRF_LOG_INFO("Template example started.");
advertising_start(erase_bonds); // 開啓廣播
application_timers_start(); // 定時器應用開啓(在此開啓自定義定時器)
ADC_Disable(); // 禁用ADC,進入低功耗模式,等待讀取時再開啓
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
3)在定時器回調中開啓ADC讀取,然後再開一個10ms定時器關閉ADC進入低功耗
static void timer_batteryMeasureOnCallback(void *arg)
{
UNUSED_PARAMETER(arg);
ADC_Enable(); // 開啓ADC
ADC_Read();
app_timer_start(s_batteryMeasureOffTimer, BATTERY_MEASURE_OFF_PERIOD, NULL);
}
4)在ADC中斷處理回調函數中,將採集ADC值進行電壓值轉換
/**
@brief ADC中斷處理回調函數
@param 無
@return 無
*/
static void adcCallbackFunc(nrf_drv_saadc_evt_t const *pEvent)
{
if(pEvent->type == NRF_DRV_SAADC_EVT_DONE) // 採樣完成
{
nrf_saadc_value_t adcResult;
uint16 batteryVoltage;
ret_code_t errCode;
// 設置好緩存,爲下次轉換緩衝做準備,並且把導入到緩衝的值提取出來
errCode = nrf_drv_saadc_buffer_convert(pEvent->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(errCode);
adcResult = pEvent->data.done.p_buffer[0];
// 電池電壓轉換計算
batteryVoltage = ADC_RESULT_IN_MILLI_VOLTS(adcResult);
}
}
• 由 Leung 寫於 2020 年 1 月 6 日