概要
SPIとは
dsPIC33CHシリーズにはSPIモジュールがマスターコアに2ch、スレーブコアに1ch搭載されています。
今回はこのSPIの動作を確認してみます。
SPIとは
SPIとは「Serial Peripheral Interface」の頭文字をとった用語で、主に基板内でデバイス同士の通信を行う規格です。シリアルの名前通り比較的接続ラインが少なく済むデータ転送方式。
ペリフェラルの構造
dsPIC33C系のSPIモジュールブロックの概略図は以下の通りです。

ハードウェア構成とタイミング
今回のハードウェア構成はSPI1の出力を内部でエコーバックしSPI2でデータを返送する構成としました
- SCCP1モジュールは100kHz周期のトリガを生成
- DMA0はSCCP1のトリガタイミングでRAMからSPI1TXに順次データを転送
- SPI1は自動でデータを送信します。SPI1は内部でSPI2と接続
- SPI2は受信割り込み後に受信したデータをエコーバック

レジスタ
SPI関連のレジスタは以下の12種類です。
| レジスタ名 | 機能 | 説明 |
| SPIxCON1L | SPIx Control Register 1 Low | SPIx制御レジスタL 主にSPIモードやピンの有効化など主要な設定を行う |
| SPIxCON1H | SPIx Control Register 1 High | SPIx制御レジスタH 主にオーディオコーデックなどの設定を行う |
| SPIxCON2L | SPIx Control Register 2 Low | 可変長ビット幅の設定 |
| SPIxSTATL | SPIx Status Register Low | SPIxステータスレジスタL 送受信ステータスや割り込みの有効化など設定を行う |
| SPIxSTATH | SPIx Status Register High | SPIxステータスレジスタH 拡張モードにおいて送受信バッファのカウントを示す |
| SPIxBUFL SPIxBUFH | SPIx Buffer Register Low/High | SPIx送受信バッファレジスタL/H このレジスタを介しデータの送受信を行う |
| SPIxBRGL | SPIx Baud Rate Generator Register Low | SPIxボーレートレジスタ 送受信のボーレートを設定する |
| SPIxIMSKL SPIxIMSKH | SPIx Interrupt Mask Register Low/ High | SPIx割り込みマスクレジスタL/H SPI割り込みの有効化レジスタ |
| SPIxURDTL SPIxURDTH | SPIx Underrun Data Register Low/ High | SPIxアンダーランレジスタ |
ソースコード
今回はエンハンストモードで動作させています。
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入力に入力されています



コメント