SH-2A 基板に AC97 コーデックをつなぐ (7)

外部回路により SSIWS / SYNC を発生する方式についても実験してみます。
ATmega48/88/168/328 や ATtiny2313 は手持ちがあるのですが、さすがに 28 ピンや 20 ピンの IC を実装して実験する気にはなれなかったので、8 ピンの ATtiny13 を買ってきて使うことにしました。
ATtiny13 には PWM 波形を発生できる 8 ビットタイマがひとつしか内蔵されていないので、SYNC 信号の立ち上がり、あるいは立ち下がりの、いずれかのエッジが SSIWS のエッジと一致する形式の信号しか発生できません。
具体的には下の図のようになります。

C ソースプログラムを下に示します。

/****************************************************/
/* ac97gen.c : SYNC / SSIWS generator               */
/*             for SH-2A - AC97 link using ATtiny13 */
/*                                                  */
/*                        +-+_+-+                   */
/* (from SH-2A) AC97RES# -|1   8|- Vcc (3.3V)       */
/* (from AC97)   BIT_CLK -|2   7|- POS_SEL          */
/*                ENABLE -|3   6|- SYNC  (to AC97)  */
/*                   GND -|4   5|- SSIWS (to SH-2A) */
/*                        +-----+                   */
/*                                                  */
/* 2010-06-24 created by pcm1723                    */
/****************************************************/

#include <avr/io.h> // include file for AVR
#include <avr/sleep.h>

FUSES = { // fuse definition in .elf file
  .low  =  (FUSE_SPIEN  & FUSE_SUT1 & FUSE_SUT0 
          & FUSE_CKSEL1 & FUSE_CKSEL0), // external clock
  .high = HFUSE_DEFAULT
}; 

#define SYNC_WIDTH (16)
#define WS_PERIOD (512)
#define POS_SEL_BIT (PINB2) // SYNC pos sel bit = PINB4
#define ENABLE_BIT  (PINB4) // Hi-Z control

int main()
{
  PORTB  = _BV(PORTB5) 
         | _BV(PORTB4)
         | _BV(PORTB2); // enable pullup

  if (_BV(ENABLE_BIT) & PINB) { // enabled
    TCCR0A = _BV(COM0A0) // toggle OC0A on OCR0A match
           | _BV(WGM01)  
           | _BV(WGM00); // WGM02:01:00 = 1:1:1 (fast PWM)

    OCR0A  = (WS_PERIOD / 2) - 1; // TOP count

    if (_BV(POS_SEL_BIT) & PINB) {// SYNC falls on SSIWS edge
      TCCR0A |= _BV(COM0B1);   // OC0B = L on OCR0B match
      OCR0B   = (SYNC_WIDTH-1);// SYNC pulse width
    } else {                   // SYNC rises on SSIWS edge
      TCCR0A |= _BV(COM0B1) 
              | _BV(COM0B0);   // OC0B = H on OCR0B match
      OCR0B  = 256-SYNC_WIDTH-1; // SYNC pulse width
    } // if (_BV(POS_SEL_BIT) & PINB)  ...

    TCCR0B = _BV(CS00)   // CS02:01:00  = 0:0:1 (1/1 clk)
           | _BV(WGM02); // WGM02:01:00 = 1:1:1 (fast PWM)

    DDRB = _BV(PORTB0)   // PB0/OC0A is output
         | _BV(PORTB1);  // PB1/OC0B is output
  } // if (_BV(ENABLE_BIT) & PINB) ...

  set_sleep_mode(SLEEP_MODE_IDLE); // sleep CPU only

  do {
    sleep_mode(); // sleep forever
  } while (1);
}

オブジェクトのサイズは、フラッシュ上のプログラムが 106 バイトで、RAM は 1 バイトも使っていません。
フューズビットは、

  • 外部クロック入力
  • スタートアップ・ディレイ最小、
  • リセット入力有効

の設定にします。
具体的には、 L バイトが 0x70、H バイトが 0xFF です。
ATtiny13 のリセット入力には、SH-2A の出力ポートが接続され、初期化プログラムからソフトウェア・リセットをかけるので、リセット後の ms 単位のディレイが付いていると邪魔になりますから、ディレイのないモードに設定します。
最も簡単な構成での SH-2A との接続を下に示します。

SSIWS0 への出力および SYNC への出力が 10 kΩ の抵抗でプルダウンしてあるのは、ATtiny13 がリセットされて、各ピンがハイインピーダンス状態の場合にプルダウンにより「L」レベルを確定させるためです。
AC97 コーデックは、RESET# 信号が H になる時点、つまり、リセットが解除される時点で SDATA_OUT および SYNC 信号が共に L レベルであれば通常動作しますが、いずれか一方でも H レベルになっていればテストモードに入ってしまいます。
意図しないテストモードに入ることを避けるために、わざわざ SH-2A 側が AC97 コーデックのリセット信号をコントロールしており、リセット解除時には各信号のレベルを確定させています。
実際に実験に使う回路は、上の構成を含めて 3 種の構成が可能なものを考えており、次回に説明したいと思います。