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
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.
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.
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.
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
__interruptvoidP3_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
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
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?
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
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.
>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
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...
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...