1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

nRF SDK:今さらPPIというものを理解する

Posted at

PPIとは

今回は珍しく旧SDK(nRF5 SDK)の話です。(もちろんnRF Connect SDKにもありますがそれは後ほど)
PPIというのはProgrammable Peripheral Interconnectの略です。具体的にはペリフェラル同士の割り込みを結び付けてCPUを介さずにペリフェラルを動かすというもので、この程度の説明はたしかマニュアルにも書かれていたような気がするのですが、ぶっちゃけ何のことかさっぱり分かっていませんでした。色んなCPUを触っていますが、おそらく他のCPUにはこんな機能はないと思います。

他のCPUにないというのが何のことか理解できなかった理由ということで・・・

でも・・・

そうは言ってみたところで、正直なところ具体的に何に使えばいいのか分からない・・・そりゃそうですよね。他のCPUではそんな機能がないのでそんなのすぐに使えって言っても無理があります。
と思ったところでそういえば誰かが過去にPPIのことを書いているのでは?と思って調べてみたら・・・ありました。

まさにこれです。ここではGPIO(入力)→TIMER(出力)ですが、これはなんでもかまいません。例えば私が作ったサンプルだと以下のようにTIMER(入力)→GPIO(出力)です。

main.c
    // Timer
    ret_code_t err_code;
    uint32_t time_ticks;
    nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;

    // Configure timer1
    err_code = nrfx_timer_init(&m_timer1, &timer_cfg, timer_event_handler);
    APP_ERROR_CHECK(err_code);
    // Set timer interval
    time_ticks = nrfx_timer_ms_to_ticks(&m_timer1, 100);
    nrfx_timer_extended_compare(&m_timer1, NRF_TIMER_CC_CHANNEL1, time_ticks, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);

    // GPIOTE Handler
    if (!nrfx_gpiote_is_init())
    {
        err_code = nrfx_gpiote_init();
        APP_ERROR_CHECK(err_code);
    }

    // Output
    nrfx_gpiote_out_config_t const out_config = {
        .action = NRF_GPIOTE_POLARITY_TOGGLE,
        .init_state = 1,
        .task_pin = true,
    };

    err_code = nrfx_gpiote_out_init(BSP_LED_0, &out_config);
    APP_ERROR_CHECK(err_code);

    nrfx_gpiote_out_task_enable(BSP_LED_0);

    // PPI
    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    nrf_ppi_channel_t channel;
    err_code = nrfx_ppi_channel_alloc(&channel);
    APP_ERROR_CHECK(err_code);

    err_code = nrfx_ppi_channel_assign(channel,
        nrfx_timer_compare_event_address_get(&m_timer1, NRF_TIMER_CC_CHANNEL1),
        nrfx_gpiote_out_task_addr_get(BSP_LED_0));
    APP_ERROR_CHECK(err_code);

    /* Enable (D)PPI channel. */
    err_code = nrfx_ppi_channel_enable(channel);
    APP_ERROR_CHECK(err_code);

    nrfx_timer_enable(&m_timer1);

上記のサンプル(メイン部分だけ抜粋)を動かすと500ミリ秒毎にLEDが点いたり消えたりします。飛び先の関数としてtimer_event_handlerを指定していますが実際に飛ばす必要はありませんし、実際に飛んだ先の関数内には何も記述していません。(ので記載を省略しています)
マイクロオーダーのタイマーイベントでI/Oを制御しようとすると割り込み待ちでずれてしまうことも多々あります。そういう場合にCPUを介さずに制御することでそういった遅延をなくすことができます。また、おそらくですが消費電力も少なくなります。

実はPPIのサンプルは存在している

ちゃんと理解せずに使っていたので気が付きませんでしたが、実はPPIを使ったサンプルというのは最初から用意されています。A/D変換(saadc)のサンプルです。しっかり読むとPPIを使ってTIMER(入力)→SAADC(出力)という制御をしています。

コピペで使っていたので何をしているのか全く理解していなかったのさ。てへぺろ

まとめ

操作して動かす場合はGPIOによるPPIを、一定間隔で繰り返すものはTIMERでのPPIを組んで積極的にCPU負荷(と消費電力)を減らしていきましょう。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?