2015年10月8日木曜日

PSoC 4 Pioneer KitでMCP4922を使う DDSで波形生成

以前「PSoC 4 Pionner Kit 外付けDACでサイン波生成」でもMCP4922でサイン波を出力してみたが、もう少し詳しくテストしてみた。

2ch出力


少しがんばって2ch出力させてみた。


1Hzと0.5Hzのサイン波を生成して、LEDを点滅させている。

SPIクロックとサンプリング周波数

System Clockは48MHzに設定。

TopDesign

TCPWM_P4のConfigureダイアログ

TC_Sampling_TimerのPeriodを249に設定した。

0..249でカウントアップしてオーバーフローしたらISR_Sampling_Timerの割り込みがかかる。
12MHz / 250 = 48kHz
今まで(ぴゅんぴゅん3号など)250に設定していたので、間違っていたかも。

割り込みと同時にOVからPin_Sampling_ClockにH/Lの信号が出力される。
→オシロをあてればサンプリング周波数が測定できる。

main.c
 /* ========================================  
  *  
  * Copyright YOUR COMPANY, THE YEAR  
  * All Rights Reserved  
  * UNPUBLISHED, LICENSED SOFTWARE.  
  *  
  * CONFIDENTIAL AND PROPRIETARY INFORMATION  
  * WHICH IS THE PROPERTY OF your company.  
  *  
  * 2015.10.07 2ch同時出力  
  * 2015.10.06 2ch DACに対応(2ch同時出力は不可)  
  * 2015.10.06 TC_Sampling_TimerのOVをPINに出力  
  * 2015.10.03 MCP4922に12bitデータを送信  
  * ========================================  
 */  
 #include <project.h>  
 #include <math.h>  
 #include "wavetable.h"  
 #define SAMPLE_CLOCK (48000.0f)  
 #define WAVE_FREQUENCY_A (1000.0f)  
 #define WAVE_FREQUENCY_B (2000.0f)  
 // ERROR CODE  
 #define ERR_DAC_CHANNEL_OUT_OF_RANGE 0x01  
 volatile uint32 phaseRegisterA;  
 volatile uint32 phaseRegisterB;  
 volatile uint32 tuningWordA;  
 volatile uint32 tuningWordB;  
 void DACSetVoltage(uint16 value, int channel)  
 {  
   uint16 txData;  
   switch (channel) {  
   case 0:  
     // Highバイト(0x30=OUTA/BUFなし/1x/シャットダウンなし)  
     txData = (value & ~0xF000) | 0x3000;  
     break;  
   case 1:  
     // Highバイト(0xB0=OUTB/BUFなし/1x/シャットダウンなし)  
     txData = (value & ~0xF000) | 0xB000;  
     break;  
   default:  
     ;  
     // error(ERR_DAC_CHANNEL_OUT_OF_RANGE);  
   }  
      Pin_LDAC_Write(1u);  
   SPIM_DAC_SpiUartWriteTxData(txData);  
      while(0u == (SPIM_DAC_GetMasterInterruptSource() & SPIM_DAC_INTR_MASTER_SPI_DONE))  
      {  
           /* Wait while Master completes transfer */  
      }  
   Pin_LDAC_Write(0u);  
      /* Clear interrupt source after transfer completion */  
      SPIM_DAC_ClearMasterInterruptSource(SPIM_DAC_INTR_MASTER_SPI_DONE);  
 }  
 CY_ISR(ISR_Sampling_Timer_Handler)  
 {  
   uint32 index;  
      // Caluclate Wave Value A  
   //  
      phaseRegisterA += tuningWordA;  
      // 32bitのphaseRegisterをテーブルの10bit(1024個)に丸める  
      index = phaseRegisterA >> 22;  
   uint16 waveValueA = waveTableSine[index];  
   // Caluclate Wave Value B  
   //  
   phaseRegisterB += tuningWordB;  
   index = phaseRegisterB >> 22;  
   uint16 waveValueB = waveTableSine[index];  
   // Output  
   //  
      DACSetVoltage(waveValueA, 0);  
   DACSetVoltage(waveValueB, 1);  
   TC_Sampling_Timer_ClearInterrupt(TC_Sampling_Timer_INTR_MASK_TC);  
 }  
 int main()  
 {   
   // 変数の初期化  
      tuningWordA = WAVE_FREQUENCY_A * pow(2.0, 32) / SAMPLE_CLOCK;  
   phaseRegisterA = 0;  
   tuningWordB = WAVE_FREQUENCY_B * pow(2.0, 32) / SAMPLE_CLOCK;  
   phaseRegisterB = 0;  
   // コンポーネントの初期化  
   TC_Sampling_Timer_Start();  
   ISR_Sampling_Timer_StartEx(ISR_Sampling_Timer_Handler);  
   SPIM_DAC_Start();  
   CyGlobalIntEnable;  
   for(;;)  
   {  
   }  
 }  
 /* [] END OF FILE */  

MCP4922のchannelAから1kHz、channelBから2kHzのサイン波を出力するプログラムだ。

オシロで出力をチェック


ch1:TimerのOVの出力 ch2:DACのchannelBの出力
(1)F:47.94kHz
(2)F:998.0Hz
channelBは2kHzのはずなのでおかしい。


ch1:TimerOV ch2:LDAC(DACのラッチ)

よく見てみるとサンプリング区間の2回に1回しかラッチ信号が出力されていない。(ラッチ信号は2ch分あるので連続で2回出力される)

SPIのクロックを上げる

SPIMのConfigureダイアログ

SPIクロックを4MHzから8MHzにあげてみた。

また、TX data bitsを16に変更(デフォルトは8)。これで16bitまとめて出力できる。main.cもこれに合わせて変更した。

4MHzのSPI信号

ch1:MOSI ch2:SCK

SCKは4MHzになっている

8MHzに変更したSPI信号

SCKはだいたい8MHzになっている

オシロで出力をチェック


ch1:TimerOV ch2:LDAC

サンプリング区間ごとにラッチ信号が2回ずつ出力されている。


ch1:TimerOV ch2:DACのchannelB

出力周波数も無事2kHzに(^q^/)

メモ:

  • MCP4922の2ch同時出力はできたが、SPIの信号出力で相当時間が食われている。SPIの出力が終わるまで非同期で別の処理ができればいいが、波形生成の演算はSPI出力の前に終わらせる必要があるし、2ch出力は努力目標ということにしようかな。
  • リズムマシンはDecayやLevel、トラックの合成など結構計算量がありそうだし、無理そうならサンプリング周波数を32kHzに落とすとか。
  • DACの片チャンネルは仮想GNDの電位を出力する?
  • 出力段のLPFは必須


PSoC Creatorのプロジェクト
https://github.com/ryood/PSoC-DDS-MCP4922