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 件のコメント:
コメントを投稿