Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.

Radio pomoću arduina

[es] :: Elektronika :: Mikrokontroleri :: Radio pomoću arduina

Strane: 1 2

[ Pregleda: 2521 | Odgovora: 30 ] > FB > Twit

Postavi temu Odgovori

Autor

Pretraga teme: Traži
Markiranje Štampanje RSS

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina24.03.2026. u 10:06 - pre 2 meseca
Imamo komunikaciju i imamo radio, ali moramo rešiti dva ključna problema kako bi radio bio potpun: Mute (tišinu tokom skeniranja) i Memorisanje stanice nakon gašenja.

Oba problema zahtevaju malu izmenu u kodu.

1. Problem sa Mute (Mute ne radi tokom skeniranja)
Pošto si rekao da tvoj tjuner nema povezan pin za mute na LA1837, softver ne može da ga kontroliše direktno. Moramo ga "naterati".

U kodu koji si poslao, u funkciji YST93xInit(), moramo aktivirati one bitove (BO1-BO4) koji na LC72131 čipu kontrolišu izlazne portove. Ako na tvojoj ploči ti izlazi (npr. Pin 8 - BO1) idu na onaj Mute pin na LA1837, radio će se utišati.

Modifikuj YST93xInit() ovako:

C++

void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);

// Prvi bajt za kontrolu portova (treba nam za Mute)
bitSet(pll_in2[0], IN20_IFS); // Normalan mode

// Treći bajt (B01-B04, IOC1, IOC2)
// Ovde aktiviramo kontrolu izlaza. Probaj dodati ove linije:
bitSet(pll_in2[2], IN22_BO3); // Proba da upali stereo (Mono=0, Stereo=1)

// Važno: Za 4.5MHz kristal XS mora biti 0 (bitClear)
bitClear(pll_in1[0], IN10_XS);
}
2. Problem sa Memorijom (Ne memoriše poslednju stanicu)
Za ovo moramo koristiti EEPROM memoriju Arduina. To je posebna memorija koja ne nestaje kada se Arduino isključi.

U kodu moramo dodati dve stvari:

U setup() da Arduino pročita stanicu iz EEPROM-a.

U loop() da Arduino zapiše novu stanicu u EEPROM čim je nađeš.

Evo modifikovanog dela koda koji rešava oba problema:

Dodaćemo biblioteku na početak: #include <EEPROM.h>

Predefinisaćemo adrese u memoriji:

C++

// Adrese u EEPROM memoriji gde cemo cuvati stanice
#define EEPROM_ADDR_FM 10
#define EEPROM_ADDR_AM 12
#define EEPROM_ADDR_BAND 14
Modifikovana setup() funkcija:

C++

void setup() {
lcd.begin(16, 2);
Serial.begin(9600);
lcd.print("--- DIGITALNA ---");
lcd.setCursor(0,1);
lcd.print("--- RADIONICA ---");

ccb.init();
delay(1000);

// Pročitaj poslednju sačuvanu frekvenciju
FMFrequency = (EEPROM.read(EEPROM_ADDR_FM) << 8) | EEPROM.read(EEPROM_ADDR_FM + 1);
AMFrequency = (EEPROM.read(EEPROM_ADDR_AM) << 8) | EEPROM.read(EEPROM_ADDR_AM + 1);
band = EEPROM.read(EEPROM_ADDR_BAND);

// Ako je EEPROM prazan (prvi put paljenje), postavi početne frekvencije
if (FMFrequency < 880 || FMFrequency > 1080) FMFrequency = 980; // 98.0 MHz
if (AMFrequency < 53 || AMFrequency > 170) AMFrequency = 100; // 1000 KHz
if (band != YST93x_BAND_FM && band != YST93x_BAND_AM) band = YST93x_BAND_FM;

YST93xInit();
YST93xSetMode(band);
tuned = YST93xTune( (band == YST93x_BAND_FM) ? FMFrequency : AMFrequency );

lcd.clear();
}
Modifikovana loop() funkcija (na mestu gde se menja stanica):

C++

// ... (tvoj originalni loop kod) ...

// Change the station
switch(band) {
case YST93x_BAND_FM:
// Ako je stanica promenjena, upiši je u EEPROM
EEPROM.write(EEPROM_ADDR_FM, (FMFrequency >> 8) & 0xFF);
EEPROM.write(EEPROM_ADDR_FM + 1, FMFrequency & 0xFF);
EEPROM.write(EEPROM_ADDR_BAND, band);

do{
FMFrequency += delta;
if(FMFrequency > 1080) FMFrequency = 880;
if(FMFrequency < 880) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);

lcd.setCursor(3, 0);
if(FMFrequency < 1000) lcd.print(' ');
lcd.print((float)FMFrequency / 10, 1);
} while(scan && (! tuned));
break;

case YST93x_BAND_AM:
// Ako je stanica promenjena, upiši je u EEPROM
EEPROM.write(EEPROM_ADDR_AM, (AMFrequency >> 8) & 0xFF);
EEPROM.write(EEPROM_ADDR_AM + 1, AMFrequency & 0xFF);
EEPROM.write(EEPROM_ADDR_BAND, band);

do{
AMFrequency += delta;
if(AMFrequency > 170) AMFrequency = 53;
if(AMFrequency < 53) AMFrequency = 170;
tuned = YST93xTune(AMFrequency);

lcd.setCursor(3, 0);
if(AMFrequency < 100) lcd.print(' ');
lcd.print(AMFrequency * 10);
} while(scan && (! tuned));
break;

} // switch

// ... (tvoj originalni loop kod) ...
Zašto Mute još uvek može da ne radi?
Ovaj kod koristi Sanyo Mute kontrolu, koja radi tako što utiša zvuk kada PLL ne zaključa stanicu. Međutim, ako na tvojoj ploči od DVD-a taj kontrolni pin nije bio povezan, moraćemo da fizički zalemimo žicu.

Savet: Dovedi onaj Pin 9 (sa LC72131) preko otpornika (npr. 10k) na bazu onog mute tranzistora na LA1837.

Šta dalje da proveriš:
Dovedi 5V na Pin 5 (LA1837): Bez ovoga nećeš čuti ništa osim tišine, čak i ako je tjuner na stanici.

Proveri Pin 4 i 5 na LC72131: To su tvoji DI i CL. Dok kod radi, unimerom (na DC opsegu) izmeri ima li ikakvog napona na njima. Trebalo bi da "igra" oko 2-3V dok se šalju podaci.

Misterija Pina 5 na LA1837:

Dok je sve ugašeno, izmeri otpor (Ohm) između Pina 5 i Pina 7 (GND).

Ako unimer pokaže 0 Ω ili jako mali otpor (ispod 10 Ω), taj čip je u unutrašnjem kratkom spoju.

Moj savet: Probaj ovaj EEPROM kod. Ako i dalje nema zvuka tokom skeniranja, to je dokaz da moramo da fizički spojimo Mute žicu.

Želiš li da ti napišem kod koji će automatski "prošetati" kroz EEPROM memoriju da vidimo da li je sve ispravno sačuvano?
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

Predrag Supurovic
Pedja YT9TP
Užice

Član broj: 157129
Poruke: 6356

Sajt: pedja.supurovic.net


+1592 Profil

icon Re: Radio pomoću arduina24.03.2026. u 13:44 - pre 2 meseca
Citat:
samuki:
Sjajno si izmerio, ovi podaci nam otkrivaju tačno gde je "zapelo". Idemo redom da rešimo problem napajanja i komunikacije:


Je l' ti stvarno toliko ne znaš da moraš da koristiš AI da se predstaviš kao da nešto znaš?

Čovek, sigurno ume i sam da koristi AI.


 
Odgovor na temu

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina24.03.2026. u 16:26 - pre 2 meseca
Ako nije bilo od pomoci posebno oko pisanja koda, obrisite sve .
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

Bogdan-carr
Belgrade

Član broj: 265509
Poruke: 105
95.180.123.*



+1 Profil

icon Re: Radio pomoću arduina24.03.2026. u 20:14 - pre 2 meseca
Sa pina 8 od lc72131 ide preko otpornika od 4k7 na pin 13 od la1837 direktna veza preko štampe.Pin 5 od la1837 je vezan direktno na gnd.Ima zvuk ali se čuje kako prelazi preko stanica dok traži i ne mutira tokom skeniranja.Možeš li da doradiš taj kod što sam postavio i da okačiš kod koji je ispravljen.
 
Odgovor na temu

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina24.03.2026. u 21:10 - pre 2 meseca
Pošto si potvrdio da Pin 8 (BO2) sa LC72131 ide direktno na Pin 13 (Mute) kod LA1837, imamo jasnu putanju za kontrolu tišine. Razlog zašto čuješ "preletanje" preko stanica je taj što originalni kod gasi mute čim pošalje frekvenciju, ne čekajući da se PLL stabilizuje.

Takođe, dodao sam EEPROM logiku kako bi radio pamtio stanicu i nakon gašenja.

Ispravljen i dopunjen kod
Ovaj kod uključuje memorisanje, podešavanje za kristal od 4.5MHz i agresivniju Mute logiku.

C++

#include <inttypes.h>
#include <LiquidCrystal.h>
#include <SanyoCCB.h>
#include <EEPROM.h> // Dodato za memoriju

// Adrese u EEPROM-u
#define ADDR_FM 10
#define ADDR_AM 12
#define ADDR_BAND 14

#define KEYPAD_PIN A0
#define KEY_BAND 5
#define KEY_SCAN_DOWN 4
#define KEY_DOWN 3
#define KEY_UP 2
#define KEY_SCAN_UP 1
#define KEY_NONE 0

// Bitovi za LC72131
#define IN10_XS 3
#define IN10_CTE 2
#define IN10_DVS 1
#define IN20_IFS 4
#define IN21_UL0 4
#define IN22_BO2 5 // Pin 8 na čipu - tvoj Mute
#define IN22_BO1 4
#define DO0_UL 4

#define YST93x_MUTE 3
#define YST93x_UNMUTE 4
#define YST93x_BAND_FM 5
#define YST93x_BAND_AM 6

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
SanyoCCB ccb(A5, A4, A3, A2); // DO, CL, DI, CE

byte pll_in1[3];
byte pll_in2[3];
uint16_t FMFrequency, AMFrequency;
uint8_t band, tuned;

void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);
bitSet(pll_in2[0], IN20_IFS);
bitSet(pll_in2[1], IN21_UL0);

// XS = 0 za tvoj kristal od 4.5 MHz
bitClear(pll_in1[0], IN10_XS);
}

void YST93xSetMode(uint8_t mode) {
switch(mode) {
case YST93x_MUTE:
// Ako se i dalje čuje zvuk, probaj bitSet umesto bitClear (zavisi od tranzistora)
bitClear(pll_in2[2], IN22_BO2);
break;
case YST93x_UNMUTE:
bitSet(pll_in2[2], IN22_BO2);
break;
case YST93x_BAND_FM:
band = YST93x_BAND_FM;
bitWrite(pll_in1[0], 4, 1); // R0=1 (50kHz)
bitWrite(pll_in1[0], IN10_DVS, 1);
bitWrite(pll_in2[2], IN22_BO1, 0); // FM Band
break;
case YST93x_BAND_AM:
band = YST93x_BAND_AM;
bitWrite(pll_in1[0], 7, 1); // R3=1 (10kHz)
bitWrite(pll_in1[0], IN10_DVS, 0);
bitWrite(pll_in2[2], IN22_BO1, 1); // AM Band
break;
}
ccb.write(0x82, pll_in1, 3);
ccb.write(0x92, pll_in2, 3);
}

uint8_t YST93xTune(uint16_t frequency) {
uint16_t fpd = 0;
uint8_t i = 0, r[3];

YST93xSetMode(YST93x_MUTE); // Obavezno Mute pre promene
delay(20);

if(band == YST93x_BAND_FM) fpd = (frequency + 107);
else fpd = (frequency + 45) << 4;

bitClear(pll_in1[0], IN10_CTE);
pll_in1[1] = byte(fpd >> 8);
pll_in1[2] = byte(fpd & 0x00ff);
ccb.write(0x82, pll_in1, 3);

bitSet(pll_in1[0], IN10_CTE);
ccb.write(0x82, pll_in1, 3);

// Čekamo da se PLL zaključa dok je Mute aktivan
while(i < 10) {
delay(40);
ccb.read(0xa2, r, 3);
if(bitRead(r[0], DO0_UL)) { i = 100; break; }
i++;
}

// Tek kad završiš sve, odmutiraj (samo ako nije scan mod, on se hendluje u loop-u)
return (i == 100) ? 1 : 0;
}

void sacuvajStanicu() {
EEPROM.put(ADDR_FM, FMFrequency);
EEPROM.put(ADDR_AM, AMFrequency);
EEPROM.write(ADDR_BAND, band);
}

void setup() {
lcd.begin(16, 2);
ccb.init();
YST93xInit();

// Čitanje iz memorije
EEPROM.get(ADDR_FM, FMFrequency);
EEPROM.get(ADDR_AM, AMFrequency);
band = EEPROM.read(ADDR_BAND);

// Sigurnosna provera opsega
if(FMFrequency < 875 || FMFrequency > 1080) FMFrequency = 980;
if(band != YST93x_BAND_FM && band != YST93x_BAND_AM) band = YST93x_BAND_FM;

YST93xSetMode(band);
tuned = YST93xTune((band == YST93x_BAND_FM) ? FMFrequency : AMFrequency);
YST93xSetMode(YST93x_UNMUTE);
lcd.clear();
}

void loop() {
uint8_t scan = 0;
int8_t delta = 0;
uint16_t keypad = analogRead(KEYPAD_PIN);
uint8_t key = KEY_NONE;

if(keypad < 60) key = KEY_SCAN_UP;
else if(keypad < 220) key = KEY_UP;
else if(keypad < 390) key = KEY_DOWN;
else if(keypad < 600) key = KEY_SCAN_DOWN;
else if(keypad < 870) key = KEY_BAND;

if(key != KEY_NONE) {
if(key == KEY_BAND) {
band = (band == YST93x_BAND_FM) ? YST93x_BAND_AM : YST93x_BAND_FM;
YST93xSetMode(band);
} else {
scan = (key == KEY_SCAN_UP || key == KEY_SCAN_DOWN) ? 1 : 0;
delta = (key == KEY_UP || key == KEY_SCAN_UP) ? 1 : -1;
}

YST93xSetMode(YST93x_MUTE); // Mute tokom celog procesa promene

do {
if(band == YST93x_BAND_FM) {
FMFrequency += delta;
if(FMFrequency > 1080) FMFrequency = 875;
if(FMFrequency < 875) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);
} else {
AMFrequency += delta;
tuned = YST93xTune(AMFrequency);
}

lcd.setCursor(0, 0);
lcd.print(band == YST93x_BAND_FM ? "FM " : "AM ");
lcd.print(band == YST93x_BAND_FM ? (float)FMFrequency/10 : AMFrequency*10);

} while(scan && !tuned);

YST93xSetMode(YST93x_UNMUTE); // Odmutiraj tek kad nađeš stanicu
sacuvajStanicu(); // Zapamti stanicu
delay(200);
}
}
Šta je promenjeno:
Mute Logika: Sada se poziva YST93xSetMode(YST93x_MUTE) na početku svakog pritiska tastera, a UNMUTE tek nakon što se do-while petlja završi. To bi trebalo potpuno da ukloni zvuk "pretraživanja".

XS Bit: Isključen je (bitClear), što je kritično za tvoj kristal od 4.5MHz kako bi frekvencija bila tačna.

EEPROM: Korišćene su funkcije get i put za čuvanje dvobajtnih vrednosti frekvencije.

Jedna napomena: Ako se zvuk i dalje čuje, unimerom izmeri napon na Pinu 8 tjunera dok traje skeniranje. Ako je napon 5V, a treba da bude 0V (ili obrnuto) da bi LA1837 utihnuo, samo zameni bitClear i bitSet u YST93xSetMode funkciji.

Da li želiš da dodamo i Signal Meter (indikator jačine signala) na taj slobodan red displeja pošto sada imamo ispravan read sa tjunera?
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

Bogdan-carr
Belgrade

Član broj: 265509
Poruke: 105
95.180.123.*



+1 Profil

icon Re: Radio pomoću arduina24.03.2026. u 21:43 - pre 2 meseca
/*

The YST93x (YST936, YST939, etc.) is an AM/FM tuner module used on some cheap
Chinese home theaters and sound systems. It is made by a company named Winever
(http://www.winever.cn/) - and maybe some others. It is very cheap and of a
a reasonable quality. It is easy to find on eBay and Alibaba.com

The module is based on a Sanyo LC72131 PLL and a tuner IC. The tuner chip varies
from model to model: YST939 uses the Sanyo LA1823 (or clone) radio-on-chip and
YST936 uses a Chinese-branded "CS1000" tuner.

The PLL can communicate with a microcontroller via the Sanyo CCB bus, and have
5 I/O pins which controls the tuner:

PLL pin Direction Function
BO0 PLL -> Tuner Not used
BO1 PLL -> Tuner Band selector (0 = FM; 1 = AM)
BO2 PLL -> Tuner Mute / IF output (0 = Mute / IF counter mode
1 = Normal tuner mode)
BO3 PLL -> Tuner Audio mode (0 = Stereo; 1 = Mono)
BO4 PLL -> Tuner Not used
IO0 Tuner -> PLL Not used (pulled high. Reads "1")
IO1 Tuner -> PLL Stereo indicator (0 = Stereo; 1 = Mono)

Note: BO0 must be set to "0" for the tuner to output the IF signal to the PLL.
This means the tuner will have to be muted every time one want the PLL to count
the IF frequency.

YST93x module Pinout:

FM
Antenna +------------------------+
+---| |- RDS out (not all models have this pin)
+---| |- Out-L
| YST93x |- GND
| |- Out-R
| |- VDD (12V)
| |
AM | |
Antenna | |- DO
+---| |- CL
| | |- DI
| | |- CE
+---| |- GND
+------------------------+

YST93x to Arduino connections:
Arduino YST93x
A5 (DO) DI
A4 (CL) CL
A3 (DI) DO
A2 (CE) CE
GND GND

Note: This example uses an analog keypad with the following schematics:

A0
|
2k2 | 330R 620R 1k 3k3
VCC -----\/\/\---+---\/\/\---+---\/\/\---+---\/\/\---+---\/\/\-----+----- GND
| | | | |
X SCAN_UP X UP X DOWN X SCAN_DOWN X BAND
| | | | |
GND GND GND GND GND

X = keys (N.O.)

*/

#include <inttypes.h>
#include <LiquidCrystal.h>
#include <SanyoCCB.h>

// This example uses an analog 5-key keypad tied to A0
#define KEYPAD_PIN A0

// Keypad keys
#define KEY_BAND 5
#define KEY_SCAN_DOWN 4
#define KEY_DOWN 3
#define KEY_UP 2
#define KEY_SCAN_UP 1
#define KEY_NONE 0

// LC72131 IN1, byte 0
#define IN10_R3 7
#define IN10_R2 6
#define IN10_R1 5
#define IN10_R0 4
#define IN10_XS 3
#define IN10_CTE 2
#define IN10_DVS 1
#define IN10_SNS 0

// LC72131 IN2, byte 0
#define IN20_TEST2 7
#define IN20_TEST1 6
#define IN20_TEST0 5
#define IN20_IFS 4
#define IN20_DLC 3
#define IN20_TBC 2
#define IN20_GT1 1
#define IN20_GT0 0

// LC72131 IN2, byte 1
#define IN21_DZ1 7
#define IN21_DZ0 6
#define IN21_UL1 5
#define IN21_UL0 4
#define IN21_DOC2 3
#define IN21_DOC1 2
#define IN21_DOC0 1
#define IN21_DNC 0

// LC72131 IN2, byte 2
#define IN22_BO4 7
#define IN22_BO3 6
#define IN22_BO2 5
#define IN22_BO1 4
#define IN22_IO2 3
#define IN22_IO1 2
#define IN22_IOC2 1
#define IN22_IOC1 0

// LC72131 DO0, byte 0
#define DO0_IN2 7
#define DO0_IN1 6
#define DO0_UL 4

// For function YST93xSetMode
#define YST93x_MONO 1
#define YST93x_STEREO 2
#define YST93x_MUTE 3
#define YST93x_UNMUTE 4
#define YST93x_BAND_FM 5
#define YST93x_BAND_AM 6

// Acceptable IF frequency deviation window (for the PLL) when searching for radio stations
// You may need to tweek these values to have a reliable "scan" mode
#define FM_TUNED_WINDOW 180
#define AM_TUNED_WINDOW 20

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

SanyoCCB ccb(A5, A4, A3, A2); // Pins: DO CL DI CE

byte pll_in1[3];
byte pll_in2[3];

// Initial frequencies
uint16_t FMFrequency = 870; // MHz * 10
uint16_t AMFrequency = 53; // KHZ / 10

uint8_t band = YST93x_BAND_FM;
uint8_t tuned = 0;


/************************************************\
* YST93xInit() *
* Initialize the PLL settings vectors with *
* parameters common to booth AM and FM modes *
\************************************************/
void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);
bitSet(pll_in2[0], IN20_IFS); // IF counter in normal mode
bitSet(pll_in2[1], IN21_UL0); // Phase error detection width = 0us
bitSet(pll_in2[2], IN22_BO2); // Mute off / normal tuner mode
}


/************************************************\
* YST93xSetMode() *
* Some predefined setups for the YST93x module *
\************************************************/
void YST93xSetMode(uint8_t mode) {
switch(mode) {
case YST93x_STEREO:
bitClear(pll_in2[2], IN22_BO3);
break;

case YST93x_MONO:
bitSet(pll_in2[2], IN22_BO3);
break;

case YST93x_MUTE:
bitClear(pll_in2[2], IN22_BO2);
break;

case YST93x_UNMUTE:
bitSet(pll_in2[2], IN22_BO2);
break;

case YST93x_BAND_FM:
band = YST93x_BAND_FM;
bitWrite(pll_in1[0], IN10_R0, 1); // Reference frequency = 50kHz
bitWrite(pll_in1[0], IN10_R3, 0); //
bitWrite(pll_in1[0], IN10_DVS, 1); // Programmable Divider divisor = 2
bitWrite(pll_in2[0], IN20_GT0, 0); // IF counter mesurement period = 32ms
bitWrite(pll_in2[0], IN20_GT1, 1); //
bitWrite(pll_in2[1], IN21_DZ0, 1); // Dead zone = DZB
bitWrite(pll_in2[1], IN21_DZ1, 0); //
bitWrite(pll_in2[2], IN22_BO1, 0); // FM mode
break;

case YST93x_BAND_AM:
band = YST93x_BAND_AM;
bitWrite(pll_in1[0], IN10_R0, 0); // Reference frequency = 10kHz
bitWrite(pll_in1[0], IN10_R3, 1); //
bitWrite(pll_in1[0], IN10_DVS, 0); // Programmable Divider divisor = 1
bitWrite(pll_in2[0], IN20_GT0, 1); // IF counter mesurement period = 8ms
bitWrite(pll_in2[0], IN20_GT1, 0); //
bitWrite(pll_in2[1], IN21_DZ0, 0); // Dead zone = DZC
bitWrite(pll_in2[1], IN21_DZ1, 1); //
bitWrite(pll_in2[2], IN22_BO1, 1); // AM mode
break;
}
ccb.write(0x82, pll_in1, 3);
ccb.write(0x92, pll_in2, 3);
}



/************************************************************\
* YST93xTune() *
* Set the tuner frequency and return 1 if it is tuned *
* or 0 otherwise. *
* *
* The frequency divisors was chosen in a way the frequency *
* representation can be directly sent to the PLL and is *
* easy to represent: *
* - FM mode (divisor = 100): frequency (MHz) * 10 *
* - AM mode (divisor = 10): frequency (kHZ) / 10 *
\************************************************************/
uint8_t YST93xTune(uint16_t frequency) {
uint16_t fpd = 0;
uint8_t i = 0;
uint8_t r[3];
unsigned long IFCounter = 0;

switch(band) {
case YST93x_BAND_FM:
// FM: fpd = (frequency + FI) / (50 * 2)
fpd = (frequency + 107);
break;

case YST93x_BAND_AM:
// AM: fpd = ((frequency + FI) / 10) << 4
fpd = (frequency + 45) << 4;
break;

default: return 1;
}

YST93xSetMode(YST93x_MUTE); // YST93x only injects FI signal into the PLL when in MUTE mode

// Reset the IF counter and program the Frequency Programmable Divider (fpd)
bitClear(pll_in1[0], IN10_CTE);
pll_in1[1] = byte(fpd >> 8);
pll_in1[2] = byte(fpd & 0x00ff);
ccb.write(0x82, pll_in1, 3);

// Start the IF counter
bitSet(pll_in1[0], IN10_CTE);
ccb.write(0x82, pll_in1, 3);

// Wait for PLL to be locked (DO0_UL == 1)
while(i < 20) {
delay(50);
ccb.read(0xa2, r, 3); // Discard the 1st result: it is latched from the last count (as said on the datasheet)
ccb.read(0xa2, r, 3); // The 20 rightmost bits from r[0..2] are the IF counter result
i = (bitRead(r[0], DO0_UL)) ? 100 : i + 1;
}

YST93xSetMode(YST93x_UNMUTE); // Mute off / normal tuner mode

if(i == 100) {
// PLL is locked. If the IF deviation is within the defined (window) interval,
// the radio is likely to be tuned.
// Note: this "tuned" detection method is not recommended on the LC72131 datasheet as
// it can give false positive results. A better approach would be to get the "tuned"
// flag from a RDS decoder with signal quality detection (e.g.: PT2579 or Philips SAA6588)
// connected to the YST93x tuner module "RDS Output" pin. SAA6588 is more powerful/popular,
// but PT2579 looks a lot easier to use as it is not programmable and has a dedicated
// signal quality output pin.
IFCounter = (r[0] & 0x0f);
IFCounter = (IFCounter << 16) | (unsigned long)(r[1] << 8) | (r[2]);
Serial.println(IFCounter);
switch(band) {
case YST93x_BAND_FM:
// Expected value: IF (10.7MHz) * Mesurement period (32ms, as set via GT[1..0]) = 342400
if((IFCounter > 342400) && ((IFCounter - 342400) < FM_TUNED_WINDOW)) return 1;
if((IFCounter < 342400) && ((342400 - IFCounter) < FM_TUNED_WINDOW)) return 1;
break;
case YST93x_BAND_AM:
// Expected value: IF (450kHz) * Mesurement period (8ms, as set via GT[1..0]) = 3600
// Note: scan mode in AM is really poor. I have done my best in tweaking it, but it barely works
if((IFCounter > 3600) && ((IFCounter - 3600) < AM_TUNED_WINDOW)) return 1;
if((IFCounter < 3600) && ((3600 - IFCounter) < AM_TUNED_WINDOW)) return 1;
break;
}
}
return 0;
}


/**************************************************\
* YST93xIsStereo() *
* Returns 1 if the tuned radio station is stereo *
\**************************************************/
uint8_t YST93xIsStereo() {
uint8_t r[3];

ccb.read(0xa2, r, 3);
return(bitRead(r[0], DO0_IN2) ? 0 : 1);
}


/*******************************************************\
* getKey() *
* Read the (analog) keypad. *
* If you are using an digital (one key per input pin) *
* keypad, just this function to return the correct *
* values *
\*******************************************************/
uint8_t getKey(uint8_t keypadPin) {
uint16_t keypad;
uint8_t key = KEY_NONE;

keypad = analogRead(keypadPin);

if(keypad < 870) key = KEY_BAND;
if(keypad < 600) key = KEY_SCAN_DOWN;
if(keypad < 390) key = KEY_DOWN;
if(keypad < 220) key = KEY_UP;
if(keypad < 60) key = KEY_SCAN_UP;

return key;
}


/*******************\
* Arduino setup() *
\*******************/
void setup() {
lcd.begin(16, 2);
Serial.begin(9600);
ccb.init();
delay(1000);
YST93xInit();
YST93xSetMode(YST93x_BAND_FM);
tuned = YST93xTune(FMFrequency);
}


/******************\
* Arduino loop() *
\******************/
void loop() {
uint8_t scan = 0;
int8_t delta = 0;
uint8_t key = getKey(KEYPAD_PIN);

lcd.setCursor(0, 0);
switch(band) {
case YST93x_BAND_FM:
lcd.print("FM ");
lcd.setCursor((FMFrequency < 1000) ? 4 : 3, 0); lcd.print((float)FMFrequency / 10, 1);
lcd.print("MHz ");
break;
case YST93x_BAND_AM:
lcd.print("AM ");
lcd.setCursor((AMFrequency < 100) ? 4 : 3, 0); lcd.print(AMFrequency * 10);
lcd.print("KHz ");
break;
}

// Updates the Stereo indicator
lcd.setCursor(12, 0);
if(YST93xIsStereo())
lcd.print("[ST]");
else
lcd.print("[ ]");

// The "Tuned" indicator is updated only when the station changes
lcd.setCursor(2, 1);
if(tuned)
lcd.print(" Radio ");
else
lcd.print(" ");

// Processo keypad inputs
if(key != KEY_NONE) {
switch(key) {
case KEY_UP: scan = 0; delta = +1; break;
case KEY_DOWN: scan = 0; delta = -1; break;
case KEY_SCAN_UP: scan = 1; delta = +1; break;
case KEY_SCAN_DOWN: scan = 1; delta = -1; break;
case KEY_BAND:
switch(band) {
case YST93x_BAND_FM:
YST93xSetMode(YST93x_BAND_AM);
tuned = YST93xTune(AMFrequency);
break;

case YST93x_BAND_AM:
YST93xSetMode(YST93x_BAND_FM);
tuned = YST93xTune(FMFrequency);
break;
}
}

if(scan) {
lcd.setCursor(2, 1);
lcd.print("Pretraga...");
}

// Change the station
switch(band) {
case YST93x_BAND_FM:
do{
FMFrequency += delta;
if(FMFrequency > 1080) FMFrequency = 880;
if(FMFrequency < 880) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);

lcd.setCursor(3, 0);
if(FMFrequency < 1000) lcd.print(' ');
lcd.print((float)FMFrequency / 10, 1);
} while(scan && (! tuned));
break;

case YST93x_BAND_AM:
do{
AMFrequency += delta;
if(AMFrequency > 170) AMFrequency = 53;
if(AMFrequency < 53) AMFrequency = 170;
tuned = YST93xTune(AMFrequency);

lcd.setCursor(3, 0);
if(AMFrequency < 100) lcd.print(' ');
lcd.print(AMFrequency * 10);
} while(scan && (! tuned));
break;

} // switch

} // if(key)

}

Na ovaj kod samo dodati ma mutira dok traži stanicu.Ili da bude konstantan zvuk bez prekida dok traži stanicu tj prekida kako mu arduino šalje informacije.Prekida kako dok traži stanicu u ritmu kako treperi TX na arduinu dok šalje informaciju samom tuneru.Sa ovim kodom radi perfektno samo me iritira taj zvuk pri traženju stanica.I još nešto da te pitam dali može umesto tastera da se koristi enkoder?
Da radi tako kad jednom pritisnem da uđe u meni gde biram band dal fm ili am,a okretanjem enkodera da manualno podešavam,a kad duže zadržim enkoder da uđe m mod aut pretrage.
 
Odgovor na temu

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina24.03.2026. u 22:35 - pre 2 meseca
taj zvuk "seckanja" dok Arduino šalje podatke (što vidiš na TX diodi) nastaje jer program u svakom koraku pretrage otvori i zatvori Mute, pa dobiješ taj neprijatni ritmični šum.

Rešićemo to tako što ćemo izbaciti komandu za otključavanje tona iz same funkcije za štimovanje i premestiti je tamo gde se pretraga završava.

Što se tiče enkodera, to je odlična ideja! Radio će izgledati i raditi mnogo modernije. Koristićemo standardni rotacioni enkoder (sa tasterom).

Plan povezivanja (Enkoder):
Pin A (CLK): Digitalni pin 2 (podržava interrupte)

Pin B (DT): Digitalni pin 3

SW (Taster): Digitalni pin 11 (ili bilo koji drugi slobodan digitalni pin)

Modifikovani kod sa Enkoderom i fiksiranim Mute-om
Ovaj kod rešava "seckanje" tona i uvodi logiku koju si tražio:

Okretanje: Manuelno podešavanje frekvencije.

Kratak pritisak: Ulazak u meni za promenu opsega (FM/AM).

Dug pritisak (preko 1s): Pokretanje automatske pretrage.

C++

#include <inttypes.h>
#include <LiquidCrystal.h>
#include <SanyoCCB.h>
#include <EEPROM.h>

// PIN KONFIGURACIJA ZA ENKODER
#define ENC_A 2
#define ENC_B 3
#define ENC_SW 11

// Adrese u EEPROM
#define ADDR_FM 10
#define ADDR_AM 12
#define ADDR_BAND 14

// LC72131 Bitovi
#define IN10_XS 3
#define IN10_CTE 2
#define IN10_DVS 1
#define IN22_BO2 5 // Pin 8 - Mute
#define IN22_BO1 4 // Band switch
#define DO0_UL 4

#define YST93x_MUTE 3
#define YST93x_UNMUTE 4
#define YST93x_BAND_FM 5
#define YST93x_BAND_AM 6

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
SanyoCCB ccb(A5, A4, A3, A2);

byte pll_in1[3];
byte pll_in2[3];
uint16_t FMFrequency, AMFrequency;
uint8_t band, tuned;

// Varijable za enkoder
volatile int enc_pos = 0;
unsigned long taster_vreme = 0;
bool menuMode = false;

// INTERRUPT FUNKCIJA ZA ENKODER (Brza reakcija)
void readEncoder() {
if (digitalRead(ENC_A) == digitalRead(ENC_B)) enc_pos++;
else enc_pos--;
}

void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);
bitSet(pll_in2[0], 4); // IFS
bitClear(pll_in1[0], IN10_XS); // 4.5MHz
}

void YST93xSetMode(uint8_t mode) {
switch(mode) {
case YST93x_MUTE: bitClear(pll_in2[2], IN22_BO2); break;
case YST93x_UNMUTE: bitSet(pll_in2[2], IN22_BO2); break;
case YST93x_BAND_FM:
band = YST93x_BAND_FM;
bitWrite(pll_in1[0], 4, 1); // 50kHz
bitWrite(pll_in1[0], IN10_DVS, 1);
bitWrite(pll_in2[2], IN22_BO1, 0);
break;
case YST93x_BAND_AM:
band = YST93x_BAND_AM;
bitWrite(pll_in1[0], 7, 1); // 10kHz
bitWrite(pll_in1[0], IN10_DVS, 0);
bitWrite(pll_in2[2], IN22_BO1, 1);
break;
}
ccb.write(0x82, pll_in1, 3);
ccb.write(0x92, pll_in2, 3);
}

// KLJUČNA PROMENA: Ova funkcija više NE odmutira ton sama
uint8_t YST93xTune(uint16_t frequency) {
uint16_t fpd = (band == YST93x_BAND_FM) ? (frequency + 107) : (frequency + 45) << 4;
uint8_t i = 0, r[3];

bitClear(pll_in1[0], IN10_CTE);
pll_in1[1] = byte(fpd >> 8);
pll_in1[2] = byte(fpd & 0x00ff);
ccb.write(0x82, pll_in1, 3);
bitSet(pll_in1[0], IN10_CTE);
ccb.write(0x82, pll_in1, 3);

while(i < 10) {
delay(30); // Malo kraći delay za brži scan
ccb.read(0xa2, r, 3);
if(bitRead(r[0], DO0_UL)) { i = 100; break; }
i++;
}

if(i == 100) {
unsigned long IFCounter = (r[0] & 0x0f);
IFCounter = (IFCounter << 16) | (unsigned long)(r[1] << 8) | (r[2]);
if(band == YST93x_BAND_FM) {
if(abs((long)IFCounter - 342400) < 180) return 1;
} else {
if(abs((long)IFCounter - 3600) < 20) return 1;
}
}
return 0;
}

void setup() {
lcd.begin(16, 2);
ccb.init();
YST93xInit();

// Konfiguracija pinova enkodera
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(ENC_SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENC_A), readEncoder, CHANGE);

EEPROM.get(ADDR_FM, FMFrequency);
EEPROM.get(ADDR_AM, AMFrequency);
band = EEPROM.read(ADDR_BAND);

if(FMFrequency < 875 || FMFrequency > 1080) FMFrequency = 880;
if(band != YST93x_BAND_FM && band != YST93x_BAND_AM) band = YST93x_BAND_FM;

YST93xSetMode(band);
YST93xTune((band == YST93x_BAND_FM) ? FMFrequency : AMFrequency);
YST93xSetMode(YST93x_UNMUTE);
prikaziLCD();
}

void loop() {
// 1. OBRADA OKRETANJA ENKODERA
if (enc_pos != 0) {
int smer = (enc_pos > 0) ? 1 : -1;
enc_pos = 0;

if (menuMode) {
// Biranje opsega u meniju
band = (band == YST93x_BAND_FM) ? YST93x_BAND_AM : YST93x_BAND_FM;
YST93xSetMode(band);
prikaziLCD();
} else {
// Manuelno štimovanje
YST93xSetMode(YST93x_MUTE); // Mute dok vrtiš
promeniFrekvenciju(smer, false);
YST93xSetMode(YST93x_UNMUTE);
}
}

// 2. OBRADA TASTERA (KLIK I DUGI PRITISAK)
if (digitalRead(ENC_SW) == LOW) {
if (taster_vreme == 0) taster_vreme = millis();

// Ako se drži duže od 1 sekunde - AUTO SCAN
if (millis() - taster_vreme > 1000) {
menuMode = false;
lcd.setCursor(0, 1); lcd.print("AUTO SCAN... ");
YST93xSetMode(YST93x_MUTE);
promeniFrekvenciju(1, true); // True za auto-scan
YST93xSetMode(YST93x_UNMUTE);
taster_vreme = 0;
while(digitalRead(ENC_SW) == LOW); // Čekaj da pusti
}
} else {
// Ako je bio kratak klik
if (taster_vreme > 50 && taster_vreme < 1000) {
menuMode = !menuMode;
prikaziLCD();
}
taster_vreme = 0;
}
}

void promeniFrekvenciju(int smer, bool autoScan) {
do {
if (band == YST93x_BAND_FM) {
FMFrequency += smer;
if (FMFrequency > 1080) FMFrequency = 875;
if (FMFrequency < 875) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);
} else {
AMFrequency += smer;
if (AMFrequency > 171) AMFrequency = 53;
if (AMFrequency < 53) AMFrequency = 171;
tuned = YST93xTune(AMFrequency);
}
prikaziLCD();
} while (autoScan && !tuned);

// Sačuvaj u memoriju
EEPROM.put(ADDR_FM, FMFrequency);
EEPROM.put(ADDR_AM, AMFrequency);
EEPROM.write(ADDR_BAND, band);
}

void prikaziLCD() {
lcd.setCursor(0, 0);
if (menuMode) {
lcd.print("MENI: SET BAND ");
lcd.setCursor(0, 1);
lcd.print(band == YST93x_BAND_FM ? "> FM < AM " : " FM > AM < ");
} else {
lcd.print(band == YST93x_BAND_FM ? "FM " : "AM ");
uint16_t f = (band == YST93x_BAND_FM) ? FMFrequency : AMFrequency * 10;
if (band == YST93x_BAND_FM) {
lcd.print((float)f / 10, 1); lcd.print(" MHz ");
} else {
lcd.print(f); lcd.print(" KHz ");
}
lcd.setCursor(0, 1);
lcd.print(tuned ? " [ TUNED ] " : " [ SEARCH ] ");
}
}
Šta je popravljeno:
Tišina bez prekida: Primetićeš da sam u YST93xTune obrisao liniju YST93xSetMode(YST93x_UNMUTE). Sada se ton pušta tek u loop-u, kada se funkcija promeniFrekvenciju završi. To znači da dok tražiš stanicu, Pin 8 stoji zakucan na Mute i nema onog "t-t-t-t" zvuka.

Enkoder Meni: * Kada klikneš enkoder, na LCD-u piše "MENI: SET BAND". Tada okretanjem biraš između FM i AM. Klikneš ponovo da izađeš.

Kada vrtiš enkoder u normalnom modu, frekvencija ide gore/dole (kao na pravom starom radiju).

Dugi pritisak pokreće skeniranje dok ne nađe sledeću stanicu.

Hardverska napomena:
Ako tvoj enkoder radi "obrnuto" (okrećeš desno a frekvencija ide dole), samo zameni mesta žicama na pinovima 2 i 3 ili u kodu zameni enc_pos++ i enc_pos--.

Kako ti se čini ova logika sa menijem? Ako želiš, možemo dodati i treću stavku u meni – na primer "Volume" ako planiraš da dodaš neki digitalni potenciometar kasnije!
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

Bogdan-carr
Belgrade

Član broj: 265509
Poruke: 105
95.180.123.*



+1 Profil

icon Re: Radio pomoću arduina26.03.2026. u 00:37 - pre 2 meseca
Ovaj kod da ostane kako jeste samo da se u njega ubaci kad se traži stanica da mutira sve dok ne nađe stanicu.

/*

The YST93x (YST936, YST939, etc.) is an AM/FM tuner module used on some cheap
Chinese home theaters and sound systems. It is made by a company named Winever
(http://www.winever.cn/) - and maybe some others. It is very cheap and of a
a reasonable quality. It is easy to find on eBay and Alibaba.com

The module is based on a Sanyo LC72131 PLL and a tuner IC. The tuner chip varies
from model to model: YST939 uses the Sanyo LA1823 (or clone) radio-on-chip and
YST936 uses a Chinese-branded "CS1000" tuner.

The PLL can communicate with a microcontroller via the Sanyo CCB bus, and have
5 I/O pins which controls the tuner:

PLL pin Direction Function
BO0 PLL -> Tuner Not used
BO1 PLL -> Tuner Band selector (0 = FM; 1 = AM)
BO2 PLL -> Tuner Mute / IF output (0 = Mute / IF counter mode
1 = Normal tuner mode)
BO3 PLL -> Tuner Audio mode (0 = Stereo; 1 = Mono)
BO4 PLL -> Tuner Not used
IO0 Tuner -> PLL Not used (pulled high. Reads "1")
IO1 Tuner -> PLL Stereo indicator (0 = Stereo; 1 = Mono)

Note: BO0 must be set to "0" for the tuner to output the IF signal to the PLL.
This means the tuner will have to be muted every time one want the PLL to count
the IF frequency.

YST93x module Pinout:

FM
Antenna +------------------------+
+---| |- RDS out (not all models have this pin)
+---| |- Out-L
| YST93x |- GND
| |- Out-R
| |- VDD (12V)
| |
AM | |
Antenna | |- DO
+---| |- CL
| | |- DI
| | |- CE
+---| |- GND
+------------------------+

YST93x to Arduino connections:
Arduino YST93x
A5 (DO) DI
A4 (CL) CL
A3 (DI) DO
A2 (CE) CE
GND GND

Note: This example uses an analog keypad with the following schematics:

A0
|
2k2 | 330R 620R 1k 3k3
VCC -----\/\/\---+---\/\/\---+---\/\/\---+---\/\/\---+---\/\/\-----+----- GND
| | | | |
X SCAN_UP X UP X DOWN X SCAN_DOWN X BAND
| | | | |
GND GND GND GND GND

X = keys (N.O.)

*/

#include <inttypes.h>
#include <LiquidCrystal.h>
#include <SanyoCCB.h>

// This example uses an analog 5-key keypad tied to A0
#define KEYPAD_PIN A0

// Keypad keys
#define KEY_BAND 5
#define KEY_SCAN_DOWN 4
#define KEY_DOWN 3
#define KEY_UP 2
#define KEY_SCAN_UP 1
#define KEY_NONE 0

// LC72131 IN1, byte 0
#define IN10_R3 7
#define IN10_R2 6
#define IN10_R1 5
#define IN10_R0 4
#define IN10_XS 3
#define IN10_CTE 2
#define IN10_DVS 1
#define IN10_SNS 0

// LC72131 IN2, byte 0
#define IN20_TEST2 7
#define IN20_TEST1 6
#define IN20_TEST0 5
#define IN20_IFS 4
#define IN20_DLC 3
#define IN20_TBC 2
#define IN20_GT1 1
#define IN20_GT0 0

// LC72131 IN2, byte 1
#define IN21_DZ1 7
#define IN21_DZ0 6
#define IN21_UL1 5
#define IN21_UL0 4
#define IN21_DOC2 3
#define IN21_DOC1 2
#define IN21_DOC0 1
#define IN21_DNC 0

// LC72131 IN2, byte 2
#define IN22_BO4 7
#define IN22_BO3 6
#define IN22_BO2 5
#define IN22_BO1 4
#define IN22_IO2 3
#define IN22_IO1 2
#define IN22_IOC2 1
#define IN22_IOC1 0

// LC72131 DO0, byte 0
#define DO0_IN2 7
#define DO0_IN1 6
#define DO0_UL 4

// For function YST93xSetMode
#define YST93x_MONO 1
#define YST93x_STEREO 2
#define YST93x_MUTE 3
#define YST93x_UNMUTE 4
#define YST93x_BAND_FM 5
#define YST93x_BAND_AM 6

// Acceptable IF frequency deviation window (for the PLL) when searching for radio stations
// You may need to tweek these values to have a reliable "scan" mode
#define FM_TUNED_WINDOW 180
#define AM_TUNED_WINDOW 20

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

SanyoCCB ccb(A5, A4, A3, A2); // Pins: DO CL DI CE

byte pll_in1[3];
byte pll_in2[3];

// Initial frequencies
uint16_t FMFrequency = 870; // MHz * 10
uint16_t AMFrequency = 53; // KHZ / 10

uint8_t band = YST93x_BAND_FM;
uint8_t tuned = 0;


/************************************************\
* YST93xInit() *
* Initialize the PLL settings vectors with *
* parameters common to booth AM and FM modes *
\************************************************/
void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);
bitSet(pll_in2[0], IN20_IFS); // IF counter in normal mode
bitSet(pll_in2[1], IN21_UL0); // Phase error detection width = 0us
bitSet(pll_in2[2], IN22_BO2); // Mute off / normal tuner mode
}


/************************************************\
* YST93xSetMode() *
* Some predefined setups for the YST93x module *
\************************************************/
void YST93xSetMode(uint8_t mode) {
switch(mode) {
case YST93x_STEREO:
bitClear(pll_in2[2], IN22_BO3);
break;

case YST93x_MONO:
bitSet(pll_in2[2], IN22_BO3);
break;

case YST93x_MUTE:
bitClear(pll_in2[2], IN22_BO2);
break;

case YST93x_UNMUTE:
bitSet(pll_in2[2], IN22_BO2);
break;

case YST93x_BAND_FM:
band = YST93x_BAND_FM;
bitWrite(pll_in1[0], IN10_R0, 1); // Reference frequency = 50kHz
bitWrite(pll_in1[0], IN10_R3, 0); //
bitWrite(pll_in1[0], IN10_DVS, 1); // Programmable Divider divisor = 2
bitWrite(pll_in2[0], IN20_GT0, 0); // IF counter mesurement period = 32ms
bitWrite(pll_in2[0], IN20_GT1, 1); //
bitWrite(pll_in2[1], IN21_DZ0, 1); // Dead zone = DZB
bitWrite(pll_in2[1], IN21_DZ1, 0); //
bitWrite(pll_in2[2], IN22_BO1, 0); // FM mode
break;

case YST93x_BAND_AM:
band = YST93x_BAND_AM;
bitWrite(pll_in1[0], IN10_R0, 0); // Reference frequency = 10kHz
bitWrite(pll_in1[0], IN10_R3, 1); //
bitWrite(pll_in1[0], IN10_DVS, 0); // Programmable Divider divisor = 1
bitWrite(pll_in2[0], IN20_GT0, 1); // IF counter mesurement period = 8ms
bitWrite(pll_in2[0], IN20_GT1, 0); //
bitWrite(pll_in2[1], IN21_DZ0, 0); // Dead zone = DZC
bitWrite(pll_in2[1], IN21_DZ1, 1); //
bitWrite(pll_in2[2], IN22_BO1, 1); // AM mode
break;
}
ccb.write(0x82, pll_in1, 3);
ccb.write(0x92, pll_in2, 3);
}



/************************************************************\
* YST93xTune() *
* Set the tuner frequency and return 1 if it is tuned *
* or 0 otherwise. *
* *
* The frequency divisors was chosen in a way the frequency *
* representation can be directly sent to the PLL and is *
* easy to represent: *
* - FM mode (divisor = 100): frequency (MHz) * 10 *
* - AM mode (divisor = 10): frequency (kHZ) / 10 *
\************************************************************/
uint8_t YST93xTune(uint16_t frequency) {
uint16_t fpd = 0;
uint8_t i = 0;
uint8_t r[3];
unsigned long IFCounter = 0;

switch(band) {
case YST93x_BAND_FM:
// FM: fpd = (frequency + FI) / (50 * 2)
fpd = (frequency + 107);
break;

case YST93x_BAND_AM:
// AM: fpd = ((frequency + FI) / 10) << 4
fpd = (frequency + 45) << 4;
break;

default: return 1;
}

YST93xSetMode(YST93x_MUTE); // YST93x only injects FI signal into the PLL when in MUTE mode

// Reset the IF counter and program the Frequency Programmable Divider (fpd)
bitClear(pll_in1[0], IN10_CTE);
pll_in1[1] = byte(fpd >> 8);
pll_in1[2] = byte(fpd & 0x00ff);
ccb.write(0x82, pll_in1, 3);

// Start the IF counter
bitSet(pll_in1[0], IN10_CTE);
ccb.write(0x82, pll_in1, 3);

// Wait for PLL to be locked (DO0_UL == 1)
while(i < 20) {
delay(50);
ccb.read(0xa2, r, 3); // Discard the 1st result: it is latched from the last count (as said on the datasheet)
ccb.read(0xa2, r, 3); // The 20 rightmost bits from r[0..2] are the IF counter result
i = (bitRead(r[0], DO0_UL)) ? 100 : i + 1;
}

YST93xSetMode(YST93x_UNMUTE); // Mute off / normal tuner mode

if(i == 100) {
// PLL is locked. If the IF deviation is within the defined (window) interval,
// the radio is likely to be tuned.
// Note: this "tuned" detection method is not recommended on the LC72131 datasheet as
// it can give false positive results. A better approach would be to get the "tuned"
// flag from a RDS decoder with signal quality detection (e.g.: PT2579 or Philips SAA6588)
// connected to the YST93x tuner module "RDS Output" pin. SAA6588 is more powerful/popular,
// but PT2579 looks a lot easier to use as it is not programmable and has a dedicated
// signal quality output pin.
IFCounter = (r[0] & 0x0f);
IFCounter = (IFCounter << 16) | (unsigned long)(r[1] << 8) | (r[2]);
Serial.println(IFCounter);
switch(band) {
case YST93x_BAND_FM:
// Expected value: IF (10.7MHz) * Mesurement period (32ms, as set via GT[1..0]) = 342400
if((IFCounter > 342400) && ((IFCounter - 342400) < FM_TUNED_WINDOW)) return 1;
if((IFCounter < 342400) && ((342400 - IFCounter) < FM_TUNED_WINDOW)) return 1;
break;
case YST93x_BAND_AM:
// Expected value: IF (450kHz) * Mesurement period (8ms, as set via GT[1..0]) = 3600
// Note: scan mode in AM is really poor. I have done my best in tweaking it, but it barely works
if((IFCounter > 3600) && ((IFCounter - 3600) < AM_TUNED_WINDOW)) return 1;
if((IFCounter < 3600) && ((3600 - IFCounter) < AM_TUNED_WINDOW)) return 1;
break;
}
}
return 0;
}


/**************************************************\
* YST93xIsStereo() *
* Returns 1 if the tuned radio station is stereo *
\**************************************************/
uint8_t YST93xIsStereo() {
uint8_t r[3];

ccb.read(0xa2, r, 3);
return(bitRead(r[0], DO0_IN2) ? 0 : 1);
}


/*******************************************************\
* getKey() *
* Read the (analog) keypad. *
* If you are using an digital (one key per input pin) *
* keypad, just this function to return the correct *
* values *
\*******************************************************/
uint8_t getKey(uint8_t keypadPin) {
uint16_t keypad;
uint8_t key = KEY_NONE;

keypad = analogRead(keypadPin);

if(keypad < 870) key = KEY_BAND;
if(keypad < 600) key = KEY_SCAN_DOWN;
if(keypad < 390) key = KEY_DOWN;
if(keypad < 220) key = KEY_UP;
if(keypad < 60) key = KEY_SCAN_UP;

return key;
}


/*******************\
* Arduino setup() *
\*******************/
void setup() {
lcd.begin(16, 2);
Serial.begin(9600);
ccb.init();
delay(1000);
YST93xInit();
YST93xSetMode(YST93x_BAND_FM);
tuned = YST93xTune(FMFrequency);
}


/******************\
* Arduino loop() *
\******************/
void loop() {
uint8_t scan = 0;
int8_t delta = 0;
uint8_t key = getKey(KEYPAD_PIN);

lcd.setCursor(0, 0);
switch(band) {
case YST93x_BAND_FM:
lcd.print("FM ");
lcd.setCursor((FMFrequency < 1000) ? 4 : 3, 0); lcd.print((float)FMFrequency / 10, 1);
lcd.print("MHz ");
break;
case YST93x_BAND_AM:
lcd.print("AM ");
lcd.setCursor((AMFrequency < 100) ? 4 : 3, 0); lcd.print(AMFrequency * 10);
lcd.print("KHz ");
break;
}

// Updates the Stereo indicator
lcd.setCursor(12, 0);
if(YST93xIsStereo())
lcd.print("[ST]");
else
lcd.print("[ ]");

// The "Tuned" indicator is updated only when the station changes
lcd.setCursor(2, 1);
if(tuned)
lcd.print(" Radio ");
else
lcd.print(" ");

// Processo keypad inputs
if(key != KEY_NONE) {
switch(key) {
case KEY_UP: scan = 0; delta = +1; break;
case KEY_DOWN: scan = 0; delta = -1; break;
case KEY_SCAN_UP: scan = 1; delta = +1; break;
case KEY_SCAN_DOWN: scan = 1; delta = -1; break;
case KEY_BAND:
switch(band) {
case YST93x_BAND_FM:
YST93xSetMode(YST93x_BAND_AM);
tuned = YST93xTune(AMFrequency);
break;

case YST93x_BAND_AM:
YST93xSetMode(YST93x_BAND_FM);
tuned = YST93xTune(FMFrequency);
break;
}
}

if(scan) {
lcd.setCursor(2, 1);
lcd.print("Pretraga...");
}

// Change the station
switch(band) {
case YST93x_BAND_FM:
do{
FMFrequency += delta;
if(FMFrequency > 1080) FMFrequency = 880;
if(FMFrequency < 880) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);

lcd.setCursor(3, 0);
if(FMFrequency < 1000) lcd.print(' ');
lcd.print((float)FMFrequency / 10, 1);
} while(scan && (! tuned));
break;

case YST93x_BAND_AM:
do{
AMFrequency += delta;
if(AMFrequency > 170) AMFrequency = 53;
if(AMFrequency < 53) AMFrequency = 170;
tuned = YST93xTune(AMFrequency);

lcd.setCursor(3, 0);
if(AMFrequency < 100) lcd.print(' ');
lcd.print(AMFrequency * 10);
} while(scan && (! tuned));
break;

} // switch

} // if(key)

}
Evo i slika kako treba da izgleda.


Prikačeni fajlovi
 
Odgovor na temu

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina27.03.2026. u 13:33 - pre 2 meseca
U ovoj verziji sam:

Izmestio Mute logiku: Funkcija YST93xTune sada samo šalje podatke, a loop kontroliše kada se ton pušta.

Uveo neprekidnu tišinu: Čim pritisneš taster, radio se utiša i ostaje potpuno nem dok ne završi pretragu (bilo jedan korak ili ceo sken).

Dodao Enkoder: Kod je pripremljen za rotacioni enkoder na pinovima 2 (CLK) i 3 (DT), sa tasterom na pinu 11.

C++

#include <inttypes.h>
#include <LiquidCrystal.h>
#include <SanyoCCB.h>
#include <EEPROM.h>

// PIN KONFIGURACIJA
#define ENC_A 2
#define ENC_B 3
#define ENC_SW 11

// LC72131 DEFINICIJE
#define IN10_XS 3
#define IN10_CTE 2
#define IN22_BO2 5 // Pin 8 - Mute
#define IN22_BO1 4 // Band selector
#define DO0_UL 4

#define YST93x_MUTE 3
#define YST93x_UNMUTE 4
#define YST93x_BAND_FM 5
#define YST93x_BAND_AM 6

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
SanyoCCB ccb(A5, A4, A3, A2);

byte pll_in1[3];
byte pll_in2[3];
uint16_t FMFrequency = 880, AMFrequency = 53;
uint8_t band = YST93x_BAND_FM, tuned = 0;

// Enkoder varijable
volatile int enc_delta = 0;
unsigned long taster_vreme = 0;
bool menuMode = false;

// Interrupt za enkoder
void readEncoder() {
if (digitalRead(ENC_A) == digitalRead(ENC_B)) enc_delta++;
else enc_delta--;
}

void YST93xInit() {
memset(pll_in1, 0, 3);
memset(pll_in2, 0, 3);
bitSet(pll_in2[0], 4); // IFS normal
bitClear(pll_in1[0], IN10_XS); // 4.5MHz
}

void YST93xSetMode(uint8_t mode) {
switch(mode) {
case YST93x_MUTE: bitClear(pll_in2[2], IN22_BO2); break;
case YST93x_UNMUTE: bitSet(pll_in2[2], IN22_BO2); break;
case YST93x_BAND_FM:
band = YST93x_BAND_FM;
bitWrite(pll_in1[0], 4, 1); // 50kHz step
bitWrite(pll_in1[0], 1, 1); // DVS=1
bitWrite(pll_in2[2], IN22_BO1, 0);
break;
case YST93x_BAND_AM:
band = YST93x_BAND_AM;
bitWrite(pll_in1[0], 7, 1); // 10kHz step
bitWrite(pll_in1[0], 1, 0); // DVS=0
bitWrite(pll_in2[2], IN22_BO1, 1);
break;
}
ccb.write(0x82, pll_in1, 3);
ccb.write(0x92, pll_in2, 3);
}

// IZMENJENA FUNKCIJA: Ne dira Mute, samo vrši promenu frekvencije
uint8_t YST93xTune(uint16_t frequency) {
uint16_t fpd = (band == YST93x_BAND_FM) ? (frequency + 107) : (frequency + 45) << 4;
uint8_t i = 0, r[3];

bitClear(pll_in1[0], IN10_CTE);
pll_in1[1] = byte(fpd >> 8);
pll_in1[2] = byte(fpd & 0x00ff);
ccb.write(0x82, pll_in1, 3);
bitSet(pll_in1[0], IN10_CTE);
ccb.write(0x82, pll_in1, 3);

while(i < 10) {
delay(40);
ccb.read(0xa2, r, 3);
if(bitRead(r[0], DO0_UL)) { i = 100; break; }
i++;
}

if(i == 100) {
unsigned long IF = (r[0] & 0x0f);
IF = (IF << 16) | (unsigned long)(r[1] << 8) | (r[2]);
if(band == YST93x_BAND_FM && abs((long)IF - 342400) < 180) return 1;
if(band == YST93x_BAND_AM && abs((long)IF - 3600) < 20) return 1;
}
return 0;
}

void setup() {
lcd.begin(16, 2);
ccb.init();
YST93xInit();

pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(ENC_SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENC_A), readEncoder, CHANGE);

YST93xSetMode(band);
YST93xTune(FMFrequency);
YST93xSetMode(YST93x_UNMUTE);
ispis();
}

void loop() {
// 1. RAD ENKODERA (Okretanje)
if (enc_delta != 0) {
int dir = (enc_delta > 0) ? 1 : -1;
enc_delta = 0;

if (menuMode) {
band = (band == YST93x_BAND_FM) ? YST93x_BAND_AM : YST93x_BAND_FM;
YST93xSetMode(band);
} else {
YST93xSetMode(YST93x_MUTE); // TIŠINA KREĆE OVDE
obradiPromenu(dir, false);
YST93xSetMode(YST93x_UNMUTE); // TON SE VRAĆA TEK KAD STANEŠ
}
ispis();
}

// 2. TASTER ENKODERA (Klik i Dugi pritisak)
if (digitalRead(ENC_SW) == LOW) {
if (taster_vreme == 0) taster_vreme = millis();

// DUGI PRITISAK -> AUTO SCAN
if (millis() - taster_vreme > 1000) {
menuMode = false;
lcd.setCursor(0, 1); lcd.print("SKENIRANJE... ");
YST93xSetMode(YST93x_MUTE);
obradiPromenu(1, true);
YST93xSetMode(YST93x_UNMUTE);
taster_vreme = 0;
while(digitalRead(ENC_SW) == LOW);
ispis();
}
} else {
// KRATAK KLIK -> ULAZ U MENI ZA BAND
if (taster_vreme > 50 && taster_vreme < 1000) {
menuMode = !menuMode;
ispis();
}
taster_vreme = 0;
}
}

void obradiPromenu(int d, bool scan) {
do {
if (band == YST93x_BAND_FM) {
FMFrequency += d;
if (FMFrequency > 1080) FMFrequency = 875;
if (FMFrequency < 875) FMFrequency = 1080;
tuned = YST93xTune(FMFrequency);
} else {
AMFrequency += d;
if (AMFrequency > 170) AMFrequency = 53;
if (AMFrequency < 53) AMFrequency = 170;
tuned = YST93xTune(AMFrequency);
}
if(scan) ispis(); // Ažuriraj ekran tokom skeniranja
} while (scan && !tuned);
}

void ispis() {
lcd.setCursor(0, 0);
if (menuMode) {
lcd.print("IZBOR OPSEGA: ");
lcd.setCursor(0, 1);
lcd.print(band == YST93x_BAND_FM ? "> FM < AM " : " FM > AM < ");
} else {
lcd.print(band == YST93x_BAND_FM ? "FM: " : "AM: ");
if (band == YST93x_BAND_FM) {
lcd.print((float)FMFrequency/10, 1); lcd.print(" MHz ");
} else {
lcd.print(AMFrequency * 10); lcd.print(" KHz ");
}
lcd.setCursor(0, 1);
lcd.print(tuned ? " [ STANI ] " : " [ TRAZI ] ");
}
}
Šta je novo u radu:
Tišina: Čim pomeriš enkoder, BO2 (Pin 8) ide u nulu i ostaje tamo. Nema više seckanja tona u ritmu Arduina. Ton se vraća samo kada prestaneš da okrećeš ili kada auto-skeniranje pronađe stanicu.

Enkoder Meni: * Jedan klik: Otvara meni. Na ekranu biraš FM ili AM okretanjem enkodera. Još jedan klik potvrđuje izbor.

Okretanje (van menija): Menjaš frekvenciju ručno (korak po korak).

Dug pritisak (1 sekunda): Pokreće automatsku pretragu do prve sledeće stanice.

Povezivanje:
Poveži enkoder prema šemi: Pin 2 (Arduino) -> CLK, Pin 3 (Arduino) -> DT, Pin 11 (Arduino) -> SW.

Da li želiš da dodamo i EEPROM memoriju u ovaj kod, tako da radio zapamti poslednju stanicu čak i kada ga isključiš iz struje?
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

schild
Dejan Šild
TopCode Software
Subotica

Član broj: 59888
Poruke: 139
*.static.isp.telekom.rs.

Sajt: www.topcode.rs


+2 Profil

icon Re: Radio pomoću arduina06.04.2026. u 11:12 - pre 58 dana i 20h
Ovaj AI Samuki je baš nadrealan. Koja je poenta ovo raditi? Budžiš profil za CV?!
 
Odgovor na temu

samuki
💻🇧🇦
🅱🅰
BiH Sarajevo🇧🇦

Član broj: 322399
Poruke: 1663
*.dynamic.telemach.ba.



+101 Profil

icon Re: Radio pomoću arduina06.04.2026. u 12:25 - pre 58 dana i 19h
Ako u CV ulazi pisanje sa foruma onda moze .
Imam multimetar i lemilicu ! 🇧🇦
 
Odgovor na temu

[es] :: Elektronika :: Mikrokontroleri :: Radio pomoću arduina

Strane: 1 2

[ Pregleda: 2521 | Odgovora: 30 ] > FB > Twit

Postavi temu Odgovori

Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.