Hallo allesamt!
Ich bin neu hier im Forum und habe auch gerade letzte Woche frisch mit
der Microcontroller-Programmierung angefangen. Allerdings stoße ich bei
meinem Microcontroller nun zum zweiten Mal auf ein seltsames Verhalten,
das für mich jeglicher Logik zu widersprechen scheint.
Zu meinem aktuellen Problem (wesentliche Programmteile unten
abgebildet):
Ich habe erstmal als kleinen Versuch nen ganz simples (und angesichts
von _delay_ms() auch zeitlich fehlerhaften) Countdown-Script in C
geschrieben.
Unten in der main-Methode wird erstmal die int-Variable duration mit
1234 definiert und im weiteren Verlauf auch nicht mehr von mir
angepasst. Es erfolgt ein Aufruf der Funktion startCountdown(),
übergeben wird hierbei auch duration mit dem Wert 1234. Zum Debuggen
sind nun zwei Ausgaben einprogrammiert, welche den Wert von duration
anzeigen sollen. Die erste kommt direkt am Ende der Funktion
startCountdown, hierbei wird als Wert noch wie gewünscht 1234 angezeigt.
Danach müsste er ja (direkt nach dem delay zur besseren ablesbarkeit vom
display) zurück in die main()-funktion springen und dort nun gleich
erneut den Wert von duration zurückliefern. Das tut er auch, allerdings
wird nun plötzlich der Wert 1024 angezeigt.
Diese Befehle folgen ja im Grunde direkt aufeinander und es wird
nirgendwo diese Variable verändert... Wie kann das sein?
Vielen Dank für alle Ratschläge, ich bin allmählich wirklich ratlos...
Hendrik Motza schrieb:> Unten in der main-Methode wird erstmal die int-Variable duration mit> 1234 definiert und im weiteren Verlauf auch nicht mehr von mir> angepasstHendrik Motza schrieb:> duration = setNumber(duration);
und was ist das?
....
char lcd_output[4];
....
sprintf(&lcd_output, "%04d", i);
....
Eine Zeichenkette wird immer durch eine 0x00 abgeschlossen. Der
sprintf() schreibt also 4 Zeichen + 0x00 (= 5 Bytes) in ein Array, das
nur 4 Zeichen groß ist.
Wenn man nun noch wüsste, wie led_printNumber() und setNumber()
aussehen... Siehe auch Vorposter...
@Bernd: In dem Modus lässt sich der vordefinierte Wert anpassen, wie
gesagt nehme ich jedoch keine manuelle Änderung vor. Und wie gesagt
stimmt der Wert ja noch bis einschließlich dem Ende der
startCountdown-Funktion, erst direkt danach tritt die Veränderung
offenbar in Kraft.
@g457: Ja ich bin leider auch noch neu in der Programmiersprache, habe
gerade mal 1200 Seiten Theorie zu C/C++ durchgelesen und nun erst
angefangen die Sprache praktisch zu verwenden. Ich dachte mir es müsste
eine Funktion geben, mit der die Konvertierung von int in String ganz
leicht möglich sein dürfte. Das war eine Lösung, die google mir erbracht
hat und es funktioniert offenbar.
Da die Zahl ohnehin nur 4-stellig sein darf (4 LED-Anzeigen...) sollte
lcd_output[4] eigentlich genug Speicher bereitstellen! :-D
>"%04d"
Das sind 5 Bytes, die abschließende Null wurde nicht berücksichtigt.
Diese Null überschreibt wahrscheinlich die Variable Duration, denn wenn
man bei 1234 = 0x04D2 das Low-Byte überschreibt, kommt 0x0400, also 1024
raus.
Also auf lcd_output[5] ändern, dann könnte es funktionieren.
Perfekt, ich habe das Array nun auf 5 Chars erweitert und es
funktioniert nun tatsächlich. Danke!
Ich dachte durch die Definition als Array würde sowas wie ein Nullbyte
automatisch vorhanden sein, aber wie gewissermaßen erwiesen wurde, muss
dies berücksichtigt werden! ;-)
Hendrik Motza schrieb:> Ich dachte durch die Definition als Array würde sowas wie ein Nullbyte> automatisch vorhanden sein,
Dem Compiler ist es doch egal was du in dein Array packst. Wenn es
Zahlen sind, brauchst du die '\0' ja nicht.
Aber bei Stringliteralen wie "Countdown:" wird die '\0' automatisch
angehängt. Die brauchst du dann nicht per Hand vorgeben.
Was auch geht, ist die Bestimmung von der Länge des Arrays dem Compiler
zu überlassen.
Hendrik Motza schrieb:> sprintf(&lcd_output, "%04d", i);
Das "&" muss auch noch weg. lcd_output selbst ist bereits ein Array,
verhält sich also hier wie ein Pointer. Entweder muss es heißen:
sprintf(lcd_output, "%04d", i);
oder
sprintf(&lcd_output[0], "%04d", i);
Aber das da ganz oben ist doppelt gemoppelt.
Und mach dir bei solchen Sachen die Arrays ein wenig größer. Du hast das
sowieso als lokales Array und du musst auch noch nicht Speicher sparen.
Das Problem: Derartige Dinge verändern sich gerne mal ein wenig.
Irgendwann machst du den Text ein wenig größer, so dass das Array wieder
zu klein ist, weil du natürlich vergessen hast es anzupassen und was
dann alles passieren kann hast du ja jetzt gesehen.
Das sollte weniger das Problem sein. Dieses Board verfügt nur über 4
LED-Anzeigen. Und Programmcodes, die möglicherweise mal auf anderen
Boards verwendet werden sollen (wie led- und tastensteuerungen), habe
ich ohnehin schon in ganz allgemeinen funktionssammlungen implementiert,
die direkt auf andere systeme übertragbar sind. =)
So, wieder ein kleines Problem, welches sich mir nicht erschließt... =(
Ich habe den Code wieder so weit wie möglich vereinfacht und mal
angehängt. Im Grunde gibt es hier einfach die Funktionen home() und
menu(), welche sich auf Tastendruck gegenseitig aufrufen sollen. Durch
die LCD-Ausgabe lässt sich auch leicht erkennen, in welcher Funktion er
gerade steckt.
Unglücklicherweise funktioniert die Umschaltung auf Knopfdruck nicht
ganz wie geplant. Es gibt jeweils in den beiden Funktionen einen Delay
von 1us (derzeit auskommentiert). Wenn ich einen der Delays wieder
aktiviere, dann funktioniert es auf einmal zu der anderen Funktion auf
Knopfdruck zu wechseln (zurück geht es nur, wenn auch in der zweiten
Funktion das Delay aktiviert ist).
Alternativ ist in der Funktion button_pushed() auch noch ein Delay
enthalten, der ebenfalls dafür sorgt, dass diese Sprünge auf Knopfdruck
alle beide funktionieren. Allerdings läuft es an der Stelle nur mit
einem Delay von mindestens 17ms.
Ich kann mir leider beim besten Willen nicht erklären, wo das Problem
liegt und warum es durch diese Delays auf einmal behoben scheint.
Vielleicht könnt ihr mich ja erleuchten! =)
Zumal die Tastenentprellung ja auch nur dafür sorgen soll, dass ein
Tastendruck als einer und nicht viele Klicks wahrgenommen wird. Mein
Problem besteht aber leider mehr darin, dass offenbar gar kein
Tastendruck akzeptiert wird (ohne delays oder andere verzögerungen)...
Hendrik Motza schrieb:> Ich dachte das hätte ich verwendet! ;-)
Das geht alles viel zu schnell!
Tastenprellen spielt sich im Millisekunden Bereich ab. Für deinen µC ist
eine Millisekunde eine viertel Ewigkeit.
Entprellung
Hol dir die letzte Lösung von dort, die mit dem Timer, und du bist das
Problem Prellen ein für alle mal los.
AUsserdem geht das so nicht, dass du von einer Funktion die andere
aufrufst und dann wieder zurück aufrufst. Du stapelst hier
Funktionsaufrufe ineinander, ohne das ganze irgendwann aufzulösen.
?
> TIMSK |= (1<<0) | (1<<2);
0 ist der Overflow vom Timer 0. Aber was macht das Bit 2?
Schreib das doch so
TIMSK |= (1 << TOIE0);
Dann sieht man auf einen Blick dass du den
1
_T_imer _O_verflow _I_nterrupt _E_nable vom Timer _0_ (eben den TOIE0)
freigibst.
Und: man gibt niemals, unter keinen Umständen, einen Interrupt frei, für
den man keinen Handler hat.
Weiters dürfte da ein Problem entstehen:
Du fragst den Button indirekt per Polling ab (also innerhalb der
Schleifen), fragst aber auch mit dem Timer die Tasten ab.
Entscheide dich für EINEN Mechanismus, der sich um die Tasten kümmert!
Über die Timer-Interrupts wird eigentlich regelmäßig geprüft, ob der
Knopf gedrückt ist und hier ist ebenfalls die Entprellung eingebunden.
Das scheint mit dem Wert auch ganz gut zu funktionieren, die Tasten habe
ich durchaus schon erfolgreich verwenden können (nur hier kam es nun
erstmalig zu den Problemen, die beispielsweise erst durch so einen Delay
behoben werden).
>Du fragst den Button indirekt per Polling ab (also innerhalb der>Schleifen), fragst aber auch mit dem Timer die Tasten ab.>Entscheide dich für EINEN Mechanismus, der sich um die Tasten kümmert!
Wenn durch die Timer-Interrupts ein Tastendruck (entprellt) festgestellt
wird, wird die eine status-Variable auf true gesetzt, dadurch wird erst
ein entprellt getätigter Tastendruck anerkannt. Die Schleifen in meinen
eigentlichen Funktionen fragen dann (über die button_pushed()-Funktion)
nur noch diesen status ab.
Dieser Mechanismus erschien mir bisher als relativ günstig, da es
insgesamt 4 Knöpfe gibt, die sowohl kurz als auch lang gedrückt werden
können. Diese Nechanismen werden häufig in unterschiedlichen Funktionen
benötigt, zudem ist die Funktionssammlung für Buttons auch so angelegt,
dass es sich relativ leicht in andere Boards einbauen lässt. Ich hab
halt alle Codes nur derart weit reduziert, dass man schneller den Code
überblicken kann und gleich deutlich die Problemstellen erkennen kann.
>AUsserdem geht das so nicht, dass du von einer Funktion die andere>aufrufst und dann wieder zurück aufrufst. Du stapelst hier>Funktionsaufrufe ineinander, ohne das ganze irgendwann aufzulösen.
Dessen bin ich mir bewusst, hierbei handelt es sich auch nur um die
stark vereinfachte Version meines Codes. Eigentlich gibt es sonst eine
globale Variable (genauer gesagt einen Funktions-Zeiger). Die Funktion,
die nun eine andere Funktion aufrufen möchte, setzt den Zeiger der
globalen Variable also auf die Zielfunktion und springt dann via
return-Anweisung in die main() zurück. Dort wird dann über eine Schleife
alles wesentliche resetet (Display und Co) und es erfolgt der Aufruf der
Zielfunktion.
Ich dachte dieser direkte gegenseitige Aufruf wäre an dieser Stelle
schneller nachvollziehbar! =)
Hendrik Motza schrieb:>>Du fragst den Button indirekt per Polling ab (also innerhalb der>>Schleifen), fragst aber auch mit dem Timer die Tasten ab.>>Entscheide dich für EINEN Mechanismus, der sich um die Tasten kümmert!>> Wenn durch die Timer-Interrupts ein Tastendruck (entprellt) festgestellt> wird, wird die eine status-Variable auf true gesetzt, dadurch wird erst> ein entprellt getätigter Tastendruck anerkannt. Die Schleifen in meinen> eigentlichen Funktionen fragen dann (über die button_pushed()-Funktion)> nur noch diesen status ab.
Du hast recht.
Ich bin schon ganz wuselig, welche Funktion von wem an welcher Stelle
aufgerufen wird. Ständig zwischen 2 Files wechseln und dann heißen alle
Funktion ähnlich :-)
OK.
Dein button_update() müsste IMHO so aussehen
Karl Heinz Buchegger schrieb:> OK.> Dein button_update() müsste IMHO so aussehen> void button_update() {> if(button_isPushed()) {> if( button_count > 5 )> pushed = true;> else> button_count++;> }> else {> button_count = 0;> }> }
Ah okay, der Code leuchtet ein. Warum mein Code etwas anders aufgebaut
ist:
Ein Knopf wird erst dann als gedrückt anerkannt, wenn er bereits wieder
losgelassen wurde. Das hat derzeit den simplen Zweck, dass auch zwischen
kurz und lang gedrückt unterschieden wird. Diese Festlegung wird bei mir
also (derzeit) erst getroffen, wenn der Knopf wieder losgelassen wird.
Bei Gelegenheit soll dies aber ohnehin noch etwas erweitert werden! ;-)
Hendrik Motza schrieb:> Ein Knopf wird erst dann als gedrückt anerkannt, wenn er bereits wieder> losgelassen wurde. Das hat derzeit den simplen Zweck, dass auch zwischen> kurz und lang gedrückt unterschieden wird. Diese Festlegung wird bei mir> also (derzeit) erst getroffen, wenn der Knopf wieder losgelassen wird.> Bei Gelegenheit soll dies aber ohnehin noch etwas erweitert werden! ;-)
Dann solltest du aber zumindest das hier
if(button_isPushed()) {
button_count++;
auf Überlauf absichern.
Ich hab das jetzt nicht mehr weiter durchdacht.
Ich benutz den Code aus Entprellung ganz unten. Der funktioniert
zuverlässig für 8 Taster und spielt alle Stückeln, inklusive
Tasten-Repeat bei längerem Drücken.
Das stimmt wohl, ne Absicherung gegen Überlauf wäre bestimmt sinnvoll.
Aber ich hatte es bereits geprüft, ein Überlauf kam bei mir nicht zu
Stande, als int dauert das ohnehin ein wenig länger (je nach
Timer-Einstellung). Im schlimmsten Fall würde der Tastendruck wohl mal
nicht korrekt erkannt werden, wenn er ewig gedrückt wird! :-D