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規格)
※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ビット幅なので、上位ビット→下位ビットの順で書き込む(読み込む)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
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; <span style="color: #0000ff;">void</span> <span style="color: #993366;"><strong>init()</strong></span>{ FM_RST = 0; delay_ms(1); FM_SEN = 1; delay_ms(1); FM_SDIO = 0; delay_ms(1); FM_SCLK = 0; delay_ms(1); } <span style="color: #0000ff;">void</span> <span style="color: #800080;">modShadowReg(char reg, unsigned int value, char set_reset)</span>{ 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 } } <span style="color: #0000ff;">void</span> <span style="color: #800080;">FM_Write(char reg, unsigned int value, char set_reset)</span>{ 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; } <span style="color: #0000ff;">void</span> <span style="color: #800080;">FM_Read(char cmd)</span>{ //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; } <span style="color: #0000ff;">void</span> <span style="color: #800080;">FMClick_Init()</span>{ 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); } <span style="color: #0000ff;">char</span> <span style="color: #800080;">FM_Activate()</span>{ 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]; } <span style="color: #0000ff;">void</span> <span style="color: #800080;">FM_Tune(unsigned int freq)</span>{ 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 } } <span style="color: #0000ff;">void</span> <span style="color: #800080;">FM_Seek(char direction)</span>{ 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); } } <span style="color: #0000ff;">void</span> <span style="color: #800080;">SetVolume(char vol)</span>{ 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); } } <span style="color: #0000ff;">void</span> <span style="color: #800080;">main()</span> { 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変調では搬送波周波数を中心として、変調信号とは違うスペクトラムが発生するので非線形変調となります。