PIC32MX370F512F 64pin (４．１) SPI通信 with DMA

# DMAを使用して、SPI通信をハード制御で出力。

![IMG_5024.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/d2d6f772-e69b-9d07-70e5-6968207748c3.jpeg)

SPI16ビットIOｴｷｽﾊﾟﾝﾀﾞｰMCP23S17の通信をDMAを介して、通信させます。わずか送信４バイトですが、DMA使用時のコードがわかりやすいので、ひな型としてコードを載せておきます。
TFT液晶128×160の画面を、DMAを使って高速描画できたので、このひな形で行けるはずです。
DMAを使用しないSPI通信の記事は、PIC32MX370F512F 64pin (４) SPI通信で書いてありますので、まずDMAを使用しない場合で通信ができてから、DMAを使ってください。

![pin割り付け表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/c0611bde-98bc-b61a-27e8-d28b864b625d.png)



:::note

# DMAをSPI送信に使うための初期化
```C:DMA初期化
uint8_t txDATA1[10];//SPI送信バッファ
void DMA2Init(void)
{
    IEC2bits.DMA2IE = 0;   // 設定のためDMA2を一時停止させる
   
    DCH2CON = 0;
    DCH2ECON = 0;
    DCH2INT = 0;

    DCH2CONbits.CHPRI = 3;              // DMAチャネルの優先度(3)
    DCH2ECONbits.CHSIRQ = _TIMER_2_IRQ;   // DMAを開始する割り込み番号
    DCH2ECONbits.SIRQEN = 1;            // 割り込みによる転送を有効にする
    DCH2INTbits.CHBCIE = 1;             // DMA割り込みを有効にする
   
    DCH2SSA = KVA_TO_PA(&txDATA1);
    DCH2SSIZ = 4;//転送元全サイズ(byte)
    DCH2DSA = KVA_TO_PA(&SPI2BUF);
    DCH2DSIZ = 1;//転送先大きさ(byte)
    DCH2CSIZ = 1;//転送単位(byte)
    
    IFS2bits.DMA2IF = 0;   // 割り込みフラグのクリア
    IPC11bits.DMA2IP = 5;  // 割り込み優先度1:Lowest 7:Highest
    IPC11bits.DMA2IS = 3;  // 副割り込み優先度
    IEC2bits.DMA2IE = 1;   // DMA2割り込みを有効にする
    DCH2CONbits.CHEN = 1;               // DMA1を使用する
    DMACONbits.ON = 1;     // DMAモジュールを有効にする
}
```
DMA転送トリガーにTimer2の割り込みを使用します。
転送の開始は、
```C:DMA転送開始
    SPI2_Start();
    T2CONbits.ON=1;
```
**CSピンをLowにしてSPI通信を開始。Timer2をカウント開始させると、Timer2の割り込みがかかるたびに、
DMAのソースエリアから、ディスティネーションに１バイトずつ転送されます。**

**DCH2SSAで、転送元ソースをtxDATA1の配列先頭アドレスを指定。
DCH2DSAで、転送先ディスティネーションアドレスとしてSPI2BUFを指定。**

**転送元サイズ、転送先サイズ、転送単位を設定。**

これで、DMAの初期化は完了です。
:::

:::note
# DMAモジュールの割り込み関数。
```C:DMA割込み関数
void __ISR(_DMA2_VECTOR, IPL5AUTO) ISR_Dma2(void)
{     
    IFS2bits.DMA2IF = 0;        // DMA1割り込みフラグのクリア
    if(DCH2INTbits.CHBCIF == 1)
    {
        LATBbits.LATB1=~LATBbits.LATB1;//LED
        SPI2_Stop();//CS is High
        T2CONbits.ON=0;//Timer2 interrupt stoped.
        DCH2SSA = KVA_TO_PA(&txDATA1);
        DCH2CONbits.CHEN = 1;   // DMA1を有効にする
        
        DCH2INTbits.CHBCIF = 0; // ブロック終了割り込みフラグのクリア
        
        DCH2ECONbits.SIRQEN = 1;
    }
    
}
```
**指定したソースエリアのサイズ分の送信が完了し、
DMA転送が終了すると、上記割込み関数が実行されます。**
今回は、４バイト送信の完了で、この割り込みが発生します。

CHBCIFビットは、転送ブロックの転送完了割込みのフラグです。
ブロック転送(4byte)が完了した状態です。

SPIのCSピンをHighにもどして、SPI通信を終了させます。

Timer2をOFFにして、転送割込みトリガを止めています。

ソースアドレスにtxDATA1を再設定します。

これで、再度Timer2を稼働させるまで、DMA転送は停止状態になります。
:::

# main.c
```main.c
....<略>
void main(void) 
{
    uint8_t val,length,val1;
    uint8_t txt[10];
    unsigned int counter=0x0000;
    uint32_t address;
    unsigned int tmp_CP0_Status; // Temporary register for CP0 reg storing 
    
    //oscillator init---------------------------------
    SYSKEY = 0x0;           // ensure OSCCON is locked
    SYSKEY = 0xAA996655;    // Write Key1 to SYSKEY
    SYSKEY = 0x556699AA;    // Write Key2 to SYSKEY
    //PLL Output Divisor bits
    OSCCONbits.PLLODIV=0b000;   //000 = PLL output divided by 1
    //PeripheralBusClock diviser.
    OSCCONbits.PBDIV=0b00;  //SYSCLK divided by1
    SYSKEY = 0x0;           // ensure OSCCON is locked
    
    //port init---------------------------------------
    portInit();
    
    //peripheral initializing-------------------------
    timer1Init();
    timer2Init();
    I2C1_Init();
    LCD_Init(LcdDeviceAdd);
    I2C1BRG=0x003D;//600khz Fp=80Mhz
    LCD_Printf(LcdDeviceAdd,"PIC32MX",7,0x80);
    usartInit();
    __delay_ms(10);//これを入れないとazが出力されない。
    putchar('a');putchar('z');putchar('\r');
    DMA2Init();
    Spi2Init();
    
    MCP23S17Init(MCP23S17_deviceAdd1);
    //MCP23Sdebug(MCP23S17_deviceAdd1);
    //interrupt initializing-------------------------
    printf("\nPIC32MX370F512HT\n");
    
    //割り込み関連
    address=_CP0_GET_EBASE();//例外ベクタアドレス
    printf("EBASE=%lx\n",address);
    INTCONbits.MVEC=1;
    tmp_CP0_Status=_CP0_GET_STATUS();
    tmp_CP0_Status|=0x00000001;//CP0.STATUS.IE=1で割り込み許可
    _CP0_SET_STATUS(tmp_CP0_Status);
    // __builtin_enable_interrupts();
   
    val1=0x01;
    while(1)
    {
        if(rxU1.completed)
        {
            rxU1.completed=false;
            printf("%s\r",rxU1.buf);            
            rxU1.length=0;
            IEC1bits.U1RXIE=1;
        }
        
        if(tm1.fg)
        {
            tm1.fg=false;
            
            LATBbits.LATB0=~LATBbits.LATB0;
            I2C1_b1Write(0x4A,val);//PCF8574

            //DMA ソース転送元データの書き込み
            //MCP23S17は、シーケンシャル書き込み
            txDATA1[0]=MCP23S17_deviceAdd1;
            txDATA1[1]=0x14;
            txDATA1[2]=val1;
            txDATA1[3]=val1++;
            
            SPI2_Start();//SPI通信スタート
            T2CONbits.ON=1;//DMA転送開始
            
            if(val==0x55)
                val=0xAA;
            else
                val=0x55;
            length=sprintf(txt,"%8ld",counter++);
            LCD_Printf(LcdDeviceAdd,txt,length,0xC0);
            
            
            IEC0bits.T1IE=1;
        }
    }
    
    
    return;
}

```
```C:Peripheral.c
//Fpb=80Mhz 0.025us * 2 * 10 = 0.5us
void timer2Init(void)
{
    T2CON=0x00;
    TMR2=0x00;
    PR2=0x000A;
    T2CONbits.T32=0;//16bitsTimer
    T2CONbits.TCKPS=0b001;//1:2 prescale 
    T2CONbits.TCS=0;//internal peripheral clock
    IFS0bits.T2IF=0;
    IPC2bits.T2IP=1;
    IPC2bits.T2IS=0;
    IEC0bits.T2IE=0;
    T2CONbits.ON=0;
}


uint8_t txDATA1[10];
void DMA2Init(void)
{
    IEC2bits.DMA2IE = 0;   // 設定のためDMA2を一時停止させる
   
    DCH2CON = 0;
    DCH2ECON = 0;
    DCH2INT = 0;

    DCH2CONbits.CHPRI = 3;              // DMAチャネルの優先度(3)
    DCH2ECONbits.CHSIRQ = _TIMER_2_IRQ;   // DMAを開始する割り込み番号
    DCH2ECONbits.SIRQEN = 1;            // 割り込みによる転送を有効にする
    DCH2INTbits.CHBCIE = 1;             // DMA割り込みを有効にする
   
    DCH2SSA = KVA_TO_PA(&txDATA1);
    DCH2SSIZ = 4;
    DCH2DSA = KVA_TO_PA(&SPI2BUF);
    DCH2DSIZ = 1;
    DCH2CSIZ = 1;
    
    IFS2bits.DMA2IF = 0;   // 割り込みフラグのクリア
    IPC11bits.DMA2IP = 5;  // 割り込み優先度1:Lowest 7:Highest
    IPC11bits.DMA2IS = 3;  // 副割り込み優先度
    IEC2bits.DMA2IE = 1;   // DMA2割り込みを有効にする
    DCH2CONbits.CHEN = 1;               // DMA1を使用する
    DMACONbits.ON = 1;     // DMAモジュールを有効にする
    
}
```

```C:interrupt.c
void __ISR(_DMA2_VECTOR, IPL5AUTO) ISR_Dma2(void)
{     
    IFS2bits.DMA2IF = 0;        // DMA1割り込みフラグのクリア
    if(DCH2INTbits.CHBCIF == 1)
    {
        LATBbits.LATB1=~LATBbits.LATB1;//LED
        SPI2_Stop();//CS is High
        T2CONbits.ON=0;//Timer2 interrupt stoped.
        DCH2SSA = KVA_TO_PA(&txDATA1);
        DCH2CONbits.CHEN = 1;   // DMA1を有効にする
        
        DCH2INTbits.CHBCIF = 0; // ブロック終了割り込みフラグのクリア
        
        DCH2ECONbits.SIRQEN = 1;
    }
    
}
```