nRF51822のSPIモジュール（マスター）機能の概要

Nordic社のBLEモジュールnRF51822のSPIモジュールの機能について解説したく思います。
ここではSPIはマスターとして使用する場合を考えます。
読者様はSPI機能については既に知っているという前提で以下の解説をします。

#モジュール機能のアドレス割り当て

nRF51822のSPIモジュールには、マスターとして機能するSPI0とSPI1とスレーブとして機能するSPIS1があります。

![image](https://qiita-image-store.s3.amazonaws.com/0/150620/a354bf9a-7ba6-eeb2-edb1-5a5a9bf32607.png)


SPI Slave機能のレジスタとSPI Master 1ch機能のレジスタとI2C 1ch機能のレジスタは同じアドレス0x40004000に割り当てられています。つまり、この3つの機能を同時に使うことはできません。

また、SPI Master 0ch機能のレジスタとI2C 0ch機能のレジスタは同じアドレス0x40003000に割り当てられています。つまり、この2つの機能を同時に使うことはできません。

ですがプログラムで機能を切り替えることで、1つのファームウェアで両方の機能が使えることを確認しています。
（切り替え方法については別途記事にする予定です。）

#設定レジスタ

![image](https://qiita-image-store.s3.amazonaws.com/0/150620/8407fd18-ba31-4bdf-f7ff-cd782421f04d.png)


#シーケンス

SPIマスター機能は、MISO信号、MOSI信号、SCLK信号を制御します。
SPI制御に必要なチップセレクト信号はユーザーがどのスレーブデバイスを使うか指定するためのものなので、SPIモジュールは自動制御しません。
ユーザーがGPIOモジュールを用いてON/OFF制御してください。

SPI通信は以下のシーケンスで実行されます。

![image](https://qiita-image-store.s3.amazonaws.com/0/150620/36b17071-7196-90e7-adc8-c55688f60d2f.png)


①TXDレジスタに1byte目を書き込むと、直後に2byte目もTXDレジスタに書き込まれ、1byte目の送信が始まる。

②データを送信すると同時に、RXDレジスタにデータを受信する。
　RXDレジスタを読み込むと、次のデータがRXDレジスタに格納される。

③READYイベントがRXDレジスタにデータを格納するたびに発生する。
　RXDレジスタを読み込まなければ、いつまで待ってもREADYイベントは発生せず、次のデータ送受信は開始されない。

④TXDレジスタに送信データがなくなると動作は完了する。

![image](https://qiita-image-store.s3.amazonaws.com/0/150620/d1dae362-1178-6f75-b070-100def00faef.png)

#波形
実際にSPI通信を行った時の波形を以下に示します。

![image](https://qiita-image-store.s3.amazonaws.com/0/150620/95c9220a-5ae8-f129-54d6-1a9eb74a8761.png)

#プログラム：SPI送受信処理

以下はNordic社が公開しているサンプルプログラムからの抜粋です。
https://devzone.nordicsemi.com/attachment/f3b250dd5a8d462aca54b23d4a4d2b2c

プログラム中でSPI送受信処理を行う場合は、この関数を使います。

```c
uint32_t spi_master_send_recv(const spi_master_hw_instance_t spi_master_hw_instance,											
                              uint8_t * const p_tx_buf, const uint16_t tx_buf_len,											
                              uint8_t * const p_rx_buf, const uint16_t rx_buf_len)											
{											
    #if defined(SPI_MASTER_0_ENABLE) || defined(SPI_MASTER_1_ENABLE)											
											
    volatile spi_master_instance_t * p_spi_instance = spi_master_get_instance(											
        spi_master_hw_instance);											
    APP_ERROR_CHECK_BOOL(p_spi_instance != NULL);											
											
    uint32_t err_code   = NRF_SUCCESS;											
    uint16_t max_length = 0;											
    											
    uint8_t nested_critical_region = 0;											
    											
    //Check if disable all IRQs flag is set //割込み無効処理				
    if (p_spi_instance->disable_all_irq)											
    {											
        //Disable interrupts.											
        APP_ERROR_CHECK(sd_nvic_critical_region_enter(&nested_critical_region));											
    }											
    else											
    {											
        //Disable interrupt SPI.											
        APP_ERROR_CHECK(sd_nvic_DisableIRQ(p_spi_instance->irq_type));											
    }											
											
    //Initialize and perform data transfer											
    if (p_spi_instance->state == SPI_MASTER_STATE_IDLE) //IDLE状態かの確認				
    {											
        max_length = (rx_buf_len > tx_buf_len) ? rx_buf_len : tx_buf_len; //最大送受信バイト数確認（送信データと受信データのどちらが多いかを確認して、多いほうに合わせる。）		
											
        if (max_length > 0)											
        {											
            p_spi_instance->state        = SPI_MASTER_STATE_BUSY; //IDLE状態からBUSY状態へ遷移			
            p_spi_instance->bytes_count  = 0; //送受信カウンタクリア			
            p_spi_instance->started_flag = false; //開始フラグクリア			
            p_spi_instance->max_length   = max_length; //最大送受信バイト数設定			
											
            /* Initialize buffers */											
            spi_master_buffer_init(p_tx_buf, //送信バッファ初期化			
                                   tx_buf_len,											
                                   &(p_spi_instance->p_tx_buffer),											
                                   &(p_spi_instance->tx_length),											
                                   &(p_spi_instance->tx_index));											
            spi_master_buffer_init(p_rx_buf, //受信バッファ初期化			
                                   rx_buf_len,											
                                   &(p_spi_instance->p_rx_buffer),											
                                   &(p_spi_instance->rx_length),											
                                   &(p_spi_instance->rx_index));											
											
            nrf_gpio_pin_clear(p_spi_instance->pin_slave_select); //CS信号アサート			
            spi_master_send_initial_bytes(p_spi_instance); //1byte目送信開始			
        }											
        else //最大送受信バイト数が負ならエラー									
        {											
            err_code = NRF_ERROR_INVALID_PARAM;											
        }											
    }											
    else //IDLE状態でないならば別の送受信処理実行中なのでBUSYエラー									
    {											
        err_code = NRF_ERROR_BUSY;											
    }											
    											
    //Check if disable all IRQs flag is set. //割込み有効処理				
    if (p_spi_instance->disable_all_irq)											
    {   											
        //Enable interrupts.											
        APP_ERROR_CHECK(sd_nvic_critical_region_exit(nested_critical_region));											
    }											
    else											
    {											
        //Enable SPI interrupt.											
        APP_ERROR_CHECK(sd_nvic_EnableIRQ(p_spi_instance->irq_type));											
    }											
											
    return err_code;											
    #else											
    return NRF_ERROR_NOT_SUPPORTED;											
    #endif											
}			
```

#プログラム：READYイベント割込み処理

```c
void SPI0_TWI0_IRQHandler(void)											
{											
    if ((NRF_SPI0->EVENTS_READY == 1) && (NRF_SPI0->INTENSET & SPI_INTENSET_READY_Msk))											
    {											
        NRF_SPI0->EVENTS_READY = 0; //イベントクリア							
											
        volatile spi_master_instance_t * p_spi_instance = spi_master_get_instance(SPI_MASTER_0); //インスタンスが0か1かを確認
											
        spi_master_send_recv_irq(p_spi_instance); //送受信割込み処理					
    }											
}
```

```c
static __INLINE void spi_master_send_recv_irq(volatile spi_master_instance_t * const p_spi_instance)											
{											
    APP_ERROR_CHECK_BOOL(p_spi_instance != NULL); //インスタンスが無効でないことの確認					
											
    p_spi_instance->bytes_count++; //送受信カウンタ累積					
	
//シーケンス図のAで実行される処理：初回READYイベント発生処理						
    if (!p_spi_instance->started_flag) //スタートフラグが0の場合					
    {											
        p_spi_instance->started_flag = true; //スタートフラグ アサート					
        spi_master_signal_evt(p_spi_instance,											
                              SPI_MASTER_EVT_TRANSFER_STARTED,											
                              p_spi_instance->bytes_count);											
    }											
											
    uint8_t rx_byte = p_spi_instance->p_nrf_spi->RXD; //RXDレジスタ用ローカル変数作成					
	
//シーケンス図のBで実行される処理：RXDレジスタ受信データ読み込み処理										
    if ((p_spi_instance->p_rx_buffer != NULL) && //RXDレジスタに受信データがある場合					
        (p_spi_instance->rx_index < p_spi_instance->rx_length))											
    {											
        p_spi_instance->p_rx_buffer[p_spi_instance->rx_index++] = rx_byte; //RXDレジスタ読み込み＆受信カウンタ累積		
    }											

//シーケンス図のCで実行される処理：TXDレジスタ送信データ格納処理											
    if (p_spi_instance->tx_index < p_spi_instance->max_length) //送信データがまだある場合				
    {											
        p_spi_instance->p_nrf_spi->TXD = ((p_spi_instance->p_tx_buffer != NULL) &&											
                                          (p_spi_instance->tx_index < p_spi_instance->tx_length)) ?											
                                         p_spi_instance->p_tx_buffer[p_spi_instance->tx_index] :											
                                         SPI_DEFAULT_TX_BYTE; //送信データorダミーデータ送信				
        (p_spi_instance->tx_index)++; //送信カウンタ累積						
    }											

//シーケンス図のDで実行される処理：最終READYイベント処理											
    if (p_spi_instance->bytes_count >= p_spi_instance->max_length)											
    {											
        nrf_gpio_pin_set(p_spi_instance->pin_slave_select); //CS信号ネゲート				
											
        uint16_t transmited_bytes = p_spi_instance->tx_index;											
											
        spi_master_buffer_release(&(p_spi_instance->p_tx_buffer), &(p_spi_instance->tx_length)); //送信バッファクリア
        spi_master_buffer_release(&(p_spi_instance->p_rx_buffer), &(p_spi_instance->rx_length)); //受信バッファクリア
											
        p_spi_instance->state = SPI_MASTER_STATE_IDLE;											
											
        spi_master_signal_evt(p_spi_instance, SPI_MASTER_EVT_TRANSFER_COMPLETED, transmited_bytes); //送受信完了処理
											
    }											
}
```