Forum: Mikrocontroller und Digitale Elektronik Zufallszahlen mit Atmega8


von David (Gast)


Lesenswert?

Hallo Leute,
ich versuche jetzt seit geraumer Zeit Zufallszahlen mit meinem Atmega8 
zu generieren doch irgendwie funktioniert das nicht so richtig. Also 
probieren wollte ich es mit einem linear rückgekoppelten 
Schieberegister(http://de.wikipedia.org/wiki/Linear_r%C3%BCckgekoppeltes_Schieberegister)
um das umzusetzen nutze ich folgenden Code:
1
.include  "avr.h"
2
;------------------------------------------------------------------------
3
;Reset and Interrupt vector             ;VNr.  Beschreibung
4
  rjmp  main  ;1   POWER ON RESET
5
  reti    ;2   Int0-Interrupt
6
  reti    ;3   Int1-Interrupt
7
  reti    ;4   TC2 Compare Match
8
  reti    ;5   TC2 Overflow
9
  reti    ;6   TC1 Capture
10
  reti    ;7   TC1 Compare Match A
11
  reti    ;8   TC1 Compare Match B
12
  reti    ;9   TC1 Overflow
13
  reti    ;10  TC0 Overflow
14
  reti    ;11  SPI, STC Serial Transfer Complete
15
  reti    ;12  UART Rx Complete
16
  reti    ;13  UART Data Register Empty
17
  reti    ;14  UART Tx Complete
18
  reti    ;15  ADC Conversion Complete
19
  reti    ;16  EEPROM Ready
20
  reti    ;17  Analog Comparator
21
  reti    ;18  TWI (I²C) Serial Interface
22
  reti    ;19  Store Program Memory Ready
23
;------------------------------------------------------------------------
24
;Start, Power ON, Reset
25
main:  ldi  r16,lo8(RAMEND)
26
  out  SPL,r16
27
  ldi  r16,hi8(RAMEND)
28
  out  SPH,r16  ;init Stack
29
30
  ldi  r16,0xFF  ; PORTB als Ausgabe
31
  out  DDRB,r16
32
  
33
  ldi  r16,0b00101001  ;Startwert laden
34
  out  PORTB,r16
35
36
;------------------------------------------------------------------------
37
mainloop:
38
  mov  r17,r16  ;Register 16 nach Register 17 kopieren
39
  lsr  r17  ;Register 17 3 mal nach rechts schieben
40
  lsr  r17
41
  lsr  r17
42
  eor  r17,r16  ;Register 17 und 16 exclusiv oder verknüpfen
43
  lsr  r16  ;Register 16 nach rechts schieben
44
  sbrc  r17,0  ;wenn Bit 0 von Register 17 nicht gesetzt ist
45
  sbr  r16,5  ;dann soll Bit 5 von Register 16 nicht gesetzt werden
46
  out  PORTB,r16  ;Register 16 ausgeben
47
  rcall  delay  ;Verzögern
48
  rcall  delay
49
  rcall  delay
50
  rcall  delay
51
  rcall  delay
52
  rcall  delay
53
  rjmp  mainloop  ;und wieder von vorn
54
55
delay:
56
  ldi  r20,0x00
57
loop1:
58
  ldi  r19,0x00
59
loop2:
60
  nop
61
  nop
62
  inc  r19
63
  cpi  r19,0b11111111
64
  brne  loop2
65
66
  nop
67
  nop
68
  inc  r20
69
  cpi  r20,0b11111111
70
  brne  loop1
71
  ret
72
;------------------------------------------------------------------------

Bevor sich jemand beschwert: Ich weiß, dass die Verzögerung nicht 
optimal gelöst ist und dass man das eigentlich mit einem Timer macht, 
aber darum gings mir erstmal nicht.
Das Problem ist das er nach ein paar Schritten bei dem Wert 0b00111111 
stehen bleibt und ich verstehe nicht warum, denn eigentlich müsste dass 
quasi eine Endlosschleife sein bei der sich ständig die Werte ändern. 
Könnte mir bitte jemand einen Tip geben? Danke schon mal vornweg.
David

von Domschl (Gast)


Lesenswert?

Deinen Gedankengang mit der eor-Verknüpfung verstehe ich zwar nicht ganz 
(bin schon müde), aber ich habe erst vor kurzem das selbe Programm auf 
eine 8051 in asm und c geschrieben. Ich erkläre mal, wie ich es gelöst 
habe (hat funktioniert):
Wenn man von einem 8-Bit Schieberegister ausgeht:
Du musst die Bits 0,4,5,6 eor-Verknüpfen und an den Eingang des 
Schieberegisters legen. Meine Methode:
Du legts eine Kopie des Registers an. Danach schiebst du nach einander 
jedes der 4 relevanten Bits in das Carry-Bit und summierst mit dem 
Befehl ADC die einzelnen Bits auf. Am ende überprüfst du das LSB dieser 
Zahl und wenn es gesetzt ist dann: Das Register mit der Zufallszahl 
einmal nach rechts schieben und das MSB setzen. Sonst nur einmal 
schieben und nichts setzen.

Ich hoffe ich konnte dir helfen. Vielleicht schreib ich das Beispiel 
noch schnell. Wenn ja, poste ich es.

von David (Gast)


Lesenswert?

Danke erstmal für den Tip.
Naja wie der Alogithmus funktioniert steht ja in dem Link, da ist auch 
das mit der EOR Verknüpfung erklärt. Ich mache das halt so dass ich eine 
Kopie der Zahl anlege, dann die Kopie 3 mal nach rechts schiebe damit 
quasi das Bit 0 mit Bit 3 EOR verküpft wird.
Ich benutze zwar (erstmal) nur 6 Bit, aber ich denke dass ich dein 
Verfahren trotzdem anwenden kann. Schau'n mer ma'.

von Domschl (Gast)


Lesenswert?

Hier das Beispiel:
1
.def pzz=r16
2
.def temp=r17
3
.def summe=r18
4
.def null=r19
5
6
initialisierungen....
7
.
8
.
9
.
10
ldi pzz, 1  ;Muss mit 1 vorgeladen werden
11
.
12
.
13
14
zufall:
15
mov temp, pzz
16
clr summe
17
clr null
18
lsr temp
19
20
adc summe, null
21
lsr temp
22
lsr temp
23
lsr temp
24
lsr temp
25
adc summe,null
26
lsr temp
27
adc summe,null
28
lsr temp
29
adc summe,null
30
31
lsr pzz
32
sbrc summe, 0
33
sbr pzz,128
34
out portb,pzz
35
36
ret

Habs schnell simuliert und es sieht alles richtig aus. Nach 255 
durchläufen kommt wieder die 1. Ich hoffe ich konnte dir damit helfen.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Meine Lösung sieht so aus:
1
;generates 16 bit random value (16Bit SR with feedback on Bit 0, 2, 11, 15)
2
RANDOM:
3
 mov  Temp, RandomL    ;exor feedback outputs for SR input
4
 andi  Temp, 0b00000001
5
 lsl  Temp
6
 lsl  Temp
7
 eor  Temp, RandomL
8
 andi  Temp, 0b00000100
9
 swap   Temp
10
 lsl  Temp
11
 mov  TempH, RandomH
12
 andi  TempH, 0b00001000
13
 swap  TempH
14
 eor  TempH, RandomH
15
 andi  TempH, 0b10000000
16
 eor  TempH, Temp
17
 rol  TempH    
18
 rol  RandomL
19
 rol  RandomH
20
 ret

RandomL und RandomH müssen zu Initialisierung mit einem Wert |= 0 
geladen werden. Die Routine wird 1x pro Main-Durchlauf aufgerufen, wenn 
Zufallszahlen gebraucht werden, liest man einfach RandomL und/oder 
RandomH aus.

von Winfried (Gast)


Lesenswert?

Mal so kleiner Tipp, was Dokumentation von Code angeht: Die Kommentare 
sollen nicht beschreiben, was der Befehl macht, sondern Info darüber 
geben, warum man etwas macht. Sonst hat man keinen Informationsgewinn. 
Man schreibt z.B. nicht "Port B Pin 1 setzen" sondern "LED einschalten".

von Winfried (Gast)


Lesenswert?

@TravelRec: Schön kompakter Code, gefällt mir...

von David (Gast)


Lesenswert?

So danke an alle für die Tipps, hat super funktioniert. Nun muss ich nur 
noch ein paar schlechte Angewohnheiten (Verzögerung) ausbessern und dann 
gefällts mir richtig gut.

von Jan Z. (Firma: ujbxp) (zodiax)


Lesenswert?

Meine Idee: ich arbeite gerne mit unbeschalteten (offenen) ADC Port
und nehme das "Spannungsrauschen" auf. Das ergibt wirklich 100% Random 
;-)

.include"m32def.inc" ; hier mega 32 -müßte auch mit atmega8 
funktionieren


.def adlow     = r20  ; ADC Register
.def adhigh    = r21  ; ADC Register
.def cr_tmp    = r16  ; random zahl
.def temp1     = r17

  ldi     temp1, (1<<REFS0)      ; konfiguriere ADC
 out     ADMUX, temp1
 ldi     temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
 out     ADCSRA, temp1



;ADC-Wert einlesen

sample_adc:
    sbi     ADCSRA, ADSC        ; den ADC starten

wait_adc:
    sbic    ADCSRA, ADSC
    rjmp    wait_adc
     in      adlow, ADCL         ; LOW Byte lesen
     in      adhigh, ADCH        ; High Byte lesen

     mov cr_tmp,adlow
    add cr_tmp, adhigh


random_10: ; konvertiere zufallszahl zwischen 0 und 9
           subi  cr_tmp, 10        ; 10 abziehen
           brcc  random_10     ; ist dadurch ein Unterlauf enstanden?
           subi  cr_tmp, -9  ; ja, dann 9 dazu

end: rjmp end

;Die Zufallszahl steht in cr_temp!


cheers
Zodiax

von ArthurDent (Gast)


Lesenswert?

Ich habe seit geraumer Zeit den hier im Einsatz. Der Link ist allerdings 
tot :-(


/* This code is listed in
 * http://remus.rutgers.edu/~rhoads/Code/code.html
 *
 * in file
 * http://remus.rutgers.edu/~rhoads/Code/random2.c
 *
 * with the following description:
 *
 * Concatenation of two 16-bit Multiply with carry generators.
 * Has a list of recommended parameters. Good and even faster than the 
above.
 * Perhaps the fastest of any generator that passes the Diehard tests.
 */

/* concatenation of following two 16-bit multiply with carry generators 
*/
/* x(n)=a*x(n-1)+carry mod 2^16 and y(n)=b*y(n-1)+carry mod 2^16, */
/* number and carry packed within the same 32 bit integer.        */
/******************************************************************/

#ifndef _RRAND_H_
#define _RRAND_H_

/* return a random float >= 0 and < 1 */
#define rand_float          ((double)rrand() / 4294967296.0)

static unsigned int SEED_X = 521288629;
static unsigned int SEED_Y = 362436069;


static inline unsigned int rrand ()
   {
/* Use any pair of non-equal numbers from this list for "a" and "b"
    18000 18030 18273 18513 18879 19074 19098 19164 19215 19584
    19599 19950 20088 20508 20544 20664 20814 20970 21153 21243
    21423 21723 21954 22125 22188 22293 22860 22938 22965 22974
    23109 23124 23163 23208 23508 23520 23553 23658 23865 24114
    24219 24660 24699 24864 24948 25023 25308 25443 26004 26088
    26154 26550 26679 26838 27183 27258 27753 27795 27810 27834
    27960 28320 28380 28689 28710 28794 28854 28959 28980 29013
    29379 29889 30135 30345 30459 30714 30903 30963 31059 31083
*/
   static unsigned int a = 18000, b = 30903;

   SEED_X = a*(SEED_X&65535) + (SEED_X>>16);
   SEED_Y = b*(SEED_Y&65535) + (SEED_Y>>16);

   return ((SEED_X<<16) + (SEED_Y&65535));
   }


static inline void rrand_seed( unsigned int seed1, unsigned int seed2 )
   {
   if (seed1) SEED_X = seed1;   /* use default seeds if parameter is 0 
*/
   if (seed2) SEED_Y = seed2;
   }

#endif /* _RRAND_H_ */

von ArthurDent (Gast)


Lesenswert?

@Jan Zodiax (Firma ujbxp)

Gibt das wirklich 100% Random ?

Hat man bei so einer Hardwareloesung nicht immer die Gefahr, das 
irgendwelche Dreckeffekte einem doch irgendwelche Periodizitaeten oder 
anderes Einstrahlen?

Als Seed wuerde ich auch etwas "richtig" Zufaelliges waehlen, jedoch 
ansonsten sind Pseudo Noise Folgen erste Wahl.

von Jan Z. (Firma: ujbxp) (zodiax)


Lesenswert?

ArthurDent wrote:
> @Jan Zodiax (Firma ujbxp)
>
> Gibt das wirklich 100% Random ?
>
> Hat man bei so einer Hardwareloesung nicht immer die Gefahr, das
> irgendwelche Dreckeffekte einem doch irgendwelche Periodizitaeten oder
> anderes Einstrahlen?
>
> Als Seed wuerde ich auch etwas "richtig" Zufaelliges waehlen, jedoch
> ansonsten sind Pseudo Noise Folgen erste Wahl.

Hallo ArthurDent,

Periodizitaet habe ich bisher nicht feststellen können; ich verwende
diesen Randomgenerator häufig in Spielen.
Für ein gutes Ergebnis brauchst Du nur ein Stück Draht vom ADC Messpin 
(in diesem Fall Pin 40= A0)herauszuführen, 2-3 cm reichen vollkommen.
Der Controller empfängt dann soviel Rauschen und Datenmüll von Aussen 
(Handy\TV\Radio-strahlen etc..). Kein Mensch dann kann vorhersagen 
welche Zahl als nächstes kommt,und das ist meiner Meinung nach ein gutes 
Zufallsergebnis.
Probiers einfach mal aus, schreib die Zufallsdaten zurück ins RAM
und sende sie per serieller Schnittstelle zurück zum PC.

Gruß
Zodiax

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Ich denke, der offene ADC-Pin wird in der Hauptsache 50Hz der 
allgegenwärtigen Netzleitung messen, was keinesfalls zufällig ist. 
Versuche Deinen ADC-Zufallsgenerator mal in einem abgeschiedenen Gebiet 
ohne Stromnetz zu betreiben, ich vermute mal, die Ergebnisse werden ganz 
anders aussehen.

von Hagen R. (hagen)


Lesenswert?

Wenn man beweisbar guten Zufall haben möchte dann sollte man einen 
Pseudo Zufallsgenerator benutzen.

Ich mache es immer so:

- zwei LFSRs mit hinreichend großer Periode und unterschiedlichen 
Polynomen
- ein Startwert=Seed steht im EEPROM
- beim RESET wird Startwert aus EEPROM geladen in beide PRNGs
- der eine PRNG wird nun dazu benutzt den nächsten Seed zu berechnen, 
dieser Wert wird gespeichert im EEPROM
- der zweite PRNG dient dann in der Anwendung als Zufallsgenerator und 
läuft mit dem so berechneten Seed

Somit erzeugt man nach jedem RESET einen anderen Seed. Diese Erzeugung 
ist abhängig von dem einen PRNG. Nach dem ersten flashen der Anwendung 
startet das System (jede AVR Kopie) mit exakt den gleichen Bedingungen. 
Somit kann man auch eventuelle Fehler exakt reproduzieren und 
simulieren, da man die Anfangsbedingungen der PRNGs eben reproduzieren 
kann. Nichts ist schlimmer als eine Fehlersuche in einem Program das 
echten Zufall in dessen Programablauf benutzt.

Der dazu nötige Code beträgt 31 Opcodes.
Der Code in Main() zur Berechnung der Seeds sieht dann so aus (aus 
meinem Glühwürmchen Projekt in der Codelib)
1
  // init PRNG, load last seed from EEPROM, calculate next seed and store back to EEPROM, use another polynom as in lfsr()
2
    eeprom_read_block((uint8_t *)&seed, (uint8_t *)(0), 4);
3
    lfsr_poly(32, 0x8140C9D5);
4
    eeprom_write_block((uint8_t *)&seed, (uint8_t *)(0), 4);

Gruß Hagen

von Hagen R. (hagen)


Angehängte Dateien:

Lesenswert?

source

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Den Trick mit der wechselnden Seed im EEPROM wenden wir auch an ;-)

von Hagen R. (hagen)


Lesenswert?

Er ist sicher und sparrt den enormen Aufwand wenn man versucht sein möge 
den aktuellen Seed beim Wegfallen der Spannung speichern zu wollen ;)

Wichtig erachte ich aber den Fakt das man so wirklich eine 
Reproduzierbarkeit der Ergebnisse des Programmes bekommt. Und 
Programmieren bedeutet als Erstes für den Programmierer eine 
Reproduzierbarkeit zu erreichen.

Für die spätere Serialization des Programmes, also so das jede AVR Kopie 
einen anderen Zufall erzeugt, kann man ja die EEPROM Zellen für jede AVR 
Kopie serialisieren = individuell initialisieren.

Allgemein halte ich nicht viel von den vorgeschlagenen HW-Lösungen. Der 
so erzeugte "Zufall" ist in erster Linie mal nur abhängig von den 
physikalischen Eigenschaften der Bauteile. Erst in vierter/fünfter Linie 
ist er abhängig vom thermischen Zufalls-Rauschen auf Grund der 
Hintergrundstrahlungen des Universums, und nur das wäre eine gute Quelle 
für echten Zufall. Die Konstrukteure der Bauteile versuchen ja gerade 
all diese unerwünschten Abhängigkeiten zu minimieren, sprich 
Temperaturstabilitäten zu verbessern, Einflüsse durch EMV zu minimieren 
usw. Das Rauschen des ADCs im AVR ist also erstmal systembedingt und 
nicht so stark zufällig wie man annehmen würde.

Davon abgesehen entsteht für den Entwickler immer eine 
Argumentations-Falle. Denn beweise mir das der so erzeugte Zufall auch 
wirklich echt zufällig sein muß ! Das kann man aber nicht da man nicht 
alle Parameter des Systemes kennen kann. Im Umkehrschluß kann ich also 
mit Recht behaupten das das das System "ADC + Antenne" eben keinen 
Zufall erzeugt, sondern nur hochkomplexe und von vielen Unbekannten 
abhängige Bits sammelt.

Gruß Hagen

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Genauso kann der ADC-Pin durch Spannungeeinstreuung hochlaufen und an 
der Spannungsoberkante kleben bleiben und somit für einen längeren 
Zeitraum sauber 0x3FF (1023) ausgeben. Nicht wirklich Zufall.

von Hagen R. (hagen)


Lesenswert?

Es gibt also zwei Wege:

1.) man erzeugt "Zufall" mit mathematischen Verfahren. Die Komplexität 
dieses Verfahrens und der verwendeten Zahlengrößen bestimmt die 
Vorhersagbarkeit durch Dritte und die statistischen Eigenschaften des 
Zufalls. Je nach Verfahren und Komplexität kann man diesen "Zufall" dann 
nur reproduzieren wenn man die Anfangsbedignungen kennt, sprich das 
Seed. Schützt man dieses so wird defakto für einen Dritten der erzeugte 
Zufallsstrom wie echter icht vorhersagbarer Zufall erscheinen. Somit 
kann man Beide Vorteile als Entwickler nutzniesen, die evtl. nötige 
Reproduzierbarkeit der Ergebnisse in der Entwicklungsphase und die 
beweisbare Nicht-Reproduzierbarkeit des Zufallsstromes für Dritte die 
keinen Zugriff auf das Seed haben.

2.) man misst "Zufall". Dann benutzt man Hardware, mit ihr kann man 
keinen Zufall erzeugen sondern nur Zufallseregnisse messen. Dabei ist es 
aber dann auch wirklich wichtig das man sauber misst, also scharf 
abgrenzen kann zwischen der zu messenden Zufallsquelle und den 
physiaklsichen Störparametern der Bauteile und der Umwelt, und eben eine 
gute Quelle der physikalischen Zufallsereignisse benutzt. In letzter 
Konsequenz kann man eigentlich nur die Hintergrundstrahlung des 
Universums als solch eine Quelle als gut bezeichnen. Oft benutzt man 
radioaktive Zerfallsprozesse als Quelle. Auch diese sind als gut zu 
bewerten da sie in letzter Konsequenz mit dem zeitlichen Verlauf des 
Universums korrelieren, ergo Hintergrundstrahlung. Aber nur dann wenn 
man sie von anderen absichtlichen radioaktiven Störquellen auch 
messtechnisch unterscheiden kann.


Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

>Genauso kann der ADC-Pin durch Spannungeeinstreuung hochlaufen und an
>der Spannungsoberkante kleben bleiben und somit für einen längeren
>Zeitraum sauber 0x3FF (1023) ausgeben. Nicht wirklich Zufall.

Und viel schlimmer ist dabei der Fakt das man dieses Eregnis eben nicht 
reproduzieren noch vorhersagen kann. Man wird dann als Entwickler 
bestimmte mathematische Verfahren nachschalten müssen, also zb. die 
Entropie verdichten zb. durch Hashalgorithmen, oder statistische 
Verfahren um den so erzeugten "Zufall" zu bewerten. In letzter 
Konsequenz hat man einen enormen zusätzlichen Aufwand betrieben der mehr 
kostet als gleich einen guten beweisbaren PRNG zu benutzen.

Für den Entwickler bedeutet dies also:
Man verlässt sich in seiner Entwicklung, im Nachweis der 
Funktionstüchtigkeit des eigenen Programmes auf Unvorhersagbarkeiten. 
Das ist ein Widerspruch in der Arbeitsweise des Entwicklers.

Gruß Hagen

von Jürgen (Gast)


Lesenswert?

> Schützt man dieses so wird defakto für einen Dritten der erzeugte
> Zufallsstrom wie echter icht vorhersagbarer Zufall erscheinen.

Das gilt nur bei kryptografisch sicheren Zufallszahlengeneratoren.
Deine LFSRs gehören nicht dazu.

Jürgen

von Hagen R. (hagen)


Lesenswert?

Jo ich weiß habe auch nichts anderes behauptet ;) Es ging ja in dieser 
Aussage im Allgemeinen um die machbaren Vorteile der PRNG im Vergleich 
zu HW-RNGs.

Gruß Hagen

PS: übrigens sind es nicht meine LFSRs, ich habe sie nur in AVR Source 
umgesetzt ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.