 Geführte FSK-Modulation mit Timer1 Caption
Geführte FSK-Modulation mit Timer1 Caption     
Das FSK-Modulationsverfahren hat grundsätzlich funktioniert. Die
Modulationsfrequenz wird gemessen und dann zur Grundfrequenz addiert.
Das Ergebnis entspricht einer USB-Modulation. Aber die Präzision musste
noch gesteigert werden. Dazu sollte die Timer1 Input Capture Unit 
zum Einsatz kommen. Bei einer fallenden Flanke am Eingang T1 wird der
aktuelle Stand des Timers in das Caption Register ICR1 übernommen. Man
erhält dadurch eine zeitliche Präzision in der Größenordnung eines
Prozessortakts. Start T1 kann auch der analoge Komparator mit seinen
beiden Eingängen AC1 und AC0 verwendet werden. Damit hat man die
Möglichkeit, auch kleinere Eingangssignale auszuwerten. Auch diesmal
wurde der erste Funktionstest mit Bascom durchgeführt. 
Config Timer1 =  Timer , Prescale = 1
start Timer1
TCCR1B = &H81 ' Timer1, Vorteiler 1, ICNC1: Input Capture Noise Canceler
ACSR.ACIC = 1 ' Analog Comparator als Input
do
timer1 = 0
while ACSR.ACO =0
  if Timer1 > 65000 then exit while
wend
timer1 = 0
while ACSR.ACO=1
 if Timer1 > 65000 then exit while
wend
d1 = ICR1
while ACSR.ACO=0
 if Timer1 > 65000 then exit while
wend
while ACSR.ACO=1
 if Timer1 > 65000 then exit while
wend
if timer1 < 65000 then
   Regaddr = 3
   Regdata = 6      ' CLK0 an
   Call Siout(regaddr , Regdata)                               'enable outputs
  d2= icr1
  d = d2-d1
  f= 16000000/d
  'print f
  dd = f
  Gfreq = 7074000+ dd
  Clk = 0
  Setfreq
else
   Regaddr = 3
   Regdata = 5      ' CLK1 an
   Call Siout(regaddr , Regdata)                               'enable outputs
end if
loop
Das Programm nimmt zwei Zählerstände am Anfang und am Ende einer
Schwingung auf und speichert sie in d1 und d2.  In der Wartezeit
zwischen den Flanken kann in aller Ruhe der Timer selbst ausgelesen
werden, um ein Timeout zu erkennen. Der Timer sollte nämlich niemals
mehr als 65000 erreichen, außer wenn die Modulation abgeschaltet wurde.
Aus der Periodendauer wurde dann die Frequenz f berechnet. Zur Probe
wurden Messwerte der Frequenz seriell ausgegeben. Das Ergebnis an einem
analogen Sinusgenerator zeigt eine Zuverlässigkeit im Bereich 0,1 Hz,
was sogar für WSPR reichen sollte. 
 
Beim Einsatz mit WSJT-X kann man mit Tune eine konstante Frequenz
ausgeben und testen, was die Firmware daraus macht. Aber nun zeigten
sich plötzlich wesentlich größere Abweichungen von mehr als einem
Hertz. Die Vermutung war, dass es sich um Fehler handelt, die in der
Soundkarte entstehen. Das Sinussignal wird mit endlicher Auflösung und
endlicher Abtastrate erzeugt und dann so gefiltert, dass insgesamt >
20 kHz übertragen werden können. Dabei bleibt wohl die eine oder andere
kleine Delle im Signal erhalten, was zu kleinen Abweichungen in der
Periodendauermessung führen muss. Mit einem Bandpass- oder
Tiefpassfilter müsste man die Situation verbessern können. 
 
Also wurde ein einfaches Filter mit einer Grenzfrequenz unter 3 kHz
gebaut. Dass die Signalamplitude im oberen Bereich schon deutlich
abnimmt, ist unproblematisch, weil der Komparator ohnehin ein Rechteck
daraus macht. Und tatsächlich, nun wird auch das Signal aus dem PC mit
einer Zuverlässigkeit von ca. 0,1 Hz gemessen. Mein Bascom-Programm
steuert auch schon den PLL-Chip an. Damit konnten erfolgreich
FT8-Signal übertragen werden. Nur für WSPR reichte es noch nicht, weil
die Frequenzaufbereitung nur eine Auflösung von 1 Hz hat. Also wurde
die eigentliche Anwendung auch wieder mit Arduino programmiert. Und nun
funktioniert sogar WSPR mit seinen FSK-Stufen von nur 1,6 Hz!
 // Modulationsfrequenz messen über Analog Comparator Pin7 = AN1
  TCCR1A = 0x00;
  TCCR1B = 0x01; // Timer1 Timer 16 MHz, ICNC1: Input Capture Noise Canceler
  
  TCCR1B = 0x81; // Timer1 Timer 16 MHz, ICNC1: Input Capture Noise Canceler
  ACSR |= (1<<ACIC);  // Analog Comparator Capture Input
  pinMode(7, INPUT); //PD7 = AN1 = HiZ, PD6 = AN0 = 0
  digitalWrite (7,1);
  digitalWrite (6,1);
  delay(2);
  unsigned int d1,d2;
  int FSK = 10;
  int FSKtx = 0;
while (FSK>0){
  TCNT1 = 0; 
  while (ACSR &(1<<ACO)){
    if (TCNT1>65000) {break;
  }
  }  while ((ACSR &(1<<ACO))==0){
    if (TCNT1>65000) {break;}
  }
  TCNT1 = 0;
  while (ACSR &(1<<ACO)){
    if (TCNT1>65000) {break;}
  }
  d1 = ICR1;   
  while ((ACSR &(1<<ACO))==0){
    if (TCNT1>65000) {break;}
  }  
  while (ACSR &(1<<ACO)){
    if (TCNT1>65000) {break;}
  }
  d2 = ICR1;
  if (TCNT1 < 65000){
    unsigned long codefreq = 1600000000/(d2-d1);
  //  d1 = codefreq /10;
  //  Serial.println(d1);
  //  delay (500);
    if (codefreq < 350000){
      if (FSKtx == 0){
          digitalWrite (txOn,1);  //Relais 
         digitalWrite (keyOut,1);  //keyOut
         si5351.output_enable(SI5351_CLK2, 1);
          si5351.output_enable(SI5351_CLK1, 0);   //RX off
      }
      si5351.set_freq((freqTX * 100 + codefreq), SI5351_CLK2);   
      FSKtx = 1;
    }
  }
  else{
    FSK--;
  } 
  si5351.output_enable(SI5351_CLK2, 0);
  si5351.output_enable(SI5351_CLK1, 1);   //RX on
  digitalWrite (txOn,0);  //Relais 
  digitalWrite (keyOut,0);  //keyOut    
  pinMode(7, OUTPUT); //LCD
  digitalWrite (7,0);
  digitalWrite (7,0);
  FSKtx = 0;
 }
}
Ein
Problem war noch zu lösen: Wenn ich das SDR-Shield zusammen mit dem
LCD-Shield von Elektor verwende, belegt dieses die benötigten Leitungen
PD6 und PD7. Die oberen vier Leitungen sind der LCD-Datenbus mit den
Leitungen D4 bis D7. Ein Test hat aber gezeigt, dass man sich die Ports
leihen kann, wenn gerade keine LCD-Ausgabe ansteht. Allerdings hat das
LCD anscheinend selbst interne Pullup-Widerstände, sodass ein offener
Eingang hoch liegt. Deshalb musste ich den Vergleichspegel des
Komparators in Richtung VCC verlegen. PD6 wird deshalb hochgeschaltet
und dient als virtuelle Masse für das Tiefpassfilter.
 
In der Form habe ich nun den Audioeingang in meinen QRP-Transceiver
eingesetzt. Nun kann ich WSPR auf die alte Art und mit der direkten
Steuerung über den Mikrofoneingang verwenden, außerdem auch FT8, FT4,
JS8 und viele andere Betriebsarten. 
 
Zum Abschluss des Versuchs habe ich den WSPR-Sender, gesteuert von WSPR
2.12, mehrere Stunden lang laufen lassen. Das Ergebnis zeigt: Alles
funktioniert bestens.
