Si4703採用 FMラジオチューナークリック [CLICK-FM]

Si4703採用 FMラジオチューナークリック (CLICK-FM)は、シリコンラボラトリーズ社製のワンチップFMラジオチューナーIC、Si4703を搭載したFMラジオ受信ができるクリックボード(※1)です。電源電圧は3.3Vです。

※1:クリックボードは、mikroBUS規格のインターフェースを搭載した様々な機能性ボードの総称です。

制御は同期式シリアル通信のI2Cで行います。SCL及びSDA線はいずれも4.7kオームの抵抗でプルアップされています。

76MHz~108MHzのFM波を受信して復調することができます。出力段には2つの300mWオペアンプLM4864を搭載しており十分な音声レベルの出力を得ることができます。

Si4703はボリュームコントロール機能があるので、I2C通信で音声出力のレベルを設定できます。通常は-28dBから0dBまで指定できます。なおVOLEXTレジスタを1にセットすると-58dBから-30dBの範囲で指定できるようになります。

本体には3.5mm径のステレオジャックが搭載されています。ここに1m程度のイヤホンケーブル等を接続すると、その線がアンテナ線として機能します。必ずイヤホンやスピーカーケーブルを接続してご使用ください。

 

  • ワールドワイドなFMバンド設定、76~108MHz
  • シークチューニング機能
  • 自動ゲインコントロール(ACG)内蔵
  • 優れた過負荷耐性
  • アダプティブノイズサスペンション機能
  • 自動周波数コントロール機能
  • 電子ボリューム機能
  • LM4864オペアンプ搭載 (L,R両方)
  • アンテナ兼用の3.5mmイヤホンジャック
  • I2C通信による制御(2線式か3線式かの選択可能)
  • 電源電圧は3.3V

 

使い方の詳細はSi4703のデータシートをお読み下さい。


■本体回路図

クリックして拡大できます。


■インターフェイス端子(mikroBUS規格)

 

機能詳細 ピン名 Mikrobus logo.png ピン名 機能詳細
Si4703のGPIO2へ  GPIO2 1 AN PWM 16 NC
#リセットピン  #RST 2 RST INT 15 GPIO1 Si4703のGPIO1へ 
#セレクトイネーブル #SEN 3 CS TX 14 NC
NC 4 SCK RX 13 NC
NC 5 MISO SCL 12 SCL シリアルI2C クロック
NC 6 MOSI SDA 11 SDA シリアルI2C データ
+3.3V 電源入力 3.3V 7 +3.3V +5V 10 NC
Ground GND 8 GND GND 9 GND Ground

 

※NCは未使用ピンです。#記号は負論理です。

Si4703のGPIO1/2/3をデジタルLowに設定して消費電流を低減します。このレジスタ内の他のすべてのビットは、最後に読み取られた値に維持される必要があります。

 

3  #SEN 3線式の制御にした場合に使用します。2線式の場合にはSCLの立ち上がりエッジでSENピンがLレベルに設定されると転送が開始されます。データの最後では、SENピンをハイにした後、次のSCLの立ち下がりエッジで、データがラッチされます。(下図参照)

データシートの8ページ参照

11 I2C SDA I2Cスレーブデータ線 内部で4.7kオームでプルアップ済み
・7ビットI2Cスレーブアドレス:0x20 (固定)
・100KHz又は400KHz
12 I2C SCL I2Cスレーブクロック線  内部で4.7kオームでプルアップ済み
1,15 GPIO2

GPIO1

デフォルトではハイインピーダンスになっています。レジスタの設定で入出力方向及び論理を変えられます。詳しくはデータシートの14ページを参照下さい。

 


■I2C通信とレジスタ

本モジュールに搭載のを2線式で使用する場合のI2Cの7ビットスレーブアドレスは0x10に固定されています。Readで1を加算、Writeで0を加算します。

3線式の場合のタイミングダイアグラム

 

2線式の場合のタイミングダイアグラム

下記2線式での内容を解説します。(3線式についてはデータシートを参照ください。)

2線式、3線式どちらを使うかは本体電源投入時のピンの状態により決まります。2線式I2C通信を実行するには、本体の電源投入時にSDIOピン(I2CのSDAピン、11ピン)をLレベルにします。#SENピン(3ピン)をHレベルにすると、2線式になります。フローチャートは下記の通りです。(GPIO3は本ボードではGNDの方(=Noの方)に進みます)

レジスタ番地は、「Writeの場合」にはレジスタ0x02の上位バイトから始まり続いてレジスタ0x02の下位バイトとなります。そして、自動的にインクリメントされ0x03の上位バイト→下位バイト→0x04の上位バイト→下位バイト・・・と続きます。レジスタの最後の番地まで到達すると0x00に戻ります。I2Cのストップコンディションで終了と見なされます。

「Readの場合」にはレジスタ0x0Aの上位バイトから始まり続いてレジスタ0x0Aの下位バイトとなり、同様にインクリメントされます。レジスタの最後の番地まで到達すると0x00に戻ります。I2Cのストップコンディションで終了と見なされます。

 

例えばレジスタ0x07に値を書き込みたい場合には、レジスタ0x07を直接指定する方法はないので、レジスタ0x02からスタートして、10進めて(マイコンの場合には10回I2C_Writeを実行して)(0x02hの上位→下位→0x03の上位→下位・・・)0x07の上位バイトに到達して、そこから上位、下位の順番で書き込むことになります。この辺りはプログラムに工夫が必要となります。実際のプログラム例は本ページの例を参考にしてください。

レジスタ一覧は、データシートの22ページに記載があります。

 


■設定とプログラミング例

日本で使用する場合には特に0x05レジスタでバンド設定と、スペーシング設定を日本にする必要があります。

0x05レジスタの7:6ビット BAND[1:0]がバンドの国設定です。

BAND[1:0] Band select
00 87.5–108 MHz (US / Europe, Default)
01 76-108MHz(Japan wide band)
10 76-90MHz(Japan)

 

0x05レジスタの5:4ビット SPACE[1:0]がスペーシング(チャンネル間隔)の国設定です。

SPACE[1:0] Channel spacing
00 200 kHz (US / Australia, Default)
01 100kHz(Europe,Japan)
10 50kHz

 

よっていずれもレジスタの値を 01 にする必要があります。この設定を誤るとデフォルトの設定となってしまいFMラジオの受信ができません。実際の設定値はプログラム例で確認ください。

選局するFMラジオ局の周波数は、次の式で計算します。

Freq(MHz) = 0.1 x Channel + 76

最初の0.1MHzはスペーシング値、最後の76MHzはバンドの最小値となります。例えば東京の場合80.0MHzは東京FMですが、その場合には次のようになります。

80.0(MHz) = 0.1 × CH + 76

CH = 40  となるので設定値は40となります。この辺りの実装方法もプログラム例でご確認下さい。

 


プログラムのポイント

・レジスタに書き込むデータを配列として予め用意しておく。

・レジスタに書き込まれている既存の内容は新しいデータを書き込む前に一度コピーしておく。

・レジスタに値を書き込む場合には、必ず0x02レジスタから始まるので、目的のレジスタまでインクリメントする。

・レジスタの値を読む場合には、必ず0x0Aレジスタから始まるので、目的のレジスタまでインクリメントする。

・レジスタは16ビット幅なので、上位ビット→下位ビットの順で書き込む(読み込む)。

 

char powerOn, volume, i;
int freq, ChipID;
char shadowReg[32];

char stereo,str_size,flag_RDS,flag_Clock;

const char FM_all_Reg = 0x0F;
const char FM_0a0b_Reg = 0x02;

const char Si4703_address = 0x20;

void init(){

    FM_RST = 0;    
    delay_ms(1);

    FM_SEN = 1;
    delay_ms(1);
    
    FM_SDIO = 0;
    delay_ms(1);
    FM_SCLK = 0;
    delay_ms(1);

}

void modShadowReg(char reg, unsigned int value, char set_reset){

    char reg_pos;       //Index of register byte in shadow register
                        //Reading of Si4703 registers start from 0x0A-0x0F and then starts from 0x00-0x09
                            
    reg_pos = 12 + (reg << 1);  //Calculate position of register which needs to be modified

    if(set_reset == 1){         //Sets the desired bits in register
    
        shadowReg[reg_pos] = shadowReg[reg_pos] | (Hi(value));    //Split value to higher
        reg_pos++;
        shadowReg[reg_pos] = shadowReg[reg_pos] | (Lo(value));    //and lower nibble

    }else{ //Resets the desired bits in register

        shadowReg[reg_pos] = shadowReg[reg_pos] & ( ~Hi( value ) );  //Split value to higher
        reg_pos++;
        shadowReg[reg_pos] = shadowReg[reg_pos] & ( ~ Lo( value ) ); //and lower nibble
    }
}

void FM_Write(char reg, unsigned int value, char set_reset){
    
    char wr_cnt;
    char ii;

    wr_cnt = ( reg - 1 ) << 1;               //Calculate how much bytes need to be written
    modShadowReg(reg, value, set_reset);     //Modify register bytes

    I2C1_Start();
    I2C1_wr(Si4703_address);

    for(ii=16; ii<16+wr_cnt; ii++){
        I2C1_Wr(shadowReg[ii]);
    }
    I2C1_stop;
}

void FM_Read(char cmd){       //Reading of registers starts from 0x0A register
  
  char ii;

  if (cmd == FM_all_Reg){
     I2C1_Start();
     I2C1_wr(Si4703_address + 1 );
     
     for(ii=0;ii<30;ii++){
        shadowReg[ii] = I2C1_rd(1);
     }
     shadowReg[31] = I2C1_rd(0);
     I2C1_Stop;
  }
  
  if(cmd == FM_0a0b_Reg){           //Read only STATUS and READCHAN registers
     I2C1_Start();
     I2C1_wr(Si4703_address +1 );

     for(ii=0;ii<2;ii++){ shadowReg[ii] = I2C1_rd(1); } shadowReg[3] = I2C1_rd(0); I2C1_Stop; }
    }
  
    modShadowReg(0x02, 0x0300, 0);
    FM_Write(0x02, 0x0100, 0);      //Deactivate SEEK,keep

    while( STC_bit == 1 ){
        FM_Read(FM_0a0b_Reg);
        STC_bit = (shadowReg[0] & 0b01000000) >> 6;
        delay_ms(60);
    }
    
    FM_Read(FM_0a0b_Reg);
    SF_BL_bit = (shadowReg[0] & 0b00100000) >> 5;    
}

void FMClick_Init(){
    FM_RST = 1;               //Init chip done - 2wires interface car SDIO=0 et SDEN=1
    delay_ms(1);
    I2C1_Init(100000);        //Initialize I2C module for communication with FM Click board

    FM_Read(FM_all_Reg);
    ChipID = shadowReg[14] << 8;         //Save Chip ID before powerup.
    ChipID = ChipID | shadowReg[15];

    delay_ms(1);

    modShadowReg(0x07, 0x0081, 0);
    FM_Write(0x07, 0x8100, 1);           //Activate Crystal Oscillator

    delay_ms(500);

}

char FM_Activate(){
    
    unsigned int active;
    active = ChipID;
    
    FM_Read(FM_all_Reg);
    modShadowReg(0x02, 0x4801, 0);
    FM_Write(0x02, 0x4801, 1);      //Write ENABLE bit, disable Mute , RDS mode = Verbose
    delay_ms(200);
    
    while (active == ChipID){       //Wait for module powerup
        FM_Read(FM_all_Reg);
        active = shadowReg[14] << 8; 
        active = active | shadowReg[15]; 
    } 

    modShadowReg(0x04, 0xFFFF, 0);  //Write default Volume and RSSI Seek Threshold values 
    FM_Write(0x04, 0x1800, 1);      
    modShadowReg(0x05, 0xFFFF, 0);  //Write default values
    FM_Write(0x05, 0x105F, 1);      //RSSI seek Threshold = 0x10, band = Japan, Channel spacing = 100kHZ, Volume = max 
    delay_ms(110); 
    powerOn = 1;                    //Update powerOn flag 

    return shadowReg[0]; 

} 

void FM_Tune(unsigned int freq){

    unsigned int calc; 
    char STC_bit; 

    STC_bit = 0; 
    calc = freq; 
    
    calc = ((freq *10) - 7600) / 10;
    calc = calc | 0x8000;              //Set TUNE Flag
   
    if (powerON == 1){                 //Check for power state of FM click
        FM_Read(FM_0a0b_Reg); 
        modShadowReg(0x03, 0x83FF, 0); 
        FM_Write(0x03, calc, 1);       //Write modifiled frequency value + activate tune 
        delay_ms(60); 

        while (STC_bit == 0){
            FM_Read(FM_0a0b_Reg); 
            STC_bit = (shadowReg[0] & 0b01000000) >> 6;
            delay_ms(60);
        }
        
        FM_Write(0x03, 0x8000, 0);     //Set TUNE = 0
    }else{
        ModShadowReg(0x03, calc, 1);   //If power is off, only modify the shadow register
    }
 
}

void FM_Seek(char direction){
  char STC_bit, SF_BL_bit; 

    if(direction == 1){ 
        modShadowReg(0x02, 0x0300, 0); 
        FM_Write(0x02, 0x0300, 1);            //Activate SEEK direction up and Seek ON 
    }else{ 
        modShadowReg(0x02, 0x0300, 0); 
        FM_Write(0x02, 0x0100, 1);            //Activate SEEK direction down and Seek ON 
    } 
    
    STC_bit = 0; 

    while( STC_bit == 0 ){
        FM_Read(FM_0a0b_Reg); 
        STC_bit = (shadowReg[0] & 0b01000000) >> 6;
        delay_ms(60);
    }
}

void SetVolume(char vol){

    if ( ( vol >= 0 ) && ( vol <= 16) ){
        vol = vol & 0x0F;
        modShadowReg(0x05, 0x000F, 0);
        FM_Write(0x05, vol, 1);         //Write Volume value to SYSCONFIG2 register
        delay_ms(5);
    }
}
void main() {

    init();
    FMClick_Init();
    FM_activate();

    FM_Tune(800);        //80.0MHz
    
    while(1){
        if(Button(&PORTD,0,5,0)){
            FM_Seek(1);
        }

        if(Button(&PORTD,1,5,0)){
            FM_Seek(0);
        }
        
        if(Button(&PORTD,2,5,0)){
            if(volume<15){
                volume = volume + 1; 
            } 
            SetVolume(volume); 
        } 

        if(Button(&PORTD,3,5,0)){ 
            if(volume>0){
                volume = volume - 1;
            }
            SetVolume(volume);
        }
        
    }

}

 

※参考

FM変調とは?
FM変調とは、変調する信号の振幅に応じて搬送波の周波数を変化させる方法です。よって変調波の周波数は連続的に変化します。FM変調では搬送波周波数を中心として、変調信号とは違うスペクトラムが発生するので非線形変調となります。

wpmaster