Zählervergleich geht schief Im Unterforum Microcontroller - Beschreibung: Hardware - Software - Ideen - Projekte
Autor |
Zählervergleich geht schief |
|
|
|
|
BID = 446864
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
|
Hallo Leute, vor ca 2 Jahren habe ich ne Ansteuerung für einen modifizierten (darum soll es jetzt nicht gehen) Bildschirm gebaut, und war der Meinung dass es erfolgreich war, schließlich funktionierte es wie gewünscht, bis der Bildschirm seinen Geist aufgab.
Nachdem ich die Schaltung nun mit einem anderen Bildschirm in Betrieb nehmen wollte stellte ich nach einigem Messen fest, dass ich im Programm erstens zwei Pins vertauscht habe (bereits korrigiert und getestet) und ein Pin nicht macht was er soll.
Und bei Letzterem komm ich einfach nicht weiter. Nachdem ich durch meine spärliche Kommentierung durchgestiegen bin, das Programm nun wieder verstanden , und ordentlich auskommentiert habe nun also meine Frage zu diesem Code-Abschnitt:
Code : |
;-------Warteschleife-----------------------------------------
;die Warteschleife beendet wenn nötig den Vsync-Impuls
;und leitet bei entsprechendem Zählerstand die anstehende
;Aussteuerungskombination ein
Wait:
ldi temp2H, high(5) ;-
ldi temp2L, low(5) ;-
cpse countL, temp2L ;-
rjmp Weiter ;-
cpse countH, temp2H ;5 Zeilen um?
rjmp Weiter ;Nein
cbi PinA, 0 ;Ja!-> Vsync aus
Weiter:
ldi temp2H, high(480) ;-
ldi temp2L, low(480) ;-
cpse countH, temp2H ;-
rjmp loop ;-
cpse countL, temp2L ;"Bild" fertig?
rjmp loop ;Nein-> auf HSync-Interrupt warten gehen
clr countH ;Ja!-> Zeilen-Zähler zurückstellen (high)
clr countL ;Zeilen-Zähler zurückstellen (low)
ret ;Zurück in die Hauptroutine, und nächste Kombination ausgeben
loop:
rjmp loop ;warten auf den Interrupt |
|
Die erste Zähler-Abfrage scheint nie "true" auszugeben, denn Vsync (PinA0) wird nie "low"
Die zweite Zählerabfrage ("Bild fertig") funktioniert aber, denn der Rest des Programmes läuft tadellos ab.
Das kapier ich nicht, schließlich sind die Abfragen (bis auf die Zahlen) identisch.
Findet jemand den Bock?
Der Controller ist ein tiny26 und die Taktfrequenz beträgt 2MHz.
Das komplette Programm ist im Anhang
_________________
Es irrt der Mensch solang er strebt
[ Diese Nachricht wurde geändert von: Morgoth am 30 Jul 2007 21:02 ] |
|
BID = 446869
Jornbyte Moderator
Beiträge: 7151
|
|
Ich finde einfach kein "inc countL /countH" in der kurzen Schleife.
Für das gesammte Proggi fehlt mir heute der Nerv...
_________________
mfg Jornbyte
Es handelt sich bei dem Tipp nicht um eine Rechtsverbindliche Auskunft und
wer Tippfehler findet, kann sie behalten. |
|
BID = 446871
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
Klar ist da kein "inc", denn gezählt wird woanders, hier wird nur verglichen...
Das Zählen findet in diesem Programmabschnitt statt:
Code : |
;-------Horizontal-Interrupt----------------------------------
;der Horizontal Interrupt betätigt die H-syncronisation und
;setzt den Zähler für die Warteschleife hoch und entrümpelt
;den Stack
HInt:
ldi temp, 0x03 ;
out TCNT1, temp ;Interrupt-Counter korrigieren/"zurücksetzen"
cbi PortA, 1 ;Hsync aus
ldi temp2H, 0 ;
ldi temp2L, 1 ;
add countL, temp2L ;Zeilen-Zähler eins hochsetzen (low)
adc countH, temp2H ;Carry-Flag weitertragen sofern vorhanden
sbi PortA, 1 ;Hsync wieder an
pop r10 ;Stack entrümpeln (unerwünschte Sprungmarke abtragen)
pop r10 ;Stack entrümpeln (unerwünschte Sprungmarke abtragen)
sei ;Interrupt wieder einschalten
rjmp Wait ;In die "Warteschleife", Zähler auslesen |
|
Zugegeben, man kann auch einfacher zählen...
_________________
Es irrt der Mensch solang er strebt
|
BID = 446872
Jornbyte Moderator
Beiträge: 7151
|
Nun ja, beim Interrupt biste mutig. Ab pop r10 (was im ersten Durchlauf ja noch gehen mag) versaust du dir die Register.
Überdenke das nochmal.
_________________
mfg Jornbyte
Es handelt sich bei dem Tipp nicht um eine Rechtsverbindliche Auskunft und
wer Tippfehler findet, kann sie behalten.
|
BID = 446874
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
Das ist Absicht, da sind Sprungmarken gespeichert, die ich nicht haben will, und zwar jedesmal 2 Stück (eine davon ist vom Interrupt selbst). Wenn ich die nicht runterschaufele (R10 ist da quasi mein Mülleimer), dann geht das "ret" in dem anderen Abschnitt nicht dorthin wo es soll, und meine Routine bricht zusammen.
Wie gesagt alle andern Pins machen was sie sollen, und 5 davon könnten das niemals machen, ohne dass das Programm an sich funktioniert.
_________________
Es irrt der Mensch solang er strebt
|
BID = 446878
Jornbyte Moderator
Beiträge: 7151
|
Das Programm, so kurz es auch sein mag, ist im Desing fehlerbehaftet.
Mach einen sauberen Int mit reti. Lasse das mal durch den Debugger laufen. Dauert seine Zeit, mit Haltepunkten gehtst aber schneller.
_________________
mfg Jornbyte
Es handelt sich bei dem Tipp nicht um eine Rechtsverbindliche Auskunft und
wer Tippfehler findet, kann sie behalten.
|
BID = 446887
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
Ja, das kann sein...
Werde mir das morgen mal vorknöpfen.
Dann muss ich eben für die Bildschirmausgaben auch noch einen Zähler bauen, damit er richtig landet.
Ich muss halt aufpassen, dass ich mit den "lebenswichtigen" Dingen immer unter 64 Taktzyklen bleibe.
PS: das war mein zweites Assembler-Programm...
Danke für die Tipps!
_________________
Es irrt der Mensch solang er strebt
|
BID = 446898
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Moin,
Ja, die Sachen sind mir auch aufgefallen:
* während einer ISR kann zwar eine IRQ-Flagge gesetzt werden, aber normalerweise keine erneute ISR aufgerufen werden; es sei denn in der ISR wird die I-Flagge gesetzt (z.b. mit sei).
Das reti setzt die Interruptflagge wieder und somit können erneut ISRs ausgeführt werden.
-> Beende die ISR ordentlich mit reti und entferne cli und sei wieder. Dazu muss natürlich das Programm abgeändert werden.
* Wenn man sich entscheidet, IRQs zuzulassen (also irgendwo die I-Flagge setzt), dann sollte man alle Interruptvektoren auch am Start (angefangen bei 0x0000 PC) auflisten, und jene, die nicht benötigt werden einfach mit reti füllen. Das kostet zwar ein bischen Platzt, aber das Progamm wird nicht unkontrollierbar, wenn, aus welchen Gründen auch immer, ein IRQ auftritt und an den entsprechende Interruptvektor gehoppst wird.
* (Schönheit und Verständnis: besser, man vermeidet 0b100 (also Dualzahlen) gänzlich, man kann nämlich, da sie definiert wurden, auch direkt die Bitbezeichner wie PORTB3, TWIE oder RXEN benutzen, indem man an die entsprechende Bitposition eine 1 oder 0 schiebt (wobei 0 nur dann Sinn macht, um explizit mitzuteilen, dass ein Bit 0 sein soll, normalerweise sind alle Bits 0 wenn sie nicht auf 1 gesetzt werden...... ihr wisst, was ich nicht meine ...):
ldi r16, 1<<RXEN
Somit weiß man sofort, was gemeint ist )
Sonst habe ich auch wenig Lust, das komplette Programm zu verstehen , aber Respekt nochmal von mir, für das zweite ASM-Programm ist das schon sehr sehr gut .
(vorallem, weil du heute weiter bist, und alles, was ich oben geschrieben habe, selbst weißt ...)
_________________
|
BID = 446969
BjörnB Stammposter
Beiträge: 242 Wohnort: Dortmund
|
Hallo,
mir ist aufgefallen, dass Du von der Routine VRoutine aus die Routine Wait mit rcall aufrufst (Rücksprungadresse wird auf den Stack gelegt), von der Routine HInt aus aber zur Routine Wait mit rjmp springst (Rücksprungadresse wird nicht auf den Stack gelegt). Da Wait mit ret abschließt, erfolgt im letzten Fall ein Rücksprung an eine unklare Speicherstelle.
Ich würde dir empfehlen, Funktionsaufrufe grundsätzlich mit rcall/call zu machen und nicht manuell den Stack zu verändern, außer, um Register durch push/pop zu sichern. Bei letzterem immer darauf achten, dass push und pop zahlenmäßig ausgeglichen sind und am Anfang und Ende einer Routine spiegelbildlich sind. Bei Routineaufrufen bitte auch nicht vergessen, das Statusregister mit zu sichern.
Wie mein Vorredner schon sagte, wird das ganze noch sauberer, wenn man auch bei Leer-Interrupts grundsätzlich ein reti an die entsprechenden Speicherstelle am Programmanfang setzt.
Zwar mag Dein Programm in diesem speziellen Fall funktionieren, doch sobald man vielleicht einige Erweiterungen einfügt, kann manches schief gehen, wenn das ursprüngliche Programm unsauberen Code enthält. In sich abgeschlossene Routinen mit klarer Definition lassen sich darüber hinaus dann auch direkt in neuen Programmen weiterverwenden, so dass man nicht jedes Mal das Rad neu zu erfinden braucht.
Schöne Grüße,
Björn
|
BID = 447184
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
Danke für die zahlreiche Tipp, ich werde sie versuchen alle zu beherzigen (wobei ich keinen Grund sehe, nen Port nicht im "Klartext" umzustellen, wenn das erstens übersichtlicher ist, und zweitens schneller geht, weil ja mehrere Pins umgestellt werden)
Um euch mal ein paar Illusionen zu nehmen: Nach meinem zweiten Programm habe ich zwar noch einige µCs programmiert, aber aus den Umständen der letzten 2 Jahre heraus nicht mit selbst-geschriebenen Programmen... Erst im Moment komme ich wieder dazu Sachen selbst zu entwickeln.
_________________
Es irrt der Mensch solang er strebt
|
BID = 447194
BjörnB Stammposter
Beiträge: 242 Wohnort: Dortmund
|
Hallo,
ich verwende sowohl Dualzahlen als auch Bitbezeichner, denn mal ist das Eine, mal das Andere praktischer. Beim Ansprechen externer Ports (z.B. 8 LEDs) ist die Verwendung von Dualzahlen vorteilhaft. Wenn ich Register (z.B. der USART) beschreibe, greife ich eher zu der von DonComi dargestellten Shiftoperation und verwende die in der Include-Datei des Mikrocontrollers definierten Bitbezeichner. So ist auf den ersten Blick ersichtlich, welche Optionen gesetzt wurden z.B. UCSRB = (1<<RXCIE)|(1<<RXEN) anstatt UCSRB = 0b10010000.
Ich wünsche noch viel Erfolg mit Deinem Projekt.
Schönen Gruß,
Björn
|
BID = 447349
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Moin,
wenn ich, egal ob C oder Assembler, externe Pins zuweisen möchte, dann so:
irgendein Header:
#define LED_NETZ PORTB4
#define LED_STATUS PORTB7
/
.equ LED_NETZ = PORTB4
.equ LED_STATUS = PORTB7
...
im Programm:
PORTB |= 1<<LED_NETZ; //LED geht an
PORTB &=~(1<<LED_NETZ); //LED geht aus, alle anderen Bits bleiben, wie sie waren
PORTB |= 1<<LED_NETZ|1<<LED_STATUS; //Beide LEDn anschalten
...
bzw. in AVR-ASM:
in wr, PORTB
ori wr, 1<<LED_NETZ|1<<LED_STATUS ;beie LEDn anschalten
out PORTB, wr
oder, wenns es eine LED oder allgemein was anderes ist:
sbi PORTB, LED_NETZ / cbi ...
Um das alles noch "universeller" zu machen, kann man auch noch den PORT definieren und fortan nur noch den Aliasnamen benutzen. Das macht bspw. Sinn bei LCD-Routinen, wo die Belegung ändert und man am Kode selbst nichts mehr ändern braucht.
OK, aber manchmal mache ich auch Klartext, z.b., wenn ein kompletter Port Ausgang ist:
ldi wr, 0xFF
out DDRx, wr
Da mache ich dann eben ohne den Shiftoperator, weil das viel zu lang wäre
_________________
|
BID = 447850
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
So, habe das Programm mal umgebaut, ist jetzt alles ein bissel mehr gerade aus, und kein hin-und-her-Gehopse mehr.
Mit reti hab ich jetzt trotzdem nicht gearbeitet, weil das hier nicht nötig ist, da der Interrupt ja nicht die Ausnahme, sondern die Regel ist.
Leider kann ich es nicht simulieren weil "simulavr" keinen tiny26 simulieren kann.
Also werde ich es wohl morgen einfach brennen und ausprobieren müssen...
Ich glaube aber fast nicht, dass sich was an meinem Problem ändert, denn an der entscheidenden Stelle hat sich eigentlich nix getan.
_________________
Es irrt der Mensch solang er strebt
|
BID = 447963
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
So, mit dem angehängten Code ist das Zählerproblem endlich erledigt. Allerdings gibt er jetzt wohl die Bilder zu schnell aus,
Jedenfalls kommen an den Farbpins und den Audiopins, wo normal bisher 12Hz rauskamen, und rauskommen sollten, jetzt 30Hz raus.
Immerhin fühlt sich der Monitor jetzt schonmal angesprochen, und das Programm ist übersichtlicher geworden.
edit: macht euch mal vorerst keine Mühe.
Mir ist gerade ein Denkfehler bewusst geworden...
Das Puls-Pausen-Verhältnis von Vsync ist so groß, dass ich die Frequenz nicht messen kann... aber auf meinem Oszi für arme sieht das stark nach deutlich mehr als 60Hz aus, daher nehme ich stark an, dass die auch viel zu hoch ist... folglich ist da doch noch ein kleiner Bug im Zähler
_________________
Es irrt der Mensch solang er strebt
[ Diese Nachricht wurde geändert von: Morgoth am 4 Aug 2007 16:56 ]
|
BID = 447981
Morgoth Schreibmaschine
Beiträge: 2930 Wohnort: Rockenhausen (Pfalz)
|
Es waren die "inc"s,
Ich war der Annahme unterlegen,dass diese das Carry-Flag setzen können...
Nachdem ich nun das Z-Flag auslese funktioniert alles tadellos.
Bis auf den Monitor, der meint er müsste auch ohne Signal trotzdem schonmal alle Farben anzeigen. Da muss ich wohl Intern noch die Helligkeit runterregeln. (das liegt an dem umgebauten Monitor, ein anderer macht das nicht)
_________________
Es irrt der Mensch solang er strebt
|
|
Zum Ersatzteileshop
Bezeichnungen von Produkten, Abbildungen und Logos , die in diesem Forum oder im Shop verwendet werden, sind Eigentum des entsprechenden Herstellers oder Besitzers. Diese dienen lediglich zur Identifikation!
Impressum
Datenschutz
Copyright © Baldur Brock Fernsehtechnik und Versand Ersatzteile in Heilbronn Deutschland
gerechnet auf die letzten 30 Tage haben wir 20 Beiträge im Durchschnitt pro Tag heute wurden bisher 4 Beiträge verfasst © x sparkkelsputz Besucher : 182137450 Heute : 335 Gestern : 5489 Online : 504 7.10.2024 3:24 2 Besucher in den letzten 60 Sekunden alle 30.00 Sekunden ein neuer Besucher ---- logout ----viewtopic ---- logout ----
|
xcvb
ycvb
0.0702128410339
|