SPIモジュールの使い方

SPIモジュールの使い方

概要

SPIとは

dsPIC33CHシリーズにはSPIモジュールがマスターコアに2ch、スレーブコアに1ch搭載されています。
今回はこのSPIの動作を確認してみます。

SPIとは

SPIとは「Serial Peripheral Interface」の頭文字をとった用語で、主に基板内でデバイス同士の通信を行う規格です。シリアルの名前通り比較的接続ラインが少なく済むデータ転送方式。

ペリフェラルの構造

dsPIC33C系のSPIモジュールブロックの概略図は以下の通りです。

(fig.1)SPIモジュールブロック概略図

ハードウェア構成とタイミング

今回のハードウェア構成はSPI1の出力を内部でエコーバックしSPI2でデータを返送する構成としました

  1. SCCP1モジュールは100kHz周期のトリガを生成
  2. DMA0はSCCP1のトリガタイミングでRAMからSPI1TXに順次データを転送
  3. SPI1は自動でデータを送信します。SPI1は内部でSPI2と接続
  4. SPI2は受信割り込み後に受信したデータをエコーバック
(fig.2)SPI送受信テストブロック

レジスタ

SPI関連のレジスタは以下の12種類です。

レジスタ名機能説明
SPIxCON1LSPIx Control Register 1 LowSPIx制御レジスタL
主にSPIモードやピンの有効化など主要な設定を行う
SPIxCON1HSPIx Control Register 1 HighSPIx制御レジスタH
主にオーディオコーデックなどの設定を行う
SPIxCON2LSPIx Control Register 2 Low可変長ビット幅の設定
SPIxSTATLSPIx Status Register LowSPIxステータスレジスタL
送受信ステータスや割り込みの有効化など設定を行う
SPIxSTATHSPIx Status Register HighSPIxステータスレジスタH
拡張モードにおいて送受信バッファのカウントを示す
SPIxBUFL
SPIxBUFH
SPIx Buffer Register Low/HighSPIx送受信バッファレジスタL/H
このレジスタを介しデータの送受信を行う
SPIxBRGL
SPIx Baud Rate Generator Register LowSPIxボーレートレジスタ
送受信のボーレートを設定する
SPIxIMSKL
SPIxIMSKH
SPIx Interrupt Mask Register Low/ HighSPIx割り込みマスクレジスタL/H
SPI割り込みの有効化レジスタ
SPIxURDTL
SPIxURDTH
SPIx Underrun Data Register Low/ HighSPIxアンダーランレジスタ
(fig.3)SPI関連レジスタ

ソースコード

今回はエンハンストモードで動作させています。
1ワード受信した後に割り込みを発生させるためには、以下のような設定が必要です。

SPI2IMSKHbits.RXWIEN = 1u;  /* 受信ウォータマスク割り込み有効化 */
SPI2IMSKHbits.RXMSK = 1u;  /* 1Byte受信したら割り込み発生 */
IPC7bits.SPI2RXIP = 3u;     /* SPI2受信割り込み優先度 */
IEC1bits.SPI2RXIE = 1u;     /* SPI2受信割り込み有効化 */
INTCON2bits.GIE = 1u;       /* 全割り込み有効化 */
コンフィグレーション設定について

コンフィグレーション設定についてはコンフィグレーション設定に記載しております。

コピーして下記のソースコードの「 //ここにコンフィグレーション設定を挿入する// 」の位置に挿入してください。

クロック設定について

クロック設定用関数 vds_Main_Init_Clock_Register(); のソースコードはクロック設定のページに記載しております。

コピーして下記のソースコードの「 //ここにクロック設定ソースをコピペする// 」の位置に挿入してください。

/*------------------------------------------------------------------------------*/
/* <Chapter>        CHAPTER_2_9_4_SPI */
/* <Function>       SPIモジュールの使い方 */
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* コンフィグレーション設定*/
/*------------------------------------------------------------------------------*/
//ここにコンフィグレーション設定を挿入する//
/*------------------------------------------------------------------------------*/
/* インクルードファイル*/
/*------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
/*------------------------------------------------------------------------------*/
/* 定数定義*/
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* プロトタイプ宣言*/
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/*変数定義*/
/*------------------------------------------------------------------------------*/
unsigned int u2g_SPISendData[] = 
{
2047 ,2212 ,2376 ,2538	,2696	,
2849	,2997	,3136	,3268	,3390	,
3502	,3603	,3692	,3768	,3832	,
3882	,3918	,3939	,3947	,3939	,
3918	,3882	,3832	,3768	,3692	,
3603	,3502	,3390	,3268	,3136	,
2997	,2849	,2696	,2538	,2376	,
2212	,2047	,1881	,1717	,1555	,
1397	,1244	,1097	,957	,825	,
703	,591	,490	,401	,325	,
261	,211	,175	,154	,147	,
154	,175	,211	,261	,325	,
401	,490	,591	,703	,825	,
957	,1097	,1244	,1397	,1555	,
1717	,1881
};

unsigned int RxData;
/*------------------------------------------------------------------------------*/
/* クロック設定 */
/*------------------------------------------------------------------------------*/
//ここにクロック設定ソースを挿入する//
/*------------------------------------------------------------------------------*/
/* Main関数 */
/*------------------------------------------------------------------------------*/
int main(int argc, char** argv) 
{
    unsigned long Timer;
    unsigned int TxState;
    unsigned int RxState;
    /*------------------------------------------------------------------------*/
    /* クロック初期化*/
    /*------------------------------------------------------------------------*/
        vds_Main_Init_Clock_Register();     /* クロック初期化 */
    /*------------------------------------------------------------------------*/
    /* リマッパブルピン初期化*/
    /*------------------------------------------------------------------------*/
        RPOR18bits.RP69R = 6u;        /* SPI1_SCK = RP69 */
        RPOR19bits.RP71R = 5u;        /* SPI1_SDO = RP71*/
        RPINR20bits.SDI1R = 42u;      /* SPI1_SDI = RP42 */
        RPOR7bits.RP46R	= 7u;         /* SPI1_SS = RP46 */
        
        RPINR22bits.SCK2R = 69u;      /* SCK2_SCK = RP69 */
        RPINR22bits.SDI2R = 71u;      /* SDI2_SDI = RP71 */
        RPOR5bits.RP42R = 8u;         /* SPI2_SDO = RP42*/
        RPINR23bits.SS2R = 46u;       /* SPI2_SS = RP46 */
    /*------------------------------------------------------------------------*/
    /* SPI*/
    /*------------------------------------------------------------------------*/ 
        SPI1CON1L	= 0x0000u;	
            SPI1CON1Lbits.ENHBUF = 1u;	/* 拡張バッファ イネーブルビット */
            SPI1CON1Lbits.SPIFE	= 0u;	/* フレーム同期パルスエッジ選択ビット */
            SPI1CON1Lbits.MCLKEN = 0u;	/* マスタークロック有効化ビット */  // 1
            SPI1CON1Lbits.DISSCK = 0u;	/* SCKx ピン ディセーブル ビット */
            SPI1CON1Lbits.DISSDI = 0u;	/* SDIx ピン ディセーブル ビット */
            SPI1CON1Lbits.MSTEN	= 1u;	/* マスタモード イネーブルビット */
            SPI1CON1Lbits.CKP	= 0u;	/* クロック極性選択ビット */
            SPI1CON1Lbits.SSEN	= 0u;	/* スレーブ選択イネーブルビット */
            SPI1CON1Lbits.CKE	= 0u;	/* SPIx クロックエッジ選択ビット */
            SPI1CON1Lbits.SMP	= 0u;	/* SPIx データ入力サンプルフェイズ ビット */
            SPI1CON1Lbits.MODE16 = 1u;	/* 16Bit選択ビット */
            SPI1CON1Lbits.MODE32 = 0u;	/* 32Bit選択ビット */
            SPI1CON1Lbits.DISSDO = 0u;	/* SDOx ピン ディセーブル ビット */
            SPI1CON1Lbits.SPISIDL = 0u;	/* アイドルモード時停止ビット */
            SPI1CON1Lbits.SPIEN	= 0u;	/* SPIx イネーブルビット */
        SPI1CON1H	= 0x0000u;	 
            SPI1CON1Hbits.MSSEN = 1u;
        SPI1CON2L	= 0x0000u;	
        SPI1CON3	= 0x0000u;	   
        SPI1CON2H	= 0x0000u;	 
        SPI1CON4	= 0x0000u;	
        SPI1STATL	= 0x0000u;	
        SPI1STATH	= 0x0000u;	
        SPI1BUFL	= 0x0000u;	
        SPI1BUFH	= 0x0000u;	
        SPI1BRGL	= 4u;
        SPI1BRGH	= 0x0000u;
        
        SPI1CON1Lbits.SPIEN	= 1u;	/* SPIx イネーブルビット */
        
        SPI2CON1L	= 0x0000u;	
            SPI2CON1Lbits.ENHBUF = 1u;	/* 拡張バッファ イネーブルビット */
            SPI2CON1Lbits.SPIFE	= 0u;	/* フレーム同期パルスエッジ選択ビット */
            SPI2CON1Lbits.MCLKEN = 0u;	/* マスタークロック有効化ビット */  // 1
            SPI2CON1Lbits.DISSCK = 0u;	/* SCKx ピン ディセーブル ビット */
            SPI2CON1Lbits.DISSDI = 0u;	/* SDIx ピン ディセーブル ビット */
            SPI2CON1Lbits.MSTEN	= 0u;	/* マスタモード イネーブルビット */
            SPI2CON1Lbits.CKP	= 0u;	/* クロック極性選択ビット */
            SPI2CON1Lbits.SSEN	= 1u;	/* スレーブ選択イネーブルビット */
            SPI2CON1Lbits.CKE	= 0u;	/* SPIx クロックエッジ選択ビット */
            SPI2CON1Lbits.SMP	= 0u;	/* SPIx データ入力サンプルフェイズ ビット */
            SPI2CON1Lbits.MODE16 = 1u;	/* 16Bit選択ビット */
            SPI2CON1Lbits.MODE32 = 0u;	/* 32Bit選択ビット */
            SPI2CON1Lbits.DISSDO = 0u;	/* SDOx ピン ディセーブル ビット */
            SPI2CON1Lbits.SPISIDL = 0u;	/* アイドルモード時停止ビット */
            SPI2CON1Lbits.SPIEN	= 0u;	/* SPIx イネーブルビット */
        SPI2CON1H	= 0x0000u;	 
        SPI2CON2L	= 0x0000u;	
        SPI2CON3	= 0x0000u;	   
        SPI2CON2H	= 0x0000u;	 
        SPI2CON4	= 0x0000u;	
        SPI2STATL	= 0x0000u;	
        SPI2STATH	= 0x0000u;	
        SPI2BUFL	= 0x0000u;	
        SPI2BUFH	= 0x0000u;	
        SPI2BRGL	= 4u;
        SPI2BRGH	= 0x0000u;
        SPI2IMSKL   = 0x0000u;
        SPI2IMSKH   = 0x0000u;
            SPI2IMSKHbits.RXWIEN = 1u;  /* 受信ウォータマスク割り込み有効化 */
            SPI2IMSKHbits.RXMSK = 1u;  /* 1Byte受信したら割り込み発生 */
        SPI2CON1Lbits.SPIEN	= 1u;	/* SPIx イネーブルビット */
        IPC7bits.SPI2RXIP = 3u;         /* SPI2受信割り込み優先度 */
        IEC1bits.SPI2RXIE = 1u;         /* SPI2受信割り込み有効化 */
        
        INTCON2bits.GIE = 1u;           /* 全割り込み有効化 */
 
    /*------------------------------------------------------------------------*/
    /* CCP1初期化*/
    /*------------------------------------------------------------------------*/
        CCP1CON1L = 0x0000u;
        CCP1CON1H = 0x0000u;
        CCP1CON2H = 0x0000u;
        CCP1CON3H = 0x0000u;
        CCP1PRL	= 0u;
        CCP1PRH	= 500u;
        CCP1RB	= 0u;
        CCP1RA	= 0u;
    /*------------------------------------------------------------------------*/    
    /* DMA初期化 */		
    /*------------------------------------------------------------------------*/
        DMAH = 0xC000u;                     /* DMA転送最大アドレス */
        DMAL = 0x850u;                      /* DMA転送最小アドレス */
        DMACON = 0x0000u;
            DMACONbits.DMAEN = 1u;          /* DMAモジュール有効化 */
            DMACONbits.PRSSEL = 1u;         /* ラウンドロビン方式 */
        DMACH0 = 0x0000u;
            DMACH0bits.SAMODE = 1u;         /* 送信元アドレスをインクリメントする */
            DMACH0bits.DAMODE = 0u;         /* 受信元アドレスは何も変わらない */
            DMACH0bits.TRMODE = 1u;         /* 繰り返し、ワンショットモード */
            DMACH0bits.RELOAD = 1u;         /* 自動でリロードする */
            DMACH0bits.SIZE = 0u;           /* 16bit転送モード */
        DMAINT0 = 0x0000u;
            DMAINT0bits.CHSEL = 0x01;       /* トリガイベント = SCCP1 */
  
        DMASRC0	= (unsigned int)&u2g_SPISendData; /* 送信元データは cu2l_Data[] */
        DMADST0	= (unsigned int)&SPI1BUFL;    /* 送信先はCCP1RAレジスタ */
        DMACNT0	= 19u;                      /* 送信カウントは20(19 + 1)回 */

        DMACH0bits.CHEN = 1u;               /* DMA0チャンネルを有効にする */
        IFS0bits.DMA0IF = 0u;
        
    /*------------------------------------------------------------------------*/
    /* 有効化*/
    /*------------------------------------------------------------------------*/
        CCP1CON1Lbits.CCPON = 1u;
    /*------------------------------------------------------------------------*/
    /* メインルーチン*/
    /*------------------------------------------------------------------------*/
        while(1)
        {
        }
}

void __attribute__((interrupt, no_auto_psv)) _SPI2RXInterrupt(void)
{
    
    while (SPI2STATLbits.SPIRBE == 0)
    {
        RxData = SPI2BUFL;
        SPI2BUFL = RxData;
    }

    IFS1bits.SPI2RXIF = 0;
}

結果

1ワード受信ごとに割り込みを発生させ、読み取った値をエコーバックしています。そのためSPI1のSDOデータが1周期遅れてSDI入力に入力されています

(fig.4)送受信波形 (Ch0 = SPI1 SCK , Ch1 = SPI1 SDO , Ch2 = SPI1 SDI , Ch4 = SPI1 SDO)

参考

コメント

タイトルとURLをコピーしました