2016年5月12日木曜日

PSoC 4 Pioneer KitでSPI制御のDCOを作る。

アナログ・シンセのVCOが電圧で周波数を制御するのに対して、デジタル信号で周波数を制御するDCO(Digital Controled Oscillator)を作ってみた。

IC間通信で一般的な、I2CとSPIの実験をしてみたが、SPIの方が簡単そうなのでSPIでやってみた。

ブレッドボード図


接続は前回の「Arduino UnoをSPIのMasterにして、複数のSlaveを使う。」のPSoC 4 Pioneer Kitに、

  • IDAC(電流出力型DAC)の電流電圧変換用の1kΩの抵抗
  • ノコギリ波/矩形波切り替え用のタクトスイッチ

を追加した。

PSoCのTopDesign

IDAC8からの電流出力はR_1で電圧変換し、Opamp_IV_Convのボルテージフォロワーで強化して出力している。

IDAC8の電流出力は0~612uAに設定しているので、出力は0~612mVになる。

波形生成


<PSoC 4の割り込みルーチン>


CY_ISR(ISR_Saw_handler)
{
    Pin_Check2_Write(1);
    
    count++;
    IDAC8_SetValue(count);
 
    Timer_Sampling_ClearInterrupt(Timer_Sampling_INTR_MASK_TC);
    
    Pin_Check2_Write(0);
}

CY_ISR(ISR_Square_handler)
{
    Pin_Check2_Write(1);
    
    count++;
    IDAC8_SetValue((count / squareDuty) ? 255 : 0);    
    
    Timer_Sampling_ClearInterrupt(Timer_Sampling_INTR_MASK_TC);

    Pin_Check2_Write(0);    
}

今回はDDSではなくて、ノコギリ波と矩形波を単純な方法で生成してみた。

ISR_Saw_handlerがノコギリ波生成ルーチンで、符号なし8bit整数のcountを割り込みごとにインクリメントしているので0~255の値でノコギリ波形になるのでそのままDACに出力。

ISR_Square_handlerは矩形波生成ルーチンで、countをsquareDuty(デューティ比:符号なし8bit整数)で割り算して、デューティ比可変の0, 255の値で矩形波を生成している。

処理速度は結構シビアで、割り込みルーチン内で条件分岐してノコギリ波と矩形波を切り替えると割り込みのタイミングに追いつかなくなるので、波形ごとに割り込みルーチンを作って外部で切り替えるようにした。

ノコギリ波生成のタイミング

ch1:Pin_Check2 ch2:Pin_Check1

Pin_Check2は割り込みルーチン内の最初と最後でOn/Offしているのを捕捉。Pin_Check1はメインループの最初と最後でOn/Offしているものを補足している。

Pin_Check2のPW(正のパルス幅)を見ると割り込み処理に2.6us程度かかっている。

Pin_Check1のPWはメインループの処理時間で、波形生成の割り込みがかかっている時は処理時間が引き伸ばされている。

矩形波生成のタイミング

ch1:Pin_Check2 ch2:Pin_Check1

矩形波も同じだが、割り込みルーチン内で割り算等、処理が増えているのでPWが3.3usに増加している。

→矩形波はプログラムでの計算ではなく、TimerCounterの割り込み発生条件でうまく制御すればもっと処理が速くなると思う。

周波数の制御


TimerCounterのPeriodを変更してオーバーフローするタイミングを変えることで可変にしている。低い周波数でもなめらかに周波数を変更できるように0.1Hz単位で設定できるようにした。

符号なし16bit整数に周波数の10倍値を入れるようにした。最大値が65535なので0Hz~6553.5Hzの範囲で音程を設定できる。

再生可能周波数

実際のプログラムでは再生できる周波数はもう少し制限される。

TimerCounterを12MHz(SAMPLING_CLOCK)で駆動していて、TimerCounterのPeriodは
timerPeriod = SAMPLING_CLOCK * 10 / (frequency10 * 256);
になる。(frequency10は周波数の10倍値)

変形すると
frequency10 = SAMPLING_CLOCK * 10 / (timerPeriod * 256);
ノコギリ波の場合、波形生成の割り込みルーチンで2.6usかかっているので、timerPeriodの最小値は
2.6us / (1 / 12Mhz) = 31.2
これ以上timerPeriodを短くすると、割り込みルーチン内の処理が追いつかなくなる。
frequency10 < 12MHz * 10 / (31.2 * 256) ≒ 15024
で周波数に直すとおよそ1,502Hzになる。

音程でいうと真ん中のドより2オクターブ高いF#になる。

MIDIノートナンバーでうと0~90を(もう少し余裕をみる必要はあるが)再生できそうだ。

Github:
https://github.com/ryood/PSoC4_DCO

Arduino Master:
https://github.com/ryood/PSoC4_DCO/tree/master/Arduino/Sequencer_Test

PSoC 4 Pioneer Kit Slave:
https://github.com/ryood/PSoC4_DCO/tree/master/PSoC/PSoC4_DCO.cydsn

メモ:


割り込みのタイミングを可変にすることで周波数を制御してみたが、DDSとくらべて特にメリットはない気がする(^q^;

<追記:2016.06.09>

あ、いやDDSの矩形波やノコギリ波は波形が崩れるのでこれはこれでメリットがある。

</追記>

ARM Cortex-M0の48MHz駆動だとノコギリ波のような単純な波形でもリアルタイム生成はこのあたりが限度かも。