nRF5 SDKのQueueとタイマとSPIを使ってみた。

nRF5 SDKをちょっと使ってみたのでメモ

BLEな部分は触らずマイコンみたいに使いました。

環境：
　Hardware: nRF52832 搭載の nRF52-DK PCA10040
　Software: nRF5 SDK 14.2.0 / SoftDevice S123 5.0.0

# Queue

タスク間のデータ転送などに使う。

数年前のnRF5 SDKではmailboxがあったが、現時点ではmailboxが廃止されており、その代わりにQueueを使用します。

Queueはマクロを使って定義する。

```c
NRF_QUEUE_DEF(uint8_t, myQueue, 15, NRF_QUEUE_MODE_OVERFLOW);
```
ここで定義されるQueueはスコープがstaticなので、ファイル外からアクセスする場合はアクセス用の関数を用意する。

Queue操作のAPIはいろいろとあるけど、４つだけ使用。

```c
nrf_queue_reset(&myQueue);
nrf_queue_push(&myAppQ, &ev);
nrf_queue_is_empty(&myQueue);
nrf_queue_pop(&myQueue, &ev);
```

このQueueを使ってイベント処理を実装してみた。

```
doEvent(void)
{
    uint8_t e;
    if (nrf_queue_is_empty(&myQueue)) {
        return;
    }
    nrf_queue_pop(&myQueue, &e);

    swith(e){
    ....
    }
}
```

# アプリケーションタイマ
RTOSのタスクをタイマ起動に置き換えてみた。
今回はFreeRTOSは使いたくなかったので、タイマからの定期起動として実装してみました。

アプリケーションタイマの種類として、周期タイマとシングルショットタイマがある

周期タイマ

```c

APP_TIMER_DEF(my_timer_id); 

app_timer_create(&my_timer_id, APP_TIMER_MODE_REPEATED, smac_loop_handler);
app_timer_start(my_timer_id, APP_TIMER_TICKS(1), NULL);

```

シングルショットタイマ

```c
uint32_t timer_ms = 10;
app_timer_create(&my_timer_id, APP_TIMER_MODE_SINGLE_SHOT, WaitSendTimerCallBack));
uint64_t tick =  ( timer_ms * APP_TIMER_CLOCK_FREQ ) / ( 1000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1) );
app_timer_start(my_timer_id, tick, NULL);
```

タイマの周期を変数で指定する場合は、APP_TIMER_TICKSマクロは使えないので、APP_TIMER_TICKS()相当の計算を行う、そのときuint64_tで計算する。

アプリケーションタイマからのコールバックは、タイマ割り込みのコンテキストで実行される。　割り込みを必要とするような処理は行えないことに注意。

# ハードウェアタイマ

高精度のカウント処理が使いたいのでハードウェアタイマを使ってみた。

```c
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    timer_cfg.frequency = NRF_TIMER_FREQ_2MHz;
    timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
    APP_ERROR_CHECK(nrf_drv_timer_init(&NRF_DRV_TIMER_INSTANCE(1), &timer_cfg, timer_void_event_handler));

    nrf_drv_timer_disable(&NRF_DRV_TIMER_INSTANCE(1));
    nrf_drv_timer_clear(&NRF_DRV_TIMER_INSTANCE(1));
    nrf_drv_timer_enable(&NRF_DRV_TIMER_INSTANCE(1));
```

カウント値の取得

```c
  count = nrf_drv_timer_capture(&NRF_DRV_TIMER_INSTANCE(1), NRF_TIMER_CC_CHANNEL0)
```


# SPIマスタ

SPIの設定

SPIの設定は構造体にパラメータを設定してAPIを呼ぶ

デフォルト設定の定義があるので、これを使う。

```c
/**
 * @brief SPI master instance default configuration.
 */
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \
{                                                            \
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \
    .orc          = 0xFF,                                    \
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \
    .mode         = NRF_DRV_SPI_MODE_0,                      \
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \
}
```

```c
#define SPI_SS_PIN 29
#define SPI_MISO_PIN 28
#define SPI_MOSI_PIN 4
#define SPI_SCK_PIN 3

    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
    spi_config.ss_pin   = SPI_SS_PIN;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;
    spi_config. frequency = NRF_DRV_SPI_FREQ_1M
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
```

SPIデータの送受信

```c
    uint8_t tx[2] = 0x01;
    uint8_t rx[2];

    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, tx, 2, rx, 2));
```