概要
以下の記事に記載しましたが、従来Microchipの32bitマイコンはMIPSコアを採用したPIC32Mシリーズ(MK,MM,MX,MZ)が主流でした。最近ではCortex-Mシリーズを搭載したPIC32Cシリーズが続々と新登場しています。これは他のマイコンメーカの採用実績が裏付けるように、世界的な流れとして当然であると思われ、今後PIC32Mシリーズの新しいMCUは発売されないかも知れません。
PIC32Cシリーズに関して – ぴくおの電子工作的な何かWP (electricpico.com)
そんな中はありますが、今回はPIC32MZのDMA転送について紹介します。
環境
今回はMikroElektronikaから発売されている「MINI-32 for PIC32ボード」(PIC32MZ1024EFH064-I/MR搭載) MINI-32 for PIC32MZ – compact PIC32MZ development board | MikroElektronika を使用します。

MPLAB X IDEは v6.15、コンパイラはXC32 V4.35を使用します。
ハードウェア構成と制御ブロック
DMAモジュールは以下の通りとなっており、複数のDMAチャンネルの転送要求をアービターが調整しROM、RAM、ペリフェラルレジスタ間のデータをCPUの介在内なしに実行します。

レジスタとコンフィグレーション
DMAモジュールのレジスタは以下のとおりです。
| レジスタ名(x=1~8) | 機能 |
| DMACON | DMA制御レジスタ |
| DMASTAT | DMAステータスレジスタ |
| DMAADDR | DMAアドレスレジスタ |
| DCRCCON | DMA CRC制御レジスタ |
| DCRCDATA | DMA CRCデータレジスタ |
| DCRCXOR | DMA CRCXORレジスタ |
| DCHxCON | DMAチャンネルx制御レジスタ |
| DCHxECON | DMAチャンネルxイベント制御レジスタ |
| DCHxINT | DMAチャンネルx割り込み制御レジスタ |
| DCHxSSA | DMAチャンネルx転送元スタートアドレスレジスタ |
| DCHxDSA | DMAチャンネルx転送先スタートアドレスレジスタ |
| DCHxSSIZ | DMAチャンネルx転送元サイズレジスタ |
| DCHxDSIZ | DMAチャンネルx転送先サイズレジスタ |
| DCHxSPTR | DMAチャンネルx転送元ポインタレジスタ |
| DCHxDPTR | DMAチャンネルx転送先ポインタレジスタ |
| DCHxCSIZ | DMAチャンネルxセルサイズレジスタ |
| DCHxDAT | DMAチャンネルxパターンデータレジスタ |
ソースコード(変数間コピー)
(図3)のように、1回の転送トリガで複数バイトの変数のコピーを行うためには、DCHxSSIZ、DCHxDSIZ、DCHxCSIZを同一の値に設定します。

| 項目 | 値 |
| 転送モード | 基本転送モード |
| 転送元アドレス(DCHxSSA) | virt_to_phys(&srcData1[0]) |
| 転送元サイズ(DCHxSSIZ) | 32 |
| 転送先アドレス(DCHxDSA) | virt_to_phys(&dstData[0]) |
| 転送先サイズ(DCHxDSIZ) | 32 |
| セル転送サイズ(DCHxCSIZ) | 32 |
char __attribute__((coherent)) srcData1[512];
char __attribute__((coherent)) dstData[512];
#define ConvertToPhysicalAddress(a) ((uint32_t)KVA_TO_PA(a))
#define ConvertToVirtualAddress(a) PA_TO_KVA1(a)
int main ( void )
{
int i,cycle ;
/* Initialize all modules */
__builtin_disable_interrupts();
/* Configure Prefetch, Wait States and ECC */
PRECONbits.PREFEN = 3;
PRECONbits.PFMWS = 3;
CFGCONbits.ECCCON = 3;
__builtin_enable_interrupts();
for ( i=0;i<32 ;i++)
{
srcData1[i] = i & 0xff;
dstData[i] = 0x0a;
}
IEC1CLR=0x00010000;
IFS1CLR=0x00010000;
DMACONbits.ON = 1u; //DMA有効化
DCH0CONbits.CHPIGN = 0u; //チャンネル レジスタ データビット
DCH0CONbits.CHBUSY = 0u; //チャンネル ビジービット
DCH0CONbits.CHPIGNEN = 0u;//パターンマッチ バイト無視イネーブルビット
DCH0CONbits.CHAED = 0u;//パターン長ビット
DCH0CONbits.CHCHNS = 0u;//チェーン チャンネル選択ビット
DCH0CONbits.CHEN = 0u;//チャンネル イネーブルビット
DCH0CONbits.CHAED = 0u;//無効中チャンネル イベント検出ビット
DCH0CONbits.CHCHN = 0u;//チャンネル チェーン イネーブルビット
DCH0CONbits.CHAEN = 0u;//チャンネル自動有効化ビット
DCH0CONbits.CHEDET = 0u;//チャンネル イベント検出ビット
DCH0CONbits.CHPRI = 3u; //チャンネル優先度ビット
DCH0ECONbits.CHAIRQ = 0x00u;//チャンネル転送中止 IRQ ビット
DCH0ECONbits.CHSIRQ = 0x00u;//チャンネル転送開始 IRQ ビット
DCH0ECONbits.CFORCE = 0u;//DMA 転送開始ビット
DCH0ECONbits.CABORT = 0u;//DMA 転送中止ビット
DCH0ECONbits.PATEN = 0u;//チャンネル パターンマッチ転送中止イネーブルビット
DCH0ECONbits.SIRQEN = 0u;//チャンネル転送開始 IRQ イネーブルビット
DCH0ECONbits.AIRQEN = 0u;//チャンネル中止 IRQ イネーブルビット
DCH0SSA = virt_to_phys(&srcData1[0]);
DCH0DSA = virt_to_phys(&dstData[0]);
DCH0SSIZ = 32; // source size 200 bytes
DCH0DSIZ = 32; // destination size 200 bytes
DCH0CSIZ = 32; // 200 bytes transferred per event
DCH0INTbits.CHSDIE = 0u;//チャンネルソース完了割り込みイネーブルビット
DCH0INTbits.CHSHIE = 0u;//チャンネルソース 1/2 エンプティ割り込みイネーブルビット
DCH0INTbits.CHDDIE = 0u;//チャンネル デスティネーション完了割り込みイネーブルビット
DCH0INTbits.CHDHIE = 0u;//チャンネル デスティネーション 1/2 フル割り込みイネーブルビット
DCH0INTbits.CHBCIE = 0u;//チャンネル ブロック転送完了割り込みイネーブルビット
DCH0INTbits.CHCCIE = 0u;//チャンネルセル転送完了割り込みイネーブルビット
DCH0INTbits.CHTAIE = 0u;//チャンネル転送中止割り込みイネーブルビット
DCH0INTbits.CHERIE = 0u;//チャンネル アドレスエラー割り込みイネーブルビット
DCH0INTbits.CHSDIF = 0u;//チャンネルソース完了割り込みフラグビット
DCH0INTbits.CHSHIF = 0u;//チャンネルソース 1/2 エンプティ割り込みフラグビッ
DCH0INTbits.CHDDIF = 0u;//チャンネル デスティネーション完了割り込みフラグビット
DCH0INTbits.CHDHIF = 0u;//チャンネル デスティネーション 1/2 フル割り込みフラグビット
DCH0INTbits.CHBCIF = 0u;//チャンネル ブロック転送完了割り込みフラグビット
DCH0INTbits.CHCCIF = 0u;//チャンネル セル転送完了割り込みフラグビット
DCH0INTbits.CHTAIF = 0u;//チャンネル転送中止割り込みフラグビット
DCH0INTbits.CHERIF = 0u;// チャンネル アドレスエラー割り込みフラグビット
DCH0CONbits.CHEN = 1u;//チャンネル イネーブルビット
DCH0ECONbits.CFORCE = 1u;//DMA 転送開始ビット
cycle = 0;
while(DCH0INTbits.CHCCIF == 0)
{
cycle ++;
}
while(1)
{
;
}
}
ソースコード(変数→ペリフェラル)
DMA転送の別の例として、OCモジュールのPWMを用いた疑似D/A変換を行います。今回は図4のように、TIMER2とOC1を組み合わせ、OCの出力をローパスフィルタにかける事でPWM波形をアナログ電圧にします。
TIMER2のコンペアマッチで自身の周期をリセットし、DMAに転送トリガを与えます。この転送トリガにより、4バイトのソースデータの値がOC1RS(Duty)レジスタにコピーされます。次の転送トリガでは、次の4バイトのソースデータが転送されます。256バイトの転送が完了すると、先頭のデータから再度転送が行われます。

uint32_t __attribute__((coherent)) srcData1[64] =
{
100 ,
109 ,
119 ,
128 ,
137 ,
146 ,
155 ,
162 ,
170 ,
176 ,
182 ,
187 ,
191 ,
194 ,
197 ,
198 ,
199 ,
198 ,
197 ,
194 ,
191 ,
187 ,
182 ,
176 ,
170 ,
162 ,
155 ,
146 ,
137 ,
128 ,
119 ,
109 ,
100 ,
90 ,
80 ,
71 ,
62 ,
53 ,
44 ,
37 ,
29 ,
23 ,
17 ,
12 ,
8 ,
5 ,
2 ,
1 ,
1 ,
1 ,
2 ,
5 ,
8 ,
12 ,
17 ,
23 ,
29 ,
37 ,
44 ,
53 ,
62 ,
71 ,
80 ,
90
};
int main ( void )
{
/*-------------------------------------------------------*/
/* 割り込み禁止 */
/*-------------------------------------------------------*/
__builtin_disable_interrupts();
/*-------------------------------------------------------*/
/* ロック */
/*-------------------------------------------------------*/
SYSKEY = 0x00000000;
SYSKEY = 0xAA996655;
SYSKEY = 0x556699AA;
/*-------------------------------------------------------*/
/* PMD / PPS 設定 */
/*-------------------------------------------------------*/
CFGCONbits.PMDLOCK = 0;
PMD1 = 0x1001;
PMD2 = 0x3;
PMD3 = 0x1ff01ff;
PMD4 = 0x1ff;
PMD5 = 0xfeffffff;
PMD6 = 0xfffff0ff;
PMD7 = 0xffffffef;
PMD3bits.OC1MD = 0;
PMD4bits.T2MD = 0;
CFGCONbits.PMDLOCK = 1;
CFGCONbits.IOLOCK = 0;
CNPUG = 0xFFFF;
/* PPS Output Remapping */
RPD0R =12; //OC1
/* Lock back the system after PPS configuration */
CFGCONbits.IOLOCK = 1;
/*-------------------------------------------------------*/
/* アンロック */
/*-------------------------------------------------------*/
SYSKEY = 0x33333333;
/*-------------------------------------------------------*/
/* Configure Prefetch, Wait States and ECC */
/*-------------------------------------------------------*/
PRECONbits.PREFEN = 3;
PRECONbits.PFMWS = 3;
CFGCONbits.ECCCON = 3;
/*-------------------------------------------------------*/
/* ポート設定 */
/*-------------------------------------------------------*/
ANSELBCLR = 0xFFFF; /* Digital Mode Enable */
TRISEbits.TRISE0 = 0;
TRISEbits.TRISE1 = 0;
TRISFbits.TRISF3 = 0;
LATFbits.LATF3 = 1;
TRISDbits.TRISD0 = 0;
LATDbits.LATD0 = 0;
/*-------------------------------------------------------*/
/* 全割り込み有効化 */
/*-------------------------------------------------------*/
__builtin_enable_interrupts();
IEC1CLR=0x00010000;
IFS1CLR=0x00010000;
/*-------------------------------------------------------*/
/* TIMER2設定 */
/*-------------------------------------------------------*/
PR2 = 200;
T2CON = 0x00000000; //T2ON
/*-------------------------------------------------------*/
/* OC設定 */
/*-------------------------------------------------------*/
OC1RS = 100;
OC1R = 100;
OC1CON = 0x0006; //OC1 ON,PWMMode
T2CONSET = 0x8000;
OC1CONSET = 0x8000;
/*-------------------------------------------------------*/
/* DMA設定 */
/*-------------------------------------------------------*/
DMACONbits.ON = 1u; //DMA有効化
DCH0CONbits.CHPIGN = 0u; //チャンネル レジスタ データビット
DCH0CONbits.CHBUSY = 0u; //チャンネル ビジービット
DCH0CONbits.CHPIGNEN = 0u;//パターンマッチ バイト無視イネーブルビット
DCH0CONbits.CHAED = 0u;//パターン長ビット
DCH0CONbits.CHCHNS = 0u;//チェーン チャンネル選択ビット
DCH0CONbits.CHEN = 0u;//チャンネル イネーブルビット
DCH0CONbits.CHAED = 0u;//無効中チャンネル イベント検出ビット
DCH0CONbits.CHCHN = 0u;//チャンネル チェーン イネーブルビット
DCH0CONbits.CHAEN = 1u;//チャンネル自動有効化ビット
DCH0CONbits.CHEDET = 0u;//チャンネル イベント検出ビット
DCH0CONbits.CHPRI = 3u; //チャンネル優先度ビット
DCH0ECONbits.CHAIRQ = 0x00u;//チャンネル転送中止 IRQ ビット
DCH0ECONbits.CHSIRQ = 0x09u;//チャンネル転送開始 IRQ ビット
DCH0ECONbits.CFORCE = 0u;//DMA 転送開始ビット
DCH0ECONbits.CABORT = 0u;//DMA 転送中止ビット
DCH0ECONbits.PATEN = 0u;//チャンネル パターンマッチ転送中止イネーブルビット
DCH0ECONbits.SIRQEN = 1u;//チャンネル転送開始 IRQ イネーブルビット
DCH0ECONbits.AIRQEN = 0u;//チャンネル中止 IRQ イネーブルビット
DCH0SSA = virt_to_phys(&srcData1[0]);
DCH0DSA = virt_to_phys((const void*)&OC1RS); //ok 0x1F840010
DCH0SSIZ = 256;
DCH0DSIZ = 4;
DCH0CSIZ = 4;
DCH0INTbits.CHSDIE = 0u;//チャンネルソース完了割り込みイネーブルビット
DCH0INTbits.CHSHIE = 0u;//チャンネルソース 1/2 エンプティ割り込みイネーブルビット
DCH0INTbits.CHDDIE = 0u;//チャンネル デスティネーション完了割り込みイネーブルビット
DCH0INTbits.CHDHIE = 0u;//チャンネル デスティネーション 1/2 フル割り込みイネーブルビット
DCH0INTbits.CHBCIE = 0u;//チャンネル ブロック転送完了割り込みイネーブルビット
DCH0INTbits.CHCCIE = 0u;//チャンネルセル転送完了割り込みイネーブルビット
DCH0INTbits.CHTAIE = 0u;//チャンネル転送中止割り込みイネーブルビット
DCH0INTbits.CHERIE = 0u;//チャンネル アドレスエラー割り込みイネーブルビット
DCH0INTbits.CHSDIF = 0u;//チャンネルソース完了割り込みフラグビット
DCH0INTbits.CHSHIF = 0u;//チャンネルソース 1/2 エンプティ割り込みフラグビッ
DCH0INTbits.CHDDIF = 0u;//チャンネル デスティネーション完了割り込みフラグビット
DCH0INTbits.CHDHIF = 0u;//チャンネル デスティネーション 1/2 フル割り込みフラグビット
DCH0INTbits.CHBCIF = 0u;//チャンネル ブロック転送完了割り込みフラグビット
DCH0INTbits.CHCCIF = 0u;//チャンネル セル転送完了割り込みフラグビット
DCH0INTbits.CHTAIF = 0u;//チャンネル転送中止割り込みフラグビット
DCH0INTbits.CHERIF = 0u;// チャンネル アドレスエラー割り込みフラグビット
DCH0CONbits.CHEN = 1u;//チャンネル イネーブルビット
DCH0ECONbits.CFORCE = 1u;//DMA 転送開始ビット
while(1)
{
}
return ( EXIT_FAILURE );
}
参考文献
- MINI-32 for PIC32MZ – compact PIC32MZ development board | MikroElektronika
- 回路図 mini-32-pic32-manual-v101.pdf (mikroe.com)
- データシート
- リファレンスマニュアル(En) Section 31. DMA Controller (microchip.com)
- リファレンスマニュアル(JP)dsPIC33E FRM – Section #. Title (microchip.com)
- MagnascanによるPixabayからの画像参照


コメント