SI5351-Ansteuerung mit Bascom


Elektronik-Labor   Projekte   AVR  



Das Elektro SDR Shield 2.0


Für das Elektor-SDR-Shield und die Ansteuerung des PLL-Chips SI5351 habe ich mich bisher überwiegend an die Arduino-Programmierung gehalten. Die Aussicht auf schlankere Programme und die Verwendung des Tiny85 legen aber die Verwendung von Bascom nahe. Das VFO-Projekt von Andrew Woodfield hat mir Mut gemacht und einige Hintergründe geklärt. Damit ist es möglich geworden, ein noch kompakteres Programm zu entwickeln, das meine Anforderungen erfüllt.

Der entscheidende Punkt ist, dass ich alle drei Ausgänge verwenden will, obwohl es nur zwei PLLs gibt. Die Arduino-Bibliotheken haben das schon vorgemacht: Man stellt eine PLL fest auf 900 MHz ein und kann daraus drei Frequenzen mit den Multisynth-Teiern mit gebrochenen Teilerverhältnissen generieren.  Im ersten Test habe ich die PLL-Einstellungen für die PLL A in einer Datazeile festgehalten. Da muss also nichts mehr berechnet werden, sondern man muss nur noch die richtigen Register füllen.

Für den ersten  Test habe ich außerdem die Nachteiler für alle drei Kanäle auf ein gerades Teilerverhältnis von 900 eingestellt und ebenfalls in einer Datazeile festgehalten. Diese Daten müssen nun dreimal für die drei Ausgänge in jeweils acht Register ab den Adressen 42, 50 und 50 geschrieben werden. Der Test zeigt, dass nun tatsächlich dreimal 1 MHz herauskommt. Zwar sollte das Phasenrauchen etwas größer sein als mit einem ganzzahligen Nachteiler, aber die Signale hören sich völlig sauber an.


'Si5351.bas

$regfile = "m328pdef.dat"
$crystal = 16000000

$hwstack = 128
$swstack = 64
$framesize = 128
Baud = 9600


Dim Badr As Byte
Dim Bdat As Byte
Dim N As Byte
Dim Dat As Byte
Dim Clk As Byte
Dim L As Byte
Dim F As Single
Dim Zeile As String * 20
Dim Komm As String * 1
Dim Reg(200) As Byte


Declare Sub Regwrite
Declare Sub Siinit
Declare Sub Sifreq

Config Sda = Portc.4
Config Scl = Portc.5
I2cinit
Waitms 10
Siinit

Do
Dat = Ischarwaiting()
If Dat > 0 Then
Input Zeile
Komm = Left(zeile , 1)
L = Len(zeile)
L = L - 1
Zeile = Right(zeile , L)

If Komm = "r" Then
For Badr = 0 To 190 'Register lesem
Bdat = Reg(badr + 1)
Print Badr;
Print " " ;
Print Bdat
Next Badr
End If
End If
Loop


Sub Siinit
Badr = 183 : Bdat = 192 : Regwrite 'Xtal 10pF
Badr = 3 : Bdat = 0 : Regwrite 'Clk0 Clk1, Clk2 on
Badr = 15 : Bdat = 0 : Regwrite 'Xtal = PLL input
Badr = 16 : Bdat = 15 : Regwrite 'Clk 0:PLLA, 8mA out
Badr = 17 : Bdat = 15 : Regwrite 'Clk 1:PLLA, 8mA out
Badr = 18 : Bdat = 15 : Regwrite 'Clk 2:PLLA, 8mA out
Waitms 10
Restore Pll900
Badr = 26
For N = 26 To 33
Read Bdat
Regwrite
Next N
Waitms 50


Restore Out1000khz:
Badr = 42
For N = 42 To 49
Read Bdat
Regwrite
Next N
Badr = 3 : Bdat = 0 : Regwrite 'enable all outputs

Restore Out1000khz:
Badr = 50
For N = 50 To 57
Read Bdat
Regwrite
Next N
Badr = 3 : Bdat = 0 : Regwrite 'enable all outputs

Restore Out1000khz:
Badr = 58
For N = 58 To 65
Read Bdat
Regwrite
Next N
Badr = 3 : Bdat = 0 : Regwrite 'enable all outputs

End Sub


Sub Regwrite
I2cstart
I2cwbyte 192
Waitus 1
I2cwbyte Badr
Waitus 1
I2cwbyte Bdat
Waitus 1
I2cstop
Reg(badr + 1) = Bdat
Badr = Badr + 1
End Sub

End

Pll900: 'PLLA 900 MHz
Data 255 , 255 , 0 , 16 , 0 , 240 , 0 , 0
Out1000khz:
Data 0 , 1 , 1 , 192 , 0 , 0 , 0 , 0 'Teiler /900, Rdiv=0

Für die eigentliche Übertragung gibt es die Sub Regwrite. Darin wird jeweils ein Byte Bdat in einer Registeradresse Badr geschrieben. Anschließend wird die Adresse erhöht, was das Füllen von Bereichen erleichtert. Außerdem lege ich eine Kopie in einem Byte-Array Reg(200) an, das man sich mit dem Kommando r am PC ansehen kann. Das ist eine gute Hilfe bei der Entwicklung und beim Debuggen.

Nun kommt die eigene Berechnung für die Mutisyth-Teiler. Das gebrochene Teilerverhältnis wird mit drei Ganzzahlen a, b und b festgelegt: Teiler =  a + b /c. Der Divisor  c wird im Prinzip willkürlich festgelegt, sollte aber im Interesse einer guten Frequenzauflösung möglichst groß sein. Die maximale Größe ist durch die Registerbreite von 20 Bit festgelegt: C = 1048575  bedeutet hexadezimal 0F FF FF und füllt gerade die 20 Bit.





In der AN619 von Silabs wird beschreiben, die man die drei Zahlen a, b und c in die zugehörigen jeweils 18 oder 20 Bit breiten Register schreiben soll:



Die Funktion Floor musste ich erst nachschlagen, sie bedeutet Ganzzahlen-Abrundung (und Ceiling heißt Aufrundung), was in Basic auch als Int () geschrieben wird. Die Art von Abrundung findet automatsch statt, wenn man den Inhalt einer Real-Variablen einer Integer-Variablen zuweist. Die Berechnungen für die Register P1, P2 und P3 sind damit direkt umsetzbar.  Die zugehörigen Variablen sind als Dword mit einer Größe von 32 Bit dimensioniert, wobei die unteren drei Bytes jeweils noch einzeln zugänglich sind.

Sub Sifreq

Dim P1 As Dword
Dim P11 As Byte At P1 + 0 Overlay
Dim P12 As Byte At P1 + 1 Overlay
Dim P13 As Byte At P1 + 2 Overlay
Dim P2 As Dword
Dim P21 As Byte At P2 + 0 Overlay
Dim P22 As Byte At P2 + 1 Overlay
Dim P23 As Byte At P2 + 2 Overlay
Dim P3 As Dword
Dim P31 As Byte At P3 + 0 Overlay
Dim P32 As Byte At P3 + 1 Overlay
Dim P33 As Byte At P3 + 2 Overlay
Dim A As Dword
Dim B As Dword
Dim C As Dword
Dim D As Dword
Dim E As Dword

Dim G As Single
Dim R As Byte
Dim T As Byte

G = 900000000 / F 'Teiler
C = 1048575

R = 0 'R-Teiler R0...2
While G > 900
G = G / 2 : 'Multisynth-Teiler verkleinern
R = R + 16
Wend 'Fout = Fvfo / G / R

A = G 'G = A + B / C
G = G - A
G = G * C 'B = (G - A) * C
B = G

D = B * 128
D = D / C 'D = Int(128 * B / C)

P1 = 128 * A
P1 = P1 + D
P1 = P1 - 512 'P1 = 128 * A + (128 * B / C) - 512

P2 = B * 128
E = D * C 'E = c * Int(128 * B / C)
P2 = P2 - E 'P2 = 128 * B - C * Floor ( 128 * B/C)

P3 = C

Badr = 8 * Clk
Badr = Badr + 42
Bdat = P32
Regwrite
Bdat = P31
Regwrite
Bdat = P13 + R
Regwrite
Bdat = P12
Regwrite
Bdat = P11
Regwrite
Swap P33
Bdat = P33 + P23
Regwrite
Bdat = P22
Regwrite
Bdat = P21
Regwrite
End Sub

Die Registertabelle zeigt, welche Daten in welche Registeradresse geschrieben werden müssen. Adresse 44 enthält die beiden obersten Bits von P1 und außerdem die Einstellungen für den Nachteiler R. In Adresse 47 werden jeweils die oberen vier Bit von P2 und P3 geschrieben.



Test 1, Sweep

In einem ersten Test der Berechnung wurde ein Sweep-Generator programmiert, der die Frequenz in Schritten von 1 Hz und ohne weitere Zeitverzögerung erhöht. Das Ergebnis wurde mit dem SDR abgehört und war überzeugend. Es konnten keine ungleichmäßigen Sprünge festgestellt werden, sondern die Frequenz änderte sich quasi kontinuierlich. Auch Umschaltgeräusche waren nicht wahrnehmbar. 

Clk = 0
F = 7043000

Do
F = F + 1
Sifreq
If F > 7043000 Then F = 7040000
Loop

Test 2, Auflösung

In einem zweiten Test wurde die Auflösung untersucht. Die Frequenzvariable F wurde ja als Real-Variable (Single) dimensioniert, damit man auch Unterschiede unter einem Hz erzeugen kann. Hier wurden zwei Frequenzen bei 10 kHz erzeugt, die sich um 0,01667 Hz unterscheiden. Beide wurden an das Zweikanal-Oszilloskop gelegt. Man kann sehen, wie sich die Phasen kontinuierlich gegeneinander verschieben und jeweils nach einer Minute wieder Phasengleichzeit entsteht.

Clk = 0
F = 10000.01667
Sifreq
Clk = 2
F = 10000
Sifreq

SDR-Steuerung

Als letzte Etappe zum Ziel habe ich die Steuerung so an das vorhandene serielle Protokoll angepasst, dass es mit derselben PC-Software angesteuert werden kann wie die bisherige Firmware.




Dazu mussten die Software-Kommandos angepasst werden. Man kann nun mit F in kHz abstimmen oder mit f in Hz. Die einzelnen Ausgänge lassen sich auch abschalten. Das Problem der Kalibrierung wurde gelöst, indem die PLL-Frequenz in der Sub Sifreq mit G = 900098400 eingetragen wurde. Die Default-Frequenz ist wieder für den WSPR-Empfang im 40m-Band eingestellt.

Download: SI5351VFObas.zip

'Si5351VFO3.bas

$regfile = "m328pdef.dat"
$crystal = 16000000

$hwstack = 128
$swstack = 64
$framesize = 128
Baud = 9600


Dim Badr As Byte
Dim Bdat As Byte
Dim N As Byte
Dim Dat As Byte
Dim Clk As Byte
Dim L As Byte
Dim F As Single
Dim Zeile As String * 20
Dim Komm As String * 1
Dim En As Byte
Dim Reg(200) As Byte


Declare Sub Regwrite
Declare Sub Siinit
Declare Sub Sifreq

Config Sda = Portc.4
Config Scl = Portc.5
I2cinit
Waitms 10
Siinit
Waitms 100
Clk = 1
F = 28154400
Sifreq

Do
Dat = Ischarwaiting()
If Dat > 0 Then
Input Zeile
While Left(zeile , 1) < "A"
L = Len(zeile)
L = L - 1
Zeile = Right(zeile , L)
Wend
Komm = Left(zeile , 1)
L = Len(zeile)
L = L - 1
Zeile = Right(zeile , L)

If Komm = "r" Then
For Badr = 0 To 190 'Register lesem
Bdat = Reg(badr + 1)
Print Badr;
Print " " ;
Print Bdat
Next Badr
End If

If Komm = "f" Then 'Frequenz 0
F = Val(zeile)
F = F * 4
Clk = 1
Sifreq
End If

If Komm = "a" Then 'Frequenz 1
F = Val(zeile)
Clk = 0
Sifreq
End If

If Komm = "b" Then 'Frequenz 2
F = Val(zeile)
Clk = 2
Sifreq
End If

If Komm = "F" Then 'Frequenz 0
F = Val(zeile)
F = F * 4000
Clk = 1
Sifreq
End If

If Komm = "A" Then 'Frequenz 1
F = Val(zeile)
F = F * 1000
Clk = 0
Sifreq
End If

If Komm = "B" Then 'Frequenz 2
F = Val(zeile)
F = F * 1000
Clk = 2
Sifreq
End If
End If
Loop



Sub Siinit
Badr = 183 : Bdat = 192 : Regwrite 'Xtal 10pF
En = 15
Badr = 3 : Bdat = En : Regwrite 'Clk0 Clk1, Clk2 off
Badr = 15 : Bdat = 0 : Regwrite 'Xtal = PLL input
Badr = 16 : Bdat = 15 : Regwrite 'Clk 0:PLLA, 8mA out
Badr = 17 : Bdat = 15 : Regwrite 'Clk 1:PLLA, 8mA out
Badr = 18 : Bdat = 15 : Regwrite 'Clk 2:PLLA, 8mA out
Waitms 10
Restore Pll900
Badr = 26
For N = 26 To 33
Read Bdat
Regwrite
Next N
Waitms 50
End Sub


Sub Sifreq
Dim P1 As Dword
Dim P11 As Byte At P1 + 0 Overlay
Dim P12 As Byte At P1 + 1 Overlay
Dim P13 As Byte At P1 + 2 Overlay
Dim P2 As Dword
Dim P21 As Byte At P2 + 0 Overlay
Dim P22 As Byte At P2 + 1 Overlay
Dim P23 As Byte At P2 + 2 Overlay
Dim P3 As Dword
Dim P31 As Byte At P3 + 0 Overlay
Dim P32 As Byte At P3 + 1 Overlay
Dim P33 As Byte At P3 + 2 Overlay
Dim A As Dword
Dim B As Dword
Dim C As Dword
Dim D As Dword
Dim E As Dword

Dim G As Single
Dim R As Byte
Dim T As Byte

If F < 2000 Then
If F = 0 Then
En.clk = 1
Badr = 3 : Bdat = En : Regwrite 'Ausschalten
End If
If F > 0 Then
En.clk = 0
Badr = 3 : Bdat = En : Regwrite 'Einschalten
End If
Else
If F < 8000 Then F = 8000
If F > 150000000 Then F = 150000000
G = 900098400 / F 'Teiler
C = 1048575

R = 0 'R-Teiler R0...2
While G > 900
G = G / 2 : 'Multisynth-Teiler verkleinern
R = R + 16
' If R = 32 Then 'R-Teiler 4 vrmeiden
' G = G / 2
' R = R + 16
' End If
Wend 'Fout = Fvfo / G / R

A = G 'G = A + B / C
G = G - A
G = G * C 'B = (G - A) * C
B = G

D = B * 128
D = D / C 'D = Int(128 * B / C)

P1 = 128 * A
P1 = P1 + D
P1 = P1 - 512 'P1 = 128 * A + (128 * B / C) - 512

P2 = B * 128
E = D * C 'E = c * Int(128 * B / C)
P2 = P2 - E 'P2 = 128 * B - C * Floor ( 128 * B/C)

P3 = C

Badr = 8 * Clk
Badr = Badr + 42
Bdat = P32
Regwrite
Bdat = P31
Regwrite
Bdat = P13 + R
Regwrite
Bdat = P12
Regwrite
Bdat = P11
Regwrite
Swap P33
Bdat = P33 + P23
Regwrite
Bdat = P22
Regwrite
Bdat = P21
Regwrite

En.clk = 0
Badr = 3 : Bdat = En : Regwrite 'Einschalten
End If
End Sub


Sub Regwrite
I2cstart
I2cwbyte 192 'i2c address of si5351a
Waitus 1 'suggested delay from forum
I2cwbyte Badr
Waitus 1
I2cwbyte Bdat
Waitus 1
I2cstop
Reg(badr + 1) = Bdat
Badr = Badr + 1
End Sub

End

Pll900: 'PLLA 900 MHz
Data 255 , 255 , 0 , 16 , 0 , 240 , 0 , 0



Wer das kompilierte Hex-File in einen ganz normalen Arduino Uno (ohne Bascom-Bootloader) laden will, kann dafür das Programm Xloader verwenden:  http://www.hobbytronics.co.uk/arduino-xloader





Elektronik-Labor   Projekte   AVR  Tiny85