Forum: Mikrocontroller und Digitale Elektronik ist das gut so ?


von hannes (Gast)


Lesenswert?

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

von Oliver K. (Gast)


Lesenswert?

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

von hannes (Gast)


Lesenswert?

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 :)

von Oliver K. (Gast)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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.

von Robert Budde (Gast)


Lesenswert?

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

von mikki merten (Gast)


Lesenswert?

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.

von hannes (Gast)


Lesenswert?

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

von mikki merten (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Oliver K. (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Oliver K. (Gast)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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.

von mikki merten (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.