概要
DACOUTからは三角波やDC電圧以外にも任意波形が可能です。今回はオーソドックスなDDS(Direct Digital Synthesizer)方式でCPUのリソースを使用しない方式で出力してみます。
CPUリソースを消費させない方法として今回はDMAモジュールを使用します。dsPIC33CHシリーズにはDMAがマスターコアに6ch、スレーブコアに2ch搭載されています。
DMAとはDirect Memory Accessの略でレジスタやメモリ間のデータ転送をCPUを介さずにできる仕組みの事
DMAの構成としては以下の通りです。

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

ハードウェア構成と制御ブロック
周辺モジュールとしてDAC1とSCCP2、DMA0を使用します。
今回はRAMにSIN波のデータもしくは台形波のデータを格納し、SCCP2の間隔でそのデータを転送します。DMAの設定で全データを送信完了した場合、また先頭のデータから送信を開始させる設定にしていますので、CPUはデータの準備と開始トリガさえ打てば後は止めるまで何もする必要はありません。

レジスタ
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エンジン制御レジスタ |
| DMACHx | DMA CHANNEL n CONTROL REGISTER | DMA チャンネル n 制御レジスタ 転送モードなどを指定 |
| DMAINTx | DMA CHANNEL n INTERRUPT REGISTER | DMA チャンネル n 割り込み制御レジスタ DMAに関する割り込みを設定 |
| DMASRCx | DMA Data Source Address Pointer for Channel n Register | DMA チャンネル n データ ソースアドレス ポインタ 転送元のアドレスを指定 |
| DMADSTx | DMA Data Destination Source for Channel n Register | DMA チャンネル n データ デスティネーション アドレス ポインタ 転送先のアドレスを指定 |
| DMACNTx | DMA Transaction Counter for Channel n Register | DMA チャンネル n トランザクション カウンタ 何回データを転送するかを指定 |
ソースコード
波形の切り替えは以下のようにマクロの値を書き換えて下さい。
サイン波を出力する場合
#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の値や波形データの全体サイズを変えれば周波数も変更可能です。




コメント