GPIOTE EventでTIMER Taskを起動する
Nordic SDK 14.20のサンプルプロジェクトpin_change_intとppiを参考に
PPIを使って、GPIOTEのIO変化を契機にTIMERを開始するサンプルを書いてみた。
以下にコードと各関数の処理概要を記す。
input_sampling_event_init関数では、GPIOTEとTIMER、PPIの初期設定を行う。
GPIOTEは、PIN_IN(DK Boardのボタン1)のH→L変化を捉えて、in_pin_handlerが呼ばれる。
TIMER0は、1ms毎にtimer_handlerが呼ばれる。
PPIで、GPIOTEとTIMER0を連携する。
input_sampling_event_enable関数では、input_sampling_event_init関数で設定したPPIを有効化する。これで、GPIOTEによるEventでTIMER0のTaskが起動する。
なお、今サンプルでは、TIEMR0のTaskは開始するが停止については書いていない。
main関数では、input_sampling_event_init関数とinput_sampling_event_enable関数を呼び、空ループに入る。
#include <stdbool.h>
#include "nrf.h"
#include "nrf_drv_gpiote.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "app_error.h"
#include "boards.h"
#ifdef BSP_BUTTON_0
#define PIN_IN BSP_BUTTON_0
#endif
#ifndef PIN_IN
#error "Please indicate input pin"
#endif
#ifdef BSP_LED_0
#define PIN_OUT BSP_LED_0
#endif
#ifndef PIN_OUT
#error "Please indicate output pin"
#endif
static const nrf_drv_timer_t m_timer = NRF_DRV_TIMER_INSTANCE(0);
static nrf_ppi_channel_t m_ppi_channel;
static uint32_t m_counter = 0;
void timer_handler(nrf_timer_event_t event_type, void * p_context)
{
m_counter++;
}
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
m_counter = 0;
}
static void input_sampling_event_init(void)
{
ret_code_t err_code;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
in_config.hi_accuracy = false;
err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
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 1ms */
uint32_t ticks = nrf_drv_timer_ms_to_ticks(&m_timer, 1);
nrf_drv_timer_extended_compare(&m_timer,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
APP_ERROR_CHECK(err_code);
uint32_t gpiote_in_event_addr = nrf_drv_gpiote_in_event_addr_get(PIN_IN);
uint32_t timer_task_addr = nrf_drv_timer_task_address_get(&m_timer, NRF_TIMER_TASK_START);
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
gpiote_in_event_addr,
timer_task_addr);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}
void input_sampling_event_enable(void)
{
ret_code_t err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
APP_ERROR_CHECK(err_code);
}
/**
* @brief Function for application main entry.
*/
int main(void)
{
input_sampling_event_init();
input_sampling_event_enable();
while (true)
{
// Do nothing.
}
}
/** @} */
実は少し前、pin_change_intを参考に、IO変化のコールバック関数内でTIMERの開始を行うコードを書いていたのだが、ふとPPIを使っても出来ると思ったのでやってみた。
積極的にPPI(ハードウェア処理)に任せていくのがいいのだと思う。
応用すれば、GPIOTE(Event)→PPI→TIMER(Task/Event)→PPI→SAADC(Task)
のような数珠繋ぎにして、スイッチを押したら定期ADサンプリングを開始
するような書き方も出来る。