3.14 Sound

Die Möglichkeiten zur Tonausgabe auf dem PV sind sehr beschränkt. Einzig die Erzeugung eines "piep" ist vorgesehen.
Dafür sind die Entwickler sicher nicht zu tadeln: für die Aufgaben, die der normale Mensch auf dem PV bearbeitet, ist das sicher völlig ausreichend.
Vom SDK werden dafür die Funktionen LibBuzzerOn() und LibBuzzerOff() mitgeliefert.
Trotzdem reizt es den kreativen Menschen, auch Hacker genannt, die Möglichkeiten der sehr einfachen Hardware tiefer auszuloten.
Die erste bekannte Modifikation des "piep" ist der "klick", der dadurch entsteht, daß der "piep" extrem verkürzt wird.
Will man mehr, muß man die Feinheiten der Tonerzeugung besser verstehen. Diese stellt sich so dar:
Ein Rechtecksignalgenerator wird durch Setzen eines Bits (Bit 7, Port 0) freigegeben. Er beginnt zu schwingen, wobei er offenbar immer mit derselben Halbwelle beginnt.
Nennen wir einmal den im gesperrten Zustand anliegenden Pegel 0, so springt der Pegel bei jeder Freigabe sofort auf 1. Ist diese Halbwelle beendet, so kehrt er auf 0 zurück, um nach der zweiten Halbwelle dann wieder auf 1 zu gehen usw.
Wird das Freigabebit wieder zurückgesetzt, so geht der Ausgang sofort wieder auf 0.

Die untere rote Kurve ist das Freigabebit (Port 0/Bit 7), die obere gelbe der Ausgang des Rechteckgenerators.
Will man unabhängig vom Rechteckgenerator eine eigene Schwingung erzeugen, so geht das begrenzt:
Die Freiheit, die man hat ist die, die erste Halbwelle mit Pegel 1 früher zu beenden, als es der Rechteckgenerator selbst tun würde. Das ist durch Rücksetzen des Freigabebits ja möglich. Damit kann man eine symmetrische Rechteckschwingung mit höherer Frequenz erzeugen:

Eine niedrigere Frequenz ist nur unsymmetrisch möglich indem man den 0-Pegel "streckt":

oder auch so:

Im zweiten Fall wird der Rücksprung auf den 0-Pegel durch den Rechteckgenerator ausgelöst und nicht durch das Rücksetzen des Freigabebits.
Das Timing hat also anscheinend enge Grenzen. Aber hier kommt uns eine andere Beobachtung zu Hilfe. Setzen des Bit2 des Ports 0 schaltet den Rechteckgenerator auf eine viel tiefere Frequenz um. Die Folge ist, daß man ohne Probleme auch niedrige Frequenzen erzeugen kann.
Da man für das Timing keinen Hardware-Timer zur Verfügung hat, muß der Ablauf über Zeitschleifen realisiert werden. Die erste bekannte Lösung von Alessandro Dorigatti im Mai 2001 benutzt eine Schleife, die nach einer Zahl von Durchläufen den Ausgang umschaltet.

for(cnt1=0; cnt1 < dur; cnt1++)
{
  if (cnt1%ptch==0)
     outpsound(0x80); /* besser wäre hier 0x84 (siehe oben) */
  
  if (cnt1%ptch==wav)
     outpsound(0x00);
}
(outpsound ist eine Ausgabe an den für den Sound zuständigen Port 0)

Der Zeitpunkt des Ausschaltens ist hier variabel, was eine Steuerung der Kurvenform (unsymmetrisches Rechteck) erlaubt und sich hauptsächlich in der Lautstärke äußert.
Der Nachteil der Schleife ist, daß die Werte für ptch, die die Periodendauer und damit die Frequenz bestimmen, sehr klein sind. Damit ist eine Feinabstimmung, wie bei Musik erforderlich, kaum möglich.
Abhilfe schafft eine Lösung, die eine leichte Unregelmäßigkeit der Umschaltvorgänge in Kauf nimmt, dafür aber eine feinere Frequenzabstimmung erlaubt. Dazu wird in der Schleife die Phase akkumuliert, das heißt bei jedem Durchlauf wird dieser Wert um einen bestimmten Betrag erhöht. Der konkrete Betrag ist proportional zu Frequenz. Dann wird in Abhängigkeit der Phase der Ausgang geschaltet.

cycl=0;             /* Phasenwert */
tl=0;               /* Zähler für die Gesamtzeit */
outpsound(0x84);    /* Pegel auf 1, Ton einschalten */
while (tl<durl)  /* solange Ton an */
{
  while (cycl<voll)   /* Warten bis zur Ausschaltphase */
  {                 /* voll entspricht wav in obigem Programm */
    tl++;           /* Zeit weiterzählen */
    cycl=cycl+freq; /* Phase weiterzählen */
  }
  outpsound(0x0);   /* Pegel auf 0 */
  while (cycl<fullcycle) /* Warten bis eine Welle beendet */
  {                    /* fullcycle ist fester wert für eine Periode */
    tl++;              /* Zeit weiterzählen */
    cycl=cycl+freq;    /* Phase weiterzählen */
  }
  outpsound(0x84);      /* Pegel auf 1, neue Periode */
  cycl=cycl-fullcycle;  /* eine Periode abgearbeitet */
}
outpsound(0x0);   /* Pegel auf 0, Ton aus  */

Wichtig ist, daß der Zähler der Phase cycl am Ende nicht auf Null gesetzt wird, sondern die zu weit gelaufene Phase in die neue Periode mitgenommen wird. Damit wird der Fehler korrigiert, der dadurch entsteht, daß die Zeitschleifen nicht feiner untergliedert werden können.
Der erzeugte Klang ist erstaunlich - allerdings nur, wenn man weiß, wie er entsteht. Die heimische Hifi-Anlage ist trotzdem besser

Für Ergänzungen wenden Sie sich bitte an: Jürgen Wagner