Forum: Mikrocontroller und Digitale Elektronik Zufallszeit bestimmen - solange Taster gedrückt? (MSP430, in C)


von Olliver (Gast)


Lesenswert?

Hi,
ich möchte eine Zufallszeit zwischen 1000 und 5000 Millisekunden 
bestimmen.

Nach einiger Recherche bin ich mittlerweile bei dem Lösungsansatz 
"Erzeugung einer Zufallszahl mittels Tasten" angelangt. Soweit ich das 
richtig verstanden habe, ist die Lösung am "einfachsten".

So würde ich bei meinem Programm die Zufallszahl erzeugen, indem ich die 
Dauer des Tastendrucks auswerte bzw. so eine Zufallszahl erzeugt wird.
Ist dieser Ansatz gut/okay?

Doch wie realisiere ich die Generierung?

Mein Ansatz:

int zufallszahl=0; //init

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;
  P3IE = 0x27               // Interrupt enable
  __enable_interrupt();     // Interrupt aktivieren
  __low_power_mode_3();     // LPM3 => 0,8 uA

  return 0;
}


#pragma vector=PORT3_VECTOR
__interrupt void P3_ISR(void)
{
 while (P3IFG)
  {
    zufallszahl++;
    if (zufallszahl==5000) zufallszahl=1000;
}


Aber irgendwie klappt das leider nicht :( Die Zufallszahl bleibt immer 
gleich. Kann mir jemand helfen? Evtl. auch generelle 
Verbesserungsvorschläge?

Vielen Dank schonmal,

vg Olli

von Uwe .. (uwegw)


Lesenswert?

Ich kenn die Syntax für den MSP nicht, aber P3IFG hört sich irgendwie 
nach Interruptflag an. Du müsstest stattdessen das Bit pollen, das dir 
angibt ob die Taste gedrückt ist oder nicht.

Und üblicherweise gewinnt man mit dieser Methode erst mal nur eine 
Zufallszahl (die aber noch nicht sehr gut normalverteilt ist) und nimmt 
sie dann als seed für die rand()-Funktion. Die liefert dann bessere 
Zufallszahlen.

von Karl H. (kbuchegg)


Lesenswert?

Wird der Interrupt aufgerufen
a) wenn eine Taste gedrückt wird    oder
b) solange eine Taste gedrückt ist

Wharscheinlich macht dein µC ersteres, du willst aber letzteres.

Tasten wertet man eigentlich nicht mittels Interrupt aus. Also einfach 
den Portpin abfragen und wenn der sich im Zustand 'gedrückt' befindet 
(entweder 0 oder 1, je nach deiner Hardware) dann den Zufallszahlen 
Zähler erhöhen so wie du das gemacht hast.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Entprellung des Tasters beachten, sonst ist deine Zufallszahl die 
Prellzeit :)

Manche Compiler optimieren dir den die Anweisungen im while-Block 
radikal weg, weil zufallszahl nicht volatile gekennzeichnet ist.

Die P3_ISR könnte IMHO in einer Endlosschleife stecken bleiben oder 
wirkunslos sein, jenachdem wann dein µC das P3IFG löscht. 
Endlosschleife, wenn es beim Verlassen der ISR gelöscht wird; 
wirkungslos wenn es beim Eintritt in die ISR gelöscht wird.

Hat deine C-Library keine rand() Funktion? Als Seed (=> srand()) 
könntest du die Zeit bis zum ersten Tastendruck (oder sonst eine 
Pseudozufallseingabe) nehmen, und dann rand() eine Zufallszahl 
generieren lassen.

von viel wissen, aber keine Ahnung haben (Gast)


Lesenswert?

nach mein Wissen gitb es in C ein Befehl der Random heißt.

Vielleicht diesen benutzen, dann sind die Zahlen auch mehr zufällig, als 
hoch gezählt wie in folgender Funktion deines Programms:
1
__interrupt void P3_ISR(void)
2
{
3
 while (P3IFG)
4
  {
5
    zufallszahl++; // *1
6
    if (zufallszahl==5000) zufallszahl=1000; // *2
7
}

zu 1: plus eins, so lange while die Bedingung nicht erfüllt.
zu 2: wenn die Zahl gleich 5000 ist setz sie auf 1000 runter

PS... da fehlt eine } ... kann so ja nicht funktionieren ;-)

Weiter kann ich dein Code nicht auseinander pflücken, da mit der µC 
fremd ist

von Olliver (Gast)


Lesenswert?

Hallo,

vielen Dank schonmal für eure Antworten.
Mit rand könnte man das auch lösen - stimmt. Danke für den Tipp. Aber 
trotzdem würd ich gern wissen wie man es mit dem Taster lösen könnte 
bzw. wo mein Fehler liegt. Leider wird so keine Zufallszahl erzeugt. 
Weiß jemand vielleicht wo der Fehler liegt?



int zufallszahl=1000;

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;
  P1DIR = 0xFF; // P1.0-P1.7 als Output
  P3IES = 0x27;             // BIT0, BIT1,BIT2,BIT5 Alle 4 Taster;
  P3IE = 0x27;              // Interrupt enable für die 4 Taster
  P1OUT = 0x0F; // LED 1-4 ON FF=Aus(!!!)
  __enable_interrupt();     // Interrupt aktivieren
  __low_power_mode_3();     // LPM3 => 0,8 uA
  return 0;
}


#pragma vector=PORT3_VECTOR
__interrupt void P3_ISR(void)
{
  P1OUT = 0xFF; // LED 1-4 on -> Daran sieht man das Interrupthandler 
aufgerufen wird.

  while (P3SEL)   //P3SEL müsste laut UserGuide richtig sein
  {
    zufallszahl++;
    if (zufallszahl==5000) zufallszahl=1000;
  }
  int i= zufallszahl;
}

i müsste einen zufälligen Wert enthalten, dem ist leider nicht so :(

Vielen Dank im Voraus,

vg Olli

von Karl H. (kbuchegg)


Lesenswert?

Olliver schrieb:

> trotzdem würd ich gern wissen wie man es mit dem Taster lösen könnte
> bzw. wo mein Fehler liegt.

Dazu müsste man erst mal wissen, worauf der Interrupt reagiert. Auf die 
Flanke oder auf den Pegel.
Da hier mehr die AVR-Spezialisten unterwegs sind, musst du das 
mitteilen.

> Weiß jemand vielleicht wo der Fehler liegt?

Die Vermutungen wurden ja schon geäussert.


> int main( void )
> {
>   WDTCTL = WDTPW + WDTHOLD;
>   P1DIR = 0xFF; // P1.0-P1.7 als Output
>   P3IES = 0x27;             // BIT0, BIT1,BIT2,BIT5 Alle 4 Taster;
>   P3IE = 0x27;              // Interrupt enable für die 4 Taster
>   P1OUT = 0x0F; // LED 1-4 ON FF=Aus(!!!)
>   __enable_interrupt();     // Interrupt aktivieren
>   __low_power_mode_3();     // LPM3 => 0,8 uA
>   return 0;
> }

Keine Endlosschleife?
Was macht dein µC, wenn er aus main() rausfällt?

> #pragma vector=PORT3_VECTOR
> __interrupt void P3_ISR(void)

Wann wird dieser Interrupt aufgerufen:
wenn sich der Pegel ändert oder solange der Pin einen bestimmten Pegel 
hat?

von Olli (Gast)


Lesenswert?

Hallo,

danke für die Hilfe.

>Keine Endlosschleife?
>Was macht dein µC, wenn er aus main() rausfällt?

Achso, also gehört immer eine Endlosschleife wie while(1) {} oder for 
(;;) in die Mainfunktion? Aber was sollen die Schleifen enthalten? 
Sorry, bin noch ziemlich neu auf dem Gebiet - wie man sicher bemerkt ;)



> #pragma vector=PORT3_VECTOR
> __interrupt void P3_ISR(void)

>Wann wird dieser Interrupt aufgerufen:
>wenn sich der Pegel ändert oder solange der Pin einen bestimmten Pegel
>hat?

Der Interrupt reagiert auf die Flanke

Vg Olli

von Karl H. (kbuchegg)


Lesenswert?

Olli schrieb:

>>Keine Endlosschleife?
>>Was macht dein µC, wenn er aus main() rausfällt?
>
> Achso, also gehört immer eine Endlosschleife wie while(1) {} oder for
> (;;) in die Mainfunktion? Aber was sollen die Schleifen enthalten?
> Sorry, bin noch ziemlich neu auf dem Gebiet - wie man sicher bemerkt ;)

Üblich ist zb, dass die Runtime Library nach der Rückkehr aus main den 
Prozessor anhält. Was soll er auch sonst vernünftiges tun? Das Progrmm 
ist ja offiziell zu ende.

>> #pragma vector=PORT3_VECTOR
>> __interrupt void P3_ISR(void)
>
>>Wann wird dieser Interrupt aufgerufen:
>>wenn sich der Pegel ändert oder solange der Pin einen bestimmten Pegel
>>hat?
>
> Der Interrupt reagiert auf die Flanke

Na ja. Was erwartest du dann?
Du drückst die Taste einmal, der Interrupt wird aufgerufen, zählt einmal 
hoch (wenn überhaupt, denn das Programm ist ja offiziell schon zu ende, 
so schnell kannst du nicht drücken nachdem das Programm gestartet 
wurde). D.h. dein Haochzählen findet genau einmal statt. Und das wars 
dann.

Vorschlag: Vergiss die Idee mit dem Interrupt. Braucht kein Mensch.
Dein Programm startet und wartet darauf, dass die Taste gedrückt wird. 
Während die Taste gedrückt ist, erfolgt das Hochzählen und wenn die 
Taste losgelassen wird hast du dein Ergebnis.

Wie man einen ganz bestimmten Portpin abfrägt ob er 0 oder 1 ist, ist 
ein Wissen, das du sowieso brauchst.

von Olli (Gast)


Lesenswert?

>Dein Programm startet und wartet darauf, dass die Taste gedrückt wird.

Genau das wollte ich ja mit dem Interrupt lösen. Also würdest du 
alternativ vorschlagen in der main:
while(1)
{ if (Taste gedrückt) ... }


>Während die Taste gedrückt ist, erfolgt das Hochzählen und wenn die Taste 
>losgelassen wird hast du dein Ergebnis.

Genau das wollte ich ja auch mit der Whileschleife erzielen.

  while (P3SEL)
  {
    zufallszahl++;
    if (zufallszahl==5000) zufallszahl=1000;
  }


Okay, dann liegt der Fehler anscheinend an der Bedingung in der 
Whileschleife (P3SEL) und vielleicht an der nicht vorhandenen 
Endlosschleife im Mainprogramm...

Vielen Dank für die Hilfe,

vg Olli

von Florian (Gast)


Lesenswert?

Die einfachste Möglichkeit echte Zufallszahlen zu erzeugen, ist es, 
die Rauschspannung an einem offen in der Luft baumelnden A/D Wandler Pin 
zu messen. Wenn Dein MC also einen A/D Wandler integriert hat...

von Dennis (Gast)


Lesenswert?

Olli: Also dein erster Ansatz mit dem LPM ist besser als von anderen 
hier vorgeschlagen mit einer Endlosschleife. Der MSP430 ist ein 
"Ultra-Low-Power" Mikrocontroller, da versucht man alles mit Interrupts 
abzuarbeiten, und in main() entweder dauerhaft im LPM zu sein (dann 
brauchst du eigentlich auch keine while-schleife, wenn deine interrupts 
nicht den aktiv-modus für main aktivieren)
ansonsten wohl eher sowas in main:

while(1)
{
  while (EventFlag1 > 0 || EventFlag2 > 0)
  {
    if(EventFlag1 > 0)
       do_something();
    if(EventFlag2 > 0)
       do_something_else();
  }
  __bic_SR_register(LPM0_bits + GIE);
  __no_operation();
}

Ich denke es wird in etwa ersichtlich was gemeint ist:
Dann machst du interrupts welche dir diverse globale Event Flags setzen 
können, die dann in main abgearbeitet werden können. Ansonsten, während 
nichts passiert, ist dein System im low-power modus....
Die interrupts können dann durch __bis_SR_register_on_exit(LPM4_bits); 
das Status-Register, welches auf dem Stack liegt, manipulieren. Wenn die 
interrupt service routine dann zu Ende ist, läuft die main-schleife nach 
dem __bic_SR_register(LPM0_bits + GIE) weiter und steckt nicht im 
low-power-modus fest.

Aber zurück zu deinem eigentlichen Taster-Problem:

Eine andere Möglichkeit wäre einfach einen Timer laufen zu lassen... 
Dann machst du einen Taster-Interrupt, und liest dann dort den 
Timer-Wert aus - fertig.
Pollen ist doch unschön, und Low-Power ist das dann auch nicht...

Andererseits, wenn du wirklich die Zeit des Tastendrucks aufnehmen 
willst (da hast du dann aber wahrscheinlich eher eine Gauss-Verteilung 
um einen bestimmten Wert, und kein so richtiges "random"):

Also interrupts sind durch Flanken bestimmt, Auszug aus dem User Guide:
"If any PxIFGx flag becomes set during a Px interrupt
service routine, or is set after the RETI instruction of a Px interrupt 
service routine is executed, the set
PxIFGx flag generates another interrupt. This ensures that each 
transition is acknowledged."

D.h. du kannst einfach die ISR wie folgt machen (in etwa):

Port-ISR:

static int level = 0;
if (level = 0) {
Timer-Modul starten, einfach von 0 ab laufen lassen
}
else
{
Timer-Modul stoppen und Wert notieren, dieser Wert ist dein Random-Wert
}
return...

Das wars! Völlig ohne while-Schleife in main, und völlig ohne unnötigen 
Stromverbrauch...

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.