Hallo Leute, bin absolut blutiger Anfänger in Sachen Assembler und möchte euch um Rat fragen. Vielleicht erhört mich einer von euch :) ... Ich möchte ganz schlicht und einfach bei einem Atmega8 auf den Portpins D0 und D1 zwei Schalter einlesen (ein Schalter pro PortPin). An für sich eigentlich sehr einfach, aber: ES KLAPPT EINFACH NICHT! Habe zum Test andere Programme ausprobiert, die Taster funktionieren einwandfrei. Anbei meine .asm Datei, hoffe jemand von euch schaut sich das mal kurz an. Ist sicher ein einfacher Kniff, den ich noch nicht als Anfänger kenne... Danke im Voraus...
1. Wie sind denn die Tasten an welche Pins beschaltet? 2. was genau klappt nicht? Matth
Hallo, ich hab mir deinen Code mal angesehen. Folgende Sachen sind mir aufgefallen: > ; Ausmaskieren der Taste 1 > ldi r17,0b11111110 > out DDRD,r17 > in r17,PORTD > ldi r22,0b00000001 > eor r17,r22 Die ersten beiden Befehle konfigurieren deinen Port. Ein Pin wird als Eingang konfiguriert, die anderen als Ausgang. Das ist gefährlich, weil du damit einen Kurzschluss generieren könntest. Selbst wenn du momentan nichts an den anderen sieben Pins angeschlossen hast, empfehle ich dir, nicht benötige Pins IMMER als Eingang zu schalten. Konkret riskierst du sowieso, deinen Port zu schrotten, weil du ja ZWEI Taster angeschlossen hast, und durch die Konfiguration als Ausgang würdest du bei gedrücktem Taster an D1 riskieren, dass es raucht. Das heisst, dass du alle Ports, die lesen sollen, auch so konfigurierst (und zwar immer, nicht schrittweise), und die, die du nicht belegt hast, auch als Eingang lässt (in deinem Fall also DDRD = 00000000). Nur die Ports, die wirklich Ausgang sein müssen, werden so konfiguriert. Ist einfach sicherer :-) Dein IN-Befehl ist nicht ganz richtig, du musst vom PIND-Register lesen (P_ort__IN), nicht vom Port D Register. Hierzu empfehle ich das Studium des Datenblattes, Abschnitt IO Ports. Das PIN-Register liefert die logischen Zustände direkt an den Pins. Das Lesen des PORT-Registers sagt dir nur, ob bei Eingangskonfiguration die Pull-Ups aktiv sind und bei Ausgangskonfiguration, ob High oder Low angesteuert wird. Die Exklusiv-Veroderung (also die letzten beiden Befehle) bringen dir nichts, weil erstens deine Portkonfiguration falsch ist, und zweitens, weil du ja den kompletten Port vergleichen würdest. Was würde passieren, wenn beide Taster aktiv sind? Dann würde der Vergleich in die Hose gehen.Besser wäre es denke ich, wenn du die einzelnen Bits abfragst. Das hätte den Vorteil, dass du erstens weniger Code hast (auch in Zusammenhang mit dem oben genannten) und somit wirds verständlicher, und dass du nur das prüfst, was du prüfen musst. Also eher sowas: ; Konfigurieren des Ports LDI R17,0b00000000 OUT DDRD,R17 ; Lesen des Ports IN r17,PIND ; Prüfen der Tasten SBRC R17,0 ;Wenn Bit 0 = 0, dann nächsten Befehl überspringen RJMP PKT1 SBRC R17,1 RJMP PKT2 Die Zeitschleifen sind für deine Anwendung soweit ich das sehen kann, nicht nötig, also verzichten wir mal darauf, dann wirds wieder etwas einfacher. Der Rest deines Programms ist soweit okay, denke ich. Wenn du jetzt z.B. noch anstatt PKT1, PKT2 LED_ON und LED_OFF schreibst, wirds nochmal verständlicher. Ausserdem könntest du noch folgendes machen. Du hast drei Labels. MAIN, MAINL und MAINE. Von MAIN bis MAINL schreibst du deinen Initialisierungscode, also z.B. die Portkonfiguration. Dieser ist nur einmal notwendig, also führen wir ihn auch nur einmal aus. Ab MAINL beginnt das eigentliche Programm, welches in der Schleife ausgeführt werden soll (Das L in MAINL steht für LOOP = Schleife). MAINE ist einfach das Ende der Schleife (E = END). Dein Programm springt an PKT4 und lässt nochmal eine Zeitschleife laufen, die ja eigentlich nur ausgeführt werden sollte (obwohl nicht nötig), wenn Taste 2 gedrückt ist. Daher wäre ein Sprung direkt auf RJMP MAIN geschickter. Du könntest natürlich anstatt an PKT4 zu springen auch direkt auf MAIN springen, aber das ist unübersichtlich. Besser ist es, ein Ende zu definieren, und das Ende anzuspringen. Wenn du mehr Erfahrung hast, wirst du Unterprogramme schreiben, dort bringt diese Vorgehensweise viel Übersichtlichkeit und die Fehleranfälligkeit wird vermindert, weil du das Ende nur einmal definieren musst. Somit ergibt sich also folgendes Gesamtprogramm: MAIN: ; Konfigurieren der Ports LDI R17,0b00000000 OUT DDRD,R17 LDI R17,0b00000001 OUT DDRB,R17 LDI R17,0b00000000 ;LED ausschalten OUT PORTB,R17 MAINL: ; Lesen des Ports IN R17,PIND ; Prüfen der Tasten SBRC R17,0 ;Wenn Bit 0 = 0, dann nächsten Befehl überspringen RJMP LED_ON SBRC R17,1 ;Wenn Bit 1 = 0, dann nächsten Befehl überspringen RJMP LED_OFF RJMP MAINE LED_ON: IN R17,PORTB ORI R17,0b00000001 OUT PORTB,R17 RJMP MAINE LED_OFF: IN R17,PORTB ANDI R17,0b11111110 OUT PORTB,R17 RJMP MAINE MAINE: RJMP MAIN Die Programmteile fürs Ein- und Ausschalten der LEDs habe ich so geschrieben, dass wirklich nur die LED beeinflusst wird. Warum? Das ist vielleicht jetzt noch nicht nötig für dich, aber später wirst du auch andere Sachen am Port dran haben, und hättest mit der alten Variante diese anderen Sache mit beeinflusst, und dich dann gewundert :-) Nicht vergessen, immer nur dass beeinflussen, was beeinflusst werden soll. Die Veroderung (ORI-Befehl) bei LED_ON sorgt dafür, dass nur das Bit 0 gesetzt wird, alles andere bleibt wie es ist (vorher wurden die anderen Bits immer gelöscht). Das gleiche bei LED_OFF, die Verundung (ANDI) löscht nur das Bit 0, alles andere bleibt, wie es ist. Ich weiss, es ist ziemlich viel, was ich geschrieben habe, und mancher wird vielleicht denken, dass die ganzen Erklärungen nicht nötig sind oder dass ich völligen Schwachsinn schreibe. Was ich geschrieben habe, ist z.T. schon fortgeschrittenes Programmieren, aber ich wollte dir einen Weg zeigen, der immer funktioniert und dich vor manchen unliebsamen Überraschungen fernhält. Ich betreue unsere Azubis beim Programmieren, und wenn man nicht von Anfang an einige Grundzüge anwendet, wird man es später schwer und somit keinen Spass dran haben. Ich hoffe, ich konnte dir helfen. Leider kann ich das Programm momentan nicht testen, also halte mich bitte auf dem Laufenden. Ralf
Ralf wrote: > Von MAIN bis MAINL schreibst du deinen Initialisierungscode, also z.B. > die Portkonfiguration. Dieser ist nur einmal notwendig, also führen wir > ihn auch nur einmal aus. > MAINE: > RJMP MAIN Hier sollte es, wie du auch richtigerweise in deiner exzellenten Einführung geschrieben hast, RJMP MAINL heissen. @engine Aus genau diesem Grund möchte ich auch noch einen Punkt hinzufügen: Labelnamen noch konsequenter auf Sinnhaftigkeit prüfen. Wenn ein Programmteil eine Initialisierung macht und daher nur einmal ausgeführt werden soll, dann nenn den Abschnitt auch so INIT: ; Konfigurieren der Ports LDI R17,0b00000000 OUT DDRD,R17 LDI R17,0b00000001 OUT DDRB,R17 LDI R17,0b00000000 ;LED ausschalten OUT PORTB,R17 Nennn es INIT oder CONFIG aber nicht unbedingt MAIN. Dann fällt dir auch in der Hauptschleife auf, dass da irgendwas nicht stimmen kann. MAINE: RJMP INIT sieht nun mal seltsam aus, weil eine Initialisierung nur beim Programmstart ausgeführt werden soll (drum heist sie ja auch Initialisierung) und nicht ständig bei jedem Durchlauf der Hauptschleife.
> Hier sollte es, wie du auch richtigerweise in deiner exzellenten > Einführung geschrieben hast, > RJMP MAINL > heissen. argl :-) Man kanns noch so oft korrekturlesen, einer ist immer drin :-) Sorry. Wenn ichs im AVR-Studio geschrieben und kopiert hätte, wärs mir aufgefallen. Ralf
Hallo Ralf, Hallo Karl-Heinz, ersteinmal muss und kann ich nur sagen, es gibt noch Hoffnung für diese Welt! Wenn ich so sehe, wie ihr mir bei meinem Problem geholfen habt, kann ich mich nur verbeugen und mich demütig bedanken... DANKE!!! Danke daß ihr euch Zeit genommen habt... @ Ralf: Dadurch, daß du alles sehr penibel und eingehend beschrieben hast, ist mir alles sofort einleuchtend gewesen! Eigentlich ein absoluter Anfängerfehler, PORTD anstatt PIND zu nehmen! Auch der Befehl SBRC ist ein Einfaches.. wenn man ihn kennt :) ! Was hab ich mit EOR gegrübelt, Port-Zustand sichern, ausmaskieren... manchmal ist ein tieferer Blick in die Befehlsliste doch sehr wertvoll. Also, dein Quellcode ist für mich nun verständlich und klar (warum ORI / ANDI, PORTD anstatt PIND bei Befehl IN bei den Ein-/Ausschaltroutinen der LED usw). Ich habe ihn direkt übernommen und den Vorschlag von Karl-Heinz implementiert. Ausserdem die Tastereingang-Ports angepasst (nur Bit bei SBRC Abfrage geändert). Hier nochmal der Code: INIT: ; Konfigurieren der Ports ; Port D LDI R17,0b00000000 OUT DDRD,R17 ; Port B LDI R17,0b00000001 OUT DDRB,R17 LDI R17,0b00000000 ;LED ausschalten OUT PORTB,R17 MAIN: ; Lesen des Ports IN R17,PIND ; Prüfen der Tasten SBRC R17,1 ;Wenn Bit 0 = 0, dann nächsten Befehl überspringen RJMP LED_ON SBRC R17,0 ;Wenn Bit 1 = 0, dann nächsten Befehl überspringen RJMP LED_OFF RJMP MAINE LED_ON: IN R17,PORTB ORI R17,0b00000001 OUT PORTB,R17 RJMP MAINE LED_OFF: IN R17,PORTB ANDI R17,0b11111110 OUT PORTB,R17 RJMP MAINE MAINE: RJMP MAIN .include "m8def.inc" ; Definitionen für den ATmega8 Nun zum Ergebnis, auf was ihr sicher schon wartet: Es funktioniert, aber nur solange, wie ich Taste "Einschalten" drücke! Taste "Ausschalten" bewirkt nichts, auch gleichzeitig gedrückt (da auch ausgeschlossen im Quellcode). Ich werde den Abend weiter probieren und grübeln. Vielleicht bekomme ich ja noch einen Tipp von euch oder finde etwas, poste dann auch gleich wieder! Viele Grüsse, Engin
Zusatz: Mit "es funktioniert nur solange wie ich Taster "Einschalten" drücke" meine ich, die LED leuchtet solange, wie ich die Taste "Einschalten" drücke!
Das liegt an meinem (von Karlheinz) erkannten Fehler mit dem RJMP MAIN --> RJMP MAINL!!! Weil in der Initialisierung PortB gelöscht wird, und die Initialisierung durch den Fehler immer wieder aufgerufen wird. Ralf
Korrektur, du hast es ja schon in INIT umbenannt... Moment, ich grübel mal kurz... Ralf
Hallo Ralf, ok, bin auch noch dran! Bin aber grad noch so schlau wie davor, bin ja auch Anfänger :) ...
So! Es ist vollbracht! Ich habe des Rätsels Lösung: INIT: ; Konfigurieren der Ports ; Port D LDI R17,0b00000000 OUT DDRD,R17 ; Port B LDI R17,0b00000001 OUT DDRB,R17 LDI R17,0b00000001 ;LED ausschalten OUT PORTB,R17 MAIN: ; Lesen des Ports IN R17,PIND ; Prüfen der Tasten SBRS R17,1 ;Wenn Bit 1 = 0, dann nächsten Befehl überspringen RJMP LED_ON SBRS R17,0 ;Wenn Bit 0 = 0, dann nächsten Befehl überspringen RJMP LED_OFF RJMP MAINE LED_ON: IN R17,PORTB ANDI R17,0b11111110 OUT PORTB,R17 RJMP MAINE LED_OFF: IN R17,PORTB ORI R17,0b00000001 OUT PORTB,R17 RJMP MAINE MAINE: RJMP MAIN .include "m8def.inc" ; Definitionen für den ATmega8 Es lag ganz einfach daran, daß die LED einen LOW-Pegel benötigt, um "on" zu sein! Sprich, mit dem LOW-Pegel wird eine Masse geschalten. Das war das Problem! Dadurch haben die ANDI und ORI Befehle auch nichtmehr gepasst bzw. die Initialisierung des PORT B am Anfang. Habe nun alles angepasst... Was lerne ich daraus? Erst lesen (einstudieren), dann programmieren!!! :)
engine wrote: > Es lag ganz einfach daran, daß die LED einen LOW-Pegel benötigt, um "on" > zu sein! Sprich, mit dem LOW-Pegel wird eine Masse geschalten. Das war > das Problem! Das ist sicherlich auch ein Problem. Dass aber der Zustand nicht gehalten wurde, liegt aber eher an dieser Korrektur ; Prüfen der Tasten SBRS R17,1 ;Wenn Bit 1 = 0, dann nächsten Befehl überspringen RJMP LED_ON SBRS R17,0 ;Wenn Bit 0 = 0, dann nächsten Befehl überspringen RJMP LED_OFF Du solltest hier aber den Kommentar noch anpassen. Der passt jetzt nicht mehr zu den Befehlen.
@Engine: Freut mich, dass es nun funktioniert. Wollte eh mal nachfragen, ob du die Schaltung posten kannst, dann wären wir dann auch draufgekommen. Also als kleinen Tip: Bei der nächsten Frage bitte Schaltplan dazu, oder wenigstens beschreiben, wie was angeschlossen ist, dann wirds einfacher für alle :-) Ralf
Hallo Ralf, Hallo Karl-Heinz, danke nochmal für die Tipps. Habe soweit alles angepasst und das Programmchen abgespeichert :) Hat mich jetzt in den Bann gezogen das Ganze! Ausserdem muss ichs eh für mein Studium pauken, passt ja! Jetzt bin ich dabei, mal den Lautsprecher zu testen. Will einen Ton ausgeben über den Lautsprecher, z.B. 500Hz. Werde versuchen, die Frequenz des 4MHz Oszillators (extern) mittels einem Counter runterzuteilen... was meinst ihr, bin ich richtig, oder auf dem Holzweg? Wäre für Tipps dankbar! Grüsse und weiterhin gutes Gelingen!!!
engine wrote: > mein Studium pauken, passt ja! Jetzt bin ich dabei, mal den Lautsprecher > zu testen. Will einen Ton ausgeben über den Lautsprecher, z.B. 500Hz. > Werde versuchen, die Frequenz des 4MHz Oszillators (extern) mittels > einem Counter runterzuteilen... was meinst ihr, bin ich richtig, oder > auf dem Holzweg? Wäre für Tipps dankbar! Timer ist schon ok. Das kann man so machen. Schau ins AVR-Tutorial. Im Prinzip kannst du dafür den Timer- Beitrag hernehmen. Auch der Uhren-Beitrag ist im Grunde nichts anderes.
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.