LoginSignup
2
4

More than 5 years have passed since last update.

nRF51822のSPIモジュール(マスター)機能の概要

Posted at

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

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

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

image

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

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

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

設定レジスタ

image

シーケンス

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

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

image

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

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

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

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

image

波形

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

image

プログラム:SPI送受信処理

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

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

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イベント割込み処理

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); //送受信割込み処理                    
    }                                           
}
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); //送受信完了処理

    }                                           
}
2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4