2010年2月22日月曜日

USB FMトランスミッタ: Si4710制御ソフト

 Si4710を使うときにまず困るのが、データシートの入手です。Silicon Laboratoriesや販売店のサイトからは概要版のような2ページしかないものしかダウンロードできません。必要なのは、デバイスのデータシートプログラミングガイドの2種類です。直接リンクは避けますが、良く探すとネット上から拾うことができます。


Si4710の制御
 I2Cで制御します。AVRに埋め込まれた専用ハードを使っています。エラー処理は行っていませんが、I2C_comm() で簡単な関数化を行い、init_si4710() から呼び出しています。I2Cについてはエレキジャック誌の記事が大変参考になりました。


void I2C_comm(uint8_t rw, uint8_t word_length)
{
// rw: 0->read, 1->write

uint8_t n;

if (rw ==1) { // Write

// I2C "STRAT"
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & _BV(TWINT)));

for (n = 0; n<word_length; n++) {  //一部全角にしています

TWDR = I2C_data[n];


// 送信実行
TWCR = _BV(TWINT) | _BV(TWEN) ;

// 送信完了TWINTフラグまで待機
while(!(TWCR & _BV(TWINT)));

}

// I2C "STOP"
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN) ;

} else { // Read

// I2C "STRAT"
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);

// 送信完了TWINTフラグまで待機
while(!(TWCR & _BV(TWINT)));

TWDR = I2C_data[0];

// 送信実行
TWCR = _BV(TWINT) | _BV(TWEN) ;

// 送信完了TWINTフラグまで待機
while(!(TWCR & _BV(TWINT)));

for (n = 0; n<word_length; n++) {
//一部全角にしています


if (n == (word_length - 1)) {


// 送信実行
TWCR = _BV(TWINT) | _BV(TWEN);
} else {

// 送信実行
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
}

// 送信完了TWINTフラグまで待機
while(!(TWCR & _BV(TWINT)));

I2C_data[n] = TWDR;

}

// I2C "STOP"
TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN) ;

}
}





void init_si4710(void)
{
/*
// Power up as Digital input
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x01; // POWER UP
I2C_data[2] = 0xC2;
I2C_data[3] = 0x0F;
I2C_comm(1, 4);

// Read Response, Wait for "Clear to Send"
do{
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Digital input setting
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x01; // DIGITAL_INPUT_SAMPLE_RATE
I2C_data[4] = 0x03;
I2C_data[5] = 0x00; // (Clock must be available on DCLK/DFS pin)
I2C_data[6] = 0x00; // 32kHz: 0x7D00, 44.1kHz: 0xAC44, 48kHz: BB80
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do{
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Digital input setting
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x01; // DIGITAL_INPUT_SAMPLE_RATE
I2C_data[4] = 0x03;
I2C_data[5] = 0xAC; // (Clock must be available on DCLK/DFS pin)
I2C_data[6] = 0x44; // 32kHz: 0x7D00, 44.1kHz: 0xAC44, 48kHz: BB80
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do{
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Digital input setting
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x01; // DIGITAL_INPUT_FORMAT
I2C_data[4] = 0x01;
I2C_data[5] = 0x00; // I2S, Stereo, 16bit
I2C_data[6] = 0x00;
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do{
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

*/

// Power up as Analog input
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x01; // POWER UP
I2C_data[2] = 0xC2;
I2C_data[3] = 0x50;
I2C_comm(1, 4);

// Read Response, Wait for "Clear to Send"
do{
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));


// Analog input setting
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x21; // TX_LINE_INPUT_LEVEL
I2C_data[4] = 0x04;
I2C_data[5] = 0x20; // Line Attenuation LIATTEN[1:0] = 0b11 (Max 636mVpk)
I2C_data[6] = 0x0C; // Line Level LILEVEL[9:0] = 0x27C (636mVpk)
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

/*
// Get Revison
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x10; // GET_REV
I2C_comm(1, 2);

// Read Response
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 9);
*/

//Set RCLK
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x02; // REFCLK_FREQ
I2C_data[4] = 0x01;
I2C_data[5] = 0x7D; // REFCLKF[15:0] = 0x7D00 (32kHz)
I2C_data[6] = 0x00;
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

//Set REFCLK_PRESCALE
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x02; // REFCLK_PRESCALE
I2C_data[4] = 0x02;
I2C_data[5] = 0x01; // REFCLKP[11:0] = 0x177 (12MHz/32kHz=375)
I2C_data[6] = 0x77;
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

//Set TX_LINE_INPUT_MUTE
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x21; // TX_LINE_INPUT_MUTE
I2C_data[4] = 0x05;
I2C_data[5] = 0x00; // L,R No mute
I2C_data[6] = 0x00;
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Preemphasis
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x21; // TX_PREEMPHASIS
I2C_data[4] = 0x06;
I2C_data[5] = 0x00; // 50us
I2C_data[6] = 0x01;
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Audio Deviation
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x12; // Set property
I2C_data[2] = 0x00;
I2C_data[3] = 0x21; // TX_AUDIO_DEVIATION
I2C_data[4] = 0x01;
I2C_data[5] = 0x1A; // transmit auidio deviation
I2C_data[6] = 0xA9; // 6825(68.25kHz @90%MOD)
I2C_comm(1, 7);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// FM Transmit Frequency
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x30; // TX_TUNE_FREQ
I2C_data[2] = 0x00;
I2C_data[3] = 0x22; // Frequency must be a mutiple of 50kHz
I2C_data[4] = 0x92; // 8850(88.5MHz)
I2C_comm(1, 5);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));

// Transmit Power
I2C_data[0] = 0x22; // ADDR+W
I2C_data[1] = 0x31; // TX_TUNE_POWER
I2C_data[2] = 0x00;
I2C_data[3] = 0x00;
I2C_data[4] = 0x73; // 115dBuV, antenna auto tuning
I2C_data[5] = 0x00; // 00: Auto Tuning
I2C_comm(1, 6);

// Read Response, Wait for "Clear to Send"
do {
I2C_data[0] = 0x23; // ADDR+R
I2C_comm(0, 1);
} while (bit_is_clear(I2C_data[0], 7));
}


音声サンプルレートの測定
 サンプルレートを知るためにPCM2707から出力されるLRクロックの周期を測定します。やり方としては、LRクロックをAVRのICP1端子に接続して、LRクロックの立ち上がりエッジで割り込み TIMER1_CAPT_vect をかけて、カウンタをキャプチャ/リセットする方法を考えています。本来はアセンブラを使わなくてはいけないとおもいますが、実用になるのか試してみたところです。
 このプログラムでの各サンプルレートでの計数値は次の通り。(Cですのでコンパイラの都合で変動する場合があります)

48kHz(83システムクロック) => 計数値60
44.1kHz(91システムクロック) => 計数値67
32kHz(125システムクロック) => 計数値101

 なお、コールドスタート時のPCM2707のLRクロックは44.1kHzでした。

// 16bit Timer/Counter
// Timer/Counter1 Control Register A
TCCR1A = 0b00000000;

// Timer/Counter1 Control Register B
// ICES1: Rise Edge
// Clock Select: Start
TCCR1B = 0b01000001;

// Timer/Couter Input Capture Interrupt Enable
TIMSK |= _BV(TICIE1);


ISR (TIMER1_CAPT_vect)
{
TCCR1B = 0b000; // Counter Stop
TCNT1 = 0x0000; // Counter Reset
TCCR1B = 0b001; // Counter Start

ICR1 の処理
}


0 件のコメント:

コメントを投稿