DACとDMAモジュールの使い方(任意波形出力編)

DACモジュールの使い方

概要

DACOUTからは三角波やDC電圧以外にも任意波形が可能です。今回はオーソドックスなDDS(Direct Digital Synthesizer)方式でCPUのリソースを使用しない方式で出力してみます。

CPUリソースを消費させない方法として今回はDMAモジュールを使用します。dsPIC33CHシリーズにはDMAがマスターコアに6ch、スレーブコアに2ch搭載されています。

DDSとは

Direct Digital Synthesizerとは単一で固定の発振源から、任意の波形周波数をデジタル的に生成するための電子回路

ダイレクト・デジタル・シンセサイザ – Wikipediaより

DMAとは

DMAとはDirect Memory Accessの略でレジスタやメモリ間のデータ転送をCPUを介さずにできる仕組みの事

DMAの構成としては以下の通りです。

(fig.1)DMAモジュール DS70005371D-page 484より抜粋

dsPIC33Eシリーズなどは RAM→RAMやペリフェラルレジスタ→ ペリフェラルレジスタ が出来なかったのですが、dsPIC33Cシリーズに搭載されているDMAは全ての方向で可能となっています。そのためC言語で配列のコピーをする時にDMAを使えばCPUのリソースを使わずに済みます。

(fig.2)転送方向 DS70005371D-page 486より抜粋

ハードウェア構成と制御ブロック

周辺モジュールとしてDAC1とSCCP2、DMA0を使用します。

今回はRAMにSIN波のデータもしくは台形波のデータを格納し、SCCP2の間隔でそのデータを転送します。DMAの設定で全データを送信完了した場合、また先頭のデータから送信を開始させる設定にしていますので、CPUはデータの準備と開始トリガさえ打てば後は止めるまで何もする必要はありません。

(fig.3)全体構成

レジスタ

DMA関連のレジスタは以下の8種類

レジスタ名
(x = 1~6)
機能説明
DMAH DMA High Address
Limit Registers
DMA上限アドレスレジスタ
DMAL DMA Low Address
Limit Registers
DMA下限アドレスレジスタ
DMACON DMA ENGINE CONTROL
REGISTER
DMAエンジン制御レジスタ
DMACHxDMA CHANNEL n
CONTROL REGISTER
DMA チャンネル n 制御レジスタ
転送モードなどを指定
DMAINTxDMA CHANNEL n
INTERRUPT REGISTER
DMA チャンネル n 割り込み制御レジスタ
DMAに関する割り込みを設定
DMASRCxDMA Data Source Address
Pointer for Channel n
Register
DMA チャンネル n データ ソースアドレス ポインタ
転送元のアドレスを指定
DMADSTxDMA Data Destination
Source for
Channel n Register
DMA チャンネル n データ デスティネーション アドレス ポインタ
転送先のアドレスを指定
DMACNTxDMA Transaction Counter
for Channel n Register
DMA チャンネル n トランザクション カウンタ
何回データを転送するかを指定
(fig.5)調整レジスタ

ソースコード

波形の切り替えは以下のようにマクロの値を書き換えて下さい。

サイン波を出力する場合

#define ROM_TYPE        ROM_SINE

台形波を出力する場合

#define ROM_TYPE        ROM_TRAPEZOID
コンフィグレーション設定について

コンフィグレーション設定についてはコンフィグレーション設定に記載しております。

コピーして下記のソースコードの「 //ここにコンフィグレーション設定を挿入する// 」の位置に挿入してください。

クロック設定について

クロック設定用関数 vds_Main_Init_Clock_Register(); のソースコードはクロック設定のページに記載しております。

コピーして下記のソースコードの「 //ここにクロック設定ソースをコピペする// 」の位置に挿入してください。

/*------------------------------------------------------------------------------*/
/* @file      CMP_DAC_TRI.c */
/* @brief     DACOUTから三角波を出力 */
/* @details   GPIO(TRIS,LAT) */
/*            DAC(DACCTRL1L,DACCTRL2H,DACCTRL2L,DAC1CONL,DAC1DATH) */
/*            DAC(DAC1DATH,DAC1DATL,SLP1CONH,SLP1DAT) */
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* コンフィグレーション設定*/
/*------------------------------------------------------------------------------*/
//ここにコンフィグレーション設定を挿入する//
/*------------------------------------------------------------------------------*/
/* インクルードファイル*/
/*------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
/*------------------------------------------------------------------------------*/
/* 定数定義*/
/*------------------------------------------------------------------------------*/
#define ROM_SINE        0       //サイン波
#define ROM_TRAPEZOID   1       //台形波
#define ROM_TYPE        ROM_TRAPEZOID
/*------------------------------------------------------------------------------*/
/*【VARI】変数定義*/
/*------------------------------------------------------------------------------*/
/*波形ROM ([嵌り事]constを付けると上手く行かない)*/
unsigned int cu2l_Data[37] = 
{
#if (ROM_TYPE == ROM_SINE)
    2047,2376,2696,2997	,
    3268,3502,3692,3832	,
    3918,3947,3918,3832	,
    3692,3502,3268,2997	,
    2696,2376,2047,1717	,
    1397,1097,825,591	,
    401	,261,175,147	,
    175	,261,401,591	,
    825	,1097,1397,1717
#elif (ROM_TYPE == ROM_TRAPEZOID)
    0,0,0,0,        
    300,600,900,1200,
    1500,1800,2100,2400,
    2700,2700,2700,2700,
    2700,2700,2700,2700,
    2700,2700,2700,2700,
    2400,2100,1800,1500,
    1200,900,600,300,
    0,0,0,0
#endif
};
/*------------------------------------------------------------------------------*/
/* クロック設定 */
/*------------------------------------------------------------------------------*/
//ここにクロック設定ソースを挿入する//
/*------------------------------------------------------------------------------*/
/* Main関数 */
/*------------------------------------------------------------------------------*/
int main(int argc, char** argv) 
{
  /*-------------------------------------------------------------------------*/
    /* クロック初期化*/
    /*-------------------------------------------------------------------------*/
        vds_Main_Init_Clock_Register();     /* クロック初期化 */
    /*-------------------------------------------------------------------------*/
    /* GPIO初期化*/
    /*-------------------------------------------------------------------------*/
        ANSELBbits.ANSELB2 = 1u;            /* RB2ピンはアナログピン(DACOUT1) */
    /*-------------------------------------------------------------------------*/
    /* CMP初期化*/
    /*-------------------------------------------------------------------------*/
        DACCTRL1L = 0x0000u;
        DACCTRL1Lbits.CLKSEL = 0u;   /* DACクロック源( 0 = AFVCO/2 = 500MHz) */
        DACCTRL1Lbits.CLKDIV = 0u;   /* DACクロックディバイダ (0 = 1/1) */
        DACCTRL1Lbits.FCLKDIV = 7u;  /* Cmpフィルタクロックディバイダ */
        DACCTRL1Lbits.DACON = 1u;    /* モジュール有効化(0 = 無効,1 = 有効) */
        DACCTRL2H = 0x001Au;         /* 移行開始からの時間 */
        DACCTRL2L = 0x0055u;         /* 遷移モードの継続時間 */
        DAC1CONH = 0x0000u;
        DAC1CONL = 0x0000u;
        DAC1CONLbits.INSEL = 0u;        /* Cmp入力ソース (0 = CMPxA input pin) */
        DAC1CONLbits.HYSSEL = 1u;       /* コンパレータヒステリシス (1 = 15mV) */
        DAC1CONLbits.DACOEN = 1u;       /* DAC外部出力 (0 = 無効,1 = 有効) */
        DAC1DATH = 0x0000u;             /* コンパレータ比較値上限 */
        DAC1DATL = 0x0000u;             /* コンパレータ比較値下限 */
    /*-------------------------------------------------------------------------*/
    /* SLOPE初期化*/
    /*-------------------------------------------------------------------------*/
        SLP1CONH = 0x0000u;
        SLP1CONL = 0x0000u;
        SLP1DAT = 0x0000u;                  /* スロープ速度 */
    /*-------------------------------------------------------------------------*/
    /* DAC1の有効化*/
    /*-------------------------------------------------------------------------*/
        DAC1CONLbits.DACEN = 1u;            /* DAC1の有効化 */
    /*-------------------------------------------------------------------------*/
    /* SCCP2の間隔でDAC出力*/
    /* [嵌り事]TimerモードではDAC出力タイミングの周期はPRHで設定*/
    /* [嵌り事]OCモードではDAC出力タイミングの周期はPRL,RAL!=RABで設定*/
    /*-------------------------------------------------------------------------*/ 
        CCP2CON1L = 0x0000u;
        CCP2CON1Lbits.CLKSEL = 0u;      /* クロック源選択(0 = FP) */
        CCP2CON1Lbits.TMRPS = 0u;       /* クロックプリスケーラ選択(0 = 1:1) */
        CCP2CON1Lbits.CCSEL = 0u;       /* キャプチャモード選択 */
        CCP2CON1Lbits.MOD = 0u;         /* 動作モード(0 = 16bitモード) */
        CCP2CON1Lbits.T32 = 0u;         /* 32ビットモード */
        CCP2CON1H = 0x0000u;
        CCP2CON2H = 0x0000u;
    	CCP2CON2Hbits.AUXOUT = 1u;      /* イベント時補助出力信号選択ビット*/
        CCP2PRL = 100;                  /* 周期(PWMモード) */
        CCP2PRH = 100;                  /* 周期(タイマーモード) */
        CCP2RAL = 30;                   /* 立ち上がりタイミング(PWMモード) */
        CCP2RBL = 10;                   /* 立ち下がりタイミング(PWMモード) */
    
    	CCP2CON1Lbits.CCPON = 1u;
    /*-------------------------------------------------------------------------*/
    /* DMA初期化 */		
    /*-------------------------------------------------------------------------*/
        DMAH = 0xC000u;                     /* DMA転送最大アドレス */
        DMAL = 0x080u;                      /* DMA転送最小アドレス */
        DMACON = 0x0000u;
            DMACONbits.DMAEN = 1u;          /* DMAモジュール有効化 */
            DMACONbits.PRSSEL = 1u;         /* ラウンドロビン方式 */
        DMACH0 = 0x0000u;
            DMACH0bits.SAMODE = 1u;         /* 送信元アドレスをインクリメント*/
            DMACH0bits.DAMODE = 0u;         /* 受信元アドレスは何も変わらない */
            DMACH0bits.TRMODE = 1u;         /* 繰り返し、ワンショットモード */
            DMACH0bits.RELOAD = 1u;         /* 自動でリロードする */
            DMACH0bits.SIZE = 0u;           /* 16bit転送モード */
        DMAINT0 = 0x0000u;
            DMAINT0bits.CHSEL = 0x0C;       /* トリガイベント = SCCP2 */
        DMASRC0	= (unsigned int)&cu2l_Data; /* 送信元データは cu2l_Data[] */
        DMADST0	= (unsigned int)&DAC1DATH;  /* 送信先はDAC1DATHレジスタ */
        DMACNT0	= 35u;                      /* 送信カウントは36(35 + 1)回 */
       
        DMACH0bits.CHEN = 1u;               /* DMA0チャンネルを有効にする */
        IFS0bits.DMA0IF = 0u;
    /*-------------------------------------------------------------------------*/
    /* メインルーチン*/
    /*-------------------------------------------------------------------------*/
        while(1)
        {
        }
}

結果

DACOUTピンから任意のアナログ信号を出力可能な事が確認できました。

今回の場合は25kHz程度ですが、CCP2PRの値や波形データの全体サイズを変えれば周波数も変更可能です。

(fig.4)サイン波出力波形
(fig.5)台形波出力波形

コメント

タイトルとURLをコピーしました