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
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...
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.
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 :-/
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
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.
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.
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.
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.
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
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.
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
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.
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
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 lassenThomas 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"
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
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
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...
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 :-(
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
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
@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?!?!
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
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/Entprellungoldmax 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.
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.
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
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
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.
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
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
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