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.