RPi-Pico Raster-VFO              


Elektronik-Labor  Projekte  Mikrocontroller  Raspberry            




Ein RPi Pico kann ganz ohne zusätzliche Hardware zu einem programmierbaren Oszillator für den Kurzwellenbereich werden. Hier wurde versucht, möglichst kleine Frequenzschritte zu erreichen. Das Geheimnis liegt in der internen PLL des Pico, mit der man unterschiedliche Taktfrequenzen einstellen kann. Die Stufung ist zwar nicht sehr eng, aber zusammen mit unterschiedlichen Teilerfaktoren durch die PWM-Einheit können sehr viele unterschiedliche Frequenzen erzeugt werden. Das Verfahren beruht aus einem Ausprobieren, welche Einstellung am besten zur Wunschfrequenz passt.

Das folgende Rechenprogramm gibt einen Überblick, welche Ausgangsfrequenzen möglich werden, wenn man die Systemfrequenz in ganzen MHz zwischen 32 MHz und 130 MHz variiert.

# Mögliche PWM-Frequenzen zwischen 3,5 MHz und 3,6 MHz
min1=3499
clock =0
divisor=1
for l in range(100):
    delta1=100
    for m in range (32,130):
        for n in range(1,50):
            f=m/n*1000
            if f>min1:
                if f<3600:
                    delta2=f-min1
                    if delta2<delta1:#
                        delta1=delta2
                        min2=f
                        divisor=n
                        clock=m
    min1=min2
    print (clock, divisor, min1)

Das Ergebnis zeigt einige glatte Frequenzen, wie z.B. 88 MHz / 25 = 3,520 MHz, aber auch viele andere, krummere Werte. Man sieht aber schon, dass es insgesamt eine enge Abstufung gibt.

35 10 3500.0
123 35 3514.286
116 33 3515.152
109 31 3516.129
102 29 3517.241
95 27 3518.518
88 25 3520.0
81 23 3521.739
74 21 3523.809
67 19 3526.316
127 36 3527.777
60 17 3529.412
113 32 3531.25
53 15 3533.333
99 28 3535.714
46 13 3538.461
85 24 3541.667
124 35 3542.857
39 11 3545.455
110 31 3548.387
71 20 3550.0
103 29 3551.724
32 9 3555.556
121 34 3558.823
89 25 3560.0
...

Tatsächlich sind nicht nur glatte MHz-Frequenzen möglich, sondern auch Vielfache von  0,8, 1,2 und 1,5 MHz. Damit ergeben sich noch mehr Möglichkeiten und insgesamt ein engeres Raster möglicher Ausgangsfrequenzen. Die Berechnung und Ausgabe der Frequenzen läuft in einer Interrupt-Funktion, damit das Hauptprogramm jederzeit auf Eingaben des Benutzers reagieren kann. Er kann eine Wunschfrequenz eingeben und erhält dann die Ausgabe der genauen VFO-Frequenz und zusätzlich die zugehörige Empfangsfrequenz bei einer ZF von 455 kHz. Im Bereich 100 kHz bis 2 MHz ist die Treffergenauigkeit besser als 1 kHz. Je höher die Ausgangsfrequenz wird, desto größer werden die Abweichungen. Bei 3,5 MHz kann man noch sinnvoll Abstande von 5 kHz einstellen, bei 7 MHz noch Abstände von 10 kHz.


#VFO1.py Kurzwellen VFO, Out P2

from machine import Pin, PWM, Timer
import time
pwm1 = PWM(Pin(2))

f=6255
fout=f
sp=0
df=5
fa=0

def tune(value):
    global f, df, sp, fout
    clock = 100000
    min2=0
    delta1=500
    if sp==1:
        f=f+df
    for j in range (80,120):
        m=j*0.8
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (60,120):
        m=j*1
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (50,100):
        m=j*1.2
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (40,80):
        m=j*1.5
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    fout=min2
    if fa!=f:
        clk = 100*int(clock*10)
        #print (f,clk, fout)
        machine.freq(clk*1000)
        pwm1.freq(int(fout)*1000)
        pwm1.duty_u16(32767)

timer = Timer(period=1000, mode=Timer.PERIODIC, callback=tune)

while 1:   
    fnew=input("f=")   
    fin=int(fnew)
    if fin>100:
        f=fin
        df=0
    else:
        if fin==0:
            sp=0
        if fin==1:
            df=fin
            sp=1
        if fin==2:
            f=f-df
            sp=0
        if fin==3:
            f=f+df
            sp=0
        if fin>3:
            df=fin
            sp=1
   
    time.sleep (1.2)
    print (fout,fout-455)
   
   
Zusätzlich gibt es die Möglichkeit, einen Suchlauf ab der zuletzt gewählten Frequenz zu starten. Man gibt dazu die Schrittweite 1 kHz, 5 kHz oder mehr ein. Eine 0 stoppt den Suchlauf. Weil man dann manchmal über das Ziel hinaus gesucht hat, geht die 2 einen Schritt zurück und die 3 einen Schritt vor.



Der VFO wurde als Hautoszillator für den RX2003 getestet. Zwischen dem Empfänger und dem RPi Pico liegt ein kleiner Ringkern-Trenntrafo, der aus einer defekten Energiesparlampe ausgebaut wurde. Die Trennung reduziert Störungen, die sonst über das USB-Kabel vom PC in den Empfänger gelangen können. Hier wurden mit dem Suchlauf interessante AM-Rundfunksender im 49-, 41 und 31-m-Band gefunden.

Rpi Pico Stand Alone VFO



Diesmal wird der VFO ohne USB-Verbindung verwendet und kann mit einer einzelnen Taste abgestimmt werden. Nur die Betriebsspannung wird über die USB-Buchse zugeführt. Der VFO wurde für meinen 80m-Direktmischer verwendet, um die Drehkoabstimmung durch die PLL-Variante zu ersetzen. damit erreicht man eine sehr viel bessere Frequenzkonstanz, die den Direktmischer auch für digitale Betriebsarten tauglich macht.


#HFgen4.py 80 m Raster-VFO

from machine import Pin, PWM, Timer
import time
from machine import Pin, I2C, ADC, PWM, freq as CPU_freq
from ssd1306 import SSD1306_I2C

i2c = I2C(0, scl=Pin(1),sda=Pin(0),freq=100000)
oled = SSD1306_I2C(128,64,i2c)
p5 = Pin(5, Pin.IN, Pin.PULL_UP)
p7 = Pin(7, Pin.IN, Pin.PULL_UP)

machine.freq(32000000)
pwm1 = PWM(Pin(2))
pwm1.freq(3555000) #3555 kHz
pwm1.duty_u16(32767)

for i in range (3500,3800):
    f=i*1
    delta1=100
    for j in range (80,120):
        m=j*0.8
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (60,120):
        m=j*1
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (50,100):
        m=j*1.2
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    for j in range (40,80):
        m=j*1.5
        n = round(1000*m/f)
        f2=1000*m/n
        delta2=abs(f2-f)
        if delta2<delta1:
            delta1=delta2
            min2=f2
            divisor=n
            clock=m
    fout=min2
    clk = 100*int(clock*10)
    print (f,clk, fout)

    machine.freq(clk*1000)
    pwm1.freq(int(fout)*1000)
    pwm1.duty_u16(32767)
    fout = round(fout*10)/10
    text = str(fout) + " kHz"
    oled.fill(0)
    oled.text(text,25,2)
    oled.show()
    time.sleep (0.05)
    while p5.value()==1:
        time.sleep (0.05)

Die Sollfrequenz zwischen 3500 kHz und 3800 kHz wird hier in 1kHz-Sprüngen erhöht, wenn man auf den Taster drückt. Das Programm gibt die Sollfrequenz, den eingestellten Prozessortakt in kHz und die tatsächliche Ausgangsfrequenz aus. Auf dem OLED-Display wird nur die tatsächliche VFO-Frequenz angezeigt. Man sieht hier die Einstellungen für den Bereich 3520 kHz bis  3580 kHz. Die Frequenz wird nur an wenigen Stellen genau getroffen, die Abweichung bleibt aber meist kleiner als 1 kHz.

3520 70400 3520.0
3521 81000 3521.739
3522 81000 3521.739
3523 109200 3522.581
3524 74000 3523.809
3525 112800 3525.0
3526 95200 3525.926
3527 77600 3527.273
3528 77600 3527.273
3529 60000 3529.412
3530 60000 3529.412
3531 113000 3531.25
3532 109500 3532.258
3533 84800 3533.333
3534 63600 3533.334
3535 99000 3535.714
3536 99000 3535.714
3537 67200 3536.842
3538 92000 3538.461
3539 92000 3538.461
3540 70800 3540.0
3541 85000 3541.667
3542 85000 3541.667
3543 74400 3542.857
3544 74400 3542.857
3545 78000 3545.455
3546 78000 3545.455
3547 81600 3547.826
3548 81600 3547.827
3549 110000 3548.387
3550 71000 3550.0
3551 103000 3551.724
3552 88800 3552.0
3553 67500 3552.631
3554 92400 3553.846
3555 64000 3555.556
3556 64000 3555.556
3557 99600 3557.143
3558 103200 3558.621
3559 103200 3558.621
3560 89000 3560.0
3561 110400 3561.291
3562 114000 3562.5
3563 114000 3562.5
3564 117600 3563.637
3565 82000 3565.217
3566 85600 3566.667
3567 85600 3566.667
3568 103500 3568.966
3569 103500 3568.966
3570 92800 3569.231
3571 75000 3571.428
3572 75000 3571.428
3573 75000 3571.428
3574 118000 3575.758
3575 118000 3575.758
3576 118000 3575.758
3577 93000 3576.923
3578 68000 3578.947
3579 68000 3578.947
3580 111000 3580.645

Der VFO war auf Anhieb und ohne Trenntrafo tauglich für den 80m-Direktmischer. Der Ausgang des Empfängers wurde an die PC-Soundkarte angeschlossen und das Signal dann mit SDR-Software verarbeitet.

 

Hier wurden FT8-Signale empfangen. Die dafür nötige Konstanz der Frequenz wurde problemlos eingehalten. Die VFO-Frequenz sollte eigentlich 3573 kHz sein, Der VFO konnte an dieser Stelle allerdings nur 3571,4 kHz liefern. Den Ausgleich besorgt SDRSharp. In der Betriebsart USB wurde die Frequenz passend eingestellt. Das empfangene Signal wurde dann über den Lautsprecher und ein Mirofon an WSJT-X übergeben. Das geht zwar auch direkter, funktionierte aber auch so recht gut.






Elektronik-Labor  Projekte  Mikrocontroller  Raspberry