【dsPIC33FJ128MC802】PMP接続のDMA転送(連続転送)でaitendoの液晶を使ってみる。

2021年2月22日


◆dsPIC33FJ128MC802でaitendoの1.44インチTFT液晶ZY-FGD1442701V1-PCBをPMP接続でDMA転送してみます。

パクる気満々で色々ググってもPICマイコンでPMPのDMA転送をやられている方が
いらっしゃらないようで・・・挙句には自分のサイトが出てきてしまいました。
これはチャレンジするしかない!
以前の作成した記事PMPのDMA転送をつかってみる。
aitendoの1.44インチ液晶TFT(ZY-FGD)をPMP接続で使ってみる!を組み合わせて
ちょっと変えれば出来ます。
その他マイクロチップ社のリファレンスマニュアルを参考にしました。

PMPの設定

PMPのリファレンスDS70299のP28の35-5にDMAサポートの記載がちょろっとあります。
PMP_DMAのサポート

割り込み要求ビット

PMMODEbits.IRQM = 0b01;

 PMP書き込み後に割り込み発生をかけます。
 実際にはこの機能を利用して割り込みフラグをチェックしてDMA転送完了を知ります。
 ほかの機能は前回と同様なので省きます。

DMAの設定

 dsPIC33Fには8チャンネルあります。
 今回はチャンネル0を使用します。
 DMA0CONレジスタの各ビットを設定していきます。

データ転送サイズビット

DMA0CONbits.SIZE = 1;

 ”0”の場合は「word転送」(2byte)
 ”1”の場合は「byte転送」
 今回は1のbyte転送で設定します。

データ転送方向ビットDIR

DMA0CONbits.DIR = 0;

 "0″の場合は「peripheralアドレスから読みだしてRAMアドレスに書き込む。」
 "1″の場合は「RAMアドレスから読みだしてperipheralアドレスに書き込む。」
 PMPにDMA転送なので本BITはRAM→peripheralの"1″に設定します。

データ転送割り込みビット

DMA0CONbits.HALF = 0;

 "0″の場合は「全データ転送されたら割り込みが発生します。」
 "1″の場合は「データの半分が転送されたら割り込み発生します。」
 全部転送したら割り込みたいので"0″に設定します。

動作モードビット

DMA0CONbits.MODE = 0b01;

 よくわからないので単純な転送のワンショットモードにします。

「DMA0CNT = 255」

 転送したbyte数を入れます。ただし「0」は「1byte」なのでn-1で設定します。
 最大で9bit=1024byte設定可能です。

RAMアドレスの設定

unsigned int tex_buf[256]attribute((space(dma)));

 転送バッファーを用意します。サイズはunsigned int型です。DS70215のpage18を参照。

peripheralアドレスの設定

DMA0PAD = (volatile unsigned int)&PMDIN1;

 peripheralAddressを指示します。

⑧DMA転送開始Addressを指示

DMA0STA = __builtin_dmaoffset(tex_buf);

 DMA転送開始Addressを指示します。

DMA転送ルーチン

 ①PMADDRbits.ADDR0をセットします。
 ②tex_buf[]にデータを入れる。
 ③DMA0CONbits.CHENをセットしDMA0をENABLEにします。
 ④DMA0REQbits.FORCEをセットし強制転送を開始します。
 ⑤勝手にWRはパタパタします。
 ⑥ADDR0を固定すれば連続書き込みが可能!!
 ⑦CPUを介さないDMA転送といえど書き込み完了を待つ必要があります。
 超重要:IFS0bits.DMA0IFをチェックして転送が完了したか否かを判断します。
 これがわからずに1日費やしてしまいました。

PMPのDMA設定部のソース

//== DMA_PMP ========================================================
DMA0CON = 0x0000;    //clear
DMA0CONbits.SIZE = 1;    //<14>byte_send
DMA0CONbits.DIR = 1;                //<13>write_mode
DMA0CONbits.HALF = 0;    //<12>full_block
DMA0CONbits.MODE = 0b01;            //<1-0>one_shot
DMA0CNT = 255;          //block_size_254byte
DMA0REQbits.IRQSEL = 0b0101101;         //PMP_peripheral
DMA0PAD = (volatile unsigned int)&PMDIN1; //peripheral_address
DMA0STA = __builtin_dmaoffset(tex_buf);      //DMA_start_address
DMA0CONbits.CHEN = 0;    //dma_disable

ソース

LCDの初期化等は省きます。aitendoのままです。
ブルーバック後、任意の個所に赤ラインを描画します。
LCDに256byte連続でDMA転送してます。

//== interrupt_function_prototype ================================================
void __attribute__((interrupt,auto_psv)) _DMA0Interrupt(void);  //DMA0_Interrupt
//== DMA_PMP ====================================================================
unsigned int tex_buf[256]__attribute__((space(dma)));  //dma_buffer
unsigned char *st_pointer;      //line_pointer
unsigned char st_box[256];      //line_data
//== main ==================================================================
int main(void)
{
//== クロックの設定 ======================================================
//== Fcy=Fosc/2=7.37M*((PLLFBD+2)/(N2*N1))/2=39.61MHz ================
PLLFBDbits.PLLDIV = 41;   //M=PLLFBD+2
CLKDIVbits.PLLPOST = 0;   //N2=2
CLKDIVbits.PLLPRE  = 0;   //N1=2
OSCTUN = 0;            //TuneFRC:7.37MHz
RCONbits.SWDTEN = 0;            //Disable Watch Dog
while(OSCCONbits.LOCK != 1);    //wait for PLL Lock
//== AD切り替え ==========================================================
AD1PCFGL = 0xffff;      //全digital
//=== TRISA ===========================================================
TRISA = 0x0000;            //initial_
//== TRISB =============================================================
TRISB = 0x0000;            //input:
//== PMP_Initialize ===================================================
PMMODE = 0x0000;
PMMODEbits.MODE = 0b11;      //<9-8>MastarMode_1
PMMODEbits.IRQM = 0b01;      //<15-14>dma_interrupt_request
PMCON = 0x0000;
//== port_select ====
//== R/W:PMRD/PMWR ==
PMCONbits.PTRDEN = 1; //<8>PMRD/PMWR_enable
PMCONbits.RDSP = 1;      //<0>PMRD/PMWR
//== RS:PMA0 ========
PMAENbits.PTEN0 = 1;    //<0>PMA0_enable
//== CN_Initialize ===================================================
//== DMA_PMP ========================================================
DMA0CON = 0x0000;   //clear
DMA0CONbits.SIZE = 1;  //<14>byte_send
DMA0CONbits.DIR = 1;        //<13>write_mode
DMA0CONbits.HALF = 0;  //<12>full_block
DMA0CONbits.MODE = 0b01;    //<1-0>one_shot
DMA0CNT = 255;        //block_size_254byte
DMA0REQbits.IRQSEL = 0b0101101;            //PMP_peripheral
DMA0PAD = (volatile unsigned int)&PMDIN1; //peripheral_address
DMA0STA = __builtin_dmaoffset(tex_buf);      //DMA_start_address
//== 前処理 =====================================================
PMCONbits.PMPEN = 1;        //<15>PMP_enable
DMA0CONbits.CHEN = 0;  //dma_disable
zy_init();          //sb1602_init()
st_pointer = &st_box[0];    //first_byte_set
zy_full_set(0x00,0x1f);      //blueback
//== while文 ===========================================================
while(1)
{
zy_line(st_pointer);            //line_out
}//while(1)
}//int main(void)
//== set ===================================================
unsigned char st_box[256] =
{
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,
0xf8,0x00,0xf8,0x00,0xf8,0x00
};
//==========================================================
void zy_line(unsigned char *byte)
{
zy_write(0,0x2a); //x_address
zy_write(1,0x00); //start
zy_write(1,0x02);
zy_write(1,0x00); //end
zy_write(1,0x81);
zy_write(0,0x2b); //y_address
zy_write(1,0x00); //start
zy_write(1,0x0f);
zy_write(1,0x00); //end
zy_write(1,0x0f);
zy_write(0,0x2c); //memory_write
//check_pin
LATAbits.LATA0 = !LATAbits.LATA0;
//array_copy
memcpy(tex_buf,byte,sizeof(tex_buf));
//pmp+dma_sending
PMADDRbits.ADDR0 = 1;   //write_mode
IFS0bits.DMA0IF = 0;        //flag_off
//force_dma_one_times
DMA0CONbits.CHEN = 1;  //DMA0_enable
DMA0REQbits.FORCE = 1;    //One_times
//dma_sending_wait
while(IFS0bits.DMA0IF == 0);    //send_wait
//check_pin
LATAbits.LATA0 = !LATAbits.LATA0;
}//void zy_line()
//======================================================================
//== zy_fgd Write  0:cmd/1:data ====================================================
void zy_write(unsigned flag, unsigned char data)
{
//command_mode
if(flag == 0)
{PMADDRbits.ADDR0 = 0;}   //command_mode/cd_low
//data_mode
else{PMADDRbits.ADDR0 = 1;}      //data_mode
//PMP
while(PMMODEbits.BUSY == 1);    //PMP_OK?
PMDIN1 = data;
}//void zy_write(unsigned flag, unsigned char data)

オシロスコープにて確認

黄色がWRの挙動です。ピンクはRA1に接続してます。1ライン転送毎に切り替えてます。
1ライン分256byteDMA転送するのに19.34μsecかかり、(1byte転送は大体50nsecでした。)
DMA転送バッファーにコピーするのに13.12μsecかかりました。
TOTALで32.84μsecかかりました。PMP接続と比較すると2.5倍速くなりました。

PICマイコンのPMP接続によるDMA転送の様子

実行の様子

PMP接続の時と見た目は変わりません。

aitendoの1.44インチ液晶TFT(ZY-FGD)PMP接続DMA転送1ライン出力

回路図

PMP接続の時と見た目は変わりません。

LCDをPMP接続によるDMA転送の回路図


よろしければバナーをクリックお願いします!