Hallo! Hier nochmal ein Beispiel aus meiner Reihe einfaches Unterprogramm versaut einem das ganze Projekt. Habs bis jetzt noch nicht geschafft ein einziges Programm fertigzustellen. Einzeln läuft alles perfekt. Füge ich jedoch mehrere Unterprogramme zusammen, bleibt der Controller an seltsamen Stellen hängen. Das muss doch was mit dem Stackpointer zutun haben oder? Hab das Programm mal angehängt. Läuft perfekt, bis man die lcd_init und lcd_clear einfügt. Dann geht garnichts mehr. Wäre echt nett, wenn da mal einer mit Ahnung drübersehen würde, wo da der Bock steckt. Grüße Dominik
ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse out SPL, temp ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse out SPH, temp Versuchs mal in dieser Reihenfolge, ist bei mir ohne Probleme im Einsatz wenns dait nicht geht liegts zumindest nicht am Stack
@ Christoph: Zugriff auf 16- Bit Reg.: Schreiben: High dann Low; Lesen: Low dann High Also dürfte deine Aussage Schwachsinn sein.
DU kannst gerne Vorbeikommen und dich vom Gegenteil überzeugen. Zumal es 1 zu 1 aus dem Tutorial hier auf der Seite übernommen ist: http://www.mikrocontroller.net/tutorial/stack "Bei Controllern die mehr als 256 Byte RAM besitzen (z.B. ATmega8) passt die Adresse nicht mehr in ein Byte alleine. Deswegen gibt es bei diesen Controllern noch ein Register mit dem Namen SPH, in dem das High-Byte der Adresse gespeichert wird. Damit es funktioniert, muss das Programm dann folgendermaßen geändert werden: .include "m8def.inc" .def temp = r16 ldi temp, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse out SPL, temp ldi temp, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse out SPH, temp " Bei mir Funktionierts, bei ihm Funktionierts nicht. Ich weiss nicht was daran Schwachsinn sein sollte, ich (fast) alle meien Projekte bisher mit meinem ATmega16 gemacht udn es hat auch mit Unterprogrammen, Interupt, Push und Pop ohne Probleme funktioniert.
Habe jetzt mal beide Varianten ausprobiert und keine führt zum Erfolg. Eine Veränderung zu gestern ist aber zu sehen, obwohl der rest des Codes nicht verändert wurde. Bekommen jetzt ein OHM-Zeichen auf dem Display ausgegeben. Allerdings bei beiden Varianten das gleiche. Ich verstehe es nicht. Gibts da nicht noch andere Möglichkeiten, woran das liegen könnte? Grüße Dominik
funktioniert die Displayausgabe mit einer "festen" Variablen denn? Der Fehler hört sich eher nach Timingproblemen an. Werner
Hallo! Also die LCD-Ausgabe für sich alleine funtioniert ja einwandfrei. Nur in Verbindung mit anderen Unterprogrammen macht sie Probleme. Also dürfte es ein Timingproblem doch eigentlich nicht sein oder? Dominik
Dann setzte Dir doch bitte zwischendurch irgendwelche Ports, damit Du verfolgen kannst, an welcher Stelle er hängen bleibt. Oder simulier das Ganze. Sonst stocherst Du doch total im Dunkeln mit "Dann geht garnichts mehr". Allgemein: solange Du sinnige Namen wie "temp1" usw. vergibst und die sowohl im Hauptprogramm als auch in Unterprogrammen benutzt, wirst Du die Probleme mit dem Zusammenspiel nie loswerden.
Sers Leute, Sers Dominik, ich habe in etwa das gleiche Problem. Ich programmiere meinen AVR über den Code-Vision C-Compiler. Erstmal ein kleiner Überblick über mein Projekt: In einer Endlosschleife im Hauptprogramm wird eine Auswertung einer Tastatur vorgenommen und entsprechende Unterprogramme aufgerufen, die dann auf einem LC-Display angezeigt werden. Nun empfängt mein µC Daten über die UART, welche in einem Interrupt verarbeitet werden sollen. Nun hat sich bei meinem Projekt herausgestellt, dass die ankommenden Daten nicht immer richtig verarbeitet werden. Wenn ich mich im Hauptprogramm(der Menüführung) befinde, wird ganz normal ein Int ausgelöst und die Daten verarbeitet. Befinde ich mich jedoch in einem bestimmten Unterprogramm, so wird zwar ein Int ausgelöst. Der µC kann dann jedoch nicht mehr auf eine bestimmte Variable zugreifen bzw. diese verarbeiten. Ich kann mich noch erinnern, dass mein Prof. mal gesagt hat, dass gerade die Routinen für die LCD-Ansteuerung nicht 100% implementiert sind und schon bei manchen Projekten Probleme gemacht haben. Ergo, was ich damit sagen will, ist, dass es vielleicht nicht am Stackpointer liegt, sondern irgendwo ne andere Schraube wackelt. Bin auch noch am rumprobieren, wie ich das Problem in den Griff bekomme. Falls du etwas neues rausfindest, dann gib bitte kurz bescheid. Gruß Christian
Das sicherste wäre, mit Push und pop alle in den LCD udn warteroutinen verwendeten Varibalen zu sichern bzw rückzusichern.
Hi also ich habs mal auf dem M16 mit dem JTAG durchlaufen lassen. Bei mir bleibt er nirgends hängen. Ich hab zwar kein Display dran, aber da kein busy flag abgefragt wird, läuft es ja so durch. Wegen dem Stackpointer, so wie es für mich aussieht ist es völlig egal wie rum geschriedne wird. Es endet immer in 0x045F. Mit dem Stackpointer sollte es meiner Meinung nach keine Probleme geben. Wegen dem Kommentar von dir "PORTB bleibt auf0x00" nach dem Einfügen von den zwei Unterporgrammen. So versteh ich des. Kann ich nicht nachvollziehen. Ich setzt meinen Cursor an das "ret" in deiner Ausgabe, jedes mal wenn ich ihn bis dahin durchratern lasse erhöt sich PORTB um 1. Nach 0xFF, fängt er wieder von neuem an. Egal wie lange ich es laufen lass, einen hänger bekomme ich nicht. Mir fällt kein Fehler auf, ich sehe zwar nichts auf dem Display, was wahrscheinlich daran liegt, dass keines dranhängt. Aber sonst kann ich kein Problem entdecken. Timing Porbleme könntest du aber haben. Wenn du eine Variable mit einem Wert irgendwo überschreibst. Dann stimmen deine Timings natürlich nicht mehr. Gruß MISZOU
Kann es sein, dss du versehentlich den Watchdog aktiviert hast? Im Programm ja nicht, aber eventuell über die WDTON-Fuse. Regelmässige Resets würden am PortB kaum auffallen, er wird ja immer gleich wieder geschrieben. Aber ein Reset während der LCD-Initialisierung wäre übel. Uwe
@Mathias: Beim Stack-Pointer ist das mit der Reihenfolge, ob zuerst high und dann low oder umgekehrt, völlig egal. Oder hast Du im Datenblatt zufällig was von einem temporären Register für SPH entdeckt? Ich zumindest nicht. Das mit der bestimmten Reihenfolge, zuerst high, dann low beim schreiben und erst low, dann high beim lesen, ist nur bei 16Bit-Timer nötig um zu einem Zeitpunkt 16 Bit zu schreiben oder zu lesen, sonst hätte man unter Umständen ein verfälschtes 16Bit-Ergebnis beim lesen, bei einem Prescaler von 1 sowieso. MfG Andi
Hallo allerseits! In der Simulation hab ich auch keine Probleme. Das würde ja auf ein Timing-Problem oder den Watchdog hindeuten oder? Zum Thema Watchdog: Wie überprüfe ich denn das WDTON-Fuse-Bit? Im Studio habe ich das unter Fuses nicht aufgeführt. Oder kann ich das softwaretechnisch bearbeiten? Zum Thema Timingproblem: In den Warteschleifen werden doch die Register, die die Schleifen zählen bei jedem Unterprogrammaufruf neu gesetzt. Also müssten sie doch die gleichen Wartezeiten ergeben, wie wenn die LCD-Ausgabe für sich alleine läuft oder? Und somit dürfte doch kein Timing-Problem entstehen richtig? MFG Dominik
Dominik, wie schnell läuft Dein Mega16? Die Warteschleife für 50µs mit 205 Takte inkl. RCALL und RET ist für 4MHz ausgelegt (205 Takte / 50µs = 4,1). Wenn Dein Mega16 schneller läuft, z. B. 8MHz, dann sind das nur noch 25µs und das LCD spielt dann nicht mehr mit da mindestens 43µs das LCD benötigt. Dann heißt es, Schleife verlängern, z. B. mit 0x84 statt 0x42. MfG Andi
Hab den nur auf 1 MHz internen Takt laufen. Also sollten die Schleifen ja absolut lang genug sein oder? Und zu lange wird doch nicht wild sein oder? Gruß Dominik
Also hast Du die CKSEL-Fuses auf 0001 eingestellt oder belassen? Ja, zu langsam schadet nicht. MfG Andi
Hi kannst du mal die einzelnen Teile so reinstellen wie sie funktionieren. Vielleicht sieht man ja dann einen Fehler. Des zusammengebaute haben wir ja bereits. Wie sieht denn jetzt dein Problem aus. Bleibt dein Programm immer noch an Stellen hängen oder kommt jetzt einfach nichts auf dem Display an? Gruß MISZOU
Tschuldigung, es geht ja um einen mega16, der hat kein WDTON. Das ist der JTAGEN-Fuse gewichen. Ich dachte an den mega8... Aber am Quelltext ist nichts Auffälliges, die Schleifen in den LCD-Routinen sollten irgendwann ablaufen und das Busy-Flag vom Display wird nicht abgefragt. Interrupts sind auch nicht freigegeben. Warum sollte sich das aufhängen? Wenn es aber nicht am Programm liegt, dann vielleicht an der Hardware? Verdrahtung, Entkopplung, Reset-Beschaltung oder zu kleine VCC?
Das JTAGEN is bei mir gesetzt? Soll ich das mal rausnehmen oder is das gefährlich? So wie das Programm oben im Anhang gepostet ist, funktioniert es. Alleine das hinzufügen von rcall lcd_init ; Wenn diese Zeilen hinzugefügt werden, rcall lcd_clear ; läuft Programm nicht mehr!!!!!!!!!!! ;PORTB bleibt auf0x00 lässt es dann nicht mehr laufen. Die LCD-Ausgabe ist eins zu eins aus dem Tutorial übernommen. Das läuft bei mir auch für sich alleine. Und das oben gepostete Programm auch für sich. In dem Zustand wie es oben ist sind alle LCD Ausgaben noch herausgenommen. Gruß Dominik
Ach ja sorry. Die Hardware ist einfach ein STK500. Und als Display ein EA DIPS082 von Reichelt. Am AD-Wandler eingang hängt eine Lichtschranke, die ohne die LCD-Ausgabe auch gut funktioniert. Dann wird der zaehler inkrementiert, so wie es sein soll.
Moin JTAG ist bei dir egal, JTAG verwendet beim Mega16 die PortC2-C5/PORTE RST und die Ports verwendest du ja gar nicht. Kannst es aber testweise deaktivieren. Also bei mir läuft er durch wenn ich die Zeilen rcall lcd_init rcall lcd_clear und rcall ausgabe wieder ins programm hole, habs aber auf dem Atmega16 ablaufen lassen (JTAG) nicht im AVRStudio Simulator. Ich glaub auch langsam das irgendwie das etwas hardwareseitig nicht passt. Was machst du mit dem AD Wandler? Wenn da eine Lichtschranke dranhängt bekommst du eine 1 oder eine 0. Oder hast du was spezielleres. Oder ist der AD Wandler für eine andere Komponente? Gruß MISZOU
Guten Morgen. Ja genau der AD-Wandler ist für die Lichtschranke. Es soll, wenn ein bestimmter Wert überschritten wird, der zaehler inkrementiert werden. Soll später einfach zählen, wie oft die Lichtschranke unterbrochen wird. Is aber noch im Anfangsstadium, weil dann ja die Probleme aufgetaucht sind. An Hardware habe ich sonst nur das STK500 dranhängen. Was könnte da denn nicht stimmen? Oder soll ich es mal mit einem anderen Controller versuchen? Habe da noch einen mega32. Den könnte man ja fast problemlos austauschen. Gruß Dominik
Hallo Dominik! Du wartest mit dem ADIF-Bit (ADC Interrupt Flag) bis eine Konvertierung fertig ist. Weis nicht, ob das 100%ig geht, wenn das Bit ADIE (ADC Interrupt Enable) nicht aktiviert ist (0): ldi temp1, 0x00 ;ADC initialisieren out ADCH, temp1 out ADCL, temp1 out ADMUX, temp1 out SFIOR, temp1 warten: rcall delay ldi temp1, 0b11000101 out ADCSRA, temp1 wart: sbis ADCSRA, 4 rjmp wart Besser wäre es mit dem Bit ADSC=0 zu prüfen, ob eine Konvertierung fertig ist. ADSC wird von der Hardware nach beenden der Konvertierung automatisch auf 0 gesetzt. Ersetze mal Deinen obigen Code mit folgendem: ldi temp1, 0x00 ;ADC initialisieren out ADMUX, temp1 LDI TEMP1, 1<<ADEN | 0b101 ;0b10000101 OUT ADCSRA, TEMP1 warten: rcall delay SBI ADCSRA,1<<ADSC ;Konvertierung starten wart: SBIC ADCSRA,1<<ADSC ;Prüfen, ob Konvertierung fertig (ADSC=0) rjmp wart Die geänderten Zeilen sind groß geschrieben. ADCL und ADCH brauchst (kannst) Du nicht auf 0 setzen, da sowieso nur read only, also weglassen und SFIOR kannst Du in Ruhe lassen, da ADATE (ADC Auto Trigger Enable) sowieso nicht gesetzt/benutzt wird. MfG Andi
Falle: Wenn Interrupt-Enable-Flags gesetzt werden, dann müssen am Programmstart unbedingt die Interruptvektoren eingetragen werden. Und wenn da nur RETI steht. Denn sonst springt das Programm (einen Interrupt vermutend) die irgendwo mitten ins Programm und mangels RETI nicht wieder zurück (mit zusätzlichem Stack-Überlauf). Übrigens funktioniert das Zählen der Lichtschrankenunterbrechungen so einfach nicht. Eine Unterbrechung kann mehrere Abfragen lang dauern, dann wird mehrfach gezählt, das wolltest du sicher nicht. - Oder? Ansonsten ist das ein Programmierstil, der in eine Sackgasse führt. Mit Zeitschleifen und Busy-Warteschleifen verbrätst du nur Rechenzeit. Schaff dir mittels Timer-Interrupt eine solide Zeitbasis, in der du die zu erledigenden Aufgaben verteilst. Dort kannst du auch die Lichtschranke entprellen, indem du den Zustand (frei, unterbrochen) mit dem vorherigen Wert vergleichst und nur bei Änderung darauf reagierst. Da dich irgendwann nicht nur interessiert WIE OFT die Lichtschranke unterbrochen war, sondern auch noch WANN, wirst du später sowiso eine Uhr einbauen wollen. Da ist ein Timer-Interrupt-Takt von 10ms schon mal recht wertvoll. ...
Hi noch zu deiner Lichtschranke, was spricht dagegen diese über einen ganz normalen Pin der als Input geschaltet ist abzufragen. So wie ich dich verstanden haben geht es dir nur wie oft sie unterbrochen wurde. Dann interessiert dich ja nicht die Spannung sodern nur ob ein High(Lichtschranke frei) oder ob ein Low (Lichtschranke unterbrochen) anliegt. Meine Lichtschraken die ich bisher verwendet hab prellen nicht. Dachte immer des wäre ein mechanisches Problem bei Tastern und Schaltern. Gruß MISZOU
Hi Hannes! Das mit dem Interrupt Enable ist schon klar. ADIE war ja bisher nicht gesetzt und SEI fehlt sowieso immer noch. Jetzt erst mal schauen, das man das so wie es ist zum laufen bekommt. Das mit den Interrupts muß ja auch erst mal "verdaut" werden bis man das handeln kann und ebenfalls der Umgang mit SRAM für Variablen was hier wohl auch noch fehlt. MfG Andi
Hallo! @ HanneS: Hab die Inteerrupts ja generell nicht aktiviert. Dann dürfte doch auch wenn das Flag vom AD-Wandler gesetzt wird kein Interrupt ausgelöst werden oder? Damit in einer Unterbrechung nicht mehrmals gezählt wird, wollte ich später dann noch eine zweite Schleife einfügen, in der gewartet wird bis die Unterbrechung der Lichtschranke beendet ist und dann erst wieder in die Schleife gehen, die dann wieder einen hochzählt, wenn unterbrochen wird. Wenn ich mein Unterprogrammproblem gelöst hab denke ich aber mal über deinen Vorschlag mit dem Timer nach. Danke dafür. @ Andi K.: Ich habe jetzt meinen Code mit deinem ausgetauscht. Bekomme allerdings zwei Fehlermeldungen com Compiler: zur Zeile SBI ADCSRA,1<<ADSC ;Konvertierung starten die Fehlermeldung: error: operand 2 out of range: 0x40 und zur Zeile SBIC ADCSRA,1<<ADSC ;Prüfen, ob Konvertierung fertig die gleiche Fehlermeldung wie oben. weiß jetzt leider nicht woran es liegt, weil ich mit den "<<" operatoren noch nicht gearbeitet habe. Hast du da noch eine Idee woran es da haken könnte? Nochmal dankeschön, dass ihr euch jetzt so intensiv damit beschäftigt. MFG Dominik
Oh man, ja ja, vor lauter Bit-Verknüpferreien für OUT überträgt sich das auch anders wo. OK, sollte heißen: SBI ADCSRA,ADSC ;Konvertierung starten und SBIC ADCSRA,ADSC ;Prüfen, ob Konvertierung fertig (ADSC=0) MfG Andi
Ok so geht es. Der zaehler zählt sogar. Auch wenn lcd_init und lcd_clear eingefügt ist. Allerdings zeigt das keine Wirkung. Es werden immernoch ein bis drei Ohmzeichen ausgegeben, obwohl das Display ja ganz leer sein sollte. Wenn ich dann aber "rcall ausgabe" auch noch wieder hinzufüge, dann zählt der zaehler nichteinmal mehr. Und auf dem Display immernoch die Ohms. Irgendwie habe ich das Gefühl, dass ich so nicht wirklich weiter komme. Wäre das nicht eine Möglichkeit, dass einer von euch mal ein Programm postet oder mir einen Link sagt, was ein paar Unterprogrammaufrufe beinhaltet und auf dem STK500 so zu betreiben und nachzuvollziehen ist ob es sauber läuft? Dann könnte ich wenigstens mal überprüfen, ob es an meinem Programmietstil oder meiner Hardware liegt. Gruß Dominik
> Besser wäre es mit dem Bit ADSC=0 zu prüfen, ob eine Konvertierung > fertig ist. ADSC wird von der Hardware nach beenden der > Konvertierung automatisch auf 0 gesetzt. Noch besser wäre, den Mikrocontroller während der Konvertierung in den sleep-Modus zu versetzen. Aber dazu muß der Interrupt auch aktiviert sein, denn der muß die MCU nach der Wandlung wieder aufwecken. Aber dann ist das die einfachste Möglichkeit, abgesehen davon ist es noch etwas genauer und braucht etwas weniger Strom, was natürlich nicht bei jeder Anwendung wichtig ist.
Dominik, überprüfe mal Deine Ausgabe-Routine. Die beginnt bei ASCII '0' an zu erhöhen. Wenn jetzt z. B. die 100er-Stelle 0 hat, dann wird bis zum gesetzten Carry Zahl1 um 1 erhöht, dann ist Zahl1 aber nicht mehr 0 sondern 1 und bei 9 ist es dann ASCII ':'. Endweder Du verringerst nach jedem BRCC ZahlN oder Du fängst nicht bei ASCII '0' sondern bei ASCII '0'-1 an zu Zählen in der Ausgabe-Routine. Ansonsten kontroliere Deine LCD-Funktionen noch mal ob die nicht den Stack irgend wo 'verbiegen'. MfG Andi
Dominik, abgesehen vom Stack vergiss das letzte von mir! MfG Andi
Allerdings soltest Du vor der Ausgabe auf das LCD eine feste Position (LCD-Adresse) setzen, sonst läuft das ganze einfach nur durch. Das geht mit dem LCD-Comando 0x80 + Adresse. Je nach LCD ist die Berechnung der Adresse für Zeile und Spalte verschieden (1 Line Mode, 2 Line Mode). Siehe Datenblatt Deines LCD unter Set DDRAM Adress. MfG Andi
Bei den LCD Ausgaben kann ich nichts entdecken. Die habe ich auch so aus dem Tutorial übernommen. Was mir aber gerade wieder seltsames aufgefallen ist: Wenn ich diese Veränderungen mache, lcd_init: drin lcd_clear: raus rcall delay(in "warte"-Schleife: raus dann funktioniert wieder alles. Der "zaehler" wird erhöht und auch die Ausgabe läuft. Die Zahlen flitzen dann zwar über las LCD, weil ich die clear_lcd anweisung ja noch nicht verwende, aber er gibt den "zaehler" korrekt aus. Also macht jetzt die delay-Schleife die Probleme. Und so geht das die ganze Zeit. Ich bekomme durch Zufall das Programm wieder zum laufen, wenn ich irgendwo eine Zeile einfüge oder herausnehme, aber genausoschnell läuft es dann auch wieder nicht, wenn ich woanders was ändere. Und das finde ich absolut merkwürdig. Gruß Dominik
Trotzdem, mache mal am Anfang der Ausgabe erst mal ldi temp1,0x80 rcall lcd_command hinein um einen Schritt weiterzukommen und wieder klar denken zu können. Das Register Zähler wird ja sowieso nicht beeinflusst. Die Ausgabe ist dann oben links im LCD. Für andere Positionen einfach ändern in 0x80+nn (nn=0 bis 127 (7F)). MfG Andi
Ich drehe am rad! Jetzt habe ich nur hinzugefügt: ldi temp1,0x80 rcall lcd_command und zwar einfach in "ausgabe" über mov temp1, zahl1 ;Ausgabe dreistellig auf LCD rcall lcd_data mov temp1, zahl2 rcall lcd_data mov temp1, zahl3 rcall lcd_data ret Und jetzt wird garnichts mehr auf dem LCD ausgebgeben. Und "zaehler" läuft auch nicht mehr weiter, bzw wird nicht mehr an PORTB ausgegeben. Da liegt jetzt die ganze Zeit 0x00 an. MFG Dominik
Etwa so?: ausgabe: ldi temp1,0x80 ;LCD-Adresse ausgeben rcall lcd_command ldi zahl1, -1 + '0' ;Noch garnicht verwendet hunderter: inc zahl1 subi zaehler, 100 brcc hunderter subi zaehler, -100 ldi zahl2, -1 + '0' zehner: inc zahl2 subi zaehler, 10 brcc zehner subi zaehler, -10 ldi Zahl3, '0' add zahl3, zaehler mov temp1, zahl1 ;Ausgabe dreistellig auf LCD rcall lcd_data mov temp1, zahl2 rcall lcd_data mov temp1, zahl3 rcall lcd_data ret MfG Andi
hab deins jetzt eingefügt. Ich hatte das direkt über den mov befehlen. Aber mit deinem kommt es auf das Gleiche hinaus. So wie es in meinee letzten Antwort beschriebn ist. MFG Dominik
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.