LoginSignup
4
4

More than 5 years have passed since last update.

nRF52832のADC

Posted at

nRF52832のADC

サンプルのsaadcプロジェクトを追ってみたので、調べたことを書き残します。
SDK14.20をみたが、前後のバージョンでも変わりないんじゃないかな(調べていません)。
#誤りがあったら教えてほしいです。

main関数で、ADCの初期化、イベント(ADサンプリング)の設定、イベントの有効化をしている。
ADサンプリングが完了すれば、コールバック関数が呼ばれる仕組みとなっている。

main.cのmain関数
#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;

//省略(関数はこの後個別に抜き出し)

int main(void)
{
    uint32_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();

    err_code = nrf_drv_power_init(NULL);
    APP_ERROR_CHECK(err_code);

    ret_code_t ret_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(ret_code);

    NRF_LOG_INFO("SAADC HAL simple example.");
    saadc_init(); //ADCの初期化
    saadc_sampling_event_init(); //ADサンプリングイベントの初期化
    saadc_sampling_event_enable(); //イベントの有効化(開始)

    while (1)
    {
        nrf_pwr_mgmt_run();
        NRF_LOG_FLUSH();
    }
}

saadc_init関数では、ADC初期設定、変換結果の格納先バッファ初期化をしている。

チャネル設定の構造体変数channel_configについて、
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SEマクロで、
AD入力チャネルを定数NRF_SAADC_INPUT_AIN0とするほか、
・リファレンス電圧0.6V
・ゲイン1/6
に設定される。用途に応じて、付近で再変更すればいい。

また、サンプルプロジェクトでは、一部の設定がsdk_config.hで行われる。
・分解能(8bit/10bit/12bit/14bit)
・オーバーサンプリング
・ローパワーモード
・割込み優先度

追ってみると、nrf_drv_saadc_init関数で設定される仕組みになっている。
この関数の第一引数は、上記設定の構造体になっている。
ここをNULLとすると、関数内部でsdk_config.hの内容が用いられる。

SDKのADCモジュールは、ダブルバッファ構成になっており、
最初にnrf_drv_saadc_buffer_convert関数を2度呼ぶことで、
ダブルバッファ用のメモリを設定する仕組みになっている。

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_sampling_event_init関数では、AD変換のタイミング(周期)を設定している。
この関数の前半では、AD変換開始のためのTIMERを設定・有効にする。
サンプルプロジェクトでは、400ms毎にTIMERが発火する。

この関数の後半では、EventにTaskを割り当てている。

PPI(Programmable peripheral interconnect)は、nRF52の各機能を繋ぐような感じ
PPIはnRF52でとても重要な機能だと思う。

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);
}

saadc_sampling_event_enable関数では、PPIの有効化をしている。
これにより、TaskとEventが繋がり、Eventの発生に呼応してTaskが動作するようになる。

void saadc_sampling_event_enable(void)
{
    ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);

    APP_ERROR_CHECK(err_code);
}

Taskの動作完了により、saadc_callback関数が呼ばれるようになる。
nrf_drv_saadc_buffer_convert関数で、ダブルバッファのうち変換の完了しているものをAD変換結果の格納先に再設定する。

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++;
    }
}
  • 参考リンク

Nordic Info Center

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4