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.
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.