DMAを使用して、SPI通信をハード制御で出力。
SPI16ビットIOエキスパンダーMCP23S17の通信をDMAを介して、通信させます。わずか送信4バイトですが、DMA使用時のコードがわかりやすいので、ひな型としてコードを載せておきます。
TFT液晶128×160の画面を、DMAを使って高速描画できたので、このひな形で行けるはずです。
DMAを使用しないSPI通信の記事は、PIC32MX370F512F 64pin (4) SPI通信で書いてありますので、まずDMAを使用しない場合で通信ができてから、DMAを使ってください。
DMAをSPI送信に使うための初期化
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の割り込みを使用します。
転送の開始は、
SPI2_Start();
T2CONbits.ON=1;
CSピンをLowにしてSPI通信を開始。Timer2をカウント開始させると、Timer2の割り込みがかかるたびに、
DMAのソースエリアから、ディスティネーションに1バイトずつ転送されます。
DCH2SSAで、転送元ソースをtxDATA1の配列先頭アドレスを指定。
DCH2DSAで、転送先ディスティネーションアドレスとしてSPI2BUFを指定。
転送元サイズ、転送先サイズ、転送単位を設定。
これで、DMAの初期化は完了です。
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転送が終了すると、上記割込み関数が実行されます。
今回は、4バイト送信の完了で、この割り込みが発生します。
CHBCIFビットは、転送ブロックの転送完了割込みのフラグです。
ブロック転送(4byte)が完了した状態です。
SPIのCSピンをHighにもどして、SPI通信を終了させます。
Timer2をOFFにして、転送割込みトリガを止めています。
ソースアドレスにtxDATA1を再設定します。
これで、再度Timer2を稼働させるまで、DMA転送は停止状態になります。
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;
}
//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モジュールを有効にする
}
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;
}
}