시작하기
nRF52832에서 사용 가능한 ADC인 SAADC(Successive approximation analog-to-digital converter)에 대해서 알아보도록 하자.
8/10/12비트 분해능을 가지며 오버샘플링을 하면 14비트 분해능까지 가질 수 있으며 총 8개의 채널로 1 채널 입력, 2 채널 차동 입력으로 구성할 수 있다.
SAADC 예제는 AIN0핀을 타이머를 사용해 버퍼 크기만큼 샘플링하고 버퍼가 채워지면 샘플링된 값을 출력한다.
\examples\peripheral\saadc\pca10040\blank\ses 폴더에서 프로젝트를 실행한다.
#define SAMPLES_IN_BUFFER 5
volatile uint8_t state = 1;
static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_IN_BUFFER];
static nrf_ppi_channel_t m_ppi_channel;
static uint32_t m_adc_evt_counter;
/**
* @brief Function for main application entry.
*/
int main(void)
{
uint32_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
ret_code_t ret_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(ret_code);
saadc_init();
saadc_sampling_event_init();
saadc_sampling_event_enable();
NRF_LOG_INFO("SAADC HAL simple example started.");
while (1)
{
nrf_pwr_mgmt_run();
NRF_LOG_FLUSH();
}
}
main 함수를 살펴보면 saadc_init, saadc_sampling_event_init, saadc_sampling_event_enable 함수를 호출해 SAADC 설정이 끝난다.
SAADC를 어떻게 설정하는지 자세히 알아보도록 하자.
/**
* @brief Macro for setting @ref nrf_saadc_channel_config_t to default settings
* in single-ended mode.
*
* @param PIN_P Analog input.
*/
#define NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PIN_P) \
{ \
.resistor_p = NRF_SAADC_RESISTOR_DISABLED, \
.resistor_n = NRF_SAADC_RESISTOR_DISABLED, \
.gain = NRF_SAADC_GAIN1_6, \
.reference = NRF_SAADC_REFERENCE_INTERNAL, \
.acq_time = NRF_SAADC_ACQTIME_10US, \
.mode = NRF_SAADC_MODE_SINGLE_ENDED, \
.burst = NRF_SAADC_BURST_DISABLED, \
.pin_p = (nrf_saadc_input_t)(PIN_P), \
.pin_n = NRF_SAADC_INPUT_DISABLED \
}
void saadc_init(void)
{
ret_code_t err_code;
nrf_saadc_channel_config_t channel_config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
err_code = nrf_drv_saadc_init(NULL, saadc_callback);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(0, &channel_config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
}
먼저 saadc_init 함수를 살펴보면 NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0)로 AIN0핀을 SAADC 기본 채널 설정값으로 정의해준다.
SAADC 채널 설정값에 대해서 자세히 알아보도록 하자.
resistor_p, resistor_n은 내부 저항을 선택하는 설정으로 bypass, pull-down, pull-up, vdd/2 중 선택할 수 있다.
gain은 ADC 입력 범위에 영향을 주는 요소로 1/6, 1/5, 1/4, 1/3, 1/2, 1, 2, 4 중 선택할 수 있다.
reference는 ADC 입력 범위에 영향을 주는 요소로 내부 reference인 0.6V와 VDD/4 중 선택할 수 있다.
입력 범위 계산식은 reference / gain으로 정해진다.
예를 들어, gain은 1/4, reference는 VDD/4로 선택했다면 입력 범위는 VDD로 정해진다.
마지막으로 gain은 1/6, reference는 0.6으로 선택했다면 입력 범위는 3.6V로 정해진다.
AIN 입력은 VDD를 초과하거나 VSS보다 낮을 수 없다.
acq_time은 ADC 샘플링을 하기 위해 필요한 시간으로 Rsource 값에 따라 달라진다.
TACQ [us] | Maximum Rsource [kOhm] |
3 | 10 |
5 | 40 |
10 | 100 |
15 | 200 |
20 | 400 |
40 | 800 |
mode는 single-end, differential 중 선택할 수 있으며 single-end 모드는 ADC 입력이 positive 설정되었을 때 negative 입력은 GND로 선택된다.
differential 모드는 ADC 입력이 negative 설정되었을 때 설정 가능하다.
ADC 결과 값은 [ V(positive) - V(negative) ] * gain / reference * 2^(resolution - m)으로 정해진다.
single-end 모드는 m = 0, differential 모드는 m = 1을 가진다.
burst는 oversampling을 선택하는 설정으로 ADC 입력 신호의 잡음을 억제하는 기능을 하지만 비선형선을 개선하지는 못한다.
oversampling과 scan 모드 둘 다 ADC 입력에 대해 평균을 내므로 같이 사용하면 안 된다.
scan 모드는 ADC 입력이 positive로 설정되었을 활성화 된다.
pin_p, pin_n은 ADC 입력 채널의 positive, negative를 선택하는 설정이다.
Name | Type | Description |
P0.02 / AIN0 | Digital I/O Analog input |
General purpose I/O SAADC/COMP/LPCOMP input |
P0.03 / AIN1 | ||
P0.04 / AIN2 | ||
P0.05 / AIN3 | ||
P0.28 / AIN4 | ||
P0.29 / AIN5 | ||
P0.30 / AIN6 | ||
P0.31 / AIN7 |
// <o> SAADC_CONFIG_RESOLUTION - Resolution
// <0=> 8 bit
// <1=> 10 bit
// <2=> 12 bit
// <3=> 14 bit
#define SAADC_CONFIG_RESOLUTION 1
// <o> NRFX_SAADC_CONFIG_OVERSAMPLE - Sample period
// <0=> Disabled
// <1=> 2x
// <2=> 4x
// <3=> 8x
// <4=> 16x
// <5=> 32x
// <6=> 64x
// <7=> 128x
// <8=> 256x
#define NRFX_SAADC_CONFIG_OVERSAMPLE 0
/** @brief Macro for setting @ref nrfx_saadc_config_t to default settings. */
#define NRFX_SAADC_DEFAULT_CONFIG \
{ \
.resolution = (nrf_saadc_resolution_t)NRFX_SAADC_CONFIG_RESOLUTION, \
.oversample = (nrf_saadc_oversample_t)NRFX_SAADC_CONFIG_OVERSAMPLE, \
.interrupt_priority = NRFX_SAADC_CONFIG_IRQ_PRIORITY, \
.low_power_mode = NRFX_SAADC_CONFIG_LP_MODE \
}
/**
* @brief Function for initializing the SAADC.
*
* @param[in] p_config Pointer to the structure with initial configuration.
* If NULL, the default one is used.
* @param[in] event_handler Event handler provided by the user.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_INVALID_STATE If the driver is already initialized.
* @retval NRF_ERROR_INVALID_PARAM If event_handler is NULL.
*/
__STATIC_INLINE ret_code_t nrf_drv_saadc_init(nrf_drv_saadc_config_t const * p_config,
nrf_drv_saadc_event_handler_t event_handler)
{
if (p_config == NULL)
{
static const nrfx_saadc_config_t default_config = NRFX_SAADC_DEFAULT_CONFIG;
p_config = &default_config;
}
return nrfx_saadc_init(p_config, event_handler);
}
nrf_drv_saadc_init 함수의 전달 인자로 saadc_callback 함수를 이벤트 핸들러로 등록한다.
NRFX_SAADC_DEFAULT_CONFIG로 SAADC의 resolution, oversample, irq priority, low power를 설정한다.
기본 설정은 resolution 10bit, oversample 비활성화, irq priority는 6, low power 모드는 비활성화로 정의되어 있다.
nrf_drv_saadc_channel_init 함수의 전달 인자로 채널과 앞서 설명한 NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE 채널 설정값을 넘겨줘 ADC 입력 채널에 대한 설정을 한다.
nrf_drv_saadc_buffer_convert 함수는 ADC 입력 채널에서 샘플링한 ADC 값을 가져오는 함수로 저장할 변수와 크기를 전달 인자로 넘겨준다.
void saadc_sampling_event_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(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);
/* setup m_timer for compare event every 400ms */
uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 400);
nrf_drv_timer_extended_compare(&m_timer,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
false);
nrf_drv_timer_enable(&m_timer);
uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&m_timer,
NRF_TIMER_CC_CHANNEL0);
uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
timer_compare_event_addr,
saadc_sample_task_addr);
APP_ERROR_CHECK(err_code);
}
void saadc_sampling_event_enable(void)
{
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
APP_ERROR_CHECK(err_code);
}
saadc_sampling_event_init 함수는 timer와 saadc를 ppi 기능으로 묶어주는 설정으로 400ms 주기로 ADC 입력 채널을 샘플링한다.
saadc_sampling_event_enable 함수를 호출함으로 앞서 설정한 saadc가 동작하게 된다.
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
ret_code_t err_code;
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
int i;
NRF_LOG_INFO("ADC event number: %d", (int)m_adc_evt_counter);
for (i = 0; i < SAMPLES_IN_BUFFER; i++)
{
NRF_LOG_INFO("%d", p_event->data.done.p_buffer[i]);
}
m_adc_evt_counter++;
}
}
ADC 입력 채널 샘플링을 SAMPLES_IN_BUFFER만큼 완료하면 NRF_DRV_SAADC_EVT_DONE 이벤트가 발생하며 샘플링한 값을 출력하는 것으로 마무리된다.
이제 점프선을 이용해 GND 또는 VDD를 입력해보자.
마지막으로 출력된 결과값이 맞는지 검증해보도록 하자.
ADC 결과값 822 = ( V(positive) - 0 ) * 0.16666666666666666666666666666667 / 0.6 * 2^(10 - 0)으로 V(positive)는 약 2.88984V로 계산된다.
멀티 미터기를 이용해서 VDD 핀을 찍어보니 2.88V가 나오는 것을 확인했다.
'Embedded > nRF52 BLE 개발 안내서' 카테고리의 다른 글
nRF 52 BLE 개발하기 - nfc/record_text (0) | 2021.02.24 |
---|---|
nRF52 BLE 개발하기 - nfc/record_launch_app (0) | 2021.02.23 |
nRF52 BLE 개발하기 - pwm_driver (4) | 2021.02.07 |
nRF52 BLE 개발하기 - low_power_pwm (0) | 2021.02.04 |
nRF52 BLE 개발하기 - nRF Sniffer (0) | 2021.02.02 |
댓글