ディジタルオーディオ用 DAC をマイコンにつなぐ(10) -- STARM (Cortex-M3)
しばらくサボっていたのですが、やっとディジタルオーディオ用 DAC (ROHM BU9480F) を CQ-STARM 基板につなぎました。
CQ-STARM 基板に搭載されている STmicro STM32F103VB プロセッサには SPI が2回路内蔵されていますが、SPI1 は SD/MMC カードインターフェースに使われているので、SPI2 のほうを使うことにします。
この SPI 回路は 16 ビット転送モードを持っているので、ソフトウェアの負担は軽くなります。
DAC との接続は、このような回路になります。(→)
SPI2 は remap 不可能なので、必然的に SPI2_SCK (52 番ピン、J3-10)、SPI2_MOSI (54 番ピン、J3-8) は確定します。
サンプリング周期の割り込み源としてタイマ 3 を使っているので、DAC の LRCK としては、タイマ 3 の チャネル 4 の PWM 出力 (TIM3_CH4、36 番ピン、J3-23) を使うことにします。
タイマ 3 の初期化部分のコードは次のようになります。
// Timer 3 setup ( fs generation ) RCC_APB1ENR_bit.TIM3EN = 1; // enable clock // for TIM3 GPIOB_CRL = 0x000000A0; // PB1 = TIM3 CH4 out TIM3_CR1 = 0x94; // down count, interrupt // on OVF/UDF only TIM3_CR2 = 0x00; // TIM3_SMCR = 0x00; // slave mode disable TIM3_CCMR1 = 0x00; // no ch1, ch2 compare TIM3_CCMR2 = 0x6800;// ch3 = disable, // ch4 = PWM1, OC4 = out TIM3_PSC = 0; // set prescaler to 1/1 TIM3_ARR = 4500-1;// set fs to 16 kHz TIM3_CCR4 = (4500 >> 1); // duty register TIM3_CCER = 0x3000; // OC4 enable and invert TIM3_DIER = 0x0001; // enable UIE only SETENA0_bit.SETENA29 = 1; // enable TIM3 int TIM3_CR1_bit.CEN = 1; // enable timer 3
実際には、タイマ周期レジスタ (TIM3_ARR) および PWM のデューティーレジスタ (TIM3_CCR4) は、サンプリング周波数を変えるたびに設定し直されます。
SPI2 の初期化部分のコードは次のようになります。
// SPI setup for 16-bit Serial DAC RCC_APB1ENR_bit.SPI2EN = 1; // enable clock // for SPI2 SPI2_CR1 = 0x0B44 + (0x03 << 3); // 16bit, // master, MSB first, 72/16 = 4.5MHz clk
ここで、この SPI 回路は、マスターモードの場合でも NSS (Slave Select) 信号に影響されることを注意する必要があります。
NSS 信号の動作モードには次の 3 種類があります。
- ハードウェア入力モード
- ハードウェア出力モード
- ソフトウェアモード
ハードウェア入力モードでは、NSS ピンは入力となり、「H」レベルに保持しておかないと送信動作をしません。
ハードウェア出力モードでは、NSS ピンはスレーブセレクト出力として機能します。
ソフトウェアモードでは NSS ピンは無関係となりますが、代わりに SPI_CR1 レジスタ内に設けられた SSI ビットが送信動作のイネーブルに使われます。
最初、このことに気づかなくて、SPI が動作せず悩みました。
DAC 出力はスレーブセレクトで制御する必要はないので、ソフトウェアモードとし、初期設定でイネーブルしたままにしておきます。
サンプリング周期での割り込みルーチンでの処理は次のようになります。
// Timer3 IRQ handler void TIM3_IRQHandler(void) { int32_t lw; if (TIM3_SR_bit.UIF & 0x01) { // UIF set? t1_tick = 1; // set timer1 tick flag lw = wave_que[wq_rd_ix]; // get wave data SPI2_DR = lw; // output 16-bit data to DAC wq_rd_ix = (wq_rd_ix + 1) & (WAVEQUESIZE-1); TIM3_SR_bit.UIF = 0; // clear UIF flag } // if (TIM3->SR ... } // void IRQ_Handler()
16 ビット転送モードを使っているので、DAC 出力のために追加すべき処理は
SPI2_DR = lw; // output 16-bit data to DAC
の1行だけです。