H8/3048CPUのDMAサンプルプログラム
(sygwin+GCC)

Copyright(C) 4Jan2003
coskx

【1】 はじめに

この文書はAKI-H8のDAMのサンプルプログラムを提示するものである。

サンプルプログラムのダウンロード

DownLoad

【2】 AKI-H8のDMAテストプログラム

DMAC(Direct Memory Access Control)を用いてSIN波形の出力を行なう。
DMACを用いるとタイマ割り込みを利用するより高速なDA出力ができる。
このDMAは,メモリ上に配置されたsin1周期分の波形テーブルからDAC出力(Digital Analogue Converter)レジスタ(メモリマップトなのであるアドレスのメモリのように見える)への逐次転送(リピートモード)である。

メモリ上に置かれた波形テーブルを一定時間ごとにDACに出力する方法は通常はタイマ割り込みが用いられる。
しかしタイマ割り込みでは,割り込み関数内でCPUがCPUのレジスタ経由でメモリ内のデータをADCに与えるため高速な動作はできない。これに対してDMACを用いると,タイマにより設定された時間間隔でCPUに休みが入り,DMAコントローラがメモリ内のデータをADCのアドレスに直接与えるため,高速な動作ができる。次のプログラム例では,1μsec周期のDAC出力(Digital Analogue Converter)をDMACを用いて行っているが,この周期の割り込みによるDAC出力は16MHz駆動のH8では難しい。

プログラム中ではDMACの初期化関数で,
(1)リピートモード
(2)ITU0による割り込み要求信号で起動
(3)ソースアドレスの設定(波形テーブルの先頭)
(4)ディスティネーションアドレスの設定(DAC出力用レジスタ)
(5)転送データ数(波形テーブルのデータ数)
を設定します。
DMACの設定が終わるとタイマー割り込み要求信号が発生するたびにDMACにより以下のことが行なわれます。
(1)ソースアドレスにあるデータ(波形データの1つ)1バイトをディスティネーションアドレス(DAC)へダイレクト転送
(2)ソースアドレスを+1
(3)転送データカウンタを−1
(4)転送データカウンタが0になったら,ソースアドレスと転送データカウンタを初期値に戻す

プログラム中ではITUch0を使用して,割り込みを利用しているように見えるが,割り込み関数がない。割り込み要因として生ずる信号をDMACが受け取り,DMA動作を起こしている。

DMAを用いたSIN波形の出力

/**********************************************************
タイマch0割り込みによってDMAch1をリピートモードで起動し,正弦波
をDACch0から出力する
このテストプログラム実行時にはDACch0をオシロスコープで観察すること
DAC出力は1μsec周期なので255μsec周期の正弦波が見えます(0V-5V)
**********************************************************/
#include "3048f.h"
#define startTimer0() (ITU.TSTR.BYTE |= 0x01) /* Timer CH0 スタート */

#define WAVEDATASIZE 255    /*波形テーブルの大きさ*/
/*振幅127の正弦波 (255分割)*/
const signed char SineTable[WAVEDATASIZE] = {
        0,    3,    6,    9,   12,   15,   18,   21,
       24,   27,   30,   34,   37,   39,   42,   45,
       48,   51,   54,   57,   60,   62,   65,   68,
       70,   73,   75,   78,   80,   83,   85,   87,
       90,   92,   94,   96,   98,  100,  102,  104,
      105,  107,  109,  110,  112,  113,  115,  116,
      117,  118,  119,  120,  121,  122,  123,  124,
      124,  125,  125,  126,  126,  126,  126,  126,
      126,  126,  126,  126,  126,  125,  125,  124,
      124,  123,  122,  122,  121,  120,  119,  118,
      116,  115,  114,  112,  111,  109,  108,  106,
      104,  103,  101,   99,   97,   95,   93,   91,
       88,   86,   84,   82,   79,   77,   74,   72,
       69,   66,   64,   61,   58,   55,   53,   50,
       47,   44,   41,   38,   35,   32,   29,   26,
       23,   20,   17,   14,   10,    7,    4,    1,
       -1,   -4,   -7,  -10,  -14,  -17,  -20,  -23,
      -26,  -29,  -32,  -35,  -38,  -41,  -44,  -47,
      -50,  -53,  -55,  -58,  -61,  -64,  -66,  -69,
      -72,  -74,  -77,  -79,  -82,  -84,  -86,  -88,
      -91,  -93,  -95,  -97,  -99, -101, -103, -104,
     -106, -108, -109, -111, -112, -114, -115, -116,
     -118, -119, -120, -121, -122, -122, -123, -124,
     -124, -125, -125, -126, -126, -126, -126, -126,
     -126, -126, -126, -126, -126, -125, -125, -124,
     -124, -123, -122, -121, -120, -119, -118, -117,
     -116, -115, -113, -112, -110, -109, -107, -105,
     -104, -102, -100,  -98,  -96,  -94,  -92,  -90,
      -87,  -85,  -83,  -80,  -78,  -75,  -73,  -70,
      -68,  -65,  -62,  -60,  -57,  -54,  -51,  -48,
      -45,  -42,  -39,  -37,  -34,  -30,  -27,  -24,
      -21,  -18,  -15,  -12,   -9,   -6,   -3
};
/*DAに出力する波形を格納する配列.中央値128とする.*/
unsigned char wave[WAVEDATASIZE];

void makeWaveTable(void)
{
    int i;
    for (i=0; i<WAVEDATASIZE; i++) wave[i]=SineTable[i]+128;
}

/* ------------------------------------------------- */
/* TIMER INITIALIZATION */
/* ------------------------------------------------- */
void initTimer0Int(unsigned short int period)
/*ITUによる割り込みタイマーの設定(越智君2001による)*/
/*割り込み間隔は引数peiodで単位はμsecである*/
/*値は32767以下でなければならない*/
/*32.767msecまで*/
{
    ITU0.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチでTCNTをクリア*/
    ITU0.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU0.TCR.BIT.TPSC=3;  /*内部クロックφ/8でカウント*/
    ITU0.GRA=2*period-1;     /*割り込みの周期をperiod[μs]に指定*/
    ITU0.TIER.BIT.OVIE=0; /*オーバーフロー,アンダーフロー発生時割り込みを禁止*/
    ITU0.TIER.BIT.IMIEB=0;  /*TCNT=GRBとなったときの割り込みを禁止*/
    ITU0.TIER.BIT.IMIEA=1;  /*TCNT=GRAとなったときの割り込み要求を許可*/
}

/* -------------------------------------------------
DMAch0の初期化 ITU0によってリピートモードで起動する
バイト転送
転送元はメモリ(フルアドレス)自動インクリメント
転送先はIOレジスタ(ショートアドレス)
初期化手順が大事 自分で初期化関数を作る時は注意すること
DTEを読み出してDTE=0を確保して初期化する
ETCRの上位・下位には同じカウンタ値を入れる

void *sourceHead   転送元アドレス(テーブルの先頭)
int number         転送データ数 255以下でなければならない
void *destination  転送先IOアドレス
int triger         0:ITU0, 1:ITU1, 2:ITU2, 3:AD, 4:SCI0TxE, 5:SCI0Rx,
                   6:7:ChAフルアドレスモード,ChB外部トリガ
------------------------------------------------- */
void initDMA0withITU0(void *sourceHead,int number,void *destination,int triger)
{
    while (DMAC1A.DTCR.BIT.DTE) DMAC1A.DTCR.BIT.DTE = 0;  /*DMA動作 1:許可, 0:禁止*/
    DMAC1A.MAR = sourceHead;
    DMAC1A.IOAR = (unsigned long int)destination & 0xff;
    DMAC1A.ETCR = number+(number<<8);  /*転送カウンタ.下位8bitに繰返数,上位8bitはカウンタ*/
    DMAC1A.DTCR.BIT.DTS = triger;  /*トリガ*/
    DMAC1A.DTCR.BIT.DTIE = 0;  /*転送終了で割り込み(1:許可, 0:禁止)*/
    DMAC1A.DTCR.BIT.RPE = 1;   /*RPE=1&DTIE=0:repeatMode, RPE=0:IOMode,RPE=1&DTIE=1:IdleMode*/
    DMAC1A.DTCR.BIT.DTID = 0;  /*転送後のアドレス 0:++, 1:--*/
    DMAC1A.DTCR.BIT.DTSZ = 0;  /*転送単位 0:Byte, 1:Word*/
    DMAC1A.DTCR.BIT.DTE = 1;   /*DMA動作 1:許可, 0:禁止*/
}

void initDAC()
{
    DA.CR.BYTE |= 0xc0;
}

main()
{
    initTimer0Int(1); /*時間割り込み1μsec ch0でDMA用*/
    makeWaveTable();
    initDMA0withITU0(wave,WAVEDATASIZE,(void *)&DA.DR0,0);
    initDAC();
    E_INT();        /*CPU割り込み許可*/
    startTimer0();  /*時間割り込みタイマスタートch0*/
    while(1);       /*なにもしないループ*/
}

ディジタルオシロで見たDACch0出力(松林先生による)

【3】 AKI-H8のDMAテストプログラム その2

DMA(Direct Memory Access)を用いてSIN波形の出力を行なう。
DMAの転送速度を遅くして,タイマ割り込み関数と,ADC,SCI通信によるホストへの表示を行なって,プログラムの検証を行なっている。
DAC出力はDMAによる転送を用い,DMA起動はITUch0を用いている。
このDAC出力はADC入力に直結して動作確認に用いることにした。
ADC入力はDMAと同じ周期の割り込みを用いている。この割り込みはITUch0を用いている。
ADC入力値はSCI1を通してホストPCのターミナルウィンドウ上に表示される。

DMAを用いたSIN波形の出力(ADCとSCIによるチェック付き)

/**********************************************************
タイマch0割り込みによってDMAch1をリピートモードで起動し,正弦波
をDACch0から出力する
この動作をチェックするためタイマch1割り込みによってADCch0から
入力し,SCIch1を通してホストPCのターミナルに表示する

このテストプログラム実行時にはDACch0とADCch0を直結すること

このテストプログラムでの転送は,チェックの為にSCI通信を行なってい
るため約2msec周期が最高である。SCI通信を止めてしまえば,DMAの性能
まで周期を短くできる

Copyright(C) 4Jan2003 KOSAKA TNCT
**********************************************************/
#include "3048f.h"
#include "h8_3048.h"

#define WAVEDATASIZE 255    /*波形テーブルの大きさ*/
/*振幅127の正弦波 (255分割)*/
const signed char SineTable[WAVEDATASIZE] = {
        0,    3,    6,    9,   12,   15,   18,   21,
       24,   27,   30,   34,   37,   39,   42,   45,
       48,   51,   54,   57,   60,   62,   65,   68,
       70,   73,   75,   78,   80,   83,   85,   87,
       90,   92,   94,   96,   98,  100,  102,  104,
      105,  107,  109,  110,  112,  113,  115,  116,
      117,  118,  119,  120,  121,  122,  123,  124,
      124,  125,  125,  126,  126,  126,  126,  126,
      126,  126,  126,  126,  126,  125,  125,  124,
      124,  123,  122,  122,  121,  120,  119,  118,
      116,  115,  114,  112,  111,  109,  108,  106,
      104,  103,  101,   99,   97,   95,   93,   91,
       88,   86,   84,   82,   79,   77,   74,   72,
       69,   66,   64,   61,   58,   55,   53,   50,
       47,   44,   41,   38,   35,   32,   29,   26,
       23,   20,   17,   14,   10,    7,    4,    1,
       -1,   -4,   -7,  -10,  -14,  -17,  -20,  -23,
      -26,  -29,  -32,  -35,  -38,  -41,  -44,  -47,
      -50,  -53,  -55,  -58,  -61,  -64,  -66,  -69,
      -72,  -74,  -77,  -79,  -82,  -84,  -86,  -88,
      -91,  -93,  -95,  -97,  -99, -101, -103, -104,
     -106, -108, -109, -111, -112, -114, -115, -116,
     -118, -119, -120, -121, -122, -122, -123, -124,
     -124, -125, -125, -126, -126, -126, -126, -126,
     -126, -126, -126, -126, -126, -125, -125, -124,
     -124, -123, -122, -121, -120, -119, -118, -117,
     -116, -115, -113, -112, -110, -109, -107, -105,
     -104, -102, -100,  -98,  -96,  -94,  -92,  -90,
      -87,  -85,  -83,  -80,  -78,  -75,  -73,  -70,
      -68,  -65,  -62,  -60,  -57,  -54,  -51,  -48,
      -45,  -42,  -39,  -37,  -34,  -30,  -27,  -24,
      -21,  -18,  -15,  -12,   -9,   -6,   -3
};
/*DAに出力する波形を格納する配列.中央値128とする.*/
unsigned char wave[WAVEDATASIZE];

void makeWaveTable(void)
{
    int i;
    for (i=0; i<WAVEDATASIZE; i++) wave[i]=SineTable[i]+128;
}

/* ------------------------------------------------- */
/* TIMER INITIALIZATION */
/* ------------------------------------------------- */
void initTimer0Int(unsigned short int period)
/*ITUによる割り込みタイマーの設定(越智君2001による)*/
/*割り込み間隔は引数peiodで単位はμsecである*/
/*値は32767以下でなければならない*/
/*32.767msecまで*/
{
    ITU0.TCR.BIT.CCLR=1;  /*GRAのコンペアマッチでTCNTをクリア*/
    ITU0.TCR.BIT.CKEG=0;  /*立ち上がりエッジでカウント*/
    ITU0.TCR.BIT.TPSC=3;  /*内部クロックφ/8でカウント*/
    ITU0.GRA=2*period-1;     /*割り込みの周期をperiod[μs]に指定*/
    ITU0.TIER.BIT.OVIE=0; /*オーバーフロー,アンダーフロー発生時割り込みを禁止*/
    ITU0.TIER.BIT.IMIEB=0;  /*TCNT=GRBとなったときの割り込みを禁止*/
    ITU0.TIER.BIT.IMIEA=1;  /*TCNT=GRAとなったときの割り込み要求を許可*/
}

/* -------------------------------------------------
DMAch0の初期化 ITU0によってリピートモードで起動する
バイト転送
転送元はメモリ(フルアドレス)自動インクリメント
転送先はIOレジスタ(ショートアドレス)
初期化手順が大事 自分で初期化関数を作る時は注意すること
DTEを読み出してDTE=0を確保して初期化する
ETCRの上位・下位には同じカウンタ値を入れる

void *sourceHead   転送元アドレス(テーブルの先頭)
int number         転送データ数 255以下でなければならない
void *destination  転送先IOアドレス
int triger         0:ITU0, 1:ITU1, 2:ITU2, 3:AD, 4:SCI0TxE, 5:SCI0Rx,
                   6:7:ChAフルアドレスモード,ChB外部トリガ
------------------------------------------------- */
void initDMA0withITU0(void *sourceHead,int number,void *destination,int triger)
{
    while (DMAC1A.DTCR.BIT.DTE) DMAC1A.DTCR.BIT.DTE = 0;  /*DMA動作 1:許可, 0:禁止*/
    DMAC1A.MAR = sourceHead;
    DMAC1A.IOAR = (unsigned long int)destination & 0xff;
    DMAC1A.ETCR = number+(number<<8);  /*転送カウンタ.下位8bitに繰返数,上位8bitはカウンタ*/
    DMAC1A.DTCR.BIT.DTS = triger;  /*トリガ*/
    DMAC1A.DTCR.BIT.DTIE = 0;  /*転送終了で割り込み(1:許可, 0:禁止)*/
    DMAC1A.DTCR.BIT.RPE = 1;   /*RPE=1&DTIE=0:repeatMode, RPE=0:IOMode,RPE=1&DTIE=1:IdleMode*/
    DMAC1A.DTCR.BIT.DTID = 0;  /*転送後のアドレス 0:++, 1:--*/
    DMAC1A.DTCR.BIT.DTSZ = 0;  /*転送単位 0:Byte, 1:Word*/
    DMAC1A.DTCR.BIT.DTE = 1;   /*DMA動作 1:許可, 0:禁止*/
}

/* ------------------------------------------------- */
/* AD CONVERT */
/* ------------------------------------------------- */
unsigned int inputADC(unsigned char ch)
{
    unsigned int tmp;
    AD.CSR.BYTE = ch;     /* ADC単一モード、 chの値は0,1,2,3のみ*/
    AD.CSR.BIT.ADST = 1;  /* start */
    while(AD.CSR.BIT.ADF == 0); /* AD変換終了待機 */
    AD.CSR.BIT.ADF = 0;
    tmp = (int)(*((volatile unsigned char *)(&AD.DRA)+ch*2  )<<2)
        + (int)(*((volatile unsigned char *)(&AD.DRA)+ch*2+1)>>6);
    return tmp;
}

void initDAC()
{
    DA.CR.BYTE |= 0xc0;
}

main()
{
    initSCI1(); /*SCI-ch1の初期化*/
    initTimer0Int(2000); /*時間割り込み2msec ch0でDMA用*/
    initTimer1Int(2000); /*時間割り込み2msec ch1でチェック用割り込みルーチン用*/
    makeWaveTable();
    initDMA0withITU0(wave,WAVEDATASIZE,(void *)&DA.DR0,0);
    initDAC();
    E_INT();        /*CPU割り込み許可*/
    startTimer01();  /*時間割り込みタイマスタートch0,ch1*/
    while(1);       /*なにもしないループ*/
}

/*タイマ割り込み関数*/
#pragma interrupt
void int_imia1() /*割り込みルーチン この名前はリンカスクリプトで決まっている*/
{
    int y;
    static int cnt=0;
    y=(inputADC(0)>>2)-128;
    SCI1_printf("%5d",y);
    if (((cnt++)&0x7)==0) SCI1_printf("\n");
    ITU1.TSR.BIT.IMFA=0; /*Clear IMFA これは自分の責任で行なう*/
}

/* 実行結果 255周期で動作しているのがわかる(ADC,DACによる約2のオフセットがあるため出力値と一致しない)
    2    5    8   11   14   17   20   23
   26   29   33   36   38   41   45   47
   50   53   56   59   61   64   67   69
   72   74   77   79   82   84   86   89
   91   93   95   97   99  101  103  104
  106  108  109  111  112  114  115  116
  117  118  119  120  121  122  123  123
  124  124  125  125  125  125  125  125
  125  125  125  125  124  124  123  123
  122  121  121  120  119  118  117  115
  114  113  111  110  108  107  105  103
  102  100   98   96   94   92   90   87
   85   83   81   78   76   73   71   68
   65   63   60   57   54   52   49   46
   43   40   37   35   31   28   25   22
   19   16   13    9    6    3    0   -2
   -5   -8  -11  -15  -18  -21  -24  -27
  -30  -33  -36  -39  -42  -45  -48  -51
  -54  -56  -59  -62  -65  -67  -70  -73
  -75  -78  -80  -83  -85  -87  -89  -92
  -94  -95  -98 -100 -101 -104 -105 -106
 -109 -109 -112 -112 -114 -115 -116 -118
 -119 -120 -121 -122 -122 -123 -124 -124
 -125 -125 -126 -126 -126 -126 -126 -126
 -126 -126 -126 -126 -125 -125 -124 -124
 -123 -122 -121 -120 -119 -118 -117 -116
 -115 -113 -113 -110 -110 -108 -106 -105
 -102 -101  -99  -97  -95  -93  -91  -88
  -86  -84  -81  -79  -76  -74  -71  -69
  -66  -63  -61  -58  -55  -52  -49  -46
  -43  -40  -38  -35  -31  -28  -25  -22
  -19  -16  -13  -10   -7   -4   -1    2
    5    8   11   14   17   20   23   26
   29   33   36   38   41   44   47   50
   53   56   59   61   64   67   69   72
   74   77   79   82   84   86   89   91
   93   95   97   99  101  103  104  106
  108  109  111  112  114  115  116  117
  118  119  120  121  122  123  123  124
  124  125  125  125  125  125  125  125
  125  125  125  124  124  123  123  122
  121  121  120  119  118  117  115  114
  113  111  110  108  107  105  103  102
  100   98   96   94   92   90   87   85
   83   81   78   76   73   71   68   65
   63   60   57   54   52   49   46   43
   40   38   34   32   28   25   22   19
   16   13    9    6    3    0   -2   -5
   -8  -11  -15  -18  -21  -24  -27  -30
  -33  -36  -39  -42  -45  -48  -51  -54
  -56  -59  -62  -65  -67  -70  -73  -75
  -78  -80  -83  -85  -87  -89  -91  -94
  -96  -98 -100 -102 -104 -105 -107 -109
 -110 -112 -112 -114 -115 -116 -118 -119
 -120 -121 -122 -122 -123 -124 -124 -125
 -125 -126 -126 -126 -126 -126 -126 -126
 -126 -126 -126 -125 -125 -124 -124 -123
 -122 -121 -120 -119 -118 -117 -116 -115
 -113 -112 -110 -110 -107 -106 -105 -103
 -101  -99  -97  -95  -93  -91  -88  -86
  -84  -81  -79  -76  -74  -71  -69  -66
  -63  -61  -58  -55  -52  -49  -46  -43
  -40  -38  -35  -31  -28  -25  -22  -19
  -16  -13  -10   -7   -4   -1    2    5
*/