ディジタルオーディオ用 DAC をマイコンにつなぐ(5)

今度は Atmel ATtiny シリーズの USI (Universal Serial Interface) モジュールを使った場合です。
USI を SPI (3線動作) モードで使うと、ハードウェア的には 8 ビット・シフトレジスタ + 4 ビット・カウンタ程度の回路であり、お世辞にも使いやすいとは言えません。
USI はマスタ・モードでも自前のクロック発生源を持たず、SPI クロックは

  • タイマ/カウンタ 0 のオーバーフローでトグル
  • ソフトウェアで出力ポートをトグル
  • 外部クロック

の中から選択します。
しかも、ソフトウェアによるクロック生成でなければ、最高速 (fosc/2) は実現できません。
実際の応用例の回路図を(→こちら)に示します。
これは、まだ web で公開はしていないのですが、MIDI-CV + VCO の実現方法のひとつとして、トップ・オクターブの周波数に相当するリニア電圧でクロック用 VCO をドライブし、オクターブ番号を示すディジタル値を与えて、マイコンで DDS 方式により、のこぎり波を発生するものです。
このマイコンに ATtiny2313、オーディオ出力 DAC に BU9480 を使い、MIDI-CV 機能は ADuC7026 のFM音源プログラムに追加したプログラムで実現しています。
CPU クロックの変化範囲は 4.55 〜 9.1 MHz で、これを 68 分周して LRCK 周波数、つまりサンプリング周波数は 66.976 〜 133.95 kHz となります。
のこぎり波の周波数は、MIDI ノート番号 0 を C0 として、C0 = 8.178 Hz から C11 相当の 16.744 kHz までです。 (MIDI ノートとしては G10 = 12.544 kHz までしか存在しません)
CPU クロック用の VCO の発振周波数をなるべく下げるため、USI は最高速が得られるソフトウェア・クロック生成のモードで使いました。
初期設定部は大部分省略して、主にループ部分のリストを下に示します。 (AVR Studio の AVRASM2 使用)

    .def    _acc    = R24   ; R25:R24 = acc
    .def    _acch   = R25   
    .def    _zero   = R23   
    .def    _one    = R22   
;
    .macro  spishift1
    out     USICR,_zero ; USICLK = 0 
    out     USICR,_one  ; USICLK = 1
    .endmacro
;
    .macro  spishift4
    spishift1
    spishift1
    spishift1
    spishift1
    .endmacro
;
    .macro  spishift8
    spishift4
    spishift4
    .endmacro
;
    ...
    ldi     _zero,((1 << USIWM0) | (0 << USICS0) | (1 << USITC))
    mov     _one,_zero
    ori     _one,(1 << USICLK)
    ...
dds68_loop:
    out     USIDR,_acch ; upper byte of 16 bit audio data
    spishift8           ; generate SPI clock
    out     USIDR,_acc  ; lower byte of 16 bit audio data
    spishift8           ; generate SPI clock
;
;   total 36 cycle for LRCK = 1
    ...
;   total 36 cycle for LRCK = 0
;
    rjmp    dds68_loop  ; infinite loop (2 cyc)

例によって、L ch だけの出力です。 R ch も出力すると、他に何も処理ができなくなります。
マクロを使って簡略化してありますが、実際には 1 バイトのデータ出力に対して、クロック発生のための「out USICR,Rn」命令が 16 個並びます。 2バイト分では 32 個になります。
さらに、データを USI のシフトレジスタへ出力する「out USIDR,Rn」命令が2個必要ですから、合計では実行に 34 クロックかかります。
ループ全体では 68 クロック分で、最後の「rjmp」命令は2クロック要しますから、結局、68-34-2=32 クロック分が、その他の処理に使える時間となります。
これは、条件分岐があって処理の流れが変わっても、すべての分岐での実行クロック数は同じ値になるように調整しなければなりません。