so hab jetzt mal zeit gehabt um ein programm zu machen
nur weiß ich nicht ob man das so macht oder ob es noch ein viel viel
elegantere lösung gibt.
mag das hier mal jemmand kritisieren ?? :)
es "shifted" einfach über 2 ports
;------------------------------------------------
; timer0.asm
; Timer 0
;------------------------------------------------
.include "4433def.inc"
.def temp = r16
.def counter = r17
.def leds = r18
.def leds2= r19
.def null = r20
.def a = r21
.def b = r22
.equ start = 256 - 244 ; bei 8 MHz entspricht das ca. 1/32 sec
.equ loops = 1;
rjmp reset ; Reset Handler
reti ; IRQ0 Handler
reti ; IRQ1 Handler
reti ; Timer1 Capture Handler
reti ; Timer1 compare Handler
reti ; Timer1 Overflow Handler
rjmp timer0 ; Timer0 Overflow Handler
reti ; SPI Transfer Complete Handler
reti ; UART RX Complete Handler : RXCIE
reti ; UDR Empty Handler
reti ; UART TX Complete Handler
reti ; ADC Conversion Complete Interrupt Handler
reti ; EEPROM Ready Handler
reti ; Analog Comparator Handler
reset:
ldi temp, RAMEND
out SP, temp ; set stack pointer
ldi temp, 0xFF
out DDRB, temp ; PORTB configured for output
out DDRD, temp ; PORTB configured for output
ldi temp, 0xFF
ldi temp, 1<<TOIE0 ; 0000 0010
out TIMSK, temp ; timer 0 interrupt ein
ldi temp, start
out TCNT0, temp ; Startwert des Timers
ldi temp, 0b00000101
out TCCR0, temp ; ; Timer starten mit Vorteiler 1024
ldi counter, loops
ldi leds, 0x01
ldi leds2,0x00
ldi null,0x00
out PORTD, leds
sei ; interrupts generell aktivieren
main:
loop:
rjmp loop
;--------------------------------------------------
timer0:
dec counter
brne restart
ldi counter, loops
cp leds,null
breq shiftleds2
rol leds
out PORTD, leds
ldi leds2,0x01
;adc leds2,leds
rjmp noshiftleds
shiftleds2:
cp leds2,null
out PORTB, leds2
breq shiftleds1new:
rol leds2
;adc leds,leds2
rjmp noshiftleds
shiftleds1new:
ldi leds,0x01
ldi leds2,0x00
out PORTD,leds
out PORTB,leds2
noshiftleds:
restart:
ldi temp, start
out TCNT0, temp ;
reti
Hallo Hannes,
meine Diagnose: typischer Fall von Spaghetti-Code.
So muesste es auch gehen:
Grüße
Oliver
timer0:
dec counter
brne restart
ldi counter, loops
; Beide Register nach links schieben, ;ausgeschobenes Bit von
leds an led2
; haengen.
lsl led2
lsl leds
adc leds2,null
; Nur wenn beide Register leer sind, Register neu laden
tst leds
brne restart
tst leds2
brne restart
ldi leds,0x01
ldi leds2,0x00
restart:
out PORTD,leds
out PORTB,leds2
ldi temp, start
out TCNT0, temp
reti
supa danke ... ist nicht nur übersichtlicher sondern funktioniert auch besser bei der alten version war der übergang von portd auf portb "abgehackt" lag das an den unterschiedlichen taktzyklen ?? bei den verzweigungen ??? und noch was jetzt hab ich ändlich die "mächtigkeit" von adc verstanden man kann so ganz leicht 16 bit zahlen addieren : add a1,b1 adc a2,b2 wie ich das jetzt gemacht hab sag ich lieber nicht lol :) add a1,b1 brcc nocarry inc a2 nocarry add a2,b2 musste sein !! nicht nachmachen !!! :) hmm vielleicht sollt ich doch lieber mit nem simplem tutorial anfangen damit ich eine andere denkweise bekommen ... und nicht alles vom datenblatt raushohlen und selbst sachen erfinden weil ich somanches übersehe :)
Was ich noch vergessen habe... In einer Interruptroutine MUSS IMMER das Statusregister gesichert werden. interrupt_xyz: in rSREG_BAK, SREG ... out SREG, rSREG_BAK reti rSREG_BAK ist irgend ein freies Register. In Deinem Fall ist das nicht so dramatisch, da Du in der Hauptschleife nichts machst. Aber wenn zum Beispiel ein Interrupt zwischen einem cp-Befehl und einer bedingten Abzweigung auftritt und das Statusregister in der Interruptroutine geändert wurde, dann ist der weitere Programmablauf undefiniert. Oliver
kleine Ergänzung: im Prinzip hast du recht, es gibt aber auch Int-Programme, die keine Flags verändern, dann ist es also entbehrlich. Aber dann sollte man schon genau die Auswirkungen der einzelnen Befehle kennen, für den Anfänger gilt also: SREG immer sichern. Und noch was @Hannes: Das Sicherungsregister (heißt bei mir auch sreg_bak :-)) kann für alle Interruptprogramme zusammen genutzt werden, es ei denn, man gibt in einer ISR andere Ints frei.
Ist es nicht genauso gut das SREG auf den Stack zu pushen und anschließend wieder zu holen? Oder führt das auch zu Komplikationen? Gruß Robert
PUSH & POP sind nur dann notwendig wenn die internen Register nicht mehr ausreichen, ansonsten kosten diese beiden Befehle nur zusätzliche 2 Worte Programmspeicher und verlängern die Routine um 4 Taktzyklen.
thx @ all dann gleich noch dazu ne frage gibt es prioritäten bei den interrupts ? also wenn ich in einem hardware interrupt bin und dann ein timer interrupt aufgerufen wird ... weil dann funktioniert dann braucht man ja verschiedene Sicherungsregister... oder funktiniert das mit den interrupts anders als ch denke ??
Die Interrupt Prioritäten kannst du den Datenblättern zu den einzelnen AVR Prozessoren entnehmen. Eine weitere Interrupt-Service-Routine innerhalb einer Interrupt Routine wird nur ausgeführt, wenn weitere Interrupts mit dem Befehl SEI freigegeben sind. Ansonsten erfolgt die Freigabe automatisch mittels RETI am Ende der Routine.
Ein AVR kann immer nur einen Interrupt ausführen. Nur beim 8051 kannst Du z.B. 4 verschiedene Prioritäten einstellen. Dann kann ein Interrupt höherer Priorität einen gerade laufenden niedrigeren unterbrechen. Beim AVR kann man mit viel Tricks innerhalb eines Interrupts andere Interrupts freigeben. Das ist aber nichts für Anfänger, da es zu leicht passieren kann, daß sich Interrupts immer wieder selber unterbrechen, bis der Stack überläuft. Ich lasse auch die Finger davon und versuche immer, die Interrupts so kurz wie nur irgend möglich zu halten (< 100 Zyklen). Bei komplexeren Programmen (> 8 kByte), mit vielen Interrupts gleichzeitig, nehme ich dann aber den 8051 und programmiere in C. Peter
Hallo Peter, >Ein AVR kann immer nur einen Interrupt ausführen. >Beim AVR kann man mit viel Tricks innerhalb eines Interrupts andere Interrupts freigeben. Der "Trick" besteht aber nur im Setzen des Interruptfreigabeflags mittels "sei" in der Interruptroutine. Beim AVR ist das mit den Prioritäten etwas anders gelöst: Beim gleichzeitigen Auftreten mehrerer Interrupts gewinnt der Interrupt, der die höhere Prioritaet hat. Welche Prioritaet der gerade laufende Prozeß hat, ist von keiner Bedeutung. Die Prioritätsvergabe ist beim AVR von oben nach unten. In der Sprungtabelle (Listing von Hannes) steht schon alles. Höchste Priorität hat Reset, dann kommt Int0 und niedrigste Priorität hat folglich der Analogkomparator. Grüße Oliver
Hallo Oliver, das "SEI" ist nur scheinbar einfach. Wie gesagt, wenn man da nicht höllisch aufpaßt kann es leicht zu der genannten Kettenreaktion kommen. Einfach mal im RXC Interrupt ganz vorne SEI ausführen und Dein Programm schmiert sofort beim 1. Byte ab. D.h. da sind noch ne Menge anderer Tricks nötig, wie Sichern des Status aller anderen Interrupts, neue Freigabe und Sperrung der Interrupts entsprechend der gewünschten Priorität. Und das Ganze wieder haargenau rückwärts am Ende des Interrupts. Je mehr Interrupts, umso unübersichtlicher und fehleranfälliger wird es. Das, was Atmel fälschlich als "Priorität" bezeichnet, hat damit aber überhaupt nichts zu tun. Beim 8051 wird das richtig als "Polling Sequence" bezeichnet. D.h. es ist damit nur die "Abfragereihenfolge" gemeint, wenn z.B. am Ende eines Interrupts schon mehrere andere Interrupts auf die Behandlung warten oder zufällig 2 Interrupts genau zur selben Zeit auftreten. Peter
Hallo Peter, Deine Bedenken zu den Interrupts sind kann ich voll verstehen. Grundsätzlich sollte man nie Interrupts in Interrupts freigeben. Die daraus resultierenden möglichen Seiteneffekte sind kaum nachvollzielbar und können einem schon mal einen odere mehrere Knoten in den Gehirnwindungen erzeugen. Grüße Oliver
und wenn man sauber programmiert, d.h. die Interuptprogramm-Laufzeiten kurz hält (d.h. dort nur das nötigste tut und die Daten dann im Hauptprogramm verarbeitet), braucht man auch keine weitere Unterbrechung innerhalb der ISR. Ein Beispiel dazu: ein Timerinterrupt steuert eine 8-stellige Multiplexanzeige an. Das kann man so tun, daß bei jedem Int die jeweilige Stelle aus einer long-Variablen berechnet wird (schlecht) oder die Display-Daten fix und fertig d.h. im Siebensegmentcode in einem Array bereitstehen und nur ausgegeben werden. Die 2.Variante braucht nur einen Bruchteil der Zeit.
Das man keine Interrupt-Freigabe innerhalb von Interrupt-Routinen benötigt halte ich für ein Gerücht. Es kommt immer auf die jeweilige Anwendung an. Um fehlerfreien Code zu schreiben muss man sich halt etwas mit dem Datenblatt auseinandersetzen. Bei der Programmierung spielt natürlich auch der Zeitbedarf für einzelne Programmteile eine wichtige Rolle.
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.