ディジタルオーディオ用 DAC をマイコンにつなぐ(4)
次は Atmel ATmega シリーズの SPI モジュールを使った場合です。
オーバーサンプリング DAC を使用できるように 384 fs も生成しています。 ただし、実際に接続して確認はしていません。
ATmega168 と BU9480 との接続は下の図のように行います。
SPI は ISP (In-System Programming) 端子と共用していますから、プログラムの書き込み時に DAC の BCLK、SDAT 端子も変化します。
このとき、LRCK がフローティングだと、オーディオ出力に異音を生じますから、LRCK がフローティングにならないようにプルアップしています。
タイマ 2 で 24 MHz のクロック (スペック外) を 1/4 にして、6 MHz の 384 fs クロックを作っています。 タイマ 2 出力からタイマ 0 の外部クロック入力へ、外部で接続します。
タイマ 0 では 1/384 して LRCK (15.625 kHz) を生成しています。
SPI は次のように設定しています。
- マスター・モード
- SPI 転送モード 0
- MSB ファースト
- SPI クロック = fosc / 32
タイマおよび SPI モジュールの初期化コードを次に示します。 (WinAVR の gcc 使用)
// Timer2/Timer0 setup for digital audio DAC // 384fs generation (24 MHz / 4 = 6 MHz) using Timer2 TCCR2A = (_BV(COM2B0) | _BV(WGM21)); // OC2B flip at compare, CTC mode TCCR2B = _BV(CS20); // clk_2TS with no prescaler (24 MHz) OCR2A = 2-1; // divide by 2 OCR2B = OCR2A; // compare value DDRD |= _BV(PD3); // enable OC2B output // LRCK generation (6 MHz / 384 = 15.625 kHz) using Timer0 TCCR0A = (_BV(COM0B0) | _BV(WGM01)); // OC0B flip at compare, CTC mode TCCR0B = (_BV(CS02) | _BV(CS01)); ; // clock source = falling edge of T0 OCR0A = 192-1; // divide by 192 OCR0B = OCR0A; // compare value TIMSK0 = _BV(OCIE0A); // Timer0 Output Compare Match A interrupt DDRD |= _BV(PD5); // enable OC0B output // SPI setup for digital audio DAC SPSR = _BV(SPI2X); SPCR = (_BV(SPE) | _BV(MSTR) | _BV(SPR1)); // enable SPI, fosc/32, Master, CPHA=0, DORD=0, CPOL=0 DDRB |= (_BV(PB3) | _BV(PB5)); // enable output for MOSI(PB3), SCK(PB5) DDRC |= _BV(PC5);
SPI モジュールでは、受信側にはバッファレジスタがありますが、送信側にはバッファがなく、シフトレジスタに直接書き込む形になるので、2バイト目を出力するには、1バイト目の出力完了を待たなければなりません。
フラグをチェックしながらループで待つのは非効率ですから、1バイト目はタイマ割り込みルーチンで出力し、2バイト目の準備として SPI 送信完了割り込みを許可しておきます。
2バイト目は SPI 送信完了割り込みルーチンで出力したあと、 SPI 送信完了割り込みを禁止しておきます。
割り込みルーチンのリストを下に示します。 リストでは省略してありますが、出力データの上位バイトが GPIOR2 に、下位バイトが GPIOR1 に格納されています。
// Timer0 compare A match interrupt ISR(TIMER0_COMPA_vect) { ... SPSR; // read SPSR to clear SPIF SPDR = GPIOR2; // output high byte SPCR |= (_BV(SPIE)); // enable SPI interrupt to output low byte } // ISR() // SPI Serial Transfer Complete interrupt ISR(SPI_STC_vect) { SPDR = GPIOR1; // low byte SPCR &= (~_BV(SPIE)); // disable SPI interrupt } // ISR()