Nordic の nRF52840 は クロックが2つ必要で、メインクロックと32.768kHzの低速クロック。
低速クロックはカレンダー以外にも通信のタイミングなどに使っているらしい。低速クロックの精度が狂うと通信に影響がある。特に Bluetooth では 250ppm 以内に精度を押さえないといけないらしい。
低速クロックは外部クリスタルをつなげるようになっていて(Low frequency crystal oscillator : LFXO)、SDKでもそれ前提の設定となっている。
しかしながら外部クリスタルを省略して、内部RC発振器やメインクロックを分周して32kクロックを賄うこともできる。
外部クリスタル LFXO との違い
ではそれらを使った場合はどのような違いがあるか?
メインクロックを分周(LFSYNT)する場合
https://devzone.nordicsemi.com/f/nordic-q-a/35989/clock-source-on-ble_app_uart
によると、LFSYNTは消費電力の点で不利。
内部RC発振器(Low frequency RC oscillator : LFRC )を使った場合:
内部RC発振器駆動のための消費電流増加
nRF52840_PS_v1.0.pdf の
5.4.4.3 Low frequency crystal oscillator (LFXO)
5.4.4.4 Low frequency RC oscillator (LFRC), Normal mode
によると、
LFXO 0.23uA
LFRC 0.7uA
となっている。
精度低下のための消費電流増加
また精度低下のために、後述のキャリブレーションおよびBluetoothリスニング時間の増大(Window widening)が必要となる。それぞれ更に消費電力が拡大する。
cf., nRF52832ではあるが実測値を含む具体的なデータ https://devzone.nordicsemi.com/f/nordic-q-a/14214/how-much-current-consumption-when-we-use-internal-32k-rc
通信プロトコルの制限
ANTでは精度の関係で内蔵RCは使えないらしい(精度 50ppm が必要)。
内蔵 RC の使用
では、内蔵 RC を使う設定にするにはどうしたらいいか? SoftDevice を使う場合と使わない場合で方法は異なる。ここでは、SoftDeviceを使う場合について。
必要なキャリブレーション間隔
RC 発振器は精度が2%と大変悪い。
BLE stack を使う場合、250ppm 以内に収める必要があるので一定時間ごとにキャリブレーションを行って精度を保つ。8秒ごとのキャリブレーション、および0.5℃の変化時には4秒毎のキャリブレーションが推奨されている。
sdk_config.h の設定
元々の LFXC を使う設定は nRF_SoftDevice - NRF_SDH_ENABLED - clock の項目において下記のようになっている。
//==========================================================
// <h> Clock - SoftDevice clock configuration
//==========================================================
// <o> NRF_SDH_CLOCK_LF_SRC - SoftDevice clock source.
// <0=> NRF_CLOCK_LF_SRC_RC
// <1=> NRF_CLOCK_LF_SRC_XTAL
// <2=> NRF_CLOCK_LF_SRC_SYNTH
# ifndef NRF_SDH_CLOCK_LF_SRC
# define NRF_SDH_CLOCK_LF_SRC 1
# endif
// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval.
# ifndef NRF_SDH_CLOCK_LF_RC_CTIV
# define NRF_SDH_CLOCK_LF_RC_CTIV 0
# endif
// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature.
// <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated
// <i> if the temperature has not changed.
# ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
# define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0
# endif
// <o> NRF_SDH_CLOCK_LF_ACCURACY - External clock accuracy used in the LL to compute timing.
// <0=> NRF_CLOCK_LF_ACCURACY_250_PPM
// <1=> NRF_CLOCK_LF_ACCURACY_500_PPM
// <2=> NRF_CLOCK_LF_ACCURACY_150_PPM
// <3=> NRF_CLOCK_LF_ACCURACY_100_PPM
// <4=> NRF_CLOCK_LF_ACCURACY_75_PPM
// <5=> NRF_CLOCK_LF_ACCURACY_50_PPM
// <6=> NRF_CLOCK_LF_ACCURACY_30_PPM
// <7=> NRF_CLOCK_LF_ACCURACY_20_PPM
// <8=> NRF_CLOCK_LF_ACCURACY_10_PPM
// <9=> NRF_CLOCK_LF_ACCURACY_5_PPM
// <10=> NRF_CLOCK_LF_ACCURACY_2_PPM
// <11=> NRF_CLOCK_LF_ACCURACY_1_PPM
# ifndef NRF_SDH_CLOCK_LF_ACCURACY
# define NRF_SDH_CLOCK_LF_ACCURACY 7
# endif
このうち、有効なディレクティブは以下の4つ。
# define NRF_SDH_CLOCK_LF_SRC 1
# define NRF_SDH_CLOCK_LF_RC_CTIV 0
# define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0
# define NRF_SDH_CLOCK_LF_ACCURACY 7
これらを以下のように変更
NRF_SDH_CLOCK_LF_SRC
SoftDevice clock source.
// <0=> NRF_CLOCK_LF_SRC_RC
ということなので 0 を指定。
NRF_SDH_CLOCK_LF_RC_CTIV
SoftDevice calibration timer interval.
ここに設定する値は rc_ctiv に設定する値と同様に 1/4 秒単位だと思うけど本当かな?
ドキュメント
https://infocenter.nordicsemi.com/topic/sdk_nrf5_v16.0.0/group__nrf__sdh__config.html
は、
Note
This is an NRF_CONFIG macro.
とあるだけで役に立たない。NRF_CONFIG macroって何だ?
調べてみたら NRF_CONFIG macro についてはわからなかったが NRF_SDH_CLOCK_LF_RC_CTIV は SDK 中の components/softdevice/common/nrf_sdh.c 中で、以下のように使われていた。
nrf_clock_lf_cfg_t const clock_lf_cfg =
{
.source = NRF_SDH_CLOCK_LF_SRC,
.rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV,
.rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
.accuracy = NRF_SDH_CLOCK_LF_ACCURACY
};
nrf_clock_lf_cfg_t 構造体は先に示した https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v7.0.1/structnrf__clock__lf__cfg__t.html
で定義されていて、rc_ctivは 1/4 秒単位。
ここでは下記の NRF_SDH_CLOCK_LF_RC_TEMP_CTIV と合わせてタイマー間隔を決定する。今回はここで温度変化が0.5℃以上ある場合の間隔を指定し、推奨値を満たすために16を指定する。これでタイマー間隔は4秒間隔になる。
NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
SoftDevice calibration timer interval under constant temperature.
How often (in number of calibration intervals) the RC oscillator shall be calibrated if the temperature has not changed.
温度が変更されていない場合のキャリブレーションの頻度。単位は先の先にタイマー間隔を4秒にした。温度が変更されていない場合のキャリブレーション間隔の推奨値は8秒。ここで2を指定することでタイマー2回毎にキャリブレーションが行われる。
NRF_SDH_CLOCK_LF_ACCURACY
External clock accuracy used in the LL to compute timing.
Window widening などに関連するのではないかな? RC 発振器でキャリブレート後の精度 250ppm として 0 を指定するのが良さそうですね・・・
設定値
結局、設定値はこうなります。
# define NRF_SDH_CLOCK_LF_SRC 0
# define NRF_SDH_CLOCK_LF_RC_CTIV 16
# define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
# define NRF_SDH_CLOCK_LF_ACCURACY 0
これらを反映した SDK_config.h の Clock セクションは下記の通り。
//==========================================================
// <h> Clock - SoftDevice clock configuration
//==========================================================
// <o> NRF_SDH_CLOCK_LF_SRC - SoftDevice clock source.
// <0=> NRF_CLOCK_LF_SRC_RC
// <1=> NRF_CLOCK_LF_SRC_XTAL
// <2=> NRF_CLOCK_LF_SRC_SYNTH
# ifndef NRF_SDH_CLOCK_LF_SRC
# define NRF_SDH_CLOCK_LF_SRC 1
# endif
// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval.
# ifndef NRF_SDH_CLOCK_LF_RC_CTIV
# define NRF_SDH_CLOCK_LF_RC_CTIV 0
# endif
// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature.
// <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated
// <i> if the temperature has not changed.
# ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
# define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 0
# endif
// <o> NRF_SDH_CLOCK_LF_ACCURACY - External clock accuracy used in the LL to compute timing.
// <0=> NRF_CLOCK_LF_ACCURACY_250_PPM
// <1=> NRF_CLOCK_LF_ACCURACY_500_PPM
// <2=> NRF_CLOCK_LF_ACCURACY_150_PPM
// <3=> NRF_CLOCK_LF_ACCURACY_100_PPM
// <4=> NRF_CLOCK_LF_ACCURACY_75_PPM
// <5=> NRF_CLOCK_LF_ACCURACY_50_PPM
// <6=> NRF_CLOCK_LF_ACCURACY_30_PPM
// <7=> NRF_CLOCK_LF_ACCURACY_20_PPM
// <8=> NRF_CLOCK_LF_ACCURACY_10_PPM
// <9=> NRF_CLOCK_LF_ACCURACY_5_PPM
// <10=> NRF_CLOCK_LF_ACCURACY_2_PPM
// <11=> NRF_CLOCK_LF_ACCURACY_1_PPM
# ifndef NRF_SDH_CLOCK_LF_ACCURACY
# define NRF_SDH_CLOCK_LF_ACCURACY 7
# endif