Forum: Mikrocontroller und Digitale Elektronik Kleines aber feines Projekt


von Thomas L. (Gast)


Lesenswert?

Hallo Leute,
ich habe mich hier neu angemeldet weil ich eure Unterstützung brauche. 
Ich habe mir letztes Jahr über die Feiertage ein Projekt ausgedacht 
welches ich versuche nun zu realisieren. Angefangen hat es mit einem 
Schalplan, dann ein Platinendesign und nun ist die Platine Fertig und 
bestückt und ich würde nun meinen MC programmieren. Als MC habe ich 
übrigens den ATtiny26. Das Problem was ich nun habe, ist das ich kein 
guter Programmierer bin. Ich habe mir Assembler durch Online-Tutorials 
usw. "beigebracht".

Ich habe ein kleines Programm geschrieben bzw. mir aus verschiedenen 
Programmen was abgeschaut und zusammengestellt in der Hoffnung das es so 
passen müsste. Da mein AVR Adapter Kabel erst in 2 Wochen kommt um es 
auf den MC zu spielen und zu schauen ob dieses auch das macht was ich 
will, dachte ich an das Forum hier und wollte euch um eure Hilfe bitten. 
Evtl. fallen euch ja Sachen auf die ich Falsch gemacht habe, bzw. 
bestimmt fallen euch Sachen auf die ich Falsch gemacht habe :p

Zu dem Programm selbst. Ich messe mit meinem MC Spannung. Je nach 
Anliegender Spannung möchte ich dazu gewisse Ausgänge am MC 
schalten/aktivieren. Um die Spannung Messen zu können habe ich den 
PinPort mit einer A/D-Wandler verwendet (PinPort PA0). Die PinPort PB4, 
PB5 und PB6 möchte ich ja nach anliegender Spannung freigeben (blinken 
lassen -> habe LED-Ketten als Ansteuerung). Den PinPort PB3 möchte ich 
nicht blinken lassen sondern der soll so lange aktiviert/an sein, wie 
auch Spannung gemessen wird am A/D Eingang. Ich hoffe es ist zu 
verstehen was gemacht werden soll, ansonsten fragt bitte einfach noch 
einmal nach und ich versuche es besser zu beschreiben.

Der Code lautet:
1
//   Typische Informationen
2
3
device ATtiny26
4
.nolist
5
.include "tn26def.inc"
6
.list
7
8
//   Deklerationen:
9
10
.def  TEMP = R16
11
12
.def  ADWert = R17
13
.def  ZeitNetzzustand_3 = R18
14
    
15
16
17
.equ  Vergleich1 = $7F    ;Vergleichswerte laden
18
.equ  Vergleich2 = $20
19
20
21
22
23
// Initialisierung
24
25
Init:
26
     
27
ldi    TEMP,  RAMEND      ;Stackpointer ans RAM-Ende ($0DF) legen
28
out    SP,    TEMP
29
30
ldi    TEMP,  0b11111111    ;Port A0..A7 als Ausgang
31
out   DDRA,  TEMP      
32
33
ldi    TEMP,  0b00001111    ;Port B0..B1 als Ausgang
34
out   DDRB,  TEMP      
35
36
ldi    TEMP,  0b00000101    ;Prescaler TC0 auf CK/1024
37
out    TCCR0,  TEMP
38
39
ldi    TEMP,  0b00000000    ;TC0 Startwert laden und starten
40
out    TCNT0,  TEMP      
41
42
ldi   TEMP, 0b00100111     ;Left Adjust, Kanal 7, Pin 7, Port B4
43
out   ADMUX, TEMP
44
45
ldi   TEMP, 0b10000100     ;AD-Umsetzer Enable und ADC-Prescaler auf 16 stellen
46
out   ADCSR, TEMP
47
48
ldi   TEMP, (1<<CS13)|(0<<CS12)|(1<<CS11)|(1<<CS10)  ;Timer 1 Prescaler laden
49
out   TCCR1B, TEMP
50
51
ldi   TEMP, 0b11111111    ;Auflˆsung PWM
52
out   OCR1C, TEMP
53
54
ldi   TEMP, (1<<COM1A1)|(0<<COM1A0)|(1<<COM1B1)|(0<<COM1B0)|(0<<PWM1A)|(0<<PWM1B)    ;PWM A und PWM B AUS
55
out   TCCR1A, TEMP
56
57
ldi    TEMP,  0b00000000    ;TC1 Startwert laden und starten
58
out    TCNT1,  TEMP
59
60
ldi   TEMP, 0b10000000    ;Compare-Wert A laden
61
out   OCR1A, TEMP
62
63
ldi   TEMP, 0b10000000    ;Compare-Wert B laden
64
out   OCR1B, TEMP
65
66
clr    ZeitNetzzustand_3      ;ZeitNetzzustand_3 loeschen
67
68
69
70
// Hauptprogramm
71
72
73
74
Start:
75
76
rcall   ADUmsetzung        ;Bereich bestimmen      
77
78
cpi    ADWert, Vergleich1
79
brsh  Netzzustand_1      ;Blinken Netzzustand_1
80
81
cpi    ADWert, Vergleich2    ;Blinken Netzzustand_2
82
brsh  Netzzustand_2  
83
84
rjmp  Netzzustand_3      ;Blinken Netzzustand_3
85
86
87
LED_1:
88
89
rcall   Netzzustand_1
90
rjmp  LED
91
92
93
LED_2:
94
95
rcall  Netzzustand_2
96
rjmp  LED
97
98
99
LED_3:
100
101
rcall  Netzzustand_3
102
rjmp  LED
103
104
105
106
LED:
107
108
sbi    PORTA,  1
109
110
rcall   ZEIT
111
112
cbi    PORTA,  1
113
114
rcall   ZEIT
115
116
117
rjmp  Start          ;Beginn von vorne
118
119
120
// Unterprogramme
121
122
123
ADUmsetzung:
124
125
sbi   ADCSR, 6        ;AD-Umsetzung starten
126
127
ADUWarten:
128
in    TEMP,  ADCSR
129
sbrs  TEMP,  4        ;Warten auf AD-Umsetzung Ende (ADIF = 1)
130
rjmp  ADUWarten
131
sbi    ADCSR,  4        ;ADU-Interrupt Flag ruecksetzen (ADIF = 0)
132
133
in     ADWert, ADCH        ;umgesetzten Wert sichern
134
135
ret
136
137
138
139
Netzzustand_1:
140
141
in    TEMP,  TCCR1A      ;PWM B AUS (Netzzustand_3)
142
cbr    TEMP,   1
143
out    TCCR1A,  TEMP
144
145
in    TEMP,  TCCR1A      ;PWM A EIN (Netzzustand_2)
146
sbr    TEMP,   2
147
out    TCCR1A,  TEMP
148
149
clr    ZeitNetzzustand_3    ;ZeitSpeicher loeschen
150
sbi    PORTA,   0        ;Netzzustand_3 zuschalten
151
sbi    PORTA,   1        ;Netzzustand_2 EIN
152
153
ret
154
155
156
157
158
Netzzustand_2:
159
160
in    TEMP,  TCCR1A      ;PWM B AUS (Netzzustand_3)
161
cbr    TEMP,   1
162
out    TCCR1A,  TEMP
163
164
in    TEMP,  TCCR1A      ;PWM A EIN (Netzzustand_2)
165
sbr    TEMP,   2
166
out    TCCR1A,  TEMP
167
168
clr    ZeitSpeicher      ;ZeitNetzzustand_3 loeschen
169
sbi    PORTA,  1        ;Netzzustand_2 EIN
170
171
ret
172
173
174
175
176
Netzzustand_3:
177
178
in    TEMP,  TCCR1A      ;PWM A AUS (Netzzustand_3)
179
cbr    TEMP,   2
180
out    TCCR1A,  TEMP
181
182
in    TEMP,  TCCR1A      ;PWM B EIN (Netzzustand_3)
183
sbr    TEMP,   1
184
out    TCCR1A,  TEMP
185
186
inc    ZeitNetzzustand_3
187
cpi    ZeitNetzzustand_3, 5
188
brne  WeiterNetzzustand_3
189
190
cbi    PORTA,  1        ;Netzzustand_2 AUS
191
clr    ZeitNetzzustand_3
192
193
194
WeiterNetzzustand_3:
195
ret
196
197
198
// Zeitschleife
199
200
Zeit:
201
202
Warten:
203
in    TEMP,  TIFR
204
sbrs  TEMP,  1        ;Warten auf Ueberlauf TC0
205
rjmp  Warten
206
207
ldi    TEMP,  0b00000010    ;Ueberlauf-Bit TC0 ruecksetzen
208
out    TIFR,  TEMP
209
210
ret

: Verschoben durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Thomas L. schrieb:
> Da mein AVR Adapter Kabel erst in 2 Wochen kommt um es auf den MC zu
> spielen und zu schauen ob dieses auch das macht was ich will, dachte ich
> an das Forum hier und wollte euch um eure Hilfe bitten.
Das AVR-Studio hat einen Simulator eingebaut. Damit lassen sich solche 
Dinge incl. der controllerinternen Komponenten (Timer und ADC) tadellos 
testen.

> ich habe mich hier neu angemeldet
Du schreibst aber als Gast.

BTW: ich habe deinen Thread mal aus dem FPGA-Forum ins uC-Forum 
verschoben...

: Bearbeitet durch Moderator
von Pandur S. (jetztnicht)


Lesenswert?

Fein? - Aha.

Der Timer laeuft ja nicht im Interrupt, also muesste man das Timercount 
Register abfragen.

Gibt es irgendwelche Debugstrategien ? Eher nicht. Deshalb ist ein Tiny 
die ganz falsche Wahl. Das sind cents gespart. Wirf lieber einen Euro 
oder zwei mehr raus fuer genuegend Pins, die auch Debuging erlauben. zB 
einen Mega88 oder groesser.

: Bearbeitet durch User
von Thomas L. (Gast)


Lesenswert?

Lothar Miller schrieb:
>> ich habe mich hier neu angemeldet
> Du schreibst aber als Gast.

AAAh ok ich dachte damit ist es schon getan. Hab mich schon gewundert 
warum ich einfach so nen Beitrag schreiben kann ohne das ganze 
Anmeldeverfahren wie man es sonst so kenn :-)

Lothar Miller schrieb:
> Das AVR-Studio hat einen Simulator eingebaut. Damit lassen sich solche
> Dinge incl. der controllerinternen Komponenten (Timer und ADC) tadellos
> testen.

Ach das ist gut zu wissen dann kann ich mir das schon mal runterladen. 
Ich dachte wenn ich die HW bekomme kann ich mal anfangen mit dem Testen, 
habe die ganze zeit nur nen komischen Editor benutzt :-/

von Thomas L. (Gast)


Lesenswert?

Da ich nicht so gut im Programmieren bin, besser gesagt ein blutiger 
Anfänger würde ich doch gerne eure Meinung dazu hören....Hat jemand was 
am Code auszusetzen??

Gruss Thomas

von Mike (Gast)


Lesenswert?

Thomas L. schrieb:
> ldi    TEMP,  0b00001111    ;Port B0..B1 als Ausgang
> out   DDRB,  TEMP

Entweder stimmt hier dein Kommentar oder dein Bit-Muster nicht.

von lrep (Gast)


Lesenswert?

Es wäre auch besser, wenn du konstanten Werten Namen gibst, und sie am 
Programmanfang definierst.
Solche Dinge wie
ldi    TEMP,  0b00000010
....
cbr    TEMP,   1
machen ein Programm unwartbar, weil gleiche Zahlenwerte an vielen 
Stellen mit völlig unterschiedlicher Bedeutung vorkommen können.
Beim Debugging übersieht man dann leicht die Stellen, auf die es 
ankommt.

von Alter F. (kupferstecher)


Lesenswert?

Hallo Thomas,

du rufst die Netzzustandroutinen einmal über Sprünge, einmal über 
Call-Befehle auf:
>> rjmp  Netzzustand_3
>> rcall  Netzzustand_3
Das sind unterschiedliche Dinge. Beim CALL wird die Rücksprungadresse 
auf dem Stack abgelegt, bei RET wieder auf diese Adresse 
zurückgesprungen. Wenn du die Routine per JMP anspringst (genauso bei 
Branch; brsh) wird keine Rücksprungadresse abgelegt. Beim anschließenden 
Ausführen des RET-Befehls tut es dann einen Schlag (ruhig mal 
ausprobieren).

Ansonsten besser auf die Hardware warten und Stück-für-Stück das 
Programm zusammenbauen, je größer ein Programm, desto schwerer die 
Fehlersuche.

von Pandur S. (jetztnicht)


Lesenswert?

Ueblicherweise reserviert man sich einen freien Pin als Ausgang. Dort 
kann man im Code einen Puls ausgeben, und mit dem Oscilloskop 
kontrollieren, ob der auch kommt. Wenn komplizierterer Code dann auf 
einem gewissen Level ist, verwendet man ein UART zum debuggen. Zb Bei 
einem Regelkreis kann man so die Parameterwerte neu setzen, und 
Variablen auslesen.

Unter einem Mega644 verwende ich daher nichts mehr. Das bisschen 
Mehrkosten bringe ich auch mit einer Hunderter Serie schwer wieder rein, 
verglichen mit den Ersparnissen beim Debuggen. Einen Tiny wuerde ich 
allenfalls in Betracht ziehen bei einer Tausenderserie, nachdem das 
Ganze schon auf einem Mega644 gelaufen ist.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Thomas L. schrieb:
> ldi    TEMP,  0b11111111    ;Port A0..A7 als Ausgang
> out   DDRA,  TEMP

Du solltest einen Port, den du als Eingang benutzen willst, nicht als 
Ausgang schalten, auch wenn es ein Analogeingang ist.

Zum Programm - m.E. kannst du das ganze viel einfacher lösen. Du 
schreibst mit dem Timer einen Blinkinterrupt, der je nach einer 
PortMaske die LED blinken lässt oder nicht.
Dein Hauptprogramm muss dann nur noch den ADC bedienen und die PortMaske 
entsprechend setzen.
Die ganze Blinkerei läuft dann im Hintergrund und stört den Ablauf 
nicht. Ausserdem könntest du dann noch mehr LED zufügen, ohne das du 
alles umschreiben musst.

von Detektiv (Gast)


Lesenswert?

Thomas L. schrieb:
> Hallo Leute,
> ich habe mich hier neu angemeldet

Lügner!

von oldmax (Gast)


Lesenswert?

Hi
Schön, wieder mal bei einem Assemblerprogramm zu helfen. Das Prinzip ist 
ja schon richtig, Aufgaben in Blöcke zu setzen (Subroutinen) Sicherlich 
hat da jeder Programmierer einen eigenen Stilund deshalb sollten meine 
Hinweise lediglich Anregung sein.
Richtig ist es eine Subroutine Init zu schreiben. Um Programm aber auch 
übersichtlich zu halten, rufe ich verschiedene Initialisierungen auf.

  Setzen Stack, klar, das muss vor dem Programm direkt programmiert 
werden. Dann folgen bei mir Programmblöcke :
1
Init:
2
  ldi    r16,  RAMEND      ;Stackpointer ans RAM-Ende ($0DF) legen
3
  out    SP,    r16
4
  RCALL Init_IO                ; definieren der IO-Ports
5
  RCALL Init_Timer             ; Zeitbasis einrichtten
6
  RCALL Init_USART             ; serielle Verbindung initialisieren
7
  RCALL Init_xxx               ; weitere Initialisierungen z. B.  ISP, EEProm etc.
8
  RCALL Init_Register          ; Vorbesetzen von speziellen Registern
9
  RCALL Init_Variablen         ; Vorbesetzen von Variablen ( z.B. Ringpufferzeiger)
Dann folgt das Hauptprogramm nach EVA- "Einlesen" "Verarbeiten" und 
"Ausgeben". Es ist nicht immer so eindeutig, dennoch hilft diese Regel. 
Die Programmschleife bilde ich also prinzipiell so ab:
1
Main_Loop:
2
 ;--  "E"  --
3
  RCALL Read_IO                ; Eingänge lesen und in logische Lage drehen. Ergebnis steht in Variable "New_In" evtl. A, B etc. 
4
  RCALL Debounce_IO            ; Eingänge entprellen. Ergebnis steht in "Akt_In" als gültiges Bitmuster. Auch hier ergänzen mit A, B etc.
5
  RCALL Event_IN_0             ; Flanken von Eingängen bilden (Wechsel von 1 nach 0)
6
  RCALL Event_IN_1             ; Flanken von Eingängen bilden (Wechsel von 0 nach 1) 
7
; --  oder zusammenfassen in  RCALL Set_Event_Io --
8
  RCALL Read_Analog            ; eigene Leseroutine für Analogwerte. Leichter Wechsel zu einer ISR 
9
  RCALL Read_Ser               ; Leseroutine mit zugriff auf Ringpuffer. Der Dateneingang wird mit Interrupt erfasst
10
11
 ;--  "V"  --
12
  RCALL Chk_Event_In_1         ; Flanken von Eingängen bearbeiten
13
  RCALL Chk_RS232              ; Dateneingang bearbeiten
14
  RCALL Chk_Time_Event         ; Bits aus Timer_ISR bearbeiten
15
  RCALL Chk_Analog             ; Analogkanäle bearbeiten
16
  ; etc 
17
18
 ;--  "A"  --
19
  RCALL Write_IO
20
  RCALL Send_Ser
21
   etc
22
RJMP Main_Loop

Zu den Eingängen noch ein Hinweis. Da interne Pullup-Widerstände 
geschaltet werden können, bedeutet ein betätigter Taster ein "0" signal. 
Im Kopf setzen wir aber eine Betätigung mit einer "1" um. Abhilfe würden 
Öffner bringen, dann stimmt die Logik, aber mit dem Befehl "COM" kann 
das Bitmuster in einem Register gedreht werden. Damit stimmt dann auch 
die Zuordnung bei einem normalen Taster. z.B.
1
  Read_IO:
2
   IN   r16, PortB               ; lesen Port B
3
   COM  r16                      ; Bits in r16 drehen
4
   ANDI r16, 0b00001111          ; nur Bits 0-3 Eingänge
5
   STS  New_In, r16
6
  RET
Auf diese Weise lassen sich nun auch noch weitere Eingänge von anderen 
Ports auf die Variable New_In mischen. Dazu wird halt die Routine 
Read_IO erweitert. So kannst du Modul für Modul in Betrieb nehmen.
Das ich viel mit Flanken arbeite, ist eine Denkweise aus der 
Ereignisprogrammierung von Hochsprachen. (VB, Delphi) Dort werden die 
Ereignisse der Objekte, z. B. einem Button in einer entsprechenden 
Routine behandelt. Die Flanken ermöglichen eine solche Vorgehenweise. 
Einerseits kann ich direkt mit einem Eingang den Status auf einen 
Ausgang legen. Eine LED leuchtet, wenn der Kontakt geschlossen und 
erlischt wenn er wieder öffnet. Genausogut kann eine Flanke von 0 nach 1 
einen Ausgang einen Ausgang setzen. Halte ich den Schalter geschlossen, 
wird aber nicht mehr auf den Ausgang zugegriffen, sondern erst mit einer 
Flanke von 1 nach 0. Also sind die Ereignisse entscheident, drücken und 
loslassen. Dazwischen passiert nix.
1
Event_IN_1:
2
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
3
   LDS    r17, old_In             ; zuletzt eingelesener gültiger Status
4
   EOR    r17, r16                ; Exclusiv Oder, nur unterschiedliche Bits liegen in r17
5
   AND    r16, r17                ; wenn aktuell "1" war vorher "0", also wechsel von 0 nach 1
6
   LDS    r17, In_To_1            ; evtl noch unbearbeitete Bits laden ( bei korrekter verarbeitun nicht erforderlich)
7
   OR     r16, r17                ; Zumischen                          ( wenn korrekt bearbeitet, können diese 2 Zeilen entfallen)
8
   STS    In_to_1, r16            ; Ereignisbits Wechsel von 0 nach 1 ablegen
9
RET
Andere Flanke arbeitet genauso, aber setzt das And auf den neuen Status.
1
Event_IN_1:
2
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
3
   LDS    r17, old_In             ; zuletzt eingelesener gültiger Status
4
   EOR    r16, r17                ; Exclusiv Oder, nur unterschiedliche Bits liegen in r16
5
   AND    r17, r16                ; wenn aktuell "0" war vorher "1", also wechsel von 1 nach 0
6
   LDS    r16, In_To_0            ; evtl noch unbearbeitete Bits laden ( bei korrekter verarbeitun nicht erforderlich)
7
   OR     r16, r17                ; Zumischen                          ( wenn korrekt bearbeitet, können diese 2 Zeilen entfallen)
8
   STS    In_to_0, r16            ; Ereignisbits Wechsel von 0 nach 1 ablegen
9
;--wichtig--
10
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
11
   STS    old_in, r16
12
RET
Nachteil von 2 Routinen, es kann leicht übersehen werden, das die 
Ubernahme neu nach alt erfolgen muss, und zwar nachdem beide Flanken 
ausgewertet sind. Deshalb macht es sinn, beide Flanken in einer Routine 
zu erfassen und die Übernahme in dieser Routine einzubauen.
1
Set_Event_Io:
2
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
3
   LDS    r17, old_In             ; zuletzt eingelesener gültiger Status
4
   EOR    r17, r16                ; Exclusiv Oder, nur unterschiedliche Bits liegen in r17
5
   BREQ Event_To_0                ; kein Wechsel nach 1, dann teste Wechsel nach 0
6
   AND    r16, r17                ; wenn aktuell "1" war vorher "0", also wechsel von 0 nach 1
7
   LDS    r17, In_To_1            ; evtl noch unbearbeitete Bits laden ( bei korrekter verarbeitun nicht erforderlich)
8
   OR     r16, r17                ; Zumischen                          ( wenn korrekt bearbeitet, können diese 2 Zeilen entfallen)
9
   STS    In_to_1, r16            ; Ereignisbits Wechsel von 0 nach 1 ablegen
10
Event_to_0:
11
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
12
   LDS    r17, old_In             ; zuletzt eingelesener gültiger Status
13
   EOR    r16, r17                ; Exclusiv Oder, nur unterschiedliche Bits liegen in r16
14
   AND    r17, r16                ; wenn aktuell "0" war vorher "1", also wechsel von 1 nach 0
15
   LDS    r16, In_To_0            ; evtl noch unbearbeitete Bits laden ( bei korrekter verarbeitun nicht erforderlich)
16
   OR     r16, r17                ; Zumischen                          ( wenn korrekt bearbeitet, können diese 2 Zeilen entfallen)
17
   STS    In_to_0, r16            ; Ereignisbits Wechsel von 0 nach 1 ablegen
18
;--wichtig--
19
   LDS    r16, Akt_In             ; gültiger Status der Eingänge
20
   STS    old_in, r16
21
RET
Wichtig ist nun zu wissen, das in beiden Fällen, ob Wechsel von 0 nach 1 
oder umgekehrt, das Ereignisbit eine "1" ist. So ist eine einfache 
Abfrage gegeben:
1
Chk_Event_In_1:
2
  LDS    r16, In_to_1              ; aktuelle Flankenbits der Eingänge
3
  MOV    r8, r16                   ; Register merken, da folgende Bitabfrage Inhalt ändert
4
  ANDI   r16, 0b00000001           ; Bit 0 prüfen z.B. Flanke auf PortB Bit 0
5
  BREQ   IO_Event_1                ; Bit nicht gesetzt, dann prüfe nächstes Flankenbit
6
  RCALL  Eingang_0H                ; Aufruf Ereignisbehandlung Bit 0 bei Wechsel von Low nach High 
7
  MOV    r16, R8                   ; originalzustand zurückholen
8
  ANDI   r16, 0b11111110           ; Flankenbit Status "1"  löschen
9
  MOV    r8, r16                   ; aktualisiertes Register r16 zwischenspeichern
10
IO_Event_1:                        USW
11
12
RET
Da bei jeder Bearbeitung die Flanken- oder Eventbits gelöscht werden, 
wird ein Aufruf der ausführenden Subroutine auch nut einmal pro Ereignis 
erfolgen
Nun ist es ein leichtes, Zeitevents zu  bearbeiten, denn auch hier gilt 
die gleiche Logik.
1
Chk_Time_Event:
2
  LDS    r16, Time_Flags           ; In diese Routine trägt die Timer_ISR die Zeitsignale ein z.B. 1ms in Bit 0, 10 ms in Bit 1, 100 ms in Bit 2 usw.
3
  MOV    r8, r16                   ; Register merken, da folgende Bitabfrage Inhalt ändert
4
  ANDI   r16, 0b00000001           ; Bit 0 prüfen (1ms)
5
  BREQ   Event_10ms                ; Bit nicht gesetzt, dann prüfe nächstes Zeitsignal
6
  RCALL  Time_Order_1ms            ; Aufruf aller Zeitaufgaben im Raster 1 ms
7
  MOV    r16, R8                   ; originalzustand zurückholen
8
  ANDI   r16, 0b11111110           ; Zeitbit für 1 ms löschen
9
  MOV    r8, r16                   ; aktualisiertes Register r16 zwischenspeichern
10
Event_10ms: 
11
  MOV    r16, R8                   ; wieder r16 mit aktuellem Inhalt laden
12
  ANDI   r16, 0b00000010           ; Bit 1 prüfen (10ms)
13
  BREQ   Event_100ms               ; Bit nicht gesetzt, dann prüfe nächstes Zeitsignal
14
  RCALL  Time_Order_10ms           ; Aufruf aller Zeitaufgaben im Raster 10 ms
15
  MOV    r16, R8                   ; originalzustand zurückholen
16
  ANDI   r16, 0b11111101           ; Zeitbit für 10 ms löschen
17
  MOV    r8, r16             ; aktualisiertes Register r16 zwischenspeichern
18
Event_100ms:  usw....
19
20
21
RET

Wie du leicht erkennen kannst, rasselt die Programmbearbeitung durch die 
Main_Loop und erledigt nur das, was als Ereignis erkannt wurde. Kein 
Ereignis, keine Bearbeitung. Das ergibt einen schnellen und 
berechenbaren Zyklus. Routinen wie deine Zeitschleife
1
// Zeitschleife
2
3
Zeit:
4
5
Warten:
6
in    TEMP,  TIFR
7
sbrs  TEMP,  1        ;Warten auf Ueberlauf TC0
8
rjmp  Warten
9
10
ldi    TEMP,  0b00000010    ;Ueberlauf-Bit TC0 ruecksetzen
11
out    TIFR,  TEMP
12
13
ret
halten die Programmbearbeitung an. Du willst eine Wartezeit, dann leite 
sie aus den Zeitflags ab. Setz eine Variable auf den Wert x und zähl bei 
jedem Aufruf der entsprechenden Zeitroutine runter. Ist die Variable 0, 
kannst du dir wieder ein Statusflag setzen und entsprechende Bearbeitung 
einleiten. Dann löscht du das Statusflag und der Zustand bleibt, bis 
neue Ereignisse eine Bearbeitung erforderlich machen. Beipiel an dieser 
Stelle eine Entprellroutine für die Eingänge.
1
Debounce_IO:
2
   LDS   r16, New_In               ; aktuell gelesenen Eingänge
3
   LDS   r17, in_Debounce          ; ablage aktuell gelesener Eingänge
4
   EOR   r17, r16                   
5
   BRNE  Set_DebOunce_Time         ; Unterschiedlich ?
6
7
   LDS   r 18, Debounce_Time
8
   CPI   r18, 0                    ; Zeit schon abgelaufen ? 
9
   BREQ  End_Debounce               ; Dann keine weiteren SChritte
10
   DEC   r18                       ; zeit runterzählen, da keine Änderung der Eingänge
11
   STS   Debounce_Time, r18        ; und wieder ablegen. Statusflags der Operation DEC werden nicht verändert
12
   BRNE  End_Debounce              ; Zeit läuft noch
13
   STS   Akt_In, r16               ; Zeit abgelaufen, Eingänge gültig
14
   RJMP  End_Debounce              ; Routine beenden
15
Set_DebOunce_Time:
16
   STS   In_Debounce, r16          ; Aktuelle Eingänge für nächste Prüfung ablegen
17
   LDS   r18, 5                    ; Zeitzähler setzen
18
   STS   Debounce_Time, r18
19
End_Debounce:
20
RET
Der Aufruf wird aus der Loop_Main herausgenommen und erfolgt nun in:
1
Time_Order_1ms:
2
     RCALL Debounce_IO
3
     ; weitere Aufgaben in 1 ms
4
5
RET
Diese Routine ist in Chk_Time_Event verankert.

Sorry, das ich hier ein wenig aushole, aber ich denke, es wird dir 
helfen. Klar, bei so kleinen Progrämmchen ist es nicht so wichtig, der 
Regel E-V-A zu folgen. Doch meist kommt im Laufe der Projektbearbeitung 
noch das ein oder andere hinzu oder dir fällt ein neues Thema ein. Die 
Initialisierungen kannst du dann auch gleich übernehmen und bei Bedarf 
anpassen. Blöcke wie Entprellen, Flanken und Zeitereignisse bleiben fast 
1:1  bestehen. Und wenn du dir die Mühe machst und die Laufzeiten 
berechnest, wirst du feststellen, das da gar nicht soviel mehr 
Zykluszeit erforderlich ist.
Ein kleiner Tip noch zum Schluss: Im Forum AVR-Praxis ist ein kleiner 
Beitrag "keine Angst vor Assembler" in der Rubrik "FAQ". Der hilft dir 
vielleicht noch ein wenig weiter.
Gruß oldmax

von Zufall (Gast)


Lesenswert?

Thomas L. schrieb:
> Als MC habe ich
> übrigens den ATtiny26.

Thomas L. schrieb:
> Die PinPort PB4,
> PB5 und PB6 möchte ich ja nach anliegender Spannung freigeben (blinken
> lassen -> habe LED-Ketten als Ansteuerung). Den PinPort PB3 möchte ich
> nicht blinken lassen

Thomas L. schrieb:
> Ich messe mit meinem MC Spannung. Je nach
> Anliegender Spannung möchte ich dazu gewisse Ausgänge am MC
> schalten/aktivieren.

Also Zufälle gibts:
Beitrag "Frage zu ATtiny26 als Steuergerät"

von Thomas L. (Gast)


Lesenswert?

Zufall schrieb:
> Thomas L. schrieb:
>> Als MC habe ich
>> übrigens den ATtiny26.
>
> Thomas L. schrieb:
>> Die PinPort PB4,
>> PB5 und PB6 möchte ich ja nach anliegender Spannung freigeben (blinken
>> lassen -> habe LED-Ketten als Ansteuerung). Den PinPort PB3 möchte ich
>> nicht blinken lassen
>
> Thomas L. schrieb:
>> Ich messe mit meinem MC Spannung. Je nach
>> Anliegender Spannung möchte ich dazu gewisse Ausgänge am MC
>> schalten/aktivieren.
>
> Also Zufälle gibts:
> Beitrag "Frage zu ATtiny26 als Steuergerät"

Hahahaha das ist ja mal cool. Ich habe Rahmen meiner Ausbildung 
(Elektriker) mit meinem Meister mal gesprochen und der hat mir von so 
einem Projekt gesprochen was ich mal selbst bauen könnte in meiner 
Freizeit. Wir haben die gleichen Interessen sind beide Bastler und ja 
ich wollte mich mal beweisen indem ich was mit einem MC baue.....mein 
Meister und der Kollege aus dem anderen Forum müssen wohl beide die 
selbe Quelle haben....hab auch mal nachgeschaut aber mein Meister heist 
UDO nicht Chris :-D

oldmax schrieb:
> Hi
> Schön, wieder mal bei einem Assemblerprogramm zu helfen. Das Prinzip ist
> ja schon richtig, Aufgaben in Blöcke zu setzen (Subroutinen) Sicherlich
> hat da jeder Programmierer einen eigenen Stilund deshalb sollten meine
> Hinweise lediglich Anregung sein.
> Richtig ist es eine Subroutine Init zu schreiben. Um Programm aber auch
...........
> Gruß oldmax

ZU Oldmax....wooooow...ok das ist echt ein Batzen den du mir da 
hingestellt hast. Soll jetzt keine Beschwerde sein, bin froh drüber :-)

Also ich bin kompletter Anfänger in Assembler und habe wie beschrieben 
mir alles zusammengereimt aus verscheiden "Inspirationsquellen".

Ich muss erst mal das verstehen was du geschrieben hast und dann 
versuche ich es mal umzusetzen....das kann bissl dauern weil es so viel 
ist....muss ja irgendwie wo anfangen um das Schritt für Schritt 
abzuarbeiten....

PS: Dem anderen seine Platine aus dem Forum ist viel besser wie meine 
:-/.....also am Design brauche ich auch noch Nachhilfe...hab nen 
Platinenentwurf bei ner Google suche gefunden und den einfach nur 
angepasst :-P

von Thomas L. (Gast)


Lesenswert?

Also ich bin meinen Code noch einmal Schritt für Schritt drübergegangen. 
Die Sache ist die ich verstehe nicht wirklich was ihr mir sagen wollt 
(bestimmt kommt das von meinen fehlenden Assembler Kenntnissen), aber 
mein Code macht irgendwie für mich Sinn. Hört sich jetzt blöd an aber 
ist irgendwie so...komme damit besser zurecht...ich würde jetzt so an 
die Sache ran gehen das ich versuche meine Code vorzustellen und Ihr mir 
dann sagt was ein no go ist oder wie man es besser schreiben kann oder 
ob ich nen Denkfehler gemacht habe und der ganze Code für den Arsch 
ist...Ich sag mal direkt, ich verstehe nicht was ihr mir sagen wollt mit 
den ganzen Tipps. Ich denke Ihr müsst euer Level an Knowledge 
runterschrauben und mir das so rüberbringen als würdet ihr mit nem Affen 
reden :-P

Also hier mal Code mit Erklärung:
1
device ATtiny26
2
3
.nolist
4
5
.include "tn26def.inc"
6
7
.list

Das ist ja normal das man erst mal den Device decalriert und dann noch 
paar nützliche Sachen ladet...
1
//   Deklerationen:
2
3
4
5
.def  TEMP = R16
6
.def  ADWert = R17
7
.def  ZeitNetzzustand_3 = R18
8
9
    
10
.equ  Vergleich1 = $7F    ;Vergleichswerte laden
11
.equ  Vergleich2 = $20

Hier sind die Register deklariert. Ich werde R16 und R17 benutzen um 
Spannungswerte dort abzulegen um sie später vergleichen zu können. Im 
Register R18 wird eine Zeit abgelegt...weil später diese Zeit 
runterlaufen soll...müsst euch das so vorstellen wenn keine Spannung 
mehr anliegt will ich das er 5 sekunden zählt und dann den Netzzustand3 
(also die eine LED-Kette) erst abschaltet.

$7F und $20 snd die Register in dem die Vergleichswerte hineingeladen 
werden um verglichen zu werden
1
// Initialisierung
2
3
4
5
Init:
6
7
ldi    TEMP,  RAMEND      ;Stackpointer ans RAM-Ende ($0DF) legen
8
9
out    SP,    TEMP
10
11
12
13
ldi    TEMP,  0b11111111    ;Port A0..A7 als Eingang
14
15
out   DDRA,  TEMP      
16
17
18
19
ldi    TEMP,  0b00001111    ;Port B0..B1 als Ausgang
20
21
out   DDRB,  TEMP      
22
23
24
25
ldi    TEMP,  0b00000101    ;Prescaler TC0 auf CK/1024
26
27
out    TCCR0,  TEMP
28
29
30
31
ldi    TEMP,  0b00000000    ;TC0 Startwert laden und starten
32
33
out    TCNT0,  TEMP      
34
35
36
37
ldi   TEMP, 0b00100111     ;Left Adjust, Kanal 7, Pin 7, Port B4
38
39
out   ADMUX, TEMP
40
41
42
43
ldi   TEMP, 0b10000100     ;AD-Umsetzer Enable und ADC-Prescaler auf 16 stellen
44
45
out   ADCSR, TEMP
46
47
48
49
ldi   TEMP, (1<<CS13)|(0<<CS12)|(1<<CS11)|(1<<CS10)  ;Timer 1 Prescaler laden
50
51
out   TCCR1B, TEMP
52
53
54
55
ldi   TEMP, 0b11111111    ;Aufloesung PWM
56
57
out   OCR1C, TEMP
58
59
60
61
ldi   TEMP, (1<<COM1A1)|(0<<COM1A0)|(1<<COM1B1)|(0<<COM1B0)|(0<<PWM1A)|(0<<PWM1B)    ;PWM A und PWM B AUS
62
63
out   TCCR1A, TEMP
64
65
66
67
ldi  TEMP,  0b00000000    ;TC1 Startwert laden und starten
68
69
out  TCNT1,  TEMP
70
71
72
73
ldi   TEMP, 0b10000000    ;Compare-Wert A laden
74
75
out   OCR1A, TEMP
76
77
78
79
ldi   TEMP, 0b10000000    ;Compare-Wert B laden
80
81
out   OCR1B, TEMP
82
83
84
85
clr  ZeitNetzzustand_3    ;ZeitNetzzustand_3 loeschen

Hier werden die IO-Ports definiert (Port A Eingang, Port B Ausgang) 
usw....steht als Komentar ja nebendran was die machen/bedeuten

1
// Hauptprogramm
2
3
4
5
6
Start:
7
8
9
10
rcall   ADUmsetzung        ;Bereich bestimmen      
11
12
13
cpi      ADWert, Vergleich1
14
15
brsh  Netzzustand_1      ;Blinken Netzzustand_1
16
17
18
19
cpi    ADWert, Vergleich2    ;Blinken Netzzustand_2
20
21
brsh  Netzzustand_2  
22
23
24
25
rjmp  Netzzustand_3      ;Blinken Netzzustand_3


Hier werden am AD-Eingang die Werte eingelesen....Der Wert wird in das 
Register gelegt wo es dann mit dem anderen verglichen werden kann, 
gleichzeitig wird der Netzzustand 1 dadurch aktiviert. Der 2 Wert aus 
dem AD Eingang wird genommen und in das Register gesteckt wo es 
verglichen werden kann, gleichzeitig geht die 2te LED-Kette an...also 
Netzzustand 2.
Dann noch der Verweis das auf Netzzustand 3 gesprungen wird. Wann das 
passiert (also wann die Zeit abgelaufen ist und der aktiviert werden 
soll, kommt unten in Code erst)...

1
Netzzustand_1:
2
3
4
5
rcall   Netzzustand_1
6
7
rjmp  LED
8
9
10
11
12
13
Netzzustand_2:
14
15
16
17
rcall  Netzzustand_2
18
19
rjmp  LED
20
21
22
23
24
25
Netzzustand_3:
26
27
28
29
rcall  Netzzustand_3
30
31
rjmp  LED
32
33
34
35
36
37
38
39
LED:
40
41
42
43
sbi    PORTA,  1
44
45
46
47
rcall   ZEIT
48
49
50
51
cbi    PORTA,  1
52
53
54
55
rcall   ZEIT
56
57
58
59
60
61
rjmp  Start          ;Beginn von vorne


Das ist eine Art Anfangssequenz....wenn ich Saft auf den MC gebe soll 
dieser am Anfang die LED-Ketten zum Aufleuchten bringen das ich sehen 
das alle korrekt angeschlossen sind bzw gehen....

1
ADUmsetzung:
2
3
4
5
sbi   ADCSR, 6        ;AD-Umsetzung starten
6
7
8
9
ADUWarten:
10
11
in    TEMP,  ADCSR
12
13
sbrs  TEMP,  4        ;Warten auf AD-Umsetzung Ende (ADIF = 1)
14
15
rjmp  ADUWarten
16
17
sbi    ADCSR,  4        ;ADU-Interrupt Flag ruecksetzen (ADIF = 0)
18
19
20
21
in     ADWert, ADCH        ;umgesetzten Wert sichern
22
23
ret

Hier wird das erste Unterprogramm vorgestellt und zwar der AD Wandler 
bzw. die AD Wandler Funktion. Bei mir am MC soll der Eingang PA0 als AD 
wandler dienen....laut Datenblatt des ATtiny26 ist dies möglich bzw. 
besitzt dieser Anschluss die Funktion.
Was genau hier passiert weiss ich nicht, den AD Wandler Code habe ich 
mir durch ne Google Suche ergattert, ich nehme mal an der Wandelt wie 
der Name es schon hergibt das Signal um sodass Werte gelesen werden 
können.....
1
BlinkenNetzzustand_1:
2
3
4
5
in    TEMP,  TCCR1A      ;PWM B AUS (Netzzustand_3)
6
7
cbr    TEMP,   1
8
9
out    TCCR1A,  TEMP
10
11
12
13
in    TEMP,  TCCR1A      ;PWM A EIN (Netzzustand_2)
14
15
sbr    TEMP,   2
16
17
out    TCCR1A,  TEMP
18
19
20
21
clr    ZeitNetzzustand_3    ;ZeitSpeicher loeschen
22
23
sbi    PORTA,   0        ;Netzzustand_3 zuschalten
24
25
sbi    PORTA,   1        ;Netzzustand_2 EIN
26
27
28
29
ret
30
31
32
33
34
BlinkenNetzzustand_2:
35
36
37
38
in    TEMP,  TCCR1A      ;PWM B AUS (Netzzustand_3)
39
40
cbr    TEMP,   1
41
42
out    TCCR1A,  TEMP
43
44
45
46
in    TEMP,  TCCR1A      ;PWM A EIN (Netzzustand_2)
47
48
sbr    TEMP,   2
49
50
out    TCCR1A,  TEMP
51
52
53
54
clr    ZeitNetzzustand_3    ;ZeitNetzzustand_3 loeschen
55
56
sbi    PORTA,  1        ;Netzzustand_2 EIN
57
58
59
60
ret


Hier sind die Blinkfunktionen der LED-Ketten 1 und 2 beschrieben. 
LED-Kette 1 soll an Ausgang PB4 und LED-Kette 2 soll an Ausgang PB5 
anliegen. Wenn aktiviert sollen diese ein getaktes Signal abgeben sodass 
es zum Blinken der Ketten kommt wenn aktiviert.
1
BlinkenNetzzustand_3:
2
3
4
5
in    TEMP,  TCCR1A      ;PWM A AUS (Netzzustand_3)
6
7
cbr    TEMP,   2
8
9
out    TCCR1A,  TEMP
10
11
12
13
in    TEMP,  TCCR1A      ;PWM B EIN (Netzzustand_3)
14
15
sbr    TEMP,   1
16
17
out    TCCR1A,  TEMP
18
19
20
21
inc    ZeitNetzzustand_3
22
23
cpi    ZeitNetzzustand_3, 5
24
25
brne  WeiterNetzzustand_3
26
27
28
29
cbi    PORTA,  1        ;Netzzustand_2 AUS
30
31
clr    ZeitNetzzustand_3
32
33
34
35
36
37
WeiterNetzzustand_3:
38
39
ret

Nun das ist ein wenig Tricky und ich weiss nicht ob ich das so richtig 
umgesetzt habe wie ich es wollte aber ich beschreibe es einfach mal was 
es machen sollte...:-/
LED3 soll blinken und zwar, dass Register TCCR1A wird um 2 Bits 
manipuliert und dann wieder zurück in Temp register gesteckt...das beide 
passiert für die Pulsweitenmodulation für A und B. Dann haben wir einen 
Zeitoperanden namens ZeitNetzzustand_3 dieser bekommt den Wert 5 also 5 
Sekunden... wenn also später keine Spannung mehr anliegt soll noch 5 
sekunden lang die Kette blinken...in dem moment ist Led1 und LED2 
aus....bzw keine Werte mehr in den Register...
1
// Zeitschleife
2
3
4
5
Zeit:
6
7
8
9
Warten:
10
11
in    TEMP,  TIFR
12
13
sbrs  TEMP,  1        ;Warten auf Ueberlauf TC0
14
15
rjmp  Warten
16
17
18
19
ldi    TEMP,  0b00000010    ;Ueberlauf-Bit TC0 ruecksetzen
20
21
out    TIFR,  TEMP
22
23
24
25
ret

Das ist das Zeitschleifen Unterprogramm. Das sagt halt das die Main 
immer wieder ablaufen soll....?!?!

So ich hoffe ihr versteht mich nun besser bzw. könnt mir sagen was an 
meiner denkweise Falsch ist.....wie gesagt für mich macht der Code sinn 
nur kann ich ihn leider noch nicht testen da die nötige HW dazu noch 
nicht gekommen ist :-(

von oldmax (Gast)


Lesenswert?

Hi
Ich weiß, das in meinen Anregungen nicht alles sofort klar ist und ich 
weiß auch (aus eigener Erfahrung) das fremder Code nicht immer 
einleuchtet. Der eigene ist ja plausibel... bis die Praxis etwas anderes 
sagt. Da sind einfache Schreibfehler schon entscheident. Daher hab ich 
versucht, dir einmal eine Struktur zu zeigen, die es bei der 
Inbetriebnahme dann etwas erleichtert. Klar, das Prinzip musst du 
verstanden haben. Aber dann ist es auch in ein paar Jahren noch päsent. 
Bei einer programierung "in Linie" ist da nicht immer gesagt und schon 
gar nicht, wenns "tricky" ist. Aber warte erst mal ab, bis du die 
Hardware hast, dann wird es richtig spannend. Zu meinen Anregungen 
findest du hier auch im Tutorial eine Menge guter Anleitungen. Ergänzend 
kann ich dir nur raten, auch mal dem Vorschlag von mir nachzugehen, beim 
AVR-Praxis-Forum nachzuschauen.
Nach wie vor hast du eine Bremse in deinem Programm .... Schau dir 
unbedingt mal das Tutorial vom Timer an.
Gruß oldmax

von oldmax (Gast)


Lesenswert?

oldmax schrieb:
> Da sind einfache Schreibfehler schon entscheidend.
sowas hab ich gemeint...

Thomas L. schrieb:
> ldi    TEMP,  0b11111111    ;Port A0..A7 als Eingang
>
> out   DDRA,  TEMP

Ich müsste mich stark irren, aber setzt du nicht alle Portpins auf 
Ausgang? Ich denke eher so wird's was

> ldi    TEMP,  0b00000000    ;Port A0..A7 als Eingang (oder clr Temp)
>
> out   DDRA,  TEMP
> LDI  Temp, 0b11111111      ; PullUp einschalten
> Out PortA, Temp
Gruß oldmax

von Thomas L. (Gast)


Lesenswert?

@oldmax

Also ich bin um ehrlich zu sein bissl Überfordert. Ich sehe ein das du 
aufgrund deiner Erfahrung weisst wie es richtig geht/gemacht wird. Wie 
gesagt mein Code macht irgendwie Sinn...

Könntest du mir evtl Schritt für Schritt erklären wie ich an die Sache 
rangehen soll? Dein Text oben ist sehr lang und impulsive. Ich weiss gar 
nicht wo ich anfangen soll (klar vom Anfang), dennoch stehe ich gerade 
im Wald und weiss einfach nicht wie der erste Schritt aussehen soll. Ist 
dein Skelett das du oben geschrieben hast ne allgemeine Aussage 
oder....??

Ich weiss nicht ob du die Zeit und die Nerven dazu hast mir zu Helfen es 
richtig zu verstehen :-/ Würde mich aber freuen...wie gesagt ich bin ein 
blutiger Anfänger....

Ich hab mir übrigens die Tutorials hier auf der Seite durchgelesen und 
bissl studiert und es macht alles Sinn was ich so lese nur anwenden und 
wie ist das Problem...wie gesagt steh bissl verloren im Wald und weiss 
nicht was ich als erstes machen soll?!?!

von oldmax (Gast)


Lesenswert?

Hi
Als erstes nicht gleich solch hohe Ziele setzen. Gut, ich hab da auch 
gleich "fette" Brocken hingeschmissen,, aber damit wollte ich dir 
zeigen, das jeder seine Vorgehensweise hat. Ein anderer Spezi sieht das 
sicherlich anders. Aber das ist nicht dein Problem, sondern zum einen 
ein Vorgehen, ohne einer Möglichkeit, es auszuprobieren. Wenn du deine 
Bauteile hast, wirst du sehr schnell herausbekommen, was da nicht 
richtig läuft und was gut ist. Ich kann dir von meiner Seite aus nur 
sagen, das du aufpassen musst, wie du dokumentierst. Die beiden Listings 
haben in den Kommentaren Hinweise, die einfach nicht mit der Wahrheit 
übereinstimmen. Außerdem ist es auch etwas kritisch, einen Port mit 8 
Bit einfach zu beschreiben. Nehmen wir mal an, dein Port hat auf Bit 0 
und 1 die seriellen Verbindungen. Mitfolgender Zuweisung machst du sie 
Platt:
  LDI r16, 0b11110000
  OUT DDRx, r16
Klar, du wolltest ja nur Bit 4-7 auf Ausgang setzen, aber du hast auch 
gleichzeitig Bit 0-3 als Eingang definiert. Da ist es richtiger, erst 
einmal das DDR register zu lesen und dann nur die Bits manipulieren, die 
du beeinflussen willst.
  IN r16, DDRx
  ORI r16, 0b11110000            ; Bits 4 bis 7 sind Ausg, Rest bleibt
  OUT DDRx, r16

oder Eingänge definieren

  IN r16, DDRx
  ANDI r16, 0b11110011           ; Bits 2 u.3 sind Eing, Rest bleibt
  OUT DDRx, r16

Klar, bei deinen ersten Versuchen spielt das alles noch keine Rolle, 
aber irgendwann zieht es dich dann auf die Bretter und du suchst dir 
einen Wolf, weil du in einem Folgeschritt eine bereits getroffene 
Einstellung wieder zunichte machst.
Also geh es mal langsam an. Behalte den Begriff "EVA" im kopf und leg 
dann los. Eingänge lesen und in eine Variable schreiben. Die bits in der 
Variablen Auswerten und Ausgänge zuweisen. Erfolg, wenn es so 
funktioniert, wie du gedacht.
Im nächsten Schritt versuchst du dich an einer Stromstoßschaltung. 
Spätestens jetzt stellst du fest, das deine Schaltung nicht sauber 
funktioniert. Das liegt an den Kontakten und deren Eigenschaft zu 
prellen. Sie sind eben nicht auf oder zu, sondern wackeln bei 
Schaltvorgängen. Das musst du mit Software dann ausbügeln. Debounceist 
so eine Routine, die das erledigt. Natürlich kannst du auf der Stelle 
treten und ein wenig warten, aber damit nimmst du dem Controller die 
Möglichkeit auf andere Ereignisse zu reagieren. Lediglich Interrupts 
werden bearbeitet, aber sonst kreist er um sich selbst. Sowas wie deine 
Warten Methode ist ein Zykluskiller.
Denk da mal drüber nach. Gern werde ich dir weiter helfen, aber erwarte 
nicht, das du in ein paar Tagen alles verstehen wirst. Programmieren 
geht im Kopf vor, die Sprache ist nur das Werkzeug, deine Gedanken in 
einen Ablauf zu bringen.
Gruß oldmax

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

oldmax schrieb:
> Thomas L. schrieb:
>> ldi    TEMP,  0b11111111    ;Port A0..A7 als Eingang
>>
>> out   DDRA,  TEMP
>
> Ich müsste mich stark irren, aber setzt du nicht alle Portpins auf
> Ausgang?

Habe ich schon ein dutzend Posts weiter oben angemeckert...

Mein Tipp wäre nochmal, erstmal einen Timerinterrupt zu schreiben, der 
eine LED blinken lässt. Das kannst du dann auf mehrere LED erweitern und 
diese dann per einfacher Portmaske aus dem Hauptprogramm 
aktivieren/desaktivieren.
Das ganze kostet viel weniger Rechenzeit, du kannst die Blinkfrequenz an 
einer einzigen Stelle ändern und hast die Option auf viele LED.
Den Timerinterrupt nimmst du gleichzeitig zum Entprellen, wie es PeDa in 
seiner prima Routine vormacht:
https://www.mikrocontroller.net/articles/Entprellung

oldmax schrieb:
> Sowas wie deine
> Warten Methode ist ein Zykluskiller.

Sowas gibts dann gar nicht, der MC ist interaktiv und reagiert sofort 
auf Tasten, Ereignisse usw.

von Hagen (Gast)


Angehängte Dateien:

Lesenswert?

Jetzt versucht er es auf twago.de
http://www.twago.de/project/assembler+porgrammierer+gesucht/71418/
und nervt uns mit seinem Halbwissen ordentlich durch. Dabei ist twago 
eine professionelle Plattform. Der Typ hat nicht die geringste Ahnung! 
Ich vermute, dass er den Code von irgendwo bekommen hat, denn seine 
eigenen Kommentare sind der totale Mist! Ich glaube auch nicht, dass der 
vorhat, auch nur einen müden Cent für Hilfe zu bezahlen.

von Hagen (Gast)


Lesenswert?

oldmax, der Typ hat nicht die geringste Ahnung von dem Code, den er 
vorstellt! Das fängt schon an wenn er meint $7F und &20 wären Register. 
Hmm, geil, die Maschine mit 2^6-1 Registern will ich auch.. ggg

von Hagen (Gast)


Lesenswert?

Ach nee, er übergeht ja mit dem höheren Schwellenwert den 
"Netzzustand_2", also hat er 2^8-1 + 31 Register! Wahnsinn!!! ggg

von Hagen (Gast)


Lesenswert?

Sorry! Ein letzter Versuch: $7F = $70 + $0F = 2^7 + 2^6 + 2^5 + 31. So 
jetzt. Ist doch schon spät an Christi Himmelfahrt :-) das wären also 128 
+ 64 + 32 + 31 = 257 Register. Respekt!!! ggg Sorry wollte nich angeben, 
ich rechne nur ab und zu mal gerne was im Kopf

von Hagen (Gast)


Lesenswert?

Grummel. $7F = 2^6 + 2^5 + 2^4 + 15 = 64 + 32 + 16 + 15 = 127 oh mann = 
2^7-1 manchmal dauerts einfach bisschen länger - Snickers. 127 Register 
ist aber immer noch ordentlich ggg

von Kai M. (kai_mauer)


Lesenswert?

Gut nun?

Junge, Junge -Du machst Dich hier stockbesoffen zum Olaf!

von Hagen (Gast)


Lesenswert?

Lothar Miller schrieb:
>> ich habe mich hier neu angemeldet
> Du schreibst aber als Gast.

Lothar, das war bei Weitem nicht seine größte Lüge! Der Typ "lügt wie 
gedruckt", vor allem was die Kenntnis über "seines" Codes betrifft.

von Hagen (Gast)


Lesenswert?

Kai Mauer schrieb:
> Gut nun?
>
> Junge, Junge -Du machst Dich hier stockbesoffen zum Olaf!

Dafür kann ich Assembler! grrrrr

von Hagen (Gast)


Lesenswert?

Thomas L. schrieb:
> Also ich bin kompletter Anfänger in Assembler und habe wie beschrieben
> mir alles zusammengereimt aus verscheiden "Inspirationsquellen".

Da! Eine seiner unverschämtesten Lügen! Ich vermute der hat von irgendwo 
einen funktionierenden Code bekommen, und hat da kleine Fehler 
eingebaut, z.B. Pins 0b11111111 zu belegen, nur um zu provozieren. Lest 
seine Kommentare, die er später in seinen Code eingebaut hat. Der pure 
Schwachsinn! ggg

von Hagen (Gast)


Lesenswert?

Thomas L. schrieb:
> Ich muss erst mal das verstehen was du geschrieben hast und dann
> versuche ich es mal umzusetzen....das kann bissl dauern weil es so viel
> ist....muss ja irgendwie wo anfangen um das Schritt für Schritt
> abzuarbeiten....

Die Phrase hat er irgendwo aufgeschnappt und zitiert nun. Eine seiner 
beliebtesten Übungen ggg

von Kai M. (kai_mauer)


Lesenswert?

Laß Dich morgen mal untersuchen. Das ist doch krank. Komplett Einen an 
der Waffel! Ein Monolog über 2 Stunden -das gibt's doch gar nicht...

von oldmax (Gast)


Lesenswert?

Hi

Thomas L. schrieb:
> Ich habe ein kleines Programm geschrieben bzw. mir aus verschiedenen
> Programmen was abgeschaut und zusammengestellt in der Hoffnung das es so
> passen müsste.
Das hab ich wohl gelesen, was daran ist für einen Anfänger schlimm?

Hagen schrieb:
> oldmax, der Typ hat nicht die geringste Ahnung von dem Code, den er
> vorstellt!

Ja, das sieht danach aus. Trotzdem kann man antworten. Der rest ist dann 
seine Sache, was er mit den Antworten macht, also laßt ihm etwas Zeit. 
Ob er nach den ersten "Projekten" weitermacht oder frustriert aufgibt 
ist doch uns völlig wurscht. Klar, wir haben mit hilfreichen Texten Zeit 
investiert, aber es lesen auch andere mit und finden vielleicht 
Hinweise, nach denen sie zu fragen sie sich nicht trauen. Ich hoffe, der 
Vatertag gestern hat vielen genau so viel Spaß gemacht wie mir. War 
gestern mit Moppet unterwegs. Endlich mal wieder.....
Gruß oldmax

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.