In der Initialisierungsphase wird vor dem Programmteil des Fehlers der
Interrupt freigegeben, weil in diesem Programmteil Wartezeiten
auftreten, die zwar jetzt noch Softwarewartezeiten sind, aber später auf
Hardwarewartezeiten umgestellt werden sollen. Die verfrühte Freigabe
dürfte aber keine Fehler verursachen.
Als Ergebnis wird 00Inf ausgegeben. Interrupts und andere Ausgaben
erfolgen nicht, obwohl dies so sein müßte.
Nun erstmal die wichtigen Code-Brocken:
w002us:
cli
; 7 Zyklen: ; +9 takte =16 Takte
ldi R17, $02 ; warte 6 Zyklen
WLOOP0: dec R17
brne WLOOP0
nop ; warte 1 Zyklus
sei
ret
Ist das so richtig, daß ich in den Wartezeiten den Interrupt sperre und
diese im Unterprogramm liegen?
DSinfo: rcall rssend
ret
Hier passiert noch nichts, ist nur eine Umlenkung auf evtl. andere
Ausgabegeräte.
Bei RSSEND wird in den Pufferspeicher RS232 geschrieben, das ist
einwandfrei.
sea910:
ldi r19,0 ;Busnummer=0 setzen
sensor: ldi zl,low (senstb)
ldi zh,high(senstb) ;Zeiger auf Sensortabelle
ldi r18,maxsen ;16 Werte durchsuchen
ldi r17,'0' ;Sensorzähler=0 setzen (ASCII)
sen100: ld r16,z+ ;Wert aus Tabelle lesen
cp r16,r19 ;Sensor am aktuellen Bus?
brne sen200 ;nein -> weiter
inc r17 ;sonst Sensorzähler erhöhen
sen200: dec r18 ;alle Tabellenplätze gelesen?
brne sen100 ;nein -> Schleife
cpi r17,'9'+1 ;mehr als 9 Sensoren am Bus?
brcs sen210 ;nein -> weiter
ldi r16,7 ;sonst Zahl in Buchstaben
add r17,r16 ;wandeln: 10..16 -> A..G
sen210: mov temp,r17
rcall DSinfo ;Sensoranzahl speichern
inc r19 ;nächsten Bus testen
cpi r19,maxbus ;alle Busse getestet?
brne sensor ;nein -> Schleife
; Informationen ausgeben
ldi temp,'I'
rcall DSinfo
ldi temp,'n'
rcall DSinfo
ldi temp,'f'
rcall DSinfo
ldi temp,'o'
rcall DSinfo
ldi zl,low (senstb) ; Sensortabelle ausgeben
ldi zh,high(senstb)
ldi r17,0
W21: ld temp,z+
rcall DSinfo
inc r17
cpi r17,maxsen
brne W21
ldi temp,cr
rcall DSinfo
ldi zl,low (sensid) ; ROM-ID von allen Sensoren ausgeben
ldi zh,high(sensid)
ldi r18,0
W23: ldi r17,0
W22: ld temp,z+
rcall DSinfo
inc r17
cpi r17,8
brne W22
call craus
inc r18
cpi r18,maxsen
brne W23
; --- Initialisierung Ende -----------------------
Ich habe mich hier etwas festgebissen, weil der Ablauf bis zur
Informationsausgabe vermutlich richtig ist, da die 00 dort
richtigerweise ausgegeben wird, auch das Inf wird noch richtig
ausgegeben, aber das o und die nachfolgenden Ausgaben erfolgen nicht
mehr.
Auch eine Endlosschleife kann ich hier nicht erkennen. Da im
Mainprogramm Daten ohne Interrupt ausgegeben werden, liegt die Vermutung
nahe, daß das Programm hier nicht drüber wegkommt. Doppelt vergebene
Register können gerade an dieser Stelle nicht auftreten, weil das
Ergebnis vorher beendet wurde und danach nur noch diese tlw. RAM
Ausgaben zur Information erfolgen. Ist ja nur geradlinig angehängt.
Da waren mir eben die Timer eingefallen, darum nachfolgende Infos:
; Interrupt-Vektoren für ATmega 32
;
.org 0 rjmp start ;Programm-Start
.org INT0addr reti ;External Interrupt Request 0
.org INT1addr reti ;External Interrupt Request 1
.org INT2addr reti ;External Interrupt Request 2 nicht in
ATMega 8
.org OC2addr rjmp oc2int ;Timer/Counter2 Compare Match 8ms, Funkuhr
.org OVF2addr reti ;Timer/Counter2 Overflow
.org ICP1addr reti ;Timer/Counter1 Capture Event
.org OC1Aaddr reti ;ocr1a ;Timer/Counter1 Compare Match A, frei
.org OC1Baddr reti ;ocr1b ;Timer/Counter1 Compare Match B, Warte
.org OVF1addr reti ;Timer/Counter1 Overflow
.org OC0addr rjmp oc0int ;Timer/Counter0 Compare Match, ZMD nicht
in ATMega 8
.org OVF0addr reti ;Timer/Counter0 Overflow
.org SPIaddr reti ;SPI, STC Serial Transfer Complete
.org URXCaddr rjmp UART_Rx_C ;RXC USART, Rx Complete
.org UDREaddr rjmp UART_Empty ;UDRE USART Data Register Empty
.org UTXCaddr reti ;TXC USART, Tx Complete
.org ADCCaddr reti ;ADC Conversion Complete
.org ERDYaddr reti ;EEPROM Ready
.org ACIaddr reti ;Analog Comparator
.org TWIaddr reti ;Two-wire Serial Interface
.org SPMRaddr reti ;Store Program Memory Ready
; Timer0 48 khz, CTC, Vorteiler wird wegen ausreich. Genauigkeit
nicht resettet. für ZMD Sensor
ldi temp, (1<< ctc0)|(1<<cs01) ; CTC-Modus, Vorteiler = 8
out TCCR0,temp
; Timer2, ctc, Vorteiler/256, oc=250, 8 Mhz ---- 125 OV = 1 sek.
150=1,2 sek. DCF-Uhr
ldi temp, (1<< ctc2)|(1<<cs21)|(1<<cs22); CTC-Modus, Vorteiler = 256,
resetbar
out TCCR2,temp
ldi temp, ( 250 - 1 ) ; Compare Wert setzen auf 8ms (clock/Vorteiler
*32µs)
out OCR2, temp
ldi temp, (1<<OCIE1B)|(1<<OCIE2); Compare Interrupt für Timer 1B,2
aktivieren
out TIMSK,temp
Timer1 hat noch keinen Interrupt, kann also nicht stören.
Timer2 hat einen Compare wert von 8ms, dürfte daher auch nicht in
Betracht kommen.
Timer0 hat noch keinen comparewert, wird erst in der Anwendung gesetzt.
Wie der Timer0 abläuft, würde mich jetzt interessieren, vielleicht liegt
da die Störung.
Bleibt ja fast nichts mehr übrig. Der Vorteiler wird nicht resettet.
mfg
der Stack ist in Ordnung. Sind auch noch 1300 Bytes dafür frei. Ohne das
kontrolliert zu haben, sollte das auch reichen.
mfg
hab mich eben vertan. War ein Irrtum
Wolfram Quehl wrote:
> der Stack ist in Ordnung. Sind auch noch 29 KB dafür frei.
Öhhh, wo nimmst Du beim Mega32 29 KB RAM her?
EDIT:
> hab mich eben vertan. War ein Irrtum
Aha...
BTW: Wär schön, wenn Du den Code beim nächsten Mal formatiert schicken
könntest (mit "[avrasm]"...). Die Zeilenumbrüche bei den Kommentaren
sind etwas lästig. Das nur am Rande...
in meinem Notepad ist das alles schön formatiert. Hab das nur kopiert.
Beim Kopieren nach Wordpad gibt es ähnliche Probleme, aber nur mit den
Tabs.
Wie könnte ich das überhaupt erreichen, daß der Kommentat hier nicht in
die nächste Zeile kommt. Wenn ich mir das so ansehe, kann ich nur den
Kommentar verkürzen. Soll ich also Kommentare verkürzen?
mfg
Hab ich doch oben geschrieben wie's geht:
<eckigeKlammerauf>avrasm<eckigeKlammerzu>
Dein Code
<eckigeKlammerauf>/avrasm<eckigeKlammerzu>
Steht unten bei "Formatierung"!
ldi temp, (1<<OCIE1B)|(1<<OCIE2); Compare Interrupt für Timer 1B,2 aktivieren
10
out TIMSK,temp
war nicht so einfach, weil man das Löschen der Zeilenumbrüche nicht
sieht, erst in der Vorschau.
Hab jetzt auch gesehen, daß ich den Compare von Timer 0 nicht
freigegeben habe. Daher kann der Fehler dann da auch nicht liegen.
mfg
das Editieren hat nicht funktioniert
Also was mir aufgefallen ist, das du deine Flags nicht sicherst.
Ich nehme dafür immer eines der unteren Register (R2 oder R3).
Mit den ganzen Rechen- und Vergleichsbefehlen zerschießt man sich
ruckzuck die Flags. Und dadurch zählt ein Zähler dann mal schnell
falsch. (mit Zähler meine ich keinen Hardwarecounter, sondern eine
Softwareschleife be der ein Register oder ein Registerpaar hochgezählt
wird)
Weiterhin wenn so ein Interrupt zwischen einem Vergleich (cpi, cpc, dec)
und dem Sprungbefehl landet dann geht das üblicherweise auch in die
Hose.
Hauke Sattler wrote:
> Also was mir aufgefallen ist, das du deine Flags nicht sicherst.> Ich nehme dafür immer eines der unteren Register (R2 oder R3).> Mit den ganzen Rechen- und Vergleichsbefehlen zerschießt man sich> ruckzuck die Flags. Und dadurch zählt ein Zähler dann mal schnell> falsch.
Solange es kein Interrupt-Handler ist, ist es wurscht. Nur in
Interrupt-Handlern muss man das SREG sichern (und das eigentlich auch
nur dann, wenn im Interrupt Handler Flags verändert werden), weil man
bei einem Interrupt keine Kontrolle darüber hat, wann er auftritt.
Wer allerdings zwischen eine flagrelevante Operation und die
nachfolgende Auswertung einen (r)call setzt, ist selbst Schuld...
danke, hat ich zwar schon mal daran gedacht, auch die Register zu
sichern, hab das wieder aus den Augen verloren, weil ja bei einer
Wartezeit nach Rückkehr keine Flags abgefragt werden. Und genaugenommen
ist das nur bei Interrupts notwendig. Hier aber sicherlich zweckmäßig,
weil man sonst alle Wartezeiten daraufhin kontrollieren müßte.
Erklärt aber noch nicht, warum das gerade an dieser Stelle nicht mehr
funktioniert. Dann müßte der vorhergehende Ablauf schon
durcheinandergeraten sein und rein zufällig die richtigen Ergebnisse
rauskommen. Das mag ja noch sein, aber nach dem Inf werden keine
Wartezeiten aufgerufen. Erst wieder in der Mainloop.
mfg
Ich hab mal weitergeschaut.
Dein Zählen in ASCII-Zahlen ist "sagen wir mal" gewöhnungsbedürftig.
Weiterhin ist deine Schleifengeometrie etwas verwirrend. Aber jeder hat
seinen eigenen Stil.
Was ich vermute ist, das der AVR bei der Ausgabe der Werte aus dem
Z-Pointer abschmiert. (Also erst nach dem 'o') Das 'o' ist schon im
Zwischenbuffer des USART aber worde noch nicht ausgegeben. Evt. fischt
dir dein Pointer ein "nicht-ASCII-Zeichen" raus.
Probier mal folgenden Code
1
Informationen ausgeben
2
ldi temp,'I'
3
rcall DSinfo
4
ldi temp,'n'
5
rcall DSinfo
6
ldi temp,'f'
7
rcall DSinfo
8
ldi temp,'o'
9
rcall DSinfo
10
ldi zl,low(senstb) ; Sensortabelle ausgeben
11
ldi zh,high(senstb)
12
ldi r17,0
13
W21:
14
ld temp,z
15
swap temp
16
andi temp,0b00001111
17
subi temp,-6
18
sbrc temp,3
19
subi temp,'6'-'A'
20
subi temp,6-'0'
21
rcall DSinfo
22
23
ld temp,z+
24
andi temp,0b00001111
25
subi temp,-6
26
sbrc temp,3
27
subi temp,'6'-'A'
28
subi temp,6-'0'
29
rcall DSinfo
30
31
nirvana:
32
nop
33
rjmp nirvana
Dadurch wird das Zeichen aus dem Pointer als Hexzahl ausgegeben.
Danach landet das dingen in einer Endlosschleife.
Mal schauen was dabei rauskommt.
cu
Hauke
P.S.
Die Hexzahlausgabe mag umständlich sein, aber ich vermeide gerne
Sprünge.
Weiterhin baue ich mir immer irgendwelche Marker ein, um zu schauen wie
weit er denn wirklich gekommen ist.
danke,
die Ausgabe wird sicherlich irgendeine Hexzahl zwischen 0x00 und 0xFF
sein. Wahrscheinlich 0x00 oder 0xFF.
Aber das kann nicht das Problem sein. An anderer Stelle habe ich die A/D
Wandlung mit 03FF ausgegeben. Da gab es keine Probleme.
Das Programm vor der Informationsausgabe habe ich nicht selber
programmiert, darum wollte ich ja auch mal sehen, was da rauskommt bei
diesem Programmteil.
Die Ausgabe von 00 habe ich noch dazwischen gebracht.
das mit dem o im Puffer klingt logisch. Das er das aber nicht mehr
sendet, kann eigentlich nur mit einem abgeschalteten Interrupt zu tun
haben. Oder mit einer Endlosschleife. Da müßte ich mal sehen, wie die
Pufferzeiger stehen.
Jetzt könnte ich wohl echt ein Simulationsprogramm gebrauchen. Ob mir
das jemand simulieren kann, das Gesamtprogramm würde ich dann per Email
zusenden.
Quehl ett gmx punkt de
Für Simulationen nehm ich VMLAB.
Leider wird das nicht mehr weitergepflegt.
Ist ideal um bis zu zwei AVR mitsamt Peripherie zu simulieren.
Das Dickste was ich bisher durchsimuliert habe, war eine Kombination aus
einem Mega8, einem Mega32 und diversen TWI Busbausteinen.
War ein Logger(Mega8), welcher über einen Terminalserver(Mega32)
konfiguriert und ausgelesen wurde. Das Mega32 Programm war hinterher
über 19kB groß und bestand aus fast 11.000 Zeilen Assemblercode.
Wenn du mir den Code schickst dann kann ich den ja mal durchlaufen
lassen.
cu
Hauke
P.S.
die elektrische Postadresse ist
hsattler(at)uni-potsdam.de
ich hab im Augenblick erst mal einen Verdacht.
Nachdem das o in den Pufferspeicher gekommen ist, wird dieses erst ca.
270µs später abgesandt. (Baud 38400). In der Zeit sind die nachfolgenden
Ausgaben auch in den Pufferspeicher gelangt und das Programm läuft in
der Mainloop weiter, wo der mögliche Fehler in der Warteschleife
auftritt, aber wahrscheinlich Register in den verschiedenen Aufgaben
doppelt belegt sind und daher einen Absturz verursachen.
Dazu hätte ich die Frage, wie ich die Register der einzelnen Aufgaben
sichern kann. Innerhalb der Aufgabe ist das kein Problem. Diese können
von der nächsten Aufgabe wieder benutzt werden. Aber wenn die Register
über die Mainloop erhalten bleiben müssen, dann weiß ich nicht, wie ich
das machen soll. Mit Push und Pop in jeder Aufgabe wird das wohl nicht
funktionieren.
Muß man diese Register dann vielleicht im RAM speichern? Wäre ja bei
einer Abfrage der Flags ziemlich umständlich.
Danke,
mfg
Sicher funktioniert das mit push und pop.
Man muß sich bloß vorher ansehen welche Register von der Sub-Routine
verändert werden.
Mit in Ram Speichern kannst du vergessen. Das dauert im Prinzip genauso
lang wie push und pop.
cu
bis dann
Hauke
....
Es sollten beim Einsprung in Interruptservicroutinen alle in dieser
Routine benutzten Register durch push gesichert werden und vor dem
Rücksprung mit pop restauriert werden.
Globale Variablen gehören ins sRAM.
Bei lokalen Variablen welche nur in Registern aufbewahrt werden ist es
wichtig diese auf dem Stack zu sichern, wenn z.B. innerhalb einer
Routine eine weitere Routine aufgerufen wird, welche die selben Register
benutzt. Nach der Rückkehr aus dem Unterprogramm sind diese zu lokalen
Variablen zu restaurieren.
noch ein paar Tips
Ein Speicherplan erleichtert den Überblick über globale Variablen
ebenso hilft es beim pushen und poppen den Stackpointer im Auge zu
behalten. D.h er muss beim Verlassen einer Routine den gleichen Stand
aufweisen wie beim Eintritt in dieselbe, und es wird in umgekehrter
Reihenfolge gepoppt, in welcher gepusht wurde.
Ausnahmen bilden Parameterübergaben via Stack. Hier ist die übernehmende
Routine für die Bereinigung des Stacks verantwortlich. In solchen Fällen
empfiehlt sich die Anzahl der übergeben Bytes als ersten Parameter zu
übergeben.
In jedem Fall sollte man sich ein eineheitliches Protokoll für
Variablenübergaben und Registernutzung zu eigen machen. Dies erleichtert
die Übersicht über den Programmablauf bei strukturierten ASM-Programmen
erheblich und wirkt der Verspaghettierung entgegen, welche bei ASM
bedeutend grafierendere Knoten und Nestys (bis zu Abstürzen)bedingt als
z.B. in einem unstrukturierten Basicprogramm.
danke,
die Behandlung bei Interrupts war sowieso klar. Bei Unterprogrammen
hatte ich das zunächst nicht gepusht, hab dann aber gemerkt, daß es die
Programmierung vereinfacht. Mach ich also auch. Muß noch nachsehen, ob
ich das konsequent gemacht habe. Da rutscht dann schon leicht mal was
durch.
Worum es hier ging, waren die verschieden Aufgaben, verschiedene
Sensorenbearbeitung in einem Chip, die hier im Forum jeweils als
getrenntes Programm in unterschiedl. Chips behandelt werden. Diese
Aufgaben sind unabhängig und habe ich auch unabhängig programmiert. Auch
wenn jede Aufgabe für sich unabhängig funktioniert, so ist das in einem
Chip nicht unbedingt der Fall. Die Aufgaben laufen nacheinander in einer
Loop ab. Dabei müssen Variablen tlw. bis zum nächsten Durchgang
aufbewahrt werden. Z.B. Flags, wo abgefragt wird, ob ein Interrupt
fertig ist.
Ich habe schon öfter überlegt, vor und nach jeder Aufgabe die benutzten
Register zu pushen und poppen. Dies wird aber nur bei 2 Aufgaben
funktionieren, wobei eine ungepusht bleibt. Bei mehr Aufgaben wird das
wohl nicht mehr gehen.
Ich dachte, ich hätte über die Register Überblick, aber der Überblick
bezieht sich nur jeweils auf eine Aufgabe. Jetzt werde ich erst mal eine
Register Übersicht machen und die globalen Register von den lokalen
Registern unterscheiden. Ist "globale" Register überhaupt das richtige
Wort? Global heißt doch, daß verschiedene Aufgaben die gleichen
Variablen verwenden. Das ist hier aber nicht der Fall. Die Variablen
werden von der gleichen Aufgabe verwendet, müssen aber bis zur nächsten
Runde gerettet werden.
mfg
Wolfram Quehl wrote:
> Ist "globale" Register überhaupt das richtige> Wort? Global heißt doch, daß verschiedene Aufgaben die gleichen> Variablen verwenden.
Ich verstehe unter "globale Variable" Variablen, deren Inhalt einem
bestimmten Zweck zugeordnet sind und auf die von verschiedenen Routinen
aus zugegriffen werden kann.
> Das ist hier aber nicht der Fall. Die Variablen> werden von der gleichen Aufgabe verwendet, müssen aber bis zur nächsten> Runde gerettet werden.
Dann sind es (für mich) lokale "statische Variable", sie dienen einem
speziellen Zweck, werden aber nur von einer Routine verwendet.
Im Gegenzug dazu gibt es lokale temporäre Variable, deren Wert bis zur
"nächsten Runde" verloren geht. Diese werden nur in Registern gehalten.
Bei Registermangel (der bei Deinem sehr großen Projekt ja besteht) hält
man nur die Variablen in Registern, auf die sehr häufig und zeitkritisch
zugegriffen wird, alles Andere hält man im RAM. Da man im RAM nicht
rechnen und operieren kann, müssen die Werte bei Bearbeitung in Register
kopiert werden.
Globale Variable im RAM: Register (falls nötig) freimachen (push), Wert
in Register laden, verarbeiten, geänderten Wert ins RAM zurück, Register
wiederherstellen (pop).
Statische lokale Variable im RAM: Register freimachen, Wert in Register
laden, verarbeiten, bei Änderung ins RAM zurück, Register
wiederherstellen.
Lokale Variablen (temporär): Register freimachen (push), verarbeiten,
Register wiederherstellen (pop).
>> mfg
...
ich hab jetzt folgendes System rausgefunden:
Interrupts: verwendete Register vorrangig nach R2 und R3, sonst Push und
pop.
Unterroutinen, die eine Funktion enthalten, push und pop, weil diese von
verschiedenen Stellen aufgerufen werden können.
Unterroutinen, die nur zur Programmverkürzung erstellt wurden, benötigen
keine Sicherung, da diese in einer Programmfunktion zusammengefaßt sind.
Aufgaben, deren Register am Ende nicht mehr benötigt werden, bekommen
ein Push-Pop.
Register, die am Ende nicht mehr benötigt werden und in allen Aufgaben
diese Funktion erfüllen, benötigen kein Push-Pop.
Aufgaben, deren Register am Ende weiter benötigt werden, kommen ins RAM.
das sind 6 verschiedene Möglichkeiten und nicht nur global und lokal.
Jetzt noch eine Frage zur Sourcenreihenfolge:
Bisher habe ich die folgende Reihenfolge verwendet:
Definitionen (.def und .equ)
Datensegment
Programmsegment
Interrupts
Flashprogrammierungszähler
Initialisierung
Mainloop
Aufgabe1
Aufgabe2
u.s.w.
Interruptprogramme
Funktionale Unterprogramme
Ich neige jetzt dazu, folgende Änderungen vorzunehmen.
Mainloop
Aufgabe1
Definitionen
Hauptprogramm
zugehörige Unterprogramme
Interruptprogramm zu Aufgabe1
Aufgabe2
Definitionen
Hauptprogramm
zugehörige Unterprogramme
Interruptprogramm zu Aufgabe
u.s.w.
Funktionale Unterprogramme, die von mehreren Aufgaben verwendet
werden
kleiner Nachteil wäre hier, daß von einer Aufgabe zur nächsten mit RJMP
gesprungen werden muß, was jetzt nicht der Fall ist. Der Vorteil wäre,
daß fast alles, was zu einer Aufgabe gehört, zusammensteht. Man hat eine
bessere Übersicht, was eine Aufgabe betrifft. Ob man auch die
Initialisierung in die Aufgabe selbst bringen kann, weiß ich noch nicht.
Zumindest Register und Ports wären denkbar. Möglicherweise auch die
Peripherie. Es wäre dann eine zusätzliche Programmierung erforderlich.
Wie sind jetzt Eure Meinungen dazu?
mfg