Assembler für Assembler - Anfänger (bin selber noch einer)

Rolf.H

Neues Mitglied
17. Juni 2012
136
0
0
89
25451 Quickborn
Sprachen
Hallo Leute,
dieser Code ist nicht von mir erstellt, sondern aus Büchern
von mir überarbeitet.
So lerne ich im einzelnen die Befehle kennen.

Code:
;Projekt: Projekt01                  18.08.2012

;Datei: Test2.asm (aus dem Buch S. 58)

.INCLUDE "tn2313def.inc"

.def acc0    =r16
.def acc1    =r17
.def stavec  =r22

rjmp RESET        ; Reset Handler
	
; Initialize
RESET:
	
; set PortB(7) to output with value 1
	ldi acc0,(1<<PB7)
	out DDRB, acc0    ;PB7 = Output
 ldi acc0,0xFF
 out PORTB,acc0    ;Pullup PB0-PB7
 ;LED an PB7=1 zu Beginn EIN 

 ldi stavec,0  ;r22=0

; mainprogram
main:
	sbic PINB,PB0     ;überspringe,wenn PORT,PB0=0
	rjmp not_pressed
	
	sbrc stavec,0     ;überspringe,wenn Register r22=0
	rjmp main

	; toggle PORTB(7)
	in acc0, PORTB    ;kopie PB0-PB7=1 nach r16
	ldi acc1, 0x80     ;r17 = 0b1000 0000
	eor acc0, acc1
	;Oder-Verknüpf. r16 mit r17 (r16=FF, r17=08)
 ;Ergebnis=0x7F, Resultat=1 wenn ungleich
 ;Inhalt vom 2 Operanden r17 bleibt erhalten 
 out PORTB, acc0 ;wenn acc0=0 LED=AUS

	; store old button state
	sbr stavec,0x01 ;Maske=0b0000 0001
 ;setze das Bit in r22 was in der Maske 1 ist
	rjmp main

not_pressed:
	; store old button state
	cbr stavec,0x01 ;Maske=0b0000 0001   
	;lösche das Bit in r22 was in der Maske 1 ist
 rjmp main

der Befehl "eor" hat mir erst mal Kopfschmerzen bereitet.
 
Hi Rolf,

der Befehl "eor" hat mir erst mal Kopfschmerzen bereitet.

findest du alles im PDF "AVR Instruction-Set":

EOR – Exclusive OR
Description:
Performs the logical EOR between the contents of register Rd and register Rr and places the result in the destination register Rd.

Was "Exclusiv OR" oder auch XOR oder ExOR genau macht kannst du auch bei Wikipedia nachlesen ...
XOR-Gatter
"XOR-Gatter (von engl. eXclusive OR, exklusives Oder, „entweder oder“)"

Im Endeffekt macht der Befehl eine logische Verknüpfung von zwei Registerinhalten. Und zwar geht das Bit für Bit. Also Bit0 mit Bit0, Bit1 mit Bit1, usw.

Wenn man eines der Register mit 11111111 lädt (also 255dec oder FFhex) dann kann man zB die Bits des anderen Registers invertieren. Der Befehl wird auch gerne für einfache Verschlüsselungen verwendet.

Man kann mit dem Befehl entweder zwei Register bitweise auf Gleichheit testen oder mit einer bestimmten Vorgabe in einem Register den Inhalt des anderen Registers bitweise invertieren.

Gruß
Dino
 
Hi
Soweit ich mich erinnern kann, sind die Befehle auch in "keine Angst vor Assembler" Beitrag in der Rubrik FAQ erklärt. Ist schon ein Weilchen her, aber ich mein, ich hab dazu auch die Wahrheitstabellen geliefert.
Gruß oldmax
 
Hallo oldmax,

hab es mal rausgesucht, es war am 19.08.2010.
Sehr schön aufgelistet.
Ja die liebe Zeit, man hat einfach zu viel um die Ohren, aber ich bin ein
Frühaufsteher und bin da schon zugange.
Mit den Registern r16 aufwärts hat man es täglich zu tun, dagegen Stackpointer
oder Statusregister sind oft ein rotes Tuch.
Will mich jetzt nur mit den Tiny2313 beschäftigen, da er Timer0 und 1 hat.
Ein Märklin-Schwenkkran läuft hier noch mit Mega8 in Bascom geschrieben.
Das Projekt werde ich auf den Tiny in Assembler umschreiben.
Noch dieses Jahr will ich die Miniaturausstellung in Hamburg besuchen, um
evtl. neue Anregungen zu bekommen. Ich glaube, die Leute dort
werden die AVRs massenweise eingesetzt haben.

Grüße

Rolf
 
Hi
Noch dieses Jahr will ich die Miniaturausstellung in Hamburg besuchen, um
evtl. neue Anregungen zu bekommen. Ich glaube, die Leute dort
werden die AVRs massenweise eingesetzt haben.
Unbedingt. Es ist wirklich sehenswert. Geh früh hin, da ist noch nicht allzuviel los.
Gruß oldmax
 
...Mit den Registern r16 aufwärts hat man es täglich zu tun, dagegen Stackpointer
oder Statusregister sind oft ein rotes Tuch...
Mit dem SREG hast Du's bei jeder bedingten Verzweigung zu tun. zu mindest indirekt. Und Bei Verrechnungen mit größeren Zahlen (2byte, 3byte...), auch hier indirekt (C-Flag). Aber i.a. mußt Du seltenst direkt auf das SREG zugreifen - nur eben verstehen und ausnutzen, daß einige Befehle Informationen im SREG ablegen, und andere Befehle darauf reagieren. Selbst für die globale Freigabe/Ünterdrückung von IRQs mußt Du nicht (selbst) direkt aufs SREG zugreifen, sondern läßt das SEI/CLI für Dich erledigen.
Auch das mit dm SP siehst Du vielleicht nur etwas zu kompliziert. Er enthält einfach nur eine Zahl. Diese wird von einigen Befehlen (automatisch) als Adresse in den SRAM verwendet. Verwendet man PUSH zum ablegen eines Bytes in den SRAM, wird hinterher der der SP dekrementiert. Bei POP wird er vorher inkrementiert. Der Stack wächst also "von hinten nach vorn". Folglich sollte er möglichst weit hinten im SRAM liegen. Deswegen wird er üblicherweise mit RAMEND initialisiert - bei den neueren AVR sogar automatisch. Du kannst den Anfang aber auch irgendwo anders hinsetzen, um zB ganz hinten irgendwas anderes abzulegen - Du bist der Boss über Dein Programm.
Will mich jetzt nur mit den Tiny2313 beschäftigen, da er Timer0 und 1 hat...
Sehr schöner Tiny. Außer den beiden Timern (je mit 2 PWM, einer mit ICR) hat er die Tiny-übliche USI, UND einen richtigen Hardware-UART. Mit 2K Flash und 128 bytes SRAM und EEPROM kannst Du in ASM auch schon 'ne Menge machen. Lediglich einen ADC hab ich vermisst. Und es gibt eine SOIC-20 Variante, mit dem, noch sehr schön freihand zu verlötendem Rastermaß 1,27mm.
 
Tiny2313 / Tiny4313

Hallo,

Will mich jetzt nur mit den Tiny2313 beschäftigen, da er Timer0 und 1 hat...
Sehr schöner Tiny. Außer den beiden Timern (je mit 2 PWM, einer mit ICR) hat er die Tiny-übliche USI, UND einen richtigen Hardware-UART. Mit 2K Flash und 128 bytes SRAM und EEPROM kannst Du in ASM auch schon 'ne Menge machen. Lediglich einen ADC hab ich vermisst. Und es gibt eine SOIC-20 Variante, mit dem, noch sehr schön freihand zu verlötendem Rastermaß 1,27mm.

ich hab letztens festgestellt das es bei Pollin die DIL-Version des Tiny4313 gibt. Also die 4k-Version des Tiny2313. Ich hab mir mal 3 Stück für den Notfall mitbestellt.

Bei Bascom ist der Tiny2313 schon recht eng. Bei Assembler sind 2k Flash jedoch schon ne Menge Luft zum spielen. Der Tiny4313 ist bei Bascom dann schon in einer Region wo man auch schon etwas mehr mit anfangen kann. Ich würde mal grob sagen das bei Bascom mit UART oder LCD 2k Flash schon recht dünn werden. Mit UART und LCD sind 4k nach meiner Meinung das Minimum um was sinnvolles zu machen. Wenn man beides wegläßt sind 2k aber OK.

Gruß
Dino
 
Tiny2313-PU

Hallo LotadaC und oldmax,

nun wird es Zeit, daß ich mich mal wieder melde.

Ich war nicht untätig, denn es stand schon lange in der Warteschleife,
einen 20 Jahre alten Märklinkran (der jeden Tag mit Bascom läuft)
auf Assembler zum laufen zu bringen.
Das Teil hat zwei Motoren (Auf/Ab und Rechts/Links)
Endlagenschalter Rechts/Links
Lichtschranke beim Abfahren zur Last.

Der Tiny2313 kommt in Frage und habe mich nochmal mit den Timer0
vertraut gemacht.

Hier ein einfacher Quellcode:

Code:
; Projekt-Name: Projekt01                      Datum: 24.09.2012									

; Datei: Zeit01.asm              

; PORTB,PB7 = Output über rote LED
; PIND,PD5  = Input über Taste SW3
; Programmablauf:
; betätige sw3 / LED=Ein / Zeitablauf über ISR / LED=Aus
; AVR: Tiny2313-20PU

  .INCLUDE   "tn2313def.inc"   ; Deklaration für Tiny2313

  rjmp    reset              ; Reseteinsprung
  .ORG    OVF0addr           ; Interrupt-Vektor
  rjmp    TIMER0_OVF         ; Sprung zur ISR

  .def    accu=r16

   reset:     

  ldi     accu,0xFF          ; 0b1111.1111 
  out     DDRB,accu          ; Datenricht. PB0-PB7=Output
  ldi     accu,0x00          ; alle Bits=Low                 
  out     DDRD,accu          ; Datenricht. PD0-PD6=Input
  ldi     r16,0b01111111
  out     PORTD,accu         ; PD0 bis PD6=PULLUP

; Timer0 initialisieren:
  ldi     accu,(1<<CS02)|(1<<CS00) ; Prescale = 1024 
  out     TCCR0B,r16
  ldi     accu,(1<<TOIE0) ; Timer Overflow Interrupt einrichten
  out     TIMSK,accu
  sei                   ; Timer frei

   loop:
         
   read1:
  sbic    PIND,PD5      ; überspringe, wenn PD5=LOW
  rjmp    read1
  sbi     PORTB,PB7     ; LED=EIN
  
; Zeit = 0,833yS x 1024 x 255 = 0,22mS x 6 = 1,32 Sec.    
  ldi     r17,0x06      ; 0b0000.0110 / dez.=6
   pause1:
  tst     r17           ; teste r17 auf LOW
; wenn r17=0   C-Bit (Bit 0) im Register SREG gesetzt,
; d.h. Ende der Schleife   
  brne    pause1        
  cbi     PORTB,PB7     ; LED=AUS       

  rjmp    loop

; Interrupt-ISR

TIMER0_OVF:
  push    r2            ;Kopie r2 auf den Stack, danach SP-1
  in      r2,SREG       ;Inhalt vom Statusregister in r2 laden
  dec     r17           ;ISR Abarbeitung (dec=r17-1)
  out     SREG,r2       ;Inhalt von r2 ins SREG laden
  pop     r2            ;SP+1, danach vom Stack in r2 laden
; I-Bit wird im Statusregister (SREG) durch reti wieder gesetzt.
; D.h. Freigabe Interrupt  
  reti
  .EXIT

merkwürdig...wenn ich tst r17 lösche, kommt die Led nach drücken
von sw3 zum leuchten, aber die Zeit hängt sich irgendwie auf.
D.h. sie geht nach ca. 2 Sec. nicht aus.
Also muß ich wohl den Befehl immer mit einbauen.

Grüße

Rolf
 
Hi Rolf
Ich weiß, das meine Art Assembler zu schreiben, etwas aufwändiger ist, aber ich bin der Meinung, das es auch besser lesbar und pflegbar ist. Hier mal ein Beispiel für ein Programm ab Reset (Adresse 0)
Code:
Reset: RJMP  Start    ; Überspringen der Interrupt Vector Table

   ;-------   IVT   --------

Start:     LDI	r16 ,high(RAMEND)		; Stack Pointer auf  RAMEND
	OUT	SPH, Reg_A		; definiert in m8def.inc 
	LDI	r16, low(RAMEND)		; Den Stack definier ich immer, auch wenn es nicht 
	OUT	SPL, Reg_A                        ; erforderlich ist  
; Dann folgen Aufrufe für die Init_Routinen IO, Timer und was sonst noch so gebraucht wird
             RCALL     INIT_Timer                        ; initialisieren Timer, damit ein 1mSek. Takt verfügbar wird
             RCALL     INIT_IO                            ; Ports initialisieren
             RCALL     INIT_USART                      ; serielle Schnittstelle 
             RCALL     INIT_Sonstiges                  ; Defaultwerte setzen
Loop:
             RCALL     Read_IO             ; Die Eingänge werden angepasst auf betätigt ="1"
             RCALL     Debounce           ; Entprellen
             RCALL     Set_IO_Event      ; Flanken  setzen
             RCALL     Read_USART       ; Dateneingänge prüfen
             RCALL     Chk_Time_Flags   ; Bits, die in der Timer-ISR gesetzt werden, bearbeiten   
             RCALL     CHk_Flags           ; Flankenbits auswerten und bearbeiten
             RCALL     Weitere_Aufgaben ; Weitere Aufrufe aus der Hauptschleife
             RCALL     Send_Data          ; falls serielle Daten vorliegen
             RCALL     Set_Output         ; Ausgänge setzen
             RJMP      Loop

Auch wenn du nun sagst, soll ja erst mal ein test sein... was spricht dagegen
Routinen, die du nicht brauchst, schreibst du einfach
Code:
Weitere_Aufgaben:

RET
Und nun noch einen Tip für den kleinen Assemblierer..
Ich hab vor Urzeiten mal ein Programm Namens "Open_Eye" hier veröffentlicht. Es dient dazu, die Variablenwerte im Controller auf dem PC sichtbar zu machen. So kannst du direkt sehen, welche Bits gesetzt und welche Werte eine Variable hat. Dazu brauchst du aber zwingend die serielle Kommunikation. Bei dem ATiny bin ich mir jetzt nicht sicher, ob der eine UART hat, aber es gibt auch Software-UARTS.
Und noch ein Hinweis:
Da in der Regel Eingänge gegen GND geschaltet werden, macht es Sinn, in der Einleseroutine die Signalpegel zu drehen. Erstens geht's im Kopf immer "Taster gedrückt =1" und zweitens ist die darauf basierende Logik einfacher. Wenn du die Eingänge in einer Variablen sammelst und dann mit dem Befehl COM drehst, hast du das erwünschte Ergebnis. Legst du dann trotzdem deine Taster gegen VCC, dann brauchst du den Befehl COM nur auskommentieren. Aber alles ist in "Keine Angst vor Assembler" beschrieben....
OK, jetzt muß ich hier erst mal abbrechen.
Gruß oldmax
 
Wirklich geholfen haste ihm damit aber auch nicht...
("Was spricht gegen..." - "unnötige Hopserei" - aber darum gehts hier jetzt auch nicht...)

Zu Deiner eigentlichen Frage:
Wenn Du das TST rausnimmst, wird an dieser Stelle kein "Vergleich" durchgeführt, das SREG nicht manipuliert, und insbesondere auch das Z-Flag (Da steht in Deinen Kommentaren fälschlicherweise das C-Flag) nicht gesetzt. (sofern ich das restliche Programm richtig verstanden habe, ist das Z aber vorher/bisher noch im InitialValue, also =0).
Somit ist also die Sprungbedingung für das folgende BRNE erfüllt, und Du hast 'ne Endlosschleife fabriziert (da das SREG ja auch in der ISR ordentlich wiederhergestellt wird).
P.S.:
... Bei dem ATiny bin ich mir jetzt nicht sicher, ob der eine UART hat, aber es gibt auch Software-UARTS...
...Sehr schöner Tiny. Außer den beiden Timern (je mit 2 PWM, einer mit ICR) hat er die Tiny-übliche USI, UND einen richtigen Hardware-UART. Mit 2K Flash und 128 bytes SRAM und EEPROM kannst Du in ASM auch schon 'ne Menge machen. Lediglich einen ADC hab ich vermisst. Und es gibt eine SOIC-20 Variante, mit dem, noch sehr schön freihand zu verlötendem Rastermaß 1,27mm.

Edit: noch ein paar Anmerkungen:
-sowohl das Warten auf den Taster, als auch das Abwarten der "LED-an-Zeit" sind reine Warteschleifen (wenn auch Du das LED-warten über einen Timer verkompliziert hast). Besser wäre, den taster in der Hauptschleife zu pollen, und bei erkannter Betätigung (*) die Led zu zünden und R17 reinitialisieren und sonst in der Hauptschleife weiterzumachen (also keine Warteschleife). Zum löschen der LED verfährst Du genau so, nur daß hier eben der Zustand von R17 gepollt wird. Ist die Bedingung nicht erfüllt, wird auch hier in der Hauptschleife weitergemacht.
(*) so könnte man jetzt aber durch wiederholtes betätigen die Leuchtzeit verlängern - wenn die leuchtende LED den Taster "blockieren" soll, muß man hier auf R17 prüfen.
-Du dekrementierst in der ISR R17, und prüfst in der Hauptschleife, ob R17=0. Das ist insofern gefährlich, als daß Du im allgemeinen(**) bei sowas nicht immer sicher sein kannst, daß bis zum Vergleich nicht bereits ein weiteres dekrement stattgefunden haben kann - insbesondere auch noch mit einem Überlauf (also würde nicht mal compare (CP/CPI) greifen). Das kann man einfach umgehen, indem man weitere decrements unterbindet, indem man einfach nach dem decrement R17 mit 0 lädt, diese Instruktion aber überspringen läßt, wenn kein Überlauf erfolgte. Dummerweise beeinflußt dec nicht das C-Flag - also entweder mit SUBI "dekrementieren", oder eine ähnliche Lösung an das Z-Flag binden.
(**) in Deinem konkreten Fall geschieht dies aber nicht, weil in Deiner Hauptschleife dann nur auf Dieses Ereignis gewartet wird. Aber auch ohne Warteschleifen wäre die Hauptschleife noch (!) relativ kurz, UND aufgrund des Prescalers zählt der Timer relativ langsam.
 
Hallo LotadaC und oldmax,
danke für Eure Antworten!

Das von Lotadac finde ich sehr interessant...werde das morgen
mal geistisch versuchen zu verarbeiten.
Hatte mir im Hinterkopf schon immer Gedanken gemacht
"wie läuft denn der Ablauf in der ISR (=0,22Sec. nach jeden Overflow) mit der
Hauptroutiene ab?"

Der Code für den Kran ist fertig, soll ich ihn reinstellen?

Grüße

Rolf
 
jeder hat so seinen Programmierstiel!

ich habe mir zu oldmax Kommentar nochmal meine Gedanken gemacht
und möchte dazu nochmal Stellung nehmen.
Achtung habe ich vor solchen Leuten, die bereit sind, ihren Geistesschmalz
in ein Forum zu tragen um Anderen evtl. Anregungen zu geben.
Dazu gehört besonders für Anfänger der Quelltext mit möglichst vielen ;Kommentaren.
Als Leser von vielen Threads im mikrocontroller.net stelle ich fest, daß jeder so seinen
Programm-Stiel hat, an den er sich gewöhnt hat.
Ich kenne einen sehr intell. Typ, dessen Stiel ich hier mal zeigen will...soll keine Kritik sein.

Code:
*/
.include"m8def.inc"     ;Portnamen bekannt machen

.equ clock=8000000      ;Taktfrequenz (8MHz intern)
.equ srate=8000         ;Samplerate für Soundausgabe(8kHz)
.equ sfreq=50           ;Servo-Frequenz (Wiederholung der Impulse, 50Hz)
.equ smitte=128         ;Servoposition Mittelstellung 8-Bit (0..255)
.equ sfaktor=40         ;Servo-Skalierfaktor
.equ blink1=25          ;Zählumfang Blinker 1
.equ blink2=23          ;Zählumfang Blinker 2
.equ bing0=27           ;Zählumfang Glockenschlag-Teiler
.equ stromdauer=20      ;Dauer der Stromeinschaltung (in Blinker1-Schritten)
.equ savezeit=75        ;Tastendauer Save in 20ms-Schritten (1,5s)
.equ repeat0=50         ;Startzeit Tasten-Repeat
.equ repeat1=10         ;Wiederholzeit Tasten-Repeat
.equ startstate=11      ;Startwert für Zyklus

.equ tap=pinb           ;Eingang Tastenport
.equ tpr=pb1            ;DCC-Programmier-Taste
.equ tmo=pb2            ;Taste "Parameter-Mode/Save"
.equ tpl=pb4            ;Taste "Parameter plus"
.equ tmi=pb5            ;Taste "Parameter minus"
.equ tru=pb6            ;Taste/Steuereingang "runter"
.equ tho=pb7            ;Taste/Steuereingang "hoch"
.equ tasten=0b11110110  ;Bitmaske der benutzten Tasten
.equ tpmmsk=0b00110000  ;Bitmaske der Plus/Minus-Tasten (für Repeat)
.equ tpamsk=0b00110100  ;Bitmaske der Parametriertasten
.equ tsa=3              ;Merker für langen Tastendruck der Mode/Save-Taste

.equ blipo=portd        ;Port für Blinker
.equ roli=pd3           ;Bit für Dauer-Rotlicht
.equ bli1=pd4           ;Bit Blinker 1
.equ bli2=pd5           ;Bit Blinker 2

.equ servoport=portd    ;Port für Servos
.equ servo1=pd6         ;Bit für Servo 1
.equ servo2=pd7         ;Bit für Servo 2
.equ servostrom=pd2     ;Bit für Servostrom (L-aktiv, PNP-Transistor)

.equ darid=0b11111111   ;Datenrichtung PortD (alles Ausgang)
.equ initd=0b00000100   ;Startbitmuster PortD (alles aus, Servostrom ist inv.)
.equ darib=0b00001000   ;Datenrichtung PortB (alles Eingang)
.equ initb=0b11110111   ;alle PullUps ein

.equ t2ein=(1<<wgm21)|(1<<wgm20)|(1<<com21)|1   ;Fast-PWM mit Vorteiler 1:1

.equ soffset=12000-128*sfaktor  ;minimale Servoposition 16-bittig (1,0ms)
.equ sri=clock/srate    ;Samplerate als Intervall (125µs)
.equ tvt0=srate/sfreq   ;Startwert Servo-Telegrammstart-Vorteiler (20ms)

.dseg       ;Variablen im SRAM:
ssoll1:     .byte 1     ;Sollwert Servo 1
ssoll2:     .byte 1     ;Sollwert Servo 2
sist1:      .byte 1     ;Istwert Servo1
sist2:      .byte 1     ;Istwert Servo2
bliz1:      .byte 1     ;Blinkzähler 1
bliz2:      .byte 1     ;Blinkzähler 2
bingz:      .byte 1     ;Bingzähler (Glockenschlag)
stromto:    .byte 1     ;Timeout für Servo-Strom
aser:       .byte 1     ;aktuelle Servo-Nummer
status:     .byte 1     ;Status der Taktkette
paramode:   .byte 1     ;Modus der Parametrierung
repeat:     .byte 1     ;Tastenwiederholzähler (für Plus/Minus-Tasten)
lang:       .byte 1     ;Tastendauerzähler (für Mode/Save-Taste)
abfall:     .byte 1     ;Abfallverzögerung Runter-Steuereingang
stempo:     .byte 1     ;Vorladewert Schrankentempo

            ;Variablen in Registern:
.def null=r2            ;immer 0
.def srsk=r3            ;SREG-Sicherungskopie
.def tz0=r4             ;Tasten-Prellzähler Bit0
.def tz1=r5             ;Tasten-Prellzähler Bit1
.def tas=r6             ;entprellter Tastenstatus
.def telvt=r7           ;Telegramm-(Zeit-)Vorteiler 
.def nlz=r8             ;Nachlaufzähler (Schrankentempo)

.def merker=r19         ;einige Boolsche Variablen
    .equ blinkf1=0          ;Freigabe, Blinker 1
    .equ blinkf2=1          ;Freigabe, Blinker 2
    .equ gongf=2            ;Freigabe, Bimmel
    .equ run=3              ;Status, mindestens eine Schranke läuft noch
    .equ vorb=4             ;Status, neuer Vorblinktakt

.def tfl=r21            ;Tastenflags
.def it1=r22            ;Temp 1 im Interrupt
.def it2=r23            ;Temp 2 im Interrupt
.def wl=r24             ;working low (temp)
.def wh=r25             ;working high (temp)



.cseg
.org 0              ;Reset- und Interrupt-Vektoren AT-Mega 8
 rjmp RESET         ;Reset Handler
 rjmp nix;EXT_INT0      ;IRQ0 Handler
 rjmp nix;EXT_INT1      ;IRQ1 Handler
 rjmp nix;TIM2_COMP     ;Timer2 Compare Handler
 rjmp nix;TIM2_OVF      ;Timer2 Overflow Handler
 rjmp nix;TIM1_CAPT     ;Timer1 Capture Handler
 rjmp TIM1_COMPA    ;Timer1 CompareA Handler
 rjmp TIM1_COMPB    ;Timer1 CompareB Handler
 rjmp nix;TIM1_OVF      ;Timer1 Overflow Handler
 rjmp nix;TIM0_OVF      ;Timer0 Overflow Handler
 rjmp nix;SPI_STC       ;SPI Transfer Complete Handler
 rjmp nix;USART_RXC     ;USART RX Complete Handler
 rjmp nix;USART_UDRE    ;UDR Empty Handler
 rjmp nix;USART_TXC     ;USART TX Complete Handler
 rjmp nix;ADCC          ;ADC Conversion Complete Handler
 rjmp nix;EE_RDY        ;EEPROM Ready Handler
 rjmp nix;ANA_COMP      ;Analog Comparator Handler
 rjmp nix;TWSI          ;Two-wire Serial Interface Handler
 rjmp nix;SPM_RDY       ;Store Program Memory Ready Handler
nix:                    ;unerlaubte Interrupts
 rjmp nix                   ;Falle für Programmfehler...
;
reset:
 ldi wl, high(RAMEND)       ;Stackpointer
 out SPH,wl                 ;initialisieren
 ldi wl, low(RAMEND)
 out SPL,wl
 clr null                   ;immer 0

 ldi zl,low(e_sodata*2)     ;Z-Pointer hinter letztes
 ldi zh,high(e_sodata*2)    ;Sound-Byte platzieren

 ldi wl,initd               ;Port D auf

D.h. für mich, wenn ich das begreifen soll, muß ich hier erst mal alles löschen,
was für die eigentliche Aufgabe garnicht benötigt wird.

So, und was mir an meinen Stiel eigentlich nicht gefällt, es aber trotzdem mache
erkläre ich nach dem Frühstück.

Bis dahin Grüße

Rolf
 
Hi
Na ja, auch nicht schlecht...
Code:
Handler_nix:                      ; unerlaubte Interrupt
RJMP   Handler_Nix             ; Falle für Programfehler
An dieser Stelle hät ich evtl. eine Information verfügbar gemacht und dann mit einem RETI den Interrupt verlassen. So wird doch gar nicht klar, wo der Fehler liegt und warum der µC plötzlich im Nirwana schwimmt....
Aber so ist das mit dem Programierstil...
Gruß oldmax
 
so, jetzt gehts weiter mit meinen Stiel, der mir eigentlich nicht gefällt
und den ich mir seit August angewöhnt habe.

Code:
   read1:
  sbic    PIND,PD5      ; überspringe, wenn PD5=LOW
  rjmp    read1
  sbi     PORTB,PB7     ; LED=EIN
  
   
  ldi     r17,0x06      ; 0b0000.0110 / dez.=6
   pause1:
  tst     r17           
  brne    pause1        
  cbi     PORTB,PB7     ; LED=AUS

vorher hatte ich mit den Befehlscode um 11 Spalten nach rechts angefangen
was dann so aussah:
Code:
read1:    sbic    PIND,PD5   ; überspringe, wenn PD5=LOW
             rjmp    read1
             sbi     PORTB,PB7  ; LED=EIN
  
   
               ldi     r17,0x06   ; 0b0000.0110 / dez.=6
pause1:   tst     r17           
              brne    pause1        
              cbi     PORTB,PB7  ; LED=AUS [CODE]   

Ich setze gern Kommentare, denn wenn ich was schreibe, behalte ich es besser.
Und immer wieder wurde besonders der Kommentar beim Drucken auseinander gestückelt.
Nachdem ich den Befehl ganz links rücke ist das nicht mehr der Fall. 

Grüße

Rolf
 
-Du dekrementierst in der ISR R17, und prüfst in der Hauptschleife, ob R17=0. Das ist insofern gefährlich, als daß Du im allgemeinen(**) bei sowas nicht immer sicher sein kannst, daß bis zum Vergleich nicht bereits ein weiteres dekrement stattgefunden haben kann - insbesondere auch noch mit einem Überlauf (also würde nicht mal compare (CP/CPI) greifen). Das kann man einfach umgehen, indem man weitere decrements unterbindet, indem man einfach nach dem decrement R17 mit 0 lädt, diese Instruktion aber überspringen läßt, wenn kein Überlauf erfolgte. Dummerweise beeinflußt dec nicht das C-Flag - also entweder mit SUBI "dekrementieren", oder eine ähnliche Lösung an das Z-Flag binden.
(**) in Deinem konkreten Fall geschieht dies aber nicht, weil in Deiner Hauptschleife dann nur auf Dieses Ereignis gewartet wird. Aber auch ohne Warteschleifen wäre die Hauptschleife noch (!) relativ kurz, UND aufgrund des Prescalers zählt der Timer relativ langsam.

Oh weia....das muß ich versuchen im Quellcode einzufügen!
 
Hallo LotadaC,

was hälst Du davon?

Code:
; Interrupt-ISR

TIMER0_OVF:
  push    r2            ;Kopie r2 auf den Stack, danach SP-1
  in      r2,SREG       ;Inhalt vom Statusregister in r2 laden
  dec     r17           ;ISR Abarbeitung (dec=r17-1)
  cpi     r17,0         ;vergleiche Inhalt r17 mit Konstanten=0
  brne    warte1        ;Verzweigung nach warte1, wenn Z=0
  ldi     r17,0
  warte1:
  out     SREG,r2       ;Inhalt von r2 ins SREG laden
  pop     r2            ;SP+1, danach vom Stack in r2 laden
; I-Bit wird im Statusregister (SREG) durch reti wieder gesetzt.
; D.h. Freigabe Interrupt  
  reti
  .EXIT

Der Code läuft ohne Errors!

Nur mit PUSH u. POP will ich mich nochmal beschäftigen.

Grüße

Rolf
 
Hallo LotadaC,

was hälst Du davon?

Code:
; Interrupt-ISR

TIMER0_OVF:
  push    r2            ;Kopie r2 auf den Stack, danach SP-1
  in      r2,SREG       ;Inhalt vom Statusregister in r2 laden
  dec     r17           ;ISR Abarbeitung (dec=r17-1)
  cpi     r17,0         ;vergleiche Inhalt r17 mit Konstanten=0
  brne    warte1        ;Verzweigung nach warte1, wenn Z=0
  ldi     r17,0
  warte1:
  out     SREG,r2       ;Inhalt von r2 ins SREG laden
  pop     r2            ;SP+1, danach vom Stack in r2 laden
; I-Bit wird im Statusregister (SREG) durch reti wieder gesetzt.
; D.h. Freigabe Interrupt  
  reti
  .EXIT

Der Code läuft ohne Errors!

Nur mit PUSH u. POP will ich mich nochmal beschäftigen.

Grüße

Rolf
cpi ist überflüssig, denn dec setzt auch schon das Z-Flag.
ldi ist auch überflüssig, denn r17 ist dort schon 0.
Dann sieht man, daß der Sprung brne nichts mehr überspringt, also auch überflüssig ist.

Bleibt die Frage, ob Du den Rest so wirklich wolltest?
 
Nein.
Das Problem (daß der Timer durch 0 "überläuft") verursacht bei Dir kein Problem, da Du direkt nach dem Tastendruck R17 neu belädst, und dann auf R17=0 wartest. Deswegen, und weil der Timer langsam läuft, kannst Du dieses Ereignis nicht verpassen (aber eigentlich willst Du da ja auch noch von der Warteschleife weg). Wenn danach der Timer durch 0 "überläuft" spielt das für Dich (wegen dem ersten Satz) keine Rolle mehr (und das tut er auch - selbst jetzt noch).
Du hast jetzt folgendes:
decrementiere R17 (DEC... beeinflußt S,N,V,Z - 1 Takt)
prüfe, ob R17=0 (CPI... beeinflußt H,S,V,N,Z,C - 1 Takt)
wenn Z=0 überspringe den reload (BRNE... springt bei Z=0 - 1 bzw 2 Takte)

Also nur wenn (nach dem dec) R17=0 ist, lädst Du 0 nach R17. Beim nächsten TOV erfolgt dann also ein Decrement auf 255 usw.
Du willst aber R17 bei 0 festnageln. Also gdw R17 nach dem dec 255 ist. Das würde hier der Sprungbefehl BRCS/BRLO liefern,wenn CPI mit 255 erfolgt wäre, ABER Du kannst ja bereits die Manipulation des SREG durch das dekrementieren selbst ausnutzen. Leider hat dec keinen Einfluß auf C. Wenn Du statt dec "SUBI R17, 1" schreibst, dekrementierst Du letztendlich auch, allerdings wird hier das C manipuliert. Du könntest also direkt hinter SUBI mit BRCC/BRSH den reload überspringen. (SUBI braucht auch nur einen Takt - genau wie DEC)
 
Dummerweise beeinflußt dec nicht das C-Flag - also entweder mit SUBI "dekrementieren", oder eine ähnliche Lösung an das Z-Flag binden.

mh...hier habe ich mich erst mal fobben lassen.
Es beeinflußt ja doch das C-Flag, habe nochmal nachgeschaut.

Im Momment blicke ich garnicht mehr durch.
Glaube, ich lass alles so, denn der Brawa-Containerkran läuft und läuft, warum soll es bei den
Märklin-Kran nicht auch so sein.

Grüße

Rolf
 
Nein, DEC (Decrement) beeinflußt (unter anderem) das Z-Flag, aber nicht das C-Flag. Da Du aber auf das Z (da stand nur im Kommentar fälschlicherweise C) geprüft hast, hättest Du das folgende CPI (Compare with immediate) nicht gebraucht.
Allerdings hast Du damit nur "wenn R17=0 dann setze R17=0" programmiert, was keinen Sinn macht, aber auch keine Probleme/Fehler verursacht.
Das der, durch 0 überlaufende Timer in Deinem konkreten Fall keine Probleme macht, hab ich bereits mehrfach geschrieben. Ich wollte anfangs nur darauf hinweisen, daß man bei solchen Sachen (Timer zählt im Hintergrund rauf/runter - irgendwo/wann Abfrage auf einen bestimmten Wert) ganz gern vergißt/übersieht, daß der Timer im Hintergrund weiterläuft, ggf sogar überläuft. Mittel dagegen sind, daß man nicht auf Gleichheit, sondern auf "größergleich"/"kleinergleich" prüft (greift nicht beim Überlauf), oder den Zähler eben dort "stoppt", also nach dem inc/dec auf den ersten "ungültigen" Wert wieder mit dem "gestoppten" Wert lädt.

Ich habe das Gefühl, daß bei Dir der Knoten mit der "quasi-parallelen" Abarbeitung mehrerer Fragestellungen/Probleme/Aufgaben in einem Hauptschleifendurchlauf einfach noch nicht geplatzt ist. Hatte ja damals zu 'ner ähnlichen Geschichte Bilder malen lassen - ist hier ähnlich.
Du gehst das im allgemeinen in etwa so an (erstes Bild):
-Du hast eine Hauptschleife, in der ggf mehrere Aufgaben zu erledigen sind (wann bestimmen Ereignisse "von außen" - Taster, Timerinterrupts etc)
-Du wartest, bis die Bedingung für Aufgabe 1 erfüllt ist, und arbeitest diese dann ab
-danach wartest Du, bis die Bedingung für Aufgabe 2 erfüllt ist, und arbeitest...
-...
-Du wartest, bis die Bedingung für Aufgabe n erfüllt ist, ...
-zurück zu erstens

Dabei blockiert jede Warteschleife das Programm - wenn zB einige Aufgaben unabhängig voneinander sind, könnte vielleicht schon dann eine andere Aufgabe erledigt werden, während Du noch auf eine Aufgabe wartest. Das könnte man in etwa so realisieren (2tes Bild):
-Hauptschleife, Aufgaben, Beeinflussung der Bedingungen von außen wie oben
-Du prüfst, ob die Bedingung für Aufgabe 1 erfüllt ist, und arbeitest sie dementsprechend ab, oder nicht.
-Du prüfst, ob die Bedingung für Aufgabe 2 ...
-...
-Du prüfst, ob die Bedingung für Aufgabe n...
-zurück zu erstens

Wenn jetzt eine Aufgabe eine andere bedingt, muß das logischerweise auch dort in die Bedingung miteinfließen. Insbesondere kann es nötig sein, daß bei der Abarbeitung einer Aufgabe die Bedingung aufgehoben werden muß. Üblicherweis arbeitet man an der Stelle mit "sinnigen" Flags
(in etwa so:
-ein externes Ereignis setzt das "Aufgabe42offen-Flag"
-in der Hauptschleife wird irgendwann mal auf dieses geprüft; die Bedingung ist jetzt also erfüllt, folglich wird der entsprechende Lösungsblock abgearbeitet. Gleich als erstes jedoch wird das "Aufgabe42offen-Flag" wieder gelöscht - wir kümmern uns ja jetzt drum. Damit ist das Flag für weitere Ereignisse von außen wieder scharf.

Oldmax würde jetzt noch jedem Aufgabenabarbeitungsblock 'ne eigene Subroutine spendieren, und diese den erfüllten Bedingungen entsprechend mit CALL/RCALL anspringen lassen, oder eben nicht. Das ist dann natürlich wesentlich übersichtlicher (und vereinfacht uU die Fehlersuche), ist aber vom Programmablauf (bis auf weinge zusätzliche Sprünge) letztendlich dasselbe.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)