Pfad: Home => AVR-DE => Anwendungen => Zufall tn13 => Zufallszahlen-Analyse   This page in english: Flag EN Logo
Zufallsfarben 250*250 AVR-Anwendungen

Zufallszahlen mit ATtiny13
Anwendung und Software für Zufallszahlen in Assembler

Zufallszahlen mit ATtiny13

Berechnung Anzeigen Rauschgenerator

4 Zufallszahlen mit ATtiny13 analysieren

Ob eine generierte Reihe von Zahlen, wie im Kapitel 1 mit den berechneten Zahlen gezeigt, eine Gleichverteilung aufweist und sich, wenn überhaupt, erst nach vielen, vielen Durchläufen wiederholt, kriegt man nur heraus, wenn man hunderttausende Würfe vollzieht und sich die ergebenden Zahlenreihen genauer anschaut. Wie aber soll man das bewerkstelligen, wo der ATtiny13 doch nur 128 Byte SRAM hat? Und auch die größeren ATmega-Typen haben nicht das SRAM für 100.000nde Zahlen. Man muss daher die elendigen Zahlenreihen an einen PC senden, dort sammeln und analysieren. Diese Seite hier zeigt, wie man das hinkriegt.

4.1 Schaltbild

RS232-Sender mit ATtiny13 Das hier ist ein RS232-Sender, der einfach solche Zahlenreihen genereiert und sie über eine RS232-Schnittstelle an den PC versendet.

Der ATtiny13 nimmt, wie in der Schaltung in Kapitel 3 schon gezeigt, am ADC2-Eingang Analogsignale entgegen, wandelt diese in 10-Bit-Zahlen um, sortiert Nullen und Vollausschlag des AD-Wandlers aus, schiebt das unterste Bit des Ergebnisses in einen 8-Bit-Speicher. Ist dieser gefüllt, wird das Zufallsbyte in zwei ASCII-Zeichen verwandelt und in einen SRAM-Puffer abgelegt. ist der Speicher mit 16 solcher Zahlen befüllt, dann wird noch ein Prüfbyte angehängt (die Summe aus allen 17 Bytes muss Null ergeben). Die beiden Zeichen für Wagenrücklauf (Carriage Return, $0D) und Zeilenvorschub (Line Feed, $0A) komplettieren die Zeile zum Absenden.

RS232-Signal-Ablauf Ist eine Zeile komplett, erfolgt der Versand an die RS232-Schnittstelle. Diese arbeitet mit 9600 Baud (pro Bit 1 / 9600 = 104,167 µs), acht Datenbits, einem Startbit und zwei Stoppbits (8N2). Die serielle Ausgabe erfolgt an Portpin PB2. Diese sendet zunächst ein Startbit, dann acht Datenbits (von 0 bis 7) und danach dann zwei Stoppbits. Da der MAX232 die Polarität umkehrt, ist das Startbit an PB2 low und die Stoppbits high. Im inaktiven Zustand ist PB2 high.

Das Signal an PB2 wird dem Eingang T1IN des MAX232 zugeführt. Dieser erzeugt die nötigen Signalspannungen von +/-8V und wandelt die +5V-Pegel in serielle RS232-Pegel an T1OUT um. Das Signal geht an eine 9-polige DB-Buchse, die mit dem PC verbunden wird. Die Eingänge Data Carrier Detect (DCD), Request to Send (RTS) und Data Set Ready (DSR) werden über Widerstände auf Plus-Pegel gezogen.

4.2 Terminalprogramm auf dem PC

RealTerm-Portauswahl Um die Kommunikation mit dem PC zu bewerkstelligen, braucht der PC ein Terminalprogramm. Ich verwende dazu RealTerm, weil es vielseitig ist und alles (und noch viel mehr) kann was man so braucht.

RealTerm braucht erst mal den richtigen seriellen Port und seine Einstellungen. Wähle daher im Tab Port mit dem Ausklappfeld Port den richtigen COM-Anschluss des PCs. Stelle die Baudrate auf 9600 und die Anzahl Stopbits auf 2 ein. Mit dem Knopf Change werden die geänderten Einstellungen wahr.

RealTerm-Darstellungsauswahl Im Tab Display kann man die Darstellungsart einstellen. Will man Character sehen, sollte man ANSI einstellen. Will man hexadezimal dargestellte Werte, wählt man eben das. Hier wurden vom Testgerät Kleinbuchstaben gesendet. Die verstümmelten Zeichen zu Beginn stammen vom Flash-Programmieren des ATtiny13. Die Zeilenvorschübe wurden mit dem Button \n eingefügt.

Im Tab Capture kann man Texte fangen. Dazu später mehr.

4.3 Testprogramm für die Hardware

Um die Zusammenarbeit der Hardware über die RS232-Schnittstelle mit dem PC zu testen, habe ich das Testprogramm >zufall_test_tn13.asm geschrieben. Das Programm ist nach Wahl der Einstellungen (mit einem Texteditor entsprechend modifizieren) zu assemblieren und die Hex-Datei in das Flash des ATtiny13 zu schreiben.

Das Programm sendet Zeichen an die RS232-Schnittstelle. Es arbeitet ohne Interrupts und stellt die Signaldauer mittels Verzögerungsschleifen ein. Es kann drei Modi:
  1. swCharOnly: Ist diese Option ausgewählt (1 gesetzt) gibt die Schnittstelle etwa alle zwei Sekunden ein in swChar vorgewähltes Zeichen aus. So können Zeichen mit wenigen (Leerzeichen 0x20, '0' 0x30, 'A' 0x41) oder vielen Bitwechseln ('U' 0x55) getestet werden.
  2. swNextChar: Testet die Ausgabe von Zeichenfolgen wie 'A' bis 'Z', 'a' bis 'z', '0' bis '9'. Die untere Grenze wird in swNextFirst, die obere Grenze in swNextLast angegeben. Erreicht die Ausgabe die Obergrenze, beginnt die Zeichenfolge von vorne. Alle ca. 2 Sekunden wird ein Zeichen ausgegeben.
  3. swString: Gibt ca. alle zwei Sekunden den vordefinierten Zeichenstring aus, wie er in StringTab: abgelegt ist. Standardmäßig wird "The quick brown fox jumps over the lazy dog" ausgegeben, gefolgt von einem Wagenrücklauf und einem Zeilenvorschub.
In der letztgenannten Einstellung sollte das Terminalprogramm folgendes anzeigen:

Anzeige auf dem Terminalprogramm Wer mit dem Programm experimentieren möchte, kann z. B. mit der Änderung der Baudraten-Schleife die Sensitivität der Zeichenerkennung austesten.

Anzeige auf dem Terminalprogramm, nClock-5 Das hier kriegt man, wenn man die "6" in Zeile 110 des Programms von

  ldi rmp,(nClock-6)/3

in "5" ändert. Dabei ist nClock 125 (=1.200.000 / 9.600), und die Konstante wird zu 120 / 3 = 40. Nun ist manchmal die Erkennung von T, e, q, i und k sowie weiterer Buchstaben des Satzes gestört.

Anzeige auf dem Terminalprogramm, nClock-18 Umgekehrt kann man durch Erhöhung der "6" auch die obere Grenze austesten. Es ändert sich erst, wenn man "18" eingibt, was im Bild zu sehen ist. Da (125 - 18) / 3 abgerundet 35 ergibt, hat man die Obergrenze der Toleranz erreicht.

Die Schleifenvariablenwerte von 125-6=119 bzw. 125-17=108 ergeben 11 Takte des ATtiny13, über die die Zeichen noch korrekt erkannt werden. Das entspricht Abweichungen von 11 / 125 = -8,8% von der Baudrate oder -845 Baud. Solche Toleranzen kriegt man mit dem eingebauten RC-Taktoszillator im ATtiny13 einwandfrei hin, da braucht es keinen Quarz.

Bitte beachten, dass die Toleranz auch von der PC-Schnittstelle abhängig ist. Es können sich daher auch etwas andere Werte und Toleranzen ergeben. Bei Bedarf einfach die Schleifenvariable(n) in den Zeilen 98, 110 und 120 nachjustieren.

4.4 Messprogramm für Zufallszahlenerzeugung

Das Problem mit den Schleifendauern hat die Software zum Erzeugen von Zufallszahlen aus diversen Quellen nicht: sie arbeitet mit einem Timer und Interrupts, die exakt alle 104,17 µs zuschlagen, egal was der Prozessor noch so anderes zu tun hat.

Das macht die Software für das Messprogramm:
  1. Es startet regelmäßig den AD-Wandler und holt interruptgesteuert dessen Ergebnis ab,
  2. es prüft, ob die Unter- oder Obergrenze des Wandlers erreicht ist,
  3. wenn nicht: es schiebt das unterste der 10 Bits des AD-Wandler-Ergebnisses in ein Register,
  4. sind auf diese Weise acht Bits gesammelt, wird der Inhalt des Zufallsregisters in zwei Hexadezimal-Ziffern umgewandelt und in einem Sendepuffer im SRAM abgelegt,
  5. sind im Sendepuffer 16 Bytes (=32 Hexadezimalziffern) versammelt, wird ein Prüfbyte hinzugefügt (alle 17 Bytes zusammen müssen Null ergeben), und nach Hinzufügen von Wagenrücklauf und Zeilenvorschub wird der Puffer an die RS232-Schnittstelle gesendet.
Während des Sendens wird das Bitsammeln unterbunden, damit keine eingestreuten Störungen aus dem RS232-Signal den Zufallsgenerator und den AD-Wandler beeinflussen können.

4.5 Ein Sinusoszillator als Zufallsquelle

Ein Rechteckgenerator ist als Zufallsquelle völlig ungeeignet, da er immer nur zwischen Null und 5V herumeiert. Die würden von der Software ausgefiltert, wenn sie wirklich Nullen und Einsen wären (sind sie aber nicht, aber das ist eine andere Story).

Oszillator Wir brauchen also einen etwas gemütlichen Sinusgenerator. Dieser hier ist ein Sinusoszillator, der ein ordentliches, nicht verzerrtes Sinussignal produziert. Da wir den DA-Wandler mit 1.200.000 / 128 = 9,375 kHz sampeln, sollte der Oszillator mindestens zehnfach niedrigere Frequenz haben. Das tut er, wie man sieht, denn er liegt in der angegebenen Dimensionierung um 600 Hz herum.

4.5.1 Das Programm zum Zufallsbit sammeln und deren serielle Sendung

Mit diesem Eingangssignal füttern wir den ATtiny13 auf dem Teststand und statten diesen mit dem Programm zufall_analyse_tn13_v1.asm aus. Es produziert Zufallszahlen und gibt diese formatiert an die RS232-Schnittstelle aus.

Das Programm arbeitet folgendermaßen:
  1. Der AD-Wandler wird fest auf den Eingang ADC2 eingestellt. Da der Oszillator ein sattes Sinussignal bis fast an die 5V heran liefert, wird die Referenzspannung auf die Betriebsspannung eingestellt.
  2. Der AD-Wandler wird mit einem Vorteiler von 128 einmalig gestartet und löst bei Vorliegen des Ergebnisses den ADC complete-Interrupt aus. In der Service-Routine wird die Flagge bAdc gesetzt.
  3. Außerhalb des Interrupts wird nach dem Aufwecken des Controllers aus dem Schlaf die Flagge abgefragt und, falls gesetzt, das Ergebnis in das Registerpaar rAdcH:rAdcL geschrieben.
  4. Falls nicht gerade eine Sendephase läuft (in diesem Fall wird der ADC sofort neu gestartet), wird das unterste Bit in rAdcL in das Register rRandByte von rechts her nach links heingeschoben.
  5. Falls beim Linksschieben eine 1 im Carry landet, sind acht Schiebevorgänge komplett. Dann wird das Byte in das Hexadezimal-Format umgewandelt und in den SRAM-Puffer abgelegt. Falls die Option Prüfsumme eingestellt ist, erfolgt noch die Aufaddition im Prüfsummenregister.
  6. Steht der Pufferzeiger nach der Ablage auf dem Ende des SRAM-Puffers für 16 Hexadezimalzahlen, wird noch die Prüfsumme gebildet (mit NEG) und hexadezimal hinzugefügt (falls diese Option gesetzt ist). Schließlich werden noch ein Wagenrücklauf 0x0D und ein Zeilenvorschub 0x0A hinzugefügt. Dann wird der Sendevorgang angestoßen, der den angelegten Puffer an die RS232-Schnittstelle sendet.
Flussdiagramme Senden Der Sendevorgang läuft nach diesem Schema ab. Es beginnt in jedem Fall mit dem Sichern des Statusregisters und danach der Ausgabe der T-Flagge im Statusregister an den seriellen Ausgabepin an PB2 (bOut. Dazu wird das Portregister PORTB gelesen, das PB2-Bit mit BLD aus der T-Flagge ersetzt und an den Port zurückgeschrieben. Dieses Vorziehen des Ausgabevorganges an den Beginn des CTC-Interrupts gewährleistet, dass die Pegelwechsel am seriellen Ausgang immer im gleichen Takt erfolgen und unabhängig von der Dauer der weiteren Vorgänge sind.

Dann erfolgt die Vorbereitung des nächsten T-Wertes. Mit IJMP wird an die Addresse im Z-Registerpaar gesprungen. Das Z-Registerpaar zeigt auf die Sprungtabelle JTab:: zu Beginn auf den ersten Eintrag (Senden des Startbits), danach auf das Senden der acht Datenbits und schließlich auf das Senden der beiden Stoppbits. Mit jedem Schritt wird Z um eine Position erhöht, beim zweiten Stoppbit wird der Zeiger wieder auf den ersten Schritt gestellt. Dieser Algorithmus wurde gewählt, weil er sehr schnell ist und langwierige Abfragen von Bitzählern ersetzt. Da das Z-Register sowieso nicht anderweitig benötigt wird, ist dies die optimale Lösung.

Der erste Schritt TxStart: prüft zunächst, ob das letzte gesendete Zeichen ein Zeilenvorschub 0x0A war. Beim ersten Start der Pufferausgabe muss daher an der Position des X-Zeigers ein zusätzliches Byte vor dem Puffer stehen, das nicht 0x0A ist. Falls das letzte gesendete Byte 0x0A war, wird der Sendevorgang eingestellt, indem die OCIE0A-Flagge im TC0-Maskenregister gelöscht wird: der Zähler zählt dann weiter, aber es gibt keinen Interrupt mehr. Da als letztes ein Stoppbit gesendet wurde, bleibt der Ausgang PB2 high und das RS232-Signal bleibt bis auf Weiteres im Idle-Zustand.

Falls das letzte gesendete Zeichen nicht 0x0A war, wird das nächste Byte aus dem Puffer ins Register rTxByte geholt. Nach dem Wiederherstellen des Statusregisters (!) wird die T-Flagge auf Null gesetzt, damit beim nächsten CTC-Interrupt das Startbit gesendet wird.

Durch Erhöhen der Z-Addresse wird dann das erste Datenbit gesendet, indem das niedrigste Bit im Sendebyte in die T-Flagge im Statusregister übernommen wird. Dieses Bit wird dann in das Statusregister-Rettungsbyte rSreg kopiert, damit es beim abschließenden Wiederherstellen des Statusregisters auch dorthin gelangt. Das Sendebyte wird dann um eine Position nach rechts geschoben für das nächste Bit.

Sind alle acht Bits gesendet (die Anzahl steht in JTab:), ist das erste Stoppbit am dransten. Es setzt einfach nur die T-Flagge.

Mit dem zweiten Stoppbit wird der Z-Zeiger wieder auf Anfang gesetzt und die T-Flagge auf High.

Die Ausführungszeiten in Takten des ATtiny13 sind bei allen Vorgängen drangeschrieben. Maximal werden 29 Taktzyklen verbraten, minimal 23. Sechs Taktzyklen entsprechen bei 1,2 MHz 5 µs, was bei einer Bitdauer von 104 µs bei 9,6 kBd schon einen erheblichen Unterschied macht und das etwas ungewöhnliche Vorziehen der Bitausgabe an den Anfang der Interruptroutine rechtfertigt. Da man mit dieser Methode gerne auch höhere Baudraten wie 19,2 oder 38,4 kBd verwenden kann, ist dieses Vorziehen der Ausgabe sowieso Pflicht.

Die Senderoutine hat auf Anhieb so funktioniert, auch bei stundenlangem Senden kamen keine verstümmelten Zeichen über die Schnittstelle.

4.5.2 Ergebnisse der Sammlung

Realterm-Capture Um den erzeugten Datenstrom mit Zufallszahlen mit dem PC zu lesen, aktivieren wir wieder Realterm, stellen Port und Baudrate auf unseren Sender ein und sehen schon den empfangenen Zahlenwust. An dem sieht man auf den ersten Blick rein gar nix, sieht aus wie reiner Zufall.

Um den Zahlenstrom zu speichern, wählen wir im Tab-Fenster Capture einen Dateinamen aus, unter dem wir das Ganze ablegen möchten und starten mit dem Knopf Start Overwrite den Aufzeichnungsvorgang. Ab jetzt wächst die Dateigröße stetig an und man kann in der Fußzeile unter Char Count schön beobachten, wieviel schon zusammengekommen ist. In der Datei 600_Hz_capture.txt (996 kB) kann man sich das Elend von 450.000 solcher Zahlenwerte anschauen, nachdem man nach einigen Stunden das Capture mit Stop Capture beendet hat.

4.5.3 Analyse der Zahlenreihen

Zahlenreihen-Analyse Damit der Anschein nicht Anschein bleibt, habe ich zur Analyse dieser Zahlenreihen ein Lazarus-Programm geschrieben (Quellcode, Win64-Executable), das die Zahlenreihen ein wenig auseiandernimmt. Dem füttert man im oberen Eingabefeld die Capture-Datei (in das Feld klicken und Datei auswählen), was sie folgendermaßen quittiert.
  1. Zuerst wird die Datei gelesen und die Anzahl Zeilen festgestellt (Feld Lines).
  2. Die Länge aller Zeilen wird überprüft (muss 34 sein mit aktivierter Prüfsumme) bzw. 32 ohne. Bei zweien der 28.359 Zeilen waren diese nicht komplett (die erste und die letzte).
  3. Dann wird überprüft, ob alle Zeichen gültige Hexadezimal-Ziffern sind. Alle vollständigen Zeilen erfüllen das.
  4. Ist Prüfsumme aktiviert, wird die Summe jeder Zeile gebildet und auf Null überprüft. Alle vollständigen Zeilen hatten korrekte Prüfsummen.
  5. Alle 453.712 Zufallszahlen werden dann in ein Byte-Array übernommen und es wird gezählt, wie oft die Bytes 0 bis 255 vorkommen. Die Vorkommen werden in einer Tab-separierten Datei abgelegt, die den denselber Namen wie die Capture-Datei hat, nur mit der Extension .csv. Als Beispiel kann diese Datei hier dienen. Sie kann mit LibreOffice und der Einstellung mit Tabulator-Zeichen als Trenner geöffnet werden.
Verteilung der 453.712 Zufallszahlen Schon diese einfache Prüfung war allerdings recht enttäschend. Die Verteilung der Zahlen ist keineswegs zufällig, sondern weist regelmäßige Muster auf. Teils ist die Abweichung von Durchschnitt (hier: 1772) ganz erheblich (z. B. bei 255). Vergleicht man diesen Schrott mit der schönen Gleichverteilung bei den rein berechneten Zufallszahlen gibt es nur ein Urteil: mach das nicht, es bringt nix.

Vorkommen von Quads Etwas schöner sieht das Vorkommen von gleichartigen Zahlenreihen aus. Hier wurden mit dem Button Find quads nach Viererkombinationen gesucht. Es kommen immerhin nur sieben vor. So richtig regelmäßig, wie es aus der Verteilung zu erkennen ist, ist die Reihe dann doch nicht.

4.5.4 Fazit Sinusoszillator als Zufallsgenerator?

Kann man machen, ist aber nicht so arg schön. Eigentlich erstaunlich, dass so ein stabiler Sinusoszillator dennoch Zufall liefert, wenn auch nicht so arg schönen.

Die ideale Lösung scheint zu sein, beides zu kombinieren: ab und zu den Sinusoszillator abfragen, mit zwei Zahlen als Anfangsbedingung dann für eine Weile lang in die Rechenroutine mit EOR. Das vereint beide Welten und versöhnt mit dem erhöhten Aufwand für den Oszillator. Der Modus swMixed in der Software für die Anzeige (siehe Kapitel 2 macht das dann auch so.

Seitenanfang Hauptseite Berechnung Anzeigen Rauschgenerator
Lob, Tadel, Fehlermeldungen, Genöle und Geschimpfe oder Spam bitte über das Kommentarformular an mich.

©2020 by http://www.avr-asm-tutorial.net