본문 바로가기
Embedded/nRF52 BLE 개발 안내서

nRF52 BLE 개발하기 - saadc

by 큐찡 2021. 2. 17.
728x90
반응형

시작하기

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가 나오는 것을 확인했다.

728x90
반응형

댓글