nRF52840 - BLE機能の有効化

# 目的

BLE機能の有効化

# 前置き

BLE機能が無効化されたプロジェクトから  
BLE機能が使えるところまでは確認できたが、以下手順が全て正しいとは限らない。  

SDKバージョンは16.0、SoftDeviceはS140を使用。  
SDK内のBLE機能が実装されたサンプルコード、  
`examples/ble_central/ble_app_blinky_c`等の設定を参考にする。  

SoftDeviceによって使用が禁止、制限されるペリフェラルがあるため、  
現行のファームウェアが使用しているペリフェラルと競合するかをまず確認しておく。  
以下参照。  
[ System on Chip resource requirements -> Hardware peripherals](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s140%2FSDS%2Fs1xx%2Fsd_resource_reqs%2Fhw_block_interrupt_vector.html&cp=5_7_4_0_6_0)


# 手順

## 1. プリプロセッサ定義の追加

SoftDeviceの有効化するためプリプロセッサ定義（プロジェクトファイルの`c_preprocessor_definitions`）に以下を追加。  

```
c_preprocessor_definitions="(省略)... ;S140;SOFTDEVICE_PRESENT;NRF_SD_BLE_API_VERSION=7"
```
## 2. インクルードフォルダの追加

上記手順で有効にしたコンパイルスイッチ`SOFTDEVICE_PRESENT`によってBLE関連のコードを参照するようになるため、  
サンプルコードを参考にインクルードフォルダ（プロジェクトファイルの`c_user_include_directories`）を追加。  

## 3. SDKコンフィグ設定

SDKコンフィグ（sdk_config.h）のBLE機能に関する各種設定をサンプルを参考に取り込む。  
`BLE_`の付いた設定項目はほとんど取り込んだ。  

## 4. リンカ設定にセクション追加

リンカ設定（flash_placement.xml）のBLE機能に関するセクションをサンプルを参考に取り込む。  
以下セクションは追加必須と思われる。  
`.sdh_soc_observers`, `.sdh_ble_observers`, `.sdh_state_observers`, `.sdh_stack_observers`, `.sdh_req_observers`  

## 5. プロジェクトにCファイル追加

上記手順を実行した辺りでコンパイルを実行すると関数の未定義エラーが発生する。  
その関数が定義されたCファイルがSDK内`components`以下に存在するはずなのでプロジェクトに追加する。  

SDKコンフィグ（sdk_config.h）に`BLE_LBS_ENABLED`のような機能の有効/無効フラグがあり、  
このフラグによりCファイルの中身が無効となっており、関数の未定義エラーが発生している場合もある。  
その場合はフラグを有効に変更する。  

## 6. SoftDeviceの書込み準備

SoftDeviceを書き込むために、  
SoftDeviceの書き込まれるFlash領域と、SoftDeviceが使用するRAM領域を空ける必要がある。  

S140の場合、SDK内の`components/softdevice/s140/doc/s140_nrf52_7.0.1_release-notes.pdf`に、  
以下のように記載がある。

```
Flash: 156 kB (0x27000 bytes)
RAM: 5.6 kB (0x1678 bytes). This is the minimum required memory. The actual requirements depend on the configuration chosen at sd_ble_enable() time.
```

SoftDeviceはFlash領域の0x00アドレスから、RAM領域の0x00アドレスからを使用するため、  
リンカ設定の定義値  
`FLASH_START`を0x27000に変更、  
`FLASH_SIZE`を元の値 - 0x27000の値に変更、  
`RAM_START`を元の値 + 0x3000程度の値に変更（BLE設定次第で5.6KB以上使用するため適当に多めに領域を確保する）、  
`RAM_SIZE`を元の値 - 0x3000の値に変更する。  

※ BLEのコンフィグ変更後にメモリアクセス異常等が発生する場合、SoftDeviceに割り当てたRAM領域の不足が考えられる。  

その他開始アドレスを指定しているセクションがある場合は、  
上記でずらした分だけアドレスを変更しておく。

## 7. SoftDeviceの書込み

プロジェクトファイル内の`debug_additional_load_file`にSoftDeviceのhexファイルを指定する。  
指定することでファームウェアと一緒に書き込まれる。  

```
debug_additional_load_file="../../../../../../components/softdevice/s140/hex/s140_nrf52_7.0.1_softdevice.hex"
```

## 8. 使用ペリフェラルの変更

前置きでも記載したが、  
SoftDeviceによって使用が禁止、制限されるペリフェラル（[ System on Chip resource requirements -> Hardware peripherals](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s140%2FSDS%2Fs1xx%2Fsd_resource_reqs%2Fhw_block_interrupt_vector.html&cp=5_7_4_0_6_0)）があるため、  
競合している場合は、使用するペリフェラルを変更する必要がある。  
対応方法はペリフェラルごとに異なる。  

使用が禁止されているペリフェラルを使用した場合、  
[Q&A](https://devzone.nordicsemi.com/f/nordic-q-a/59253/err-softdevice-invalid-memory-access)にあるように、  
`app_error_weak.c`の`app_error_fault_handler`がコールされ、  
idが`NRF_FAULT_ID_APP_MEMACC`で、infoが0x00000800となる。  
しかし、複数エラーが発生していることも当然考えられるので、  
infoが0x00000800でないからといって使用禁止ペリフェラルを使用していないとは限らない。

### TIMERの場合

TIMERやRTC等は、TIMER0、RTC0が使用禁止されているが、TIMER1、RTC1は使用可能なので、  
SDKコンフィグ（sdk_config.h）の
`TIMER0_ENABLED`、`NRFX_TIMER0_ENABLED`を無効化(0)し、  
`TIMER1_ENABLED`、`NRFX_TIMER1_ENABLED`を有効化(1)する。  
また、以下のようにインスタンスを定義している箇所があるはずなので、インスタンス番号を変更する。  

```c
//const nrf_drv_timer_t timer1 = NRF_DRV_TIMER_INSTANCE(0);
const nrf_drv_timer_t timer1 = NRF_DRV_TIMER_INSTANCE(1);
```

### NVMCの場合

フラッシュアクセスに使用するNVMCは使用が制限されており、  
[こちらのQ&A](https://devzone.nordicsemi.com/f/nordic-q-a/66320/nrf52832-disable-and-enable-softdevice)では、NVMCの使用は推奨しないので、
変わりにfstorageまたはFDSを使用することを薦めている。  
fstorage、FDSのサンプルコードも用意されており、対応は可能だが実装変更が必要となってしまう。  


### POWERの場合

powerの初期化関数に以下の実装があり、  
SoftDevice（SOFTDEVICE_PRESENT）有効時の対処が入っているので、  
おそらくSoftDeviceを意識せずに使用できると思われる。  
ただし実装を見ての通り、SoftDeviceが有効状態（nrf_sdh_is_enabled()）の場合はpowerの初期化に失敗する。  
これは、SoftDeviceにてpowerは初期化されているからだと思われる。  

```c
ret_code_t nrf_drv_power_init(nrf_drv_power_config_t const * p_config)
{
    if (m_initialized)
    {
        return NRF_ERROR_MODULE_ALREADY_INITIALIZED;
    }

#ifdef SOFTDEVICE_PRESENT
    if (nrf_sdh_is_enabled())
    {
        return NRF_ERROR_INVALID_STATE;
    }
#endif
    ...
```

## 9. ペリフェラル初期化順番確認

上記手順8のPOWERの場合で記載しているが、  
SoftDeviceの初期化（ble_stack_init()）と他のペリフェラル初期化順番に制限があるものがあるため注意する。