======FM rádio s Si4703======
=====Zadání=====
* Vytvořte rozhraní pro ovládání modulu FM rádia Si4703 s pomocí vhodného zobrazovacího média
* Zobrazte informaci o aktuální naladěné stanici pomocí RDS
* Použijte vývojový kit [[http://developer.mbed.org/platforms/KL25Z/|FRDM-KL25Z]]
=====Integrované rádiové moduly=====
Většina mobilních telefonů a MP3 přehrávačů mají v sobě automaticky radiový přijímač. Není se čemu divit, výrobci je integrovali do velmi malých pouzder a při jejich masové výrobě je cena příznivá. Všechny potřebné funkce mají již v sobě (RDS, potlačení šumu, AGC, automatické vyhledávání stanic, atd.), takže stačí pouze přidat výstupní zesilovač nebo využít digitálních audio výstupů. Nejvíce mě zaujala nabídka [[http://www.silabs.com/products/audio/Pages/default.aspx|Silicon Labs.]] Nabízí mimo přijímačů i vysílače nebo kombinace obojího. Vysílače umožňují velmi jednoduše spojit MP3 přehrávač nebo mobilní telefon přímo s autorádiem. (pozn.: Česká legislativa něco takového neumožňuje)
=====Hardware=====
Vybral jsem si modul FM rádia [[https://www.sparkfun.com/datasheets/BreakoutBoards/Si4702-03-C19-1.pdf|Si4703]]. Ten má v sobě zabudovaný i RDS procesor a další funkce. Zapsáním hodnoty do příslušného registru ovládáme VCO (oscilátor řízený napětím), s pomocí kterého obvod naladí frekvenci. Modul již má audiozesilovač, takže stačí jen připojit sluchátka, které slouží zároveň i jako anténa.
{{http://i.ebayimg.com/images/a/T2eC16d,!)sE9swm,vE2BQWsmQfquw~~/s-l300.jpg}}
Jako zobrazovací médium nám poslouží kompaktní OLED displej s řadičem SSD1306, rozlišením 128x64 pixelů a úhlopříčkou 0.96 palce. Komunikace probíhá pomocí sběrnice i2c.
Budeme ho ovládat pomocí vývojového kitu [[http://www.nxp.com/products/software-and-tools/hardware-development-tools/freedom-development-boards/freedom-development-platform-for-kinetis-kl14-kl15-kl24-kl25-mcus:FRDM-KL25Z|FRDM-KL25Z.]]
=====Software=====
Vývojové prostředí www.mbed.org umožňuje vyhledat a importovat knihovny, které vytvořili ostatní uživatelé. Takto se podařilo získat knihovny pro FM modul i displej. Následující vytvořený kód má za úkol zobrazit hlavní obrazovku, na které bude zobrazena aktuálně naladěná frekvence, možnost manuálního i automatického ladění, nastavení hlasitosti a dat z RDS.
----
**Velký počet proměnných, které jsou třeba například pro uložení nastavení mně pomohly zpřehlednit struktury**
/*
+-----------------------------------------------------+
| |
| |
| FM radio s Si4703 |
| |
| |
+-----------------------------------------------------+
*/
// Autor: Adam Stepanek
// Vytvoreno v ramci projektu MPOA 2016
// Inkluze knihoven
#include "mbed.h"
#include "SparkFun-Si4703.h"
#include "SSD1306.h"
#include "millis.h"
// Definice pro tlacitka
#define LEFT 0x01
#define CENTER 0x02
#define RIGHT 0x03
// Definice pro displej
#define MAIN 0x01
#define MENU 0x02
#define ONOFF 0x04
#define VOLUME 0x05
// Konstruktory
Serial pc(USBTX, USBRX); // tx, rx
Si4703_Breakout radio(I2C_SDA, I2C_SCL, PTA1, &pc);
// Nastaveni I/O
DigitalIn btn_left(PTA5);
DigitalIn btn_center(PTC8);
DigitalIn btn_right(PTC9);
// Definice funkci
int button_pressed(void);
void show_frequency(void);
void show_screen(int screen, int position);
// Prostor pro ulozeni nastaveni
typedef struct{
bool rds;
int volume;
bool tuning;
}Menu_Setup;
Menu_Setup setup;
// Prostor pro ulozeni aktualni frekvence
typedef struct{
int current;
int high;
int low;
}Frequency_Info;
Frequency_Info frequency;
// Nezbytne promenne
char rds[8]; // Zde se uklada vysledek cteni RDS
char volume_char[2]; // Aktualni hlasitost prevedena na text
char channel_char[4]; // Aktualni frekvence prevedena na text
int btn_pressed = false; // Bylo v minulosti zmacknuto tlacitko?
bool exit_menu = false; // Navrat z menu
bool exit_submenu = false; // Navrat z menu druhe urovne
int menu_position = 0; // Ulozeni aktualni pozice sipky v menu
int submenu_position = 0; // Ulozeni aktualni pozice sipky v submenu
----
**Hlavní funkce programu**
// HLAVNI FUNKCE
int main() {
init(); // Inicializace displeje SSD1306
cls(); // Smazani obrazovky
startMillis(); // Zapnuti pocitani milisekund
radio.powerOn(); // Zapnuti radia
setup.volume = 2;
radio.setVolume(setup.volume); // Nastaveni vychozi hlasitosti
frequency.current = 760; // Naladeni vychozi frekvence
radio.setChannel(frequency.current);
show_screen(MAIN, 0); // Zobrazeni hlavni obrazovky
show_frequency(); // Zobrazeni aktualni frekvence
// HLAVNI SMYCKA
while(true){
if(setup.rds){ // Cti RDS pokud je tato funkce zapnuta
radio.readRDS(rds, 1500); // Cteni RDS s timeoutem 1.5 sekundy
OLED_ShowStr(50, 5, rds, 1);// Zobrazeni vysledku RDS, pokud byla prijata
}
switch(button_pressed()){ // Ceka, dokud nebude zmacknuto tlacitko
case LEFT: // Zmacknuto leve tlacitko
if(setup.tuning) // Je zapnuto manualni ladeni?
radio.setChannel(--frequency.current); // Manualni ladeni
else
frequency.current = radio.seekDown(); // Automaticke ladeni
if(frequency.current < 760){ // Pokud dosahneme pod minimalni frekvenci,
frequency.current = 1080; // bude naladena nejvyssi
radio.setChannel(frequency.current);
}
show_frequency();
break;
case RIGHT: // Zmacknuto prave tlacitko
if(setup.tuning) // Je zapnuto manualni ladeni?
radio.setChannel(++frequency.current); // Manualni ladeni
else
frequency.current = radio.seekUp(); // Automaticke ladeni
if(frequency.current < 760) // Ochrana proti naladeni neplatne frekvence
frequency.current = radio.seekUp();
show_frequency();
break;
case CENTER: // Vstup do MENU
while(!exit_menu){
show_screen(MENU, menu_position+2); // Ukaze sipku v menu
switch(button_pressed()){ // Ceka dokud nebude zmacknuto tlacitko
case LEFT: // Zmacknuto leve tlacitko,
if(menu_position == 0) // provede se posunuti sipky
menu_position = 3;
else
menu_position--;
break;
case RIGHT: // Zmacknuto leve tlacitko,
if(menu_position == 3) // provede se posunuti sipky
menu_position = 0;
else
menu_position++;
break;
case CENTER: // Vstup do dalsi urovne MENU
exit_submenu = false;
switch(menu_position){
case 0: // SUBMENU Nastaveni Hlasitosti
exit_submenu = false;
while(!exit_submenu){
radio.setVolume(setup.volume);
show_screen(VOLUME, 0);
switch(button_pressed()){ // Pridat volume
case LEFT:
if(setup.volume == 0);
else
setup.volume--;
break;
case RIGHT: // Ubrat volume
if(setup.volume == 15);
else
setup.volume++;
break;
case CENTER: // Navrat do hlavniho menu
exit_submenu = true;
break;
}
}
break;
case 1: // SUBMENU Manualni ladeni
exit_submenu = false;
while(!exit_submenu){
cls();
show_screen(ONOFF, setup.tuning+3);
if(button_pressed() == CENTER)
exit_submenu = true;
else
setup.tuning = !setup.tuning; // Zapnuti nebo vypnuti
} // manualniho ladeni
break;
case 2: // SUBMENU Nastaveni RDS
exit_submenu = false;
while(!exit_submenu){
cls();
show_screen(ONOFF, setup.rds+3);
if(button_pressed() == CENTER)
exit_submenu = true;
else
setup.rds = !setup.rds; // Zapnuti nebo vypnuti RDS
}
break;
case 3: // Navrat z menu
show_screen(MAIN, 0);
show_frequency();
exit_menu = true;
break;
}
break;
}
}
break;
}
exit_menu = false;
menu_position = 0;
OLED_ShowStr(0, 5, "Staince: ", 1); // Smazani informaci z RDS, pokud dojde k preladeni
}
}
----
**Funkce pro zobrazováni na displeji**
// FUNKCE
// Funkce pro zjisteni, ktere tlacitko bylo stisknuto
int button_pressed(void){
btn_pressed = false;
while(!btn_pressed){
if (btn_left)
btn_pressed = LEFT; // Indikace zmacknuteho leveho tlacitka
if (btn_center)
btn_pressed = CENTER; // Indikace zmacknuteho prostredniho tlacitka
if (btn_right)
btn_pressed = RIGHT; // Indikace zmacknuteho praveho tlacitka
}
while(btn_left || btn_center || btn_right); // Dokud je tlacitko zmacknute, ceka
wait_ms(200); // Jednoduche odstraneni zakmitu
return btn_pressed;
}
// Funkce pro zobrazeni aktualni frekvence
void show_frequency(void){
frequency.high = frequency.current / 10;
frequency.low = frequency.current % 10;
OLED_ShowStr(38, 2, " ", 2);
sprintf(channel_char, "%d.%d",frequency.high, frequency.low);
OLED_ShowStr(38, 2, channel_char, 2);
}
//Funkce pro zobrazeni displeje (menu, hlavni obrazovka...)
void show_screen(int screen, int position){
switch(screen){
case MAIN: // Zobrazeni hlavni obrazovky
cls();
locate(0, 0);
OLED_ShowStr(0, 0, " FM Radio", 2);
OLED_ShowStr(0, 2, "< MHz >", 2);
OLED_ShowStr(0, 5, "Staince:", 1);
OLED_ShowStr(0, 7, " [MENU] ", 1);
show_frequency();
break;
case MENU: // Zobrazeni menu
cls();
OLED_ShowStr(0, 0, " MENU", 2);
OLED_ShowStr(0, 2, " Hlasitost", 1);
OLED_ShowStr(0, 3, " Manualni ladeni", 1);
OLED_ShowStr(0, 4, " RDS", 1);
OLED_ShowStr(0, 5, " Zpet", 1);
OLED_ShowStr(0, 7, " [VYBRAT] ", 1);
OLED_ShowStr(0, position, "->", 1); // Zobrazeni sipky na spravne pozici
break;
case ONOFF: // Zobrazeni submenu volby On/Off
cls();
OLED_ShowStr(0, 3, " Off", 1);
OLED_ShowStr(0, 4, " On", 1);
OLED_ShowStr(0, position, "->", 1); // Zobrazeni sipky na spravne pozici
OLED_ShowStr(0, 7, " [OK] ", 1);
break;
case VOLUME: // Volba hlasitosti
cls();
sprintf(volume_char, "%d",setup.volume); // Zobrazeni aktualni hlasitosti
OLED_ShowStr(60, 3, volume_char, 2);
break;
}
}
----
**Zapojení**
{{ :2015:si4703-radio:dsc4343.jpg?direct500 |}}
----
**Ukázka funkčnosti**
{{ youtube>JFyNm8yF5Js?medium }}
=====Závěr=====
Výsledkem projektu je vytvoření aplikace, které spojuje modul rádia, displeje a tři tlačítka v použitelný radiopřijímač. Kromě automatického ladění je možné frekvenci zadat i ručně, je zde i možnost ovládání hlasitosti. Pomocí dat z RDS se podařilo zobrazit jen název u jedné stanice, a to u EVROPY 2. Lepšího výsledku by šlo dosáhnout pouze s vlastní knihovnou pro ovládání FM modulu. Programování mi zpříjemňoval poslech částečně naprogramovaného rádia.
Kromě tohoto modulu FM přijímače mě zaujal i modul pro vysílání FM, například [[https://learn.adafruit.com/adafruit-si4713-fm-radio-transmitter-with-rds-rdbs-support/overview|Si4713]]. Existují i integrované obvody, které slučují obě funkce (příjem i vysílání). Daly by se použít k bezdrátovému přenosu zvuku například z mp3 přehrávače nebo PC. Nebo k bezdrátovému přenosu dat, protože mají implementovanou funkci RDS. Dále modul [[http://www.silabs.com/products/audio/fm-am-receiver/Pages/si4707.aspx|Si4707]] pro příjem zpráv o počasí.
Celý zdrojový kód je uveřejněný na stránkách mbed.org: [[https://developer.mbed.org/users/vodavprasku/code/projekt_Si4703/| Projekt MPOA Si4703 ]].