
SI5351-Ansteuerung mit Bascom
Elektronik-Labor Projekte AVR
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