racional aproximation « YU3MA

Si5351A treci deo

Filed in SDR 1 Comment

1000000.0Hz

Heh, kako dobiti ovako nesto na izlazu iz Si5351? 🙂

Posle duzeg vremena uspeo sam da dodjem do konkretnog algoritma za kalkulaciju frekvencije.
Ovo je samo deo (mozda najbitniji) u celoj proceduri postavljanja izlazne frekvencije na Si5351.

Da pokusam da objasnim princip generisanja ucestanosti.

Frekvencija kristala (27MHz) se interno u Si5351 prvo umnozi (u mom slucaju 30 puta za PLLA) kako bi se dobila medjufrekvencija za FVCO (810MHz) koja se posle deli nekim racionalnim brojem (iskazanim u MSx registrima) radi dobijanja zeljene izlazne frekvencije.

Npr da bi dobili 1.023MHz na izlazu treba uraditi ovakav racun:
27MHz * 30 / 791.788856304985 = 1.023MHz
Ili obrnuto kako prakticno ulazimo u racun:
27MHz * 30 / 1.023MHz = 791.788856304985

Broj 791.788856304985 kao takvog ne mozemo da upisemo direktno u delitelj vec kao razlomak npr najpriblizniji je 791+269/341 sto je prakticno srz problema i uslovljeno je samom tehnickom konstrukcijom unutar IC-a. Dakle ne moze direktno da se upise ovaj decimalni broj jer bi onda sam IC morao da radi vrlo slozenu matematiku koja zahteva poprilicno procesorskog vremena i RAM memorije pa se to “prebacuje” da neko drugi radi, obicno PC ili neki snazniji mikrokontroler gde ima takvih resursa. Dodatno je malo kriticno sto algoritam za “racionalnu aproksimaciju” zahteva u vecini slucaja oko 30-50 iteracija a za jos vecu preciznost oko 300 da bi nasao rezultat sto je prilicno zahtevno.

Resenje se svodi na to da se prvo izdvoji celobrojna vrednost 791 (u dokumentaciji referenciran kao parametar a) i ostatak 0.788856304985 da se iskaze kao razlomak dve celobrojne vrednosti (u dokumentaciji referenciran kao parametar b i c), dakle delitelj=a+b/c. Primenom algoritma “racionalne aproksimacije” je moguce naci takve dve celobrojne vrednosti za b i c koje najblize odgovaraju zahtevu (269/341=0.78885630498534) u nekom specificiranom opsegu brojeva. Kada imamo a, b i c onda se dodatnim “muckanjem” to razlozi i upise u 8 x 8bit registre. U predhodnom resenju gde sam racunao a,b i c na najprostiji nacin (delio/mnozio sa 100000) sam dobijao greske i to se manifestovalo tako sto kada krenem da “motam” LO frekvenciju iz SDR programa u jednu stranu, on ide malo unapred pa se vrati (skoci) nazad pa nastavi opet u jednu stranu, prilicno lose.

Ideja za ovo resenje je potekla od izvornog code-a linux kernel drajvera za Si5351 koje sam sasvim slucajno nasao.

Dakle radi se o racunu za MSx registre (delitelj koji generise izlaznu ucestanost pojedinacnog izlaza, setite se ima ih 3 ili 8) dok je vrednost za PLLA registar (mnozitelj ulazne frekvencije kristala) fiksno postavljena na parnu decimalnu vrednost 30 kako bi se iskoristio tkz “integer mode” (FBA_INT = 1) radi smanjivanja jitter-a (preporuka proizvodjaca).

XTAL_PPM je vrednost nakon kalibracije uredjaja i koja zavisi od svakog pojedinacnog primerka sklopa i njegovog kristala. Za racun PPM-a koristite ovaj online kalkulator:
http://www.jittertime.com/resources/ppmcalc.shtml
Kao sto se moze videti na postavljenoj fotografiji, neophodan vam je neki precizan i kalibrisan frekvencmetar ili kalibraciju uraditi nekom drugom metodom.

Ovo je samo deo cele price oko Si5351 pa je i dalje neophodno dobro protumaciti tehnicku dokumentaciju i aplikacinu notu AN619.

Ovo sam postavio zato sto o tome ne pisu ni u jednom dokumentu za ovaj IC 😉

Sledeci code prikazuje moju realizaciju te kalkulacije.
U pitanju je C jezik i ovo sam implementirao u upravljackoj aplikaciji za moj ZMSDR (ne u PIC).
Jedan deo ove kalkulacije koji se tice racionalne aproksimacije je baziran na code-u sa ove stranice i bazira se na tkz “Farey-evoj sekvenci”.

Kako dobijene vrednosti SI_REGS konkretno upisati u Si5351 preko PIC mikrokontrolera koristeci I2C protokol i sta se desava u samom PIC nekom sledecom prilikom 😉

 

    double currentFrequency = 1.0; //Zahtevana frekvencija u MHz

    bool DEBUG = FALSE;
    double XTAL = 27; //MHz

    //Korekcija / kalibracija
    //double XTAL_PPM = 0; //
    double XTAL_PPM = −13.625; //korekcija za moj primerak
   
    if (XTAL_PPM != 0) {
        double tmp_ppm;
        tmp_ppm = XTAL * XTAL_PPM / 1000000;
        XTAL = XTAL + tmp_ppm;
    }
   
    //
    // Postavimo PLLA da bude recimo 30 da bi koristili integer mod
    // i ona se upisuje jednokratno u Si5351 od strane PIC−a posle reset.
    // Reprezentacija ove vrednosti za registre (dec) 26  33 je (hex):
    // 00 01 00 0D 00 00 00 00

    double PLLA_MULT_RATIO = 30;
   
    double FVCO = XTAL * PLLA_MULT_RATIO; //810MHz
   
    double REQ_DIV; //zahtevani delitelj
   
    int p1, p2, p3;
    int r = 1;
   
    unsigned char SI_REGS[8];
 
    //koji je odnos deljenja potreban za Multisynth
    REQ_DIV = FVCO / currentFrequency;
   
    double a,b,c,d,mediant,x,N; //x = frakcioni broj, N = denominator
    double out_a, out_b, out_c;
    bool loop_break = false;
   
    double cnt;
    cnt = 0; //informativno da vidim kolkiko je prolaza napravio
   
    //
    // Algoritam za racionalnu aproksimaciju zahtevanog delitelja
    // Ovo je primenljivo za PLLA, PLLB i sve MultySinth delitelje
    //

    //koji je odnos deljenja je potreban za Multisynth
    REQ_DIV = FVCO / currentFrequency;
   
    x = REQ_DIV  (int)REQ_DIV; //uzmi samo vrednost iza zareza
   
    N = 10000; //denominator
   
    a = 0;
    b = 1;
    c = 1;
    d = 1;
   
    if (x == 0) {
        //nemamo racionalni broj
        //under construction!

        //jel kojim slucajem parni da udjemo u mod MSO_INT=1
        if ((int)REQ_DIV % 2 == 1) {
            //neparan  odd
            printf(“REQ_DIV %i je neparan \n”, (int)REQ_DIV);
            //under construction!
        } else {
            //paran  even
            printf(“REQ_DIV x%i je paran \n”, (int)REQ_DIV);
            //under construction!
        }
       
        out_b = 0;
        out_c = 1;
       
    } else {
        while (b <= N && d <= N) {
           
            cnt++;
           
            mediant = (a+c)/(b+d);
            if (x == mediant) {
                if (b + d <= N) {
                    out_b = a+c;
                    out_c = b+d;
                    loop_break = TRUE;
                    break;
                } else if (d > b) {
                    out_b = c;
                    out_c = d;
                    loop_break = TRUE;
                    break;
                } else {
                    out_b = a;
                    out_c = b;
                    loop_break = TRUE;
                    break;
                }
            } else if (x > mediant) {
                a = a+c;
                b = b+d;
            } else {
                c = a+c;
                d = b+d;
            }
        }
       
        if (loop_break == FALSE) {
            if (b > N) {
                out_b = c;
                out_c = d;
            } else {
                out_b = a;
                out_c = b;
            }
        }
    }
   
    out_a = (int)REQ_DIV;
    //
    // Kraj racuna za racionalnu aproksimaciju 
    //
   
    if(DEBUG)
    printf(“REQ_DIV=%f x=%f out_a=%i out_b=%i out_c=%i cnt=%i\n”,
         REQ_DIV, x, (int)out_a, (int)out_b, (int)out_c, (int)cnt);
   
    //
    // Pretvori u zahtevan format za registre
    //   
    p1 = 128 * out_a + floor(128*out_b/out_c)  512;
    p2 = 128 * out_b  out_c * floor(128*out_b/out_c);
    p3 = out_c;
  
    if(DEBUG)
    printf(“p1=%u p2=%u p3=%u\n”, p1, p2, p3);
   
    //
    // Raspodeli vrednosti po registrima
    //
    SI_REGS[0] = (p3 >> 8) & 0xFF;
    SI_REGS[1] = (p3 >> 0) & 0xFF;
    SI_REGS[2] = ((int)(log(r)) << 4) | (0 << 2) | ((p1 >> 16) & 0x3);
    SI_REGS[3] = (p1 >> 8) & 0xFF;
    SI_REGS[4] = (p1 >> 0) & 0xFF;
    SI_REGS[5] = (((p3 >> 16) & 0xF) << 4) | (((p2 >> 16) & 0xF) << 0);
    SI_REGS[6] = (p2 >> 8) & 0xFF;
    SI_REGS[7] = (p2 >> 0) & 0xFF;
   
    if(DEBUG)
    printf(“%X %X %X %X %X %X %X %X\n”,
           SI_REGS[0], SI_REGS[1], SI_REGS[2], SI_REGS[3],
           SI_REGS[4], SI_REGS[5], SI_REGS[6], SI_REGS[7]);
   

    //
    // Odavde nanize je pisanje SI_REGS u Si5351
    // Ovo je specificno za svaku pojedinacnu HW implementaciju
    // Ja sam koristio PIC sa USB<>I2C u CDC modu
    //
    // Napomena: Ovo je samo za delitelje, neophodno je postaviti i
    // ostale registre prema upustvu iz Silicon Labs AN619
    //

    //
    // POSIX stil pisanja u filehandler
    //

    FILE *file; //stdio definicija
   
    file = fopen(“/dev/cu.usbmodem411”, “w”); // open device
    //file = fopen(“/Users/vm/proba.txt”, “w”); // open file
   
    if (!file) {
        if(DEBUG)
        printf(“File can’t be opened!”);
    }
  
    char usb_out[12];
   
    usb_out[0] = cmd; //cmd for PIC
    usb_out[1] = SI_REG_MS0[0];
    usb_out[2] = SI_REG_MS0[1];
    usb_out[3] = SI_REG_MS0[2];
    usb_out[4] = SI_REG_MS0[3];
    usb_out[5] = SI_REG_MS0[4];
    usb_out[6] = SI_REG_MS0[5];
    usb_out[7] = SI_REG_MS0[6];
    usb_out[8] = SI_REG_MS0[7];
   
    //write to device
    fwrite(usb_out, 1, 12, file);
   
    fclose(file);
   
    if(DEBUG)
    printf(“File written!”);
   

Napomena: Ovaj algoritam je direktno primenljiv na generisanje frekvencija od nekih 0.5MHz pa do 112.5MHz (imajte u vidu da za SDR treba 4x veca brzina clock-a). Za frekvencije ispod 0.5MHz neophodno je ukljuciti dodatne izlazne delitelje (R) dok za frekvencije iznad 112.5MHz pa do 150MHz treba da se menja i vrednost mnozitelja za PLLA po istom principu/algoritmu kao za MSx. Za frekvencije od 150MHz do 160MHz se koristi treci, nesto prostiji, princip 🙂

Dodatno imajte u vidu da samo delitelji u MSx mogu “u letu” da se upisuju sto daje “glitch free frequency change” a kada se menja PLLA ili PLLB mora da se radi njihov reset sto dovodi do privremenog prekida rada oscilatora.

Inace postoji jedan vrlo specifican slucaj za ultra mali clock-jitter kada su vrednosti MSx i PLLx parne celobrojne vrednosti, setuju se posebni registri FBx_INT i MSx_INT. Primetice te u gore pokazanom programu da to nisam odradio (deo gde pise “under construction!”) jer se te kombinacije desavaju samo za vrlo specificne frekvencije (ima ih oko 4490 od bezbroj raspolozivih) a posto je prakticno nemoguce da ce te imati konstrukciju bez PPM korekcije onda one i ne vaze, tj bile bi tehnicki pomerene za tu PPM razliku. Prakticno govoreci, kome je bitniji manji clock-jitter umesto tacnosti generisane frekvencije moze ovo da iskoristi. Te frekvenciju su recimo ovakve (prikazano zadnjih 30, ostale su razbacane ispod 10MHz):

si5351_ultra_low_jitter_freqs

I za kraj, vreme promene frekvencije za Si5351 je negde oko 336µs (toliko treba da se preko I2C upise 8 registra u burst modu, nema zakljucavanja) sto je odlican rezultat i prakticno 30 puta brze od Si570 kome treba oko 10ms da se “zakljuca” 🙂

73 de YU3MA

, , , ,

TOP