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 の処理
}


2010年2月21日日曜日

USB FMトランスミッタ: PCM2707 USBディスクリプタ変更

概要
  PCM2707はデバイスディスクリプタ情報をSPIインターフェース経由で変更できます。情報を変更後、USBのD+ピンを3.3V系にプルアップすると、PCからUSB機器が接続されたことが認識され、PCM2707はそのデバイスディスクリプタ情報をPCに通知します。


プログラム
 CPUはATmega8Lです。以下にプログラムを示します。後ほど説明するプログラムに必要な項目も含んでいます。
※プログラムをブログにコピペすると、タブを削除したり、一部のコードが勝手に変更されてしまいます。どうなってるんでしょう?(#includeの括弧を全角に変更しています)


#define F_CPU 4.000E6 // マスタクロック4MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include <util/delay.h>


volatile uint8_t I2C_data[7];
/*
PD7 O SS(PCM2707) <1>

PD6 I SSPND(PCM2707)<0>

PD5 O USB(D+) <0>

PD4 O LED, "Status" <0>

PD3 O LED, "Over modulation"<1>

PD2 I DIP SW, Left -1

PD1 I DIP SW, Left -2

PD0 I DIP SW, Left -3

PC6 I Reset

PC5 O I2C SCL(Si4710)

PC4 I/O I2C SDA(Si4710)
PC3 I DIP SW, Right -1

PC2 I DIP SW, Right -2

PC1 I DIP SW, Right -3

PC0 I DIP SW, Right -4

PB7 (Xtal)

PB6 (Xtal)

PB5 O SCLK(PCM2707) <0>

PB4 I N/A

PB3 O MOSI(PCM2707) <0>

PB2 O FSEL(PCM2707) <1>
PB1 O RESET(Si4710) <0>

PB0 I LRCK
*/

void init_devices(void) {
// ポート設定 0:入力, 1:出力
DDRB = 0b101110;
DDRC = 0b1100000;

DDRD = 0b10111000;

// 出力ポート初期化
// 入力ポート、1:プルアップ

PORTB = 0b010100;

PORTC = 0b1111111;

PORTD = 0b10000111;


PORTB |= _BV(1);
// RESET(Si4710) => 1

// I2C SCL周波数: 100kHz (Si4710: <400khz data-blogger-escaped-span="">
// 100kHz = 4MHz/(16+2*TWBR*PRESCALER)

TWBR = 0x0C;

TWCR = 1<
// TWI enable

// 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);

}

void init_pcm2707(void) {
// PCM2707 Device Descriptor ROM write (56bytes)


uint8_t m, n;
uint16_t SPI_PROCESS = 0x2040; // Process: Descriptor ROM address reset

uint8_t SPI_PROCESS2 = 0x10;// Process: Descriptor ROM data write

uint8_t ROMDATA[57] = {

0xBB, 0x08, // Vendor ID(2bytes): 0x08BB

0x07, 0x27, // Product ID(2bytes): 0x2707

// Product string: Product strings.(16bytes in ANSI ASCII code): "Si4710 USB FM-TX"

0x53, 0x69, 0x34, 0x37, 0x31, 0x30, 0x20, 0x55, 0x53, 0x42, 0x20, 0x46, 0x4D, 0x2D, 0x54, 0x58,

// Vendor string: Vendor strings are placed here.

// (32 bytes in ANSI ASCII code): "Burr-Brown from TI /w tamago"

0x42, 0x75, 0x72, 0x72, 0x2D, 0x42, 0x72, 0x6F, 0x77, 0x6E, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20,
0x54, 0x49, 0x20, 0x2F, 0x20, 0x74, 0x61, 0x6D, 0x61, 0x67, 0x6F, 0x20, 0x20, 0x20, 0x20, 0x20,
0xC0, // Power attribute (bmAttribute): 0x80 (Bus-powered), 0xC0(self-powered)

0x32, // Max power (maxPower): 0x0A(20mA), 0x32(100mA), 0x7D(250mA), 0xFA(500mA) 0x0A, 0x93, 0x01 // Auxiliary HID usage ID: 0x0A, 0x93, 0x01 (AL A/V capture)
};

// Note that the data bits must be sent from LSB to MSB on bus.

// This means that each data byte must be stored with its bits in reverse order.


PORTD &= ~_BV(7); // MS -> 0

for (n = 0; n < n="n+1){">
// MD write
if(bit_is_set(SPI_PROCESS, n)){
PORTB |= _BV(3);

}else{

PORTB &= ~_BV(3);

}


// MC write
PORTB |= _BV(5); // MC -> 1

PORTB &= ~_BV(5); // MC -> 0

}


PORTD |= _BV(7); // MS -> 1

PORTD &= ~_BV(7); // MS -> 0


for (m = 0; m < m="m+1){"> for (n = 0; n < n="n+1){ // MC -> 0
}


for (n = 0; n<8 data-blogger-escaped-n="n+1){ <span style=">
PORTB &= ~_BV(5); // MC -> 0

}

}


PORTD |= _BV(7); // MS -> 1

PORTD |= _BV(5); // USB(D+) -> 1

}



int main(void)

{

init_devices();
init_pcm2707();
init_si4710();

while(1); // 無限ループ
}


補足  PCM2707では、USBディスクリプタ情報のProduct StringとVendor StringをSPIで変更できるということで、上記の様な処理 init_pcm2707() を行っています。(必須の処理ではありません)
 ただ、Vendor Stringは表示されませんでした。Product Stringは、当方が設定した文字列が出ているのがわかると思います。この文字列の文字コードは、ANSI ASCIIコードです。おそらくプログラムでそのまま書けると思うのですが、よくわからなかったので
Wikipediaを見ながら書き写しました。
 このほか、USB電流の指定もでき、PC直結の場合USBルートハブのプロパティで”電力”タブを選べば確認できます。セルフパワーなのに指定することに意味があるのかは?ですが。


USBオーディオデバイスのプロパティ


サウンドとオーディオデバイスのプロパティ


USBルートハブのプロパティ(”電力”タブ)


生成したSPI波形(全体)


生成したSPI波形(拡大)


その他の問題

  • I2S出力時にアナログ音声が出ない
    これは悩みました。I2Sのデジタルデータは出力されるのに、アナログ出力は無音のままです。
    内蔵のミュート回路が動作しているのかと、いろいろ原因を調べましたが問題なし。
    ダメもとでググってみたら、森秀樹氏のブログのコメントにI2S出力モード(FSEL=L)では、
    アナログ出力なしと書いてある。他の人のところでも同じ現象が起きるなら、それで正解か。
    でもデータシートにはそんな記述は見あたらないように見える。俺の1日を返せという気分。

  • アナログ出力レベルが低い
    10kΩ負荷で1.8Vp-p出ると書いてあるのに、80mVp-pくらいしか出ません。
    詳しく調べていませんが、お気楽オーディオキットの記事にヒントがあるかも知れません。

2010年2月20日土曜日

USB FMトランスミッタ: お手上げ!


Si4710を使ったFMトランスミッタですが、うまく動かせないでいます。
ソフトとハードの両方の問題を抱えているようで、ひとまず作業ストップ。
とりあえず、現時点までの成果を備忘録がてら掲載します。


問題点

  • Si4710をアナログ入力とした場合、Lチャンネルの音がまったく出ない。
ミューティングは利いていないはず。
    QFNのはんだ付け不良でLチャンネル入力がつながっていない?
  • Si4710をデジタル入力とした場合、送信出力が出ない。
コマンドが不正?
    QFNのはんだ付け不良でDCLK入力がつながっていない?


基板の外観

 製作後の設計変更や修正でジャンパが増えてしまいました。


基板おもて



基板うら

回路図
 画像をアップしたら、見にくくリサイズされてしまいました。

回路図


電源
PCからUSB経由で電源の供給を受けます。USBのVBUS端子の電圧は5Vで、ローパワーデバイスは100mA、ハイパワーデバイスは500mA(USB2.0)までです。500mAまで使用するには、PCのUSBコントローラと通信して使用電流を設定する必要があります。今回は、動作の自由度を考えてPCM2707をセルフパワーとして設定していますが、PC側で電源を遮断されることもなく、実際にはUSBから電源供給を受けるバスパワーとして動作させています。
 それに、(高級なPCはともかく)現実的にはUSBコントローラ側で詳細に電流を管理しているとは思えず、実際にはセルフパワー扱いでも100mA以上の電流が取り出せるのではと思っています。(巷で売られているUSB携帯充電器とかUSB扇風機にコントローラチップが入っているとは思えません)
VBUS端子の5Vを、LDOの3端子レギュレータで3.3Vに安定化して、各負荷に供給します。クリスタルオシレータモジュールも3.3V動作のものを選定しています。なお、回路がショートしたりしてもPC側に影響を与えないよう、念のためポリスイッチを電源ラインに挿入しています。
なお、USBの規格書は、usb.orgからダウンロードできます。が、ドキュメントが膨大でとても読み込めません。


USB miniBコネクタ
 HDDケースにもともとついていたコネクタはUSB miniBコネクタでした。今まで使ったことのあるBコネクタは4ピンですが、このminiBコネクタは5ピンです。どうもUSB On-The-GoというUSB2.0を補足する規格に対応した"ID"端子が追加されている模様で、ホスト側(ID端子をGNDに接続)あるいはデバイス側(ID端子はオープン)かの判別に使われるそうです。とりあえずオープンにしておきます。

USBの信号電圧
 VBUSは5Vですが、2つの信号線D+, D-の信号電圧は3.3V系です。USB機器が接続されたことをPC側に伝えるためD+端子のプルアップは3.3V系のロジックに接続します。

音楽信号を伝送中のUSBの波形


高周波回路
 Si4710に内蔵されたバリキャップと外付けのコイルとで同調回路が構成されています。このコイルから2pFのコンデンサで出力を取り出すわけですが、2pFのインピーダンスはFM帯でも1kΩくらいあるのでかなり出力が低下します。図面では、さらに10dBのアッテネータを挿入しているので、出力レベルは実測で77dBuV(出力115dBuV設定時)くらいでした。
アンテナ出力のSMAコネクタには、基板エッジ用なるものを選びました。まさかこんな都合の良い製品があるとは・・・。
基板エッジ用コネクタ(Molex社データシートより)


I2Cインターフェース
Si4710の設定はI2Cインターフェースで行うので、制御用にAtmelのAVRチップ、ATMEGA8Lを使います。当初、SPIインターフェースで行うつもりでしたが、手持ちのISPライタで当該機能を有効にするヒューズビットの書き換えができないことがわかり急遽I2Cインターフェースに変更しました。今後の製作でもI2Cを使う予定があるのでこれを機会に覚えます。

 また、I2Cインターフェースは、バスラインをプルアップする必要があります。このプルアップ抵抗の決め方は、AVRのデータシートに記載されていました。SCLクロック周波数が100kHz以下なら、最小 (Vcc - 0.4V)/3mA から 最大1000 ns/Cbです。AVRの電源電圧は3.3Vなので、最小値は(3.3V-0.4V)/3mA = 約1kΩ。AVRの入力ピン容量は10pF、Si4710は50pF、I2Cバスの静電容量はよくわかりませんが、仮にこれらの合計を100 pFとすれば、最大1000 ns/100pF = 10kΩとなります。今回は、間をとって4.7kΩとしました。


SPIインターフェース
 PCM2707のROMに書かれたUSBディスクリプタ情報の書き換えを、SPIインターフェース経由で行います。前述のとおり、AVRに組み込まれた専用ハードは使用しませんでした。


AVRのクロック
 Si4710をデジタル入力で使用するには、サンプルレート値を設定する必要があります。ところが、PCM2707からはそのような情報が直接的には出てきません。そこで、I2SのLRクロック周期を測定することにしました。
 AVRには校正済みのRC発振器が内蔵されていますが、校正前の公称精度は±3%と役不足なのでクリスタルを使用しました。


AVRのISPコネクタ
 AVR-ISPの標準コネクタは、2.5mmピッチの6ピンタイプです。しかし、このコネクタは高さが高過ぎてケースに入りません。そこで高さの低い、日本圧着端子製造(JST)PHシリーズ(2mmピッチ)を選定しました。Digi-Keyで割安に買えるので今後の工作には同じメーカのXHシリーズ(2.5mmピッチ)やVHシリーズ(3.96mmピッチ)を使っていこうと思っています。ちなみにXHやVHがTDKラムダやコーセルの基板単体タイプの電源で採用されていることを意識しています。



2010年2月18日木曜日

USB FMトランスミッタ: 仕様の検討

 Si4710の特徴は、音声デジタル入力を備え、さらにFM変調までも(おそらく)デジタル処理されていることです。音源からデジタル接続できれば電波までをフルデジタル処理できます。しかし、せっかく専用ICを使用するわけですから、手軽さが命。極力シンプルな構成となるよう仕様を絞り込みました。

ブロック図


音声入力
手元の音源としては、CD/DVDプレーヤとPCのMP3があります。FM電波で音楽信号を伝送する場合、プリエンファシスにより高域信号のレベルが持ち上がってしまうので、そのぶん音量を絞り込む必要があります。しかしそれでは、S/Nが不足し一般の放送局と比べ大幅に聞き劣りすることになります。このため、FM放送局と同様な音質とするにはコンプレッサ/リミッタ等の前処理が必要になります。FM放送の特性に適したコンプレッサ/リミッタの専用ハードは業務用に限定されますが、PC用のソフトウェアなら安価に入手できます。そこでPCに接続することを前提とします。PCのデジタル音声出力としては、主にUSBとS/PDIFがありますが、後者は一般的ではありません。USBなら電源も取れますし、USBオーディオICも入手容易ですのでUSB接続を前提としました。なお、S4710には、シングルバンド構成ながら本格的なコンプレッサ/リミッタを内蔵していますので、その能力も聴いてみたいと思っています。

 USBオーディオICとしては、テキサスインスツルメンツのPCM2704がキット等でも広く使われています。このシリーズからI2Sフォーマット出力に対応しUSBディスクリプタ情報をSPI経由で変更できるPCM2707を選定しました。音声入力信号の仕様は、USBオーディオICの都合により対応サンプルレートは32/44.1/48kHz、16ビットとなります。


法規制
FMト ランスミッタは、免許が不要な微弱電波の範疇で使用しなくてはなりません。今回使用するケースは金属製のため、外部端子を設けて ケース外にアンテナを取り付けます。電波法の規制は、送信出力ではなく電界強度の上限が定められているため、その範囲を逸脱しないよう慎重に決めることに します。


ケースなど
以前、じゃんぱらで購入した2.5インチ用のUSB HDDケースです。USBコネクタやLEDの穴はそのまま使います。このケース、厚みが薄いので高さ制限を気にしながら部品選定します。


USB HDDケース



付加機能
 ケースの内部の基板上にDIPスイッチを設け、周波数の変更、モノラル・ステレオ切替、Si4710内蔵コンプレッサ/リミッタのオン・オフができるようにします。


Si4710とは関係ないですが・・・
 AVRマイコンでステレオ変調器を作っている人のページを見つけました。こんな方法があるとは!


2010年2月14日日曜日

FMトランスミッタICいろいろ

 最近のFMトランスミッタICをピックアップしてみました。ポータブルナビゲーションや携帯音楽プレーヤ等への組み込みを想定しているらしく、小型なQFNパッケージを採用している場合が多いようです。

THG4649
ザインエレクトロニクス(東京のファブレス半導体メーカ - THine Elecdtronics)の製品です。2006年のプレスリリースによると、PLL/VCO内蔵・アナログ音声入力、S/N 70dBを誇るということです。Webの製品情報に掲載されていないので生産中なのかディスコンなのかよくわかりません。チップワンストップでは取り扱いがあるようですが要問い合わせとなっています。なお、このICを採用した製品がバッファローコクヨサプライから販売されています。後継機種も同様な仕様を謳っているのでキーパーツは同じかもしれません。


SN761634
 テキサスインスツルメンツ製です。SN761634のほかラジオ付きのSN761633もあります。15kHzの音声ローパスフィルタ、PLL回路も内蔵しており、2.5~4V動作、アナログ音声入力、送信周波数76-108MHz、歪み率1%、SN55dB、セパレーション30dB。パッケージはQFN24ピン。


Si47xxシリコンラボラトリーズのSi4710は、QFN20ピンの小さなパッケージながら、デジタル音声入力に対応し、詳細は不明ながらおそらくデジタル処理型のFM変調を行っているものと思われます。このほか、音声コンプレッサ・リミッタを備えています。同社のFMトランスミッタチップには6種類の製品があります。

  • Si4710
FMトランスミッタ
  • Si4711
Si4710のRDS(Radio Data System; 欧米の文字多重放送みたいなサービス)付き
  • Si4712
Si4710のRPS(Receive Power Scan -周波数の空きチャンネルのシーク機能)付き
  • Si4713
Si4711のRPS(Receive Power Scan -周波数の空きチャンネルのシーク機能)付き
  • Si4720
FMトランシーバ。Si4711のFM受信機付き
  • Si4721
FMトランシーバ。Si4713のFM受信機付き


 いま、現時点で入手可能なFMトランスミッタICとしては最も先進的に見えるSi4710を使ったUSB FMトランスミッタの製作に取り組んでいます。

2010年2月7日日曜日

綿棒でCDプレーヤの音飛び修理

 家電の修理はホント特殊技能だと思います。趣味で回路をいじるからと言ってコンセントに差さるものなんでも直せやしません。図面があっても、測定器があっても、情報力そして根気と機転と運がないとムリだと思います。だって、目的は原因究明じゃなくて原状回復ですからね。
 でも今はGoogle先生に聞けば、(答えはなかなか教えてくれませんが)先人達が残したヒントくらいは提示してくれます。

 さて、今回のお題は、CDの音飛び修理です。捨てるくらいなら、(どうせピックアップレンズが汚れているんだろう、ボクにもできるかも)見てやるよと安易に引き受けました。
 これは、パナソニックのHD510というコンポに含まれるCDプレーヤです。こういう盆栽的オーディオって好きですが、リアパネルを見ると印象が変わります。アンプとは電源を含めフラットケーブル接続のため単体動作できません。仕方なくコンポ一式を持ち帰るハメになりました。


パナソニックのCDプレーヤ


 症状は、時折、小刻みに音が飛ぶというものです。これでは『春の海』も落ち着いて聴けません。早速、フタを明けて中を見てみますが、筐体内のピックアップレンズは曇りひとつ無い輝きを放っています。そういえば、このデッキは美術品のギャラリーで使われていたもの。当然、周囲でタバコを吸う者などいなかったことでしょう。当てが外れて、あれ?って感じです。

 そういうわけで早速、Googleで検索してみると、この故障の答えが書かれた 海外の不便な地で生活している方々のための家電修理のヒント というページが見つかりました。

”音飛びの原因は、CDの傷や汚れか、ピックアップキャリッジ(レール)のグリースの劣化(=硬化)によるもの、スピーカーの振動によるものなどです。 ~中略~ どのCDを再生しても同じ程度の演奏時間の経過後に音が飛び始める場合は、ピックアップのレール上の固くなったグリースやほこりをアルコール綿棒で拭き取ってください。

 半信半疑でレール上のグリスを拭き取ると、確かに音飛びは解消されました。ところでkakaku.comで発売年を調べてると2001年2月、もう9年たっています。それならグリースの固化も仕方ないですかね。


CDドライブを取り出し(結果的には取り出さなくても良かった)


固化したグリスを除去(ピックアップ近くのレールに白いのが見えます)


動作確認

 なお、CDのピックアップレンズからはレーザ光が出てきます。あなたの目に損傷を与える恐れがありますから修理される向きは自己責任でどうぞ。(私は危険性をよく知らないので、通電時にはかならずカバーを付けるなどかなり怖がって触っています)

2010年2月3日水曜日

ルビジウム発振器(7): 動作確認・測定

出力レベル、スプリアス
 ヒートラン後に再度分配増幅器の同調コイルを調整してみました。結構同調ズレがみられました。同調コンデンサに温度補償型ではなく一般品を使ったせいかもしれません。レベルは、実測値で約6.3dBmと目標仕様の7dBmより若干低めとなりました。

 心配していた、スイッチング電源ノイズの目立った影響はありません。1MHz以下にノイズがありますがレベルも低く特段問題ないでしょう。


スプリアス(DC-20MHz)


スプリアス(15-100MHz)


 一方、高調波は結構出ています。分配増幅器出力での2倍が-49dBc、3倍が-48.5dBc程度です。ルビジウム発振器の出力は直接測定していませんが、JA9TTT加藤氏のブログの測定波形によれば、2倍が-60dBc、3倍が-72dBc程度と良いので、分配増幅器の歪みによるものと推測しています。

 分配増幅器で使用した電流フィードバック型オペアンプAD813の データシートによると、高調波ひずみは電源電圧と負荷抵抗によって異なるようです。今回は、単電源24V動作で負荷は100Ω程度です。負荷がグラフの例 より重めということで、Vs=±5V寄りの特性であると仮定すれば、基本波10MHzの2次、3次高調波とも約-50dBc程度であると類推され実測値に 近いと言ってもいいのかな、と思います。

AD813の高調波ひずみ(データシートより)



位相雑音
 スペアナに位相雑音の測定機能を使ってみます。キャリアから10Hz離れで約-90dBc/Hzという結果となりました。LPRO-101の標準値は-96dBc/Hz(@10Hz offset)なので少し悪いです。何故?・・・というか、測定法はこれで良いのか。また、参考までにHP 8657A信号発生器も測定してみました。

2010.2.6追記
 位相雑音が期待よりも高めに測定された理由は、Noise Bandwidthが5Hzと広く設定していたせいだと思います。図でわかるように5Hzの帯域内でスペクトラムに勾配がありますが、この帯域内で平均をとればちょうどキャリアから10Hz離れのノイズレベルと一致するからいいだろうと思っていました。でもこのグラフの縦軸は、dB表示なので平均値を計算するには、いったん真数に戻して平均をとらなくてはいけません。基本的なところですが、忘れていました。この図の設定とスペクトラムの形状から想像すると、C/N値は概算で7~8dB程度悪く表示されていたのでは、と推測しています。(実機で確認したわけではありませんが・・・)

2010.2.7追記
 C/N値の概算推定値ですが、あまり変わらない、と訂正しておきます。
 この波形はデシベル表示で帯域内の周波数特性がフラットではないので帯域電力は帯域内の最大値により大きく影響を受けます。このノイズ測定帯域内ではキャリア+5~+6Hz付近の電力が最大で約-80dBm。1Hzあたりに換算すると、5で割って(-7dB)、-87dBm/Hzとなります。キャリアレベルは+5dBmなので、ノイズ帯域5HzでのC/Nは-92dBc/Hz。
 次に、キャリア+10Hz付近の1Hz帯域に着目すると-85dBmなので、C/Nは-90dBc/Hzとなります。


LPRO-101のC/N(分配増幅器出力で測定)



HP 8657AのC/N



周波数
 一番、肝心な周波数については、放送局の基準信号(おそらくルビジウム)に同期しているであろう地デジのシステムクロックを測定してみました。
 周波数カウンタの外部基準入力にルビジウム発振器を接続して、システムクロック512/63 = 8.126984126・・・MHzを測ったところ、0.1Hzの桁まで一致していたので少なくとも10のマイナス8乗程度までの精度は出ていると言ってもいいでしょう。


地デジのシステムクロック


アラーム表示
 ルビジウム発振器同期とルビジウムランプ異常の2つをつけました。電源投入時は、同期表示は消灯、ランプ異常は光出力が低いので点灯します。数分待つと、同期表示が点灯するようになりますが、点灯したばかりのときは、周波数が数ヘルツオーダーでドリフトしますが、さらにしばらくするとピタッとドリフトが止まります。

電源投入時の表示


同期表示


温度上昇
 2時間ほどヒートランして、ルビジウム発振器の温度を測定したところ約40度(周囲温度20℃)と20℃温度上昇していました。規格値は70℃以下ですので、真夏の室温を最大40℃と想定すればなんとか大丈夫というところだと思います。


おわりに
 ルビジウム発振器の機能としては単に正確な10MHzが出てくるだけですが、ジャンクで原子同期の周波数標準を手に出来るなんて10年前なら予想すらできなかったことで素晴らしいことです。今回の製作は、心臓部に出来合いのユニットを使っているため、電源や分配増幅器と言った周辺とのインターフェースを検討するだけでしたが、それでもいざやってみるとわからないこと、思い通りに行かないことが多く勉強になりました。また、完成品の出来映えは概ねイメージ通りでしたが、パネルのデザインは地味でパンチがないと感じてまして少々不満が残りました。(こういうのは苦手です・・・)