nRF52 シリーズ SDK RTCのサンプルコードの考察

***はじめに***
Nordic nRF5x シリーズにて SDK に収められている RTC サンプルコードについての覚書

ここで用いた開発環境は、nRF5 SDK であり下記からダウンロード出来る。
[https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download](https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download)

用いた サンプルコードは、[C:\nRF5_SDK_17.1.0_ddde560\examples\peripheral\rtc]　に収められている。

参考：[https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Frtc.html](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Frtc.html)

---

Counter increment frequency:
fRTC [kHz] = 32.768 / (PRESCALER + 1)

|Prescaler  |Counter   |resolution Overflow  |
|-----------|----------|---------------------|
|0          |30.517μs |512 seconds         　|
|2^8-1 =256      |7812.5μs |131072 seconds  |
|2^12-1=4095|125ms    |582.542 hours        |

サンプルコードのオリジナルは、下記の様になっている。(重要な部分のみ)

~~~main.c
#define COMPARE_COUNTERTIME  (3UL) /**< Get Compare event COMPARE_TIME seconds after the counter starts from 0. */

/** @brief: Function for handling the RTC0 interrupts.
 * Triggered on TICK and COMPARE0 match.
 * config.prescaler = 4095 なので 125ms毎に割り込みが入って来る
 * 
 */
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        nrf_gpio_pin_toggle(COMPARE_EVENT_OUTPUT);
    }
    else if (int_type == NRF_DRV_RTC_INT_TICK)
    {
        nrf_gpio_pin_toggle(TICK_EVENT_OUTPUT);
    }
}

static void rtc_config(void)
{
    uint32_t err_code;

    //Initialize RTC instance
    nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
    config.prescaler = 4095; //★重要!! 125msの設定
    err_code = nrf_drv_rtc_init(&rtc, &config, rtc_handler);
    APP_ERROR_CHECK(err_code);

    //Enable tick event & interrupt
    nrf_drv_rtc_tick_enable(&rtc,true);

    //Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
    //125ms毎に割り込みが入ってくるので X8 をすることで 1秒を設定
    err_code = nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
    APP_ERROR_CHECK(err_code);

    //Power on RTC instance
    nrf_drv_rtc_enable(&rtc);
}
~~~

オリジナルのサンプルでは、125ms毎に LED1 が点滅すると同時に
起動後 3秒後に LED2 が点灯するコードになっている。

そこで 1秒後とに LED1が点滅する様に変更した。
LED2は、起動後 3秒後に点灯することには変わりがない

~~~main.c
uint8_t Rtc_Count = 0;
/** @brief: Function for handling the RTC0 interrupts.
 * Triggered on TICK and COMPARE0 match.
 */
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    Rtc_Count ++;
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        nrf_gpio_pin_toggle(COMPARE_EVENT_OUTPUT);
    }
    else { // >= 8 にしたのがポイント!!
           if (Rtc_Count >=8 && int_type == NRF_DRV_RTC_INT_TICK)
           {
             nrf_gpio_pin_toggle(TICK_EVENT_OUTPUT);
             Rtc_Count = 0;
           }
    }
}
/**
 * @brief Function for application main entry.
 */
int main(void)
{
    leds_config();

    lfclk_config();

    //点灯から始まる様に追加
    nrf_gpio_pin_toggle(TICK_EVENT_OUTPUT);

    rtc_config();

    while (true)
    {
        __SEV();
        __WFE();
        __WFE();
    }
}
~~~

125ms x 8 = 1秒に設定するため Rtc_Count ==8 としたところ 3秒以後 LED1 が消灯せずに 点滅した状態になる。
何故か不思議に思い rtc_handler() の引数int_type と Rtc_Count　の時系列を調べると

|time(ms) |int_type                 | Rtc_Count | LED1 | LED2 |
|---------|-------------------------|-----------|------|------|
| 125     |NRF_DRV_RTC_INT_TICK     | 0 -> 1    |点灯  |消灯   | 
| 250     |NRF_DRV_RTC_INT_TICK     | 1  -> 2    |点灯  |消灯   | 
| 375     |NRF_DRV_RTC_INT_TICK     | 2 -> 3    |点灯  |消灯   | 
| 500     |NRF_DRV_RTC_INT_TICK     | 3 -> 4    |点灯  |消灯   | 
| 625     |NRF_DRV_RTC_INT_TICK     | 4 -> 5    |点灯  |消灯   | 
| 750     |NRF_DRV_RTC_INT_TICK     | 5 -> 6    |点灯  |消灯   | 
| 875     |NRF_DRV_RTC_INT_TICK     | 6 -> 7    |点灯  |消灯   | 
| 1000    |NRF_DRV_RTC_INT_TICK     | 7 -> 8<font color="Red"> ->0</font>|点灯  |消灯   | 
| 1125    |NRF_DRV_RTC_INT_TICK     | 0 -> 1    |消灯  |消灯   | 
| 1250    |NRF_DRV_RTC_INT_TICK     | 1 -> 2    |消灯  |消灯   | 
| 1375    |NRF_DRV_RTC_INT_TICK     | 2 -> 3    |消灯  |消灯   | 
| 1500    |NRF_DRV_RTC_INT_TICK     | 3 -> 4    |消灯  |消灯   | 
| 1625    |NRF_DRV_RTC_INT_TICK     | 4 -> 5    |消灯  |消灯   | 
| 1750    |NRF_DRV_RTC_INT_TICK     | 5 -> 6    |消灯  |消灯   | 
| 1875    |NRF_DRV_RTC_INT_TICK     | 6 -> 7    |消灯  |消灯   | 
| 2000    |NRF_DRV_RTC_INT_TICK     | 7 -> 8<font color="Red"> ->0</font>|点灯  |消灯   | 
| 2125    |NRF_DRV_RTC_INT_TICK     | 0 -> 1    |点灯  |消灯   | 
| 2250    |NRF_DRV_RTC_INT_TICK     | 1 -> 2    |点灯  |消灯   | 
| 2375    |NRF_DRV_RTC_INT_TICK     | 2 -> 3    |点灯  |消灯   | 
| 2500    |NRF_DRV_RTC_INT_TICK     | 3 -> 4    |点灯  |消灯   | 
| 2625    |NRF_DRV_RTC_INT_TICK     | 4 -> 5    |点灯  |消灯   | 
| 2750    |NRF_DRV_RTC_INT_TICK     | 5 -> 6    |点灯  |消灯   | 
| 2875    |NRF_DRV_RTC_INT_TICK     | 6 -> 7    |点灯  |消灯   | 
| 3000    |<font color="Red">NRF_DRV_RTC_INT_COMPARE0</font>| 7 -> 8    |点灯  |点灯   | 
| 3125    |NRF_DRV_RTC_INT_TICK     | 8 -> 9    |点灯  |点灯   | 
| 3250    |NRF_DRV_RTC_INT_TICK     | 9 -> 10   |点灯  |点灯   | 
| 3375    |NRF_DRV_RTC_INT_TICK     |10 -> 11   |点灯  |点灯   | 

上記の表の様に 3秒後は、<font color="Red"> int_type = NRF_DRV_RTC_INT_COMPARE0 </font> となり
<font color="Red"> int_type = NRF_DRV_RTC_INT_TICK </font> とならないので
<font color="Red"> Rtc_Count ==8 </font> の条件が飛ばされるのである。

故に <font color="Red">Rtc_Count >=8 </font> として回避した。
これが意味するのは、LED1は 起動直後 点灯 -> 1秒後に消灯 -> 2秒後に点灯 -> 3秒125msで消灯
なので、3秒後以後 125ms ズレることになる。

ちなみに <font color="Red">Rtc_Count ==8 </font>とした時
LED1 は、長い沈黙(点灯) の後 再び点滅を開始する。理由は、<font color="Red">uint8_t Rtc_Count </font>と定義しているので
256までカウントアップした後 0に戻る。沈黙時間を計測すると約30秒で
(125ms X 256) ÷ (125ms X 8) = 32(s) で計算通り。

また、
LED2 を考えなければ、1秒後とにLED1 を点滅させることが出来る。
この時は、設定をコメントアウトさせる

~~~main.c
//Set compare channel to trigger interrupt after COMPARE_COUNTERTIME seconds
//125ms毎に割り込みが入ってくるので X8 をすることで 1秒を設定
//err_code = nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
//APP_ERROR_CHECK(err_code);
~~~

rtc_handler() には、NRF_DRV_RTC_INT_COMPARE0 の他に
NRF_DRV_RTC_INT_COMPARE1, NRF_DRV_RTC_INT_COMPARE2, NRF_DRV_RTC_INT_COMPARE3 も入れることが出来る。

この時 それぞれに対する設定は、下記の通り

~~~main.c
    //For NRF_DRV_RTC_INT_COMPARE0
    //125ms毎に割り込みが入ってくるので X8 をすることで 1秒を設定
    err_code = nrf_drv_rtc_cc_set(&rtc,0,COMPARE_COUNTERTIME * 8,true);
    APP_ERROR_CHECK(err_code);

    //For NRF_DRV_RTC_INT_COMPARE1
    //125ms毎に割り込みが入ってくるので X16 をすることで 2秒を設定
    err_code = nrf_drv_rtc_cc_set(&rtc,1,COMPARE_COUNTERTIME * 16,true);
    APP_ERROR_CHECK(err_code);

    //For NRF_DRV_RTC_INT_COMPARE2
    //125ms毎に割り込みが入ってくるので X24 をすることで 3秒を設定
    err_code = nrf_drv_rtc_cc_set(&rtc,2,COMPARE_COUNTERTIME * 24,true);
    APP_ERROR_CHECK(err_code);

    //For NRF_DRV_RTC_INT_COMPARE3
    //125ms毎に割り込みが入ってくるので X32 をすることで 4秒を設定
    err_code = nrf_drv_rtc_cc_set(&rtc,3,COMPARE_COUNTERTIME * 32,true);
    APP_ERROR_CHECK(err_code);
~~~
---
【Ver 1.20で追記】
LED2を活かしつつ、またLED1を 125ms ずらすことなく 1秒後とに点滅させる様に下記の様に修正した

~~~main.c
uint8_t Rtc_Count = 0;

/** @brief: Function for handling the RTC0 interrupts.
 * Triggered on TICK and COMPARE0 match.
 */
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
    Rtc_Count ++;
    if (int_type == NRF_DRV_RTC_INT_COMPARE0)
    {
        nrf_gpio_pin_toggle(COMPARE_EVENT_OUTPUT);
        //以下２行を追加
        nrf_gpio_pin_toggle(TICK_EVENT_OUTPUT);
        Rtc_Count = 0;
    }
    else {
           if (Rtc_Count ==8 && int_type == NRF_DRV_RTC_INT_TICK)
           {
             nrf_gpio_pin_toggle(TICK_EVENT_OUTPUT);
             Rtc_Count = 0;
           }
    }
}
~~~

---
☆2022年 2月20日(日) 午前10時40分 初版(Ver1.00) 作成
  2022年 2月20日(日) 午後 1時40分 Ver1.10 - 間違いを修正
  2022年 2月20日(日) 午後 2時45分 Ver1.20 - 記述を追加