SDR-VFO mit dem SI5351            

Elektronik-Labor   Projekte   AVR    




Das Software Defined Radio auf Kurzwelle bis 30 MHz ist immer wieder interessant. Allerdings gab es Probleme mit abgekündigten Chips. Nun habe ich den neuen Clock-Chip SI5351 aufgegriffen, der z.B. auf dem alten Elektor-SDR oder den IQ-Mischerplatinen von Modul-Bus eingesetzt werden kann. Für die ersten Versuche empfiehlt sich das Breakout-Board von Adafruit. Dazu gibt es auch eine passende Arduino-Library.



Der SI5351 hat einen Quarzoszillator mit 25 MHz und zwei PLLs, die zwischen 600 MHz und 900 MHz eingestellt werden können. Die PLL-Teiler arbeiten mit gebrochenen Teilerverhältnissen, sodass tatsächlich eine fast beliebige Auflösung möglich ist. Der folgende MultiSynth-Teiler verwendet ebenfalls gebrochene Teilerverhältnisse. Damit hat man zwei Möglichkeiten, die Wunschfrequenz zu erzeugen.

A) Man kann die PLL fest auf z.B. 900 MHz einstellen und dann mit gebrochenen Zahlen herunterteilen.
B) Man kann die PLL kleinschrittig einstellen und dann ganzzahlig auf die Endfrequenz teilen.

Hier zunächst die Methode A. Die VFO-Frequenz liegt beim Vierfachen der Mischerfrequenz, die 12 kHz unterhalb der Empfangsfrequenz steht. Das Programm soll die Empfangsfrequenz in kHz im Textformat empfangen und umsetzen. Um 3500 kHz zu empfangen muss der Si5351 eine Ausgangsfrequenz von  4*(3500-12) kHz = 13952 kHz am Ausgang 1 erzeugen. Der PLL-Teiler wird auf 36 gestellt (25 MHz * 36 = 900 MHz).  Der Multisynth-Teiler auf  900000/13952 = 64,506. Mit der Methode kommt man runter bis 1 MHz. Für noch kleinere Frequenzen wird der zusätzliche R_DIV-Teiler durch 16 eingesetzt.




Download: si5351vfo2.zip

//SI5351_vfo  PLL fixed at 900 MHz

#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <Adafruit_SI5351.h>

Adafruit_SI5351 clockgen = Adafruit_SI5351();
void setup(void)

{
Serial.begin(9600);
Serial.println("Si5351 Clockgen"); Serial.println("");


/* Initialise the sensor */
if (clockgen.begin() != ERROR_NONE)
{
Serial.print("Error");
while(1);
}
Serial.println("OK");
clockgen.enableOutputs(true);
clockgen.setupPLL(SI5351_PLL_A, 36, 0, 1000); //900 MHz
setfreq (6000);
}

void setfreq (unsigned long freq)
{
unsigned long f2;
unsigned long f3;
unsigned long f4;
unsigned long f5;
unsigned long div2;
unsigned int Teiler2;
unsigned int rdiv;

if (freq > 0) {
f2=(freq-12)*4;
if (f2<1000) {
rdiv = 16;
f2 = f2 * 16;
}
else {
rdiv = 1;
}
div2 = 900000000/f2;
f4 = div2/1000;
f5=div2-(f4*1000);
clockgen.setupMultisynth(1, SI5351_PLL_A, f4, f5, 1000);
if (rdiv == 16) {
clockgen.setupRdiv(1, SI5351_R_DIV_16);
}
if (rdiv == 1) {
clockgen.setupRdiv(1, SI5351_R_DIV_1);
}
}
}

void loop(void)
{
unsigned long freq;
if (Serial.available()) {
freq = Serial.parseInt();
setfreq (freq);
}
}

Die Methode A hat den Vorteil, dass der VFO quasi kontinuierlich abgestimmt werden kann, d.h. es gibt keine Unterbrechung bei einem Frequenzwechsel. Die Methode B verspricht dagegen eine größere Phasenreinheit, die auch für DRM ausreicht. Allerdings wird jeder Frequenzwechsel mit einer kurzen Unterbrechung von etwa einer Millisekunde begleitet, die als Störsignal im SDR erscheint. Die Methode erfordert eine Berechnung des optimalen Nachteilers, damit die PLL immer im Bereich 600 MHz bis 900 MHz bleibt.


Downlaod: si5351vfo3.zip

//SI5351_vfo, variable PLL 

#include <Adafruit_Sensor.h>

#include <Wire.h>
#include <Adafruit_SI5351.h>

Adafruit_SI5351 clockgen = Adafruit_SI5351();
void setup(void)

{
Serial.begin(9600);
Serial.println("Si5351 VFO"); Serial.println("");

if (clockgen.begin() != ERROR_NONE)
{
Serial.print("Error");
while(1);
}
Serial.println("OK");
clockgen.enableOutputs(true);
setfreq (6000);
}

void setfreq (unsigned long freq)
{
unsigned long f2;
unsigned long f3;
unsigned long f4;
unsigned long f5;
unsigned int Teiler2;
unsigned int rdiv;

if (freq > 0)
{
f2=(freq-12)*4;
// f2=freq;
if (f2>120000) {
f2=120000;
}
if (f2<800) {
rdiv = 16;
f2 = f2 * 16;
}
else {
clockgen.setupRdiv(1, SI5351_R_DIV_1);
rdiv = 1;
}
if (f2 >= 100000) {
Teiler2 = 6;
}
if (f2 < 90000) {
Teiler2 = 10;
}
if (f2 < 60000) {
Teiler2 = 15;
}
if (f2 < 50000) {
Teiler2 = 18;
}
if (f2 < 45000) {
Teiler2 = 20;
}
if (f2 < 30000) {
Teiler2 = 30;
}
if (f2 < 20000) {
Teiler2 = 45;
}
if (f2 < 15000) {
Teiler2 = 60;
}
if (f2 < 10000) {
Teiler2 = 90;
}
if (f2 < 6000) {
Teiler2 = 150;
}
if (f2 < 4000) {
Teiler2 = 220;
}
if (f2 < 2700) {
Teiler2 = 330;
}
if (f2 < 1800) {
Teiler2 = 500;
}
if (f2 < 1500) {
Teiler2 = 600;
}
if (f2 < 1000) {
Teiler2 = 900;
}
f2=f2*Teiler2;
f2=f2*1000/25;
f3=f2 /1000;
f4 = f3/1000;
f5=f3-(f4*1000);
clockgen.setupPLL(SI5351_PLL_A, f4, f5, 1000);
clockgen.setupMultisynth(1, SI5351_PLL_A, Teiler2, 0, 2);
if (rdiv == 16) {
clockgen.setupRdiv(1, SI5351_R_DIV_16);
}
}
}

void loop(void)
{
unsigned long freq;
if (Serial.available()) {
freq = Serial.parseInt();
setfreq (freq);
}
}

Beide Programme können aus einem beliebigen Terminal heraus angesteuert werden. Für eine bequemere Bedienung wurde jedoch ein VB-Programm geschrieben. Es sendet die Wunschfrequenz mit 9600 Baud im Textformat (z.B. 3500) an den Arduino. Der Schieberegler verwendet im Bereich bis 1,6 MHz Schritte von 9 kHz und darüber das Raster 5 kHz. Zusätzlich kann man eine Wunschfrequenz direkt eingeben oder mit den Bänder-Schaltflächen auf den Anfang der einzelnen Rundfunk- oder Amateurfunkbänder klicken.




Download: SDRshield.zip (Update 31.3.16: ZF in kHz-Schritten einstellbar, COM einstellbar und gespeichert in Ini-Datei)

Private Sub Command1_Click()
f = Int(Val(Text4.Text) / 5)
HScroll1.Value = f
End Sub


Private Sub Command10_Click()
f = 17480
HScroll1.Value = f / 5
End Sub

Private Sub Command11_Click()
f = 18900
HScroll1.Value = f / 5
End Sub

Private Sub Command12_Click()
f = 28000
HScroll1.Value = f / 5
End Sub

Private Sub Command13_Click()
f = 24900
HScroll1.Value = f / 5
End Sub

Private Sub Command14_Click()
f = 21000
HScroll1.Value = f / 5
End Sub

Private Sub Command15_Click()
f = 18060
HScroll1.Value = f / 5
End Sub

Private Sub Command16_Click()
f = 5000
HScroll1.Value = f / 5
End Sub

Private Sub Command17_Click()
f = 14000
HScroll1.Value = f / 5
End Sub

Private Sub Command18_Click()
f = 10100
HScroll1.Value = f / 5
End Sub

Private Sub Command19_Click()
f = 7000
HScroll1.Value = f / 5
End Sub

Private Sub Command2_Click()
f = 531
HScroll1.Value = f / 9
End Sub

Private Sub Command20_Click()
f = 3500
HScroll1.Value = f / 5
End Sub

Private Sub Command21_Click()
f = 1810
HScroll1.Value = f / 5
End Sub

Private Sub Command3_Click()
f = 3900
HScroll1.Value = f / 5
End Sub

Private Sub Command4_Click()
f = 7200
HScroll1.Value = f / 5
End Sub

Private Sub Command5_Click()
f = 9400
HScroll1.Value = f / 5
End Sub

Private Sub Command6_Click()
f = 11600
HScroll1.Value = f / 5
End Sub

Private Sub Command7_Click()
f = 5900
HScroll1.Value = f / 5
End Sub

Private Sub Command8_Click()
f = 13570
HScroll1.Value = f / 5
End Sub

Private Sub Command9_Click()
f = 15100
HScroll1.Value = f / 5
End Sub

Private Sub Form_Load()
OPENCOM "COM2:9600, N, 8, 1"
TIMEINIT
DTR 1
DELAY 1000
DTR 0

End Sub

Private Sub HScroll1_Change()
n = HScroll1.Value
If n > 179 Then f = n * 5
If n < 180 Then f = n * 9
Text4.Text = f
vfo = f '(f + 12) * 4
Text = Str(vfo)

l = Len(Text)
For n = 1 To l
ch = Mid$(Text, n, 1)
SENDBYTE Asc(ch)
Next n
SENDBYTE 13
SENDBYTE 10

DELAY 100
End Sub


Das Elektor SDR-Shield



In Zusammenarbeit mit Elektor ist ein Arduino-SDR-Shield entstanden. Die analoge Signalverarbeitung entspricht weitgehend dem bisherigen Elektor-SDR. Aber als VFO wird der SI5351 eingesetzt. Der Prototyp ist fertig und funktioniert bestens. Die Platine wurde vom Elektor-Labor gezeichnet und funktionierte auf Anhieb. In den Elektor-Labs kann man jetzt bereits das Projekt und den Schaltplan sehen.

https://www.elektormagazine.de/labs/software-defined-radio-sdr-shield-for-arduino-150515-1

Nach der jetzigen Planung soll der Startartikel im Doppelheft 7/8 erscheinen, also Mitte Juni 16. Ich hoffe, dass dann auch die Hardware auf breiter Basis verfügbar ist. Das Shield bietet ganz neue Möglichkeiten in Zusammenhang mit dem Arduino. Es juckt mir schon in den Fingern, auch die Bedienung auf den Arduino zu verlegen.

Für mich ist das wie ein neuer Einstieg in die Kurzwelle. Vieles hat sich in den letzten Jahren geändert. Es gibt jetzt weniger Rundfunksender, dafür aber viele neue Stationen. Die Antennenproblematik ist nicht einfacher geworden, denn die Störungen haben noch zugenommen. In letzter Zeit habe ich viel probiert und zahlreiche Antennenkonzepte getestet. Und ich habe viele interessante Beobachtungen gemacht und besondere Signale gefunden.


Standalone-VFO für den Elektor-SDR  
         


Bei der Konstruktion des Elektor-SDR-Shields habe ich darauf geachtet, dass es zum Elektor-LCD-Shield passt. Der Arduino Uno, das SDR-Shield und das LCD-Shield mit Bedienelementen bilden jetzt ein Dreierpack. Und da hat man die Möglichkeit, die Bedienung vom PC auf den Arduino zu verlagern.  Auf dem LCD-Shield hat man zwei Taster und ein Poti. Damit kann eine bequeme Bedienung realisiert werden. S1 verringert die Frequenz, S2 erhöht sie. Und mit dem Poti stellt man die Schrittweite zwischen 20 Hz und 100 kHz ein.  Das LCD zeigt in der oberen Zeile die aktuelle Frequenz und in der unteren Zeile die eingestellte Schrittweite.

Mit dieser Bedienung kann man bequem sowohl einen schnellen Bandwechsel als auch eine Feinabstimmung für CW oder SSB durchführen. Beim Start wird die Frequenz 7000 kHz vorgegeben. Damit kann man sofort im  40-m-Band beginnen und nach CW- und SSB-Stationen suchen.
Aus Gründen der Kompatibilität wird auch noch die serielle Eingabe von Frequenzen unterstützt. Man kann also weiterhin auch über den PC abstimmen. Im LCD sieht man eine vom PC eingestellte Frequenz in kHz. Sobald jedoch eine der beiden Tasten auf dem Shield bedient wird, wechselt die Anzeige auf Hertz und zeigt außerdem die genaue VFO-Frequenz ohne den 12-kHz-Offset.

Man kann den VFO auch allein mit dem Poti bedienen, wenn man einen der beiden Taster mit einer Drahtbrücke kurzschließt. Dazu würde es sich anbieten ein externes Poti mit großem Drehknopf anzuschließen. Diese Art der Bedienung hat sich zur Feinabstimmung bei CW und SSB bewährt. Man kann das Shield sogar als Direktmischer ohne SDR-Software einsetzen.

Download: si5351vfo4.zip

//SI5351_vfo + LCD PLL fixed at 900 MHz

#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <Adafruit_SI5351.h>
#include <LiquidCrystal.h>
Adafruit_SI5351 clockgen = Adafruit_SI5351();

LiquidCrystal lcd(2,3,4,5,6,7);

unsigned long freq;
unsigned long freqHz;

int pot;
long fstep;
long fstepOld;

void setup(void)

{

Serial.begin(9600);
Serial.println("Si5351 Clockgen"); Serial.println("");


/* Initialise the sensor */
if (clockgen.begin() != ERROR_NONE)
{
Serial.print("Error");
while(1);
}
Serial.println("OK");
clockgen.enableOutputs(true);
clockgen.setupPLL(SI5351_PLL_A, 36, 0, 1000); //900 MHz
freq=7000;
freqHz=freq*1000;
setfreq (freqHz);
lcd.begin(16, 2);

// Print a message to the LCD.
lcd.print(freq);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
}

void setfreq (unsigned long freqHz)
{
unsigned long f2;
unsigned long f3;
unsigned long f4;
unsigned long f5;
unsigned long div2;
// unsigned int Teiler2;
unsigned int rdiv;

if (freqHz > 0) {
f2=freqHz*4;
if (f2<1000000) {
rdiv = 16;
f2 = f2 * 16;
}
else {
rdiv = 1;
}
div2 = 9000970000000/f2;
f4 = div2/10000;
f5=div2-(f4*10000);
clockgen.setupMultisynth(1, SI5351_PLL_A, f4, f5, 10000);
if (rdiv == 16) {
clockgen.setupRdiv(1, SI5351_R_DIV_16);
}
if (rdiv == 1) {
clockgen.setupRdiv(1, SI5351_R_DIV_1);
}
}
}

void loop(void)
{
pot = analogRead(A3);
fstep =1000000;
if (pot > 100) fstep =1000000;
if (pot > 200) fstep =500000;
if (pot > 300) fstep =100000;
if (pot > 400) fstep =20000;
if (pot > 500) fstep =5000;
if (pot > 600) fstep =1000;
if (pot > 700) fstep =200;
if (pot > 800) fstep =100;
if (pot > 900) fstep =20;

if (fstep != fstepOld){
lcd.setCursor(1, 1);
lcd.print(fstep);
lcd.print (" ");
}
fstepOld = fstep;

if (Serial.available()) {
freq = Serial.parseInt();
if (freq > 0){
lcd.setCursor(0, 0);
freqHz=freq*1000-12000;
lcd.print(freq);
lcd.print (" ");
setfreq (freqHz);
}
}
if (digitalRead(A0)==0){
freqHz = freqHz - fstep;
lcd.setCursor(0, 0);
//freq=freqHz/1000;
lcd.print(freqHz);
setfreq (freqHz);
lcd.print (" ");
delay (200);
}
if (digitalRead(A1)==0){
freqHz = freqHz + fstep;
lcd.setCursor(0, 0);
//freq=freqHz/1000;
lcd.print(freqHz);
setfreq (freqHz);
lcd.print (" ");
delay (200);
}
}



Elektronik-Labor   Projekte   AVR