Hallo!
Habe hier schon oft gelesen und durch die Tutorials schon viel gelernt,
aber irgendwie komme ich nun nicht weiter. Ich möchte gerne einen RGB
Fader programmieren. Habe dazu an einem Mega8 4Taster und entsprechende
LED in Rot Grün und Blau angeschlossen, Hardwareseitig funktioniert
alles. Die PWM aus dem Tutorial habe ich auf meine 3Kanäle angepasst,
das funktioniert auch. Habe auch schon die Suche bemüht, auch einige
Beispiele gefunden, die aber leider in C waren, und damit kenne ich mich
leider noch weniger aus.
Der Mega ist wie folgt belegt: PinD0-3 sind die Taster, PinB1-3 die LED
in der Reihenfolge RGB.
Mein Ziel ist es einerseits ein Rainbowprogramm mit variabler
Fadingzeit, andererseits ein statisches Programm mit frei einstellbarer
Farbe zu implementieren. Dafür die 4 Taster.
Nun zum Code, größtenteils aus dem Tutorial, nur klein wenig angepasst:
ldi temp, 1<<TOIE0 ; TOIE0: Interrupt bei Timer Overflow
31
out TIMSK, temp
32
sei
33
loop:
34
sbis PortD, 0
35
rjmp loop
36
/* dec ocr_1
37
nop*/
38
rjmp loop
39
40
timer0_overflow: ; Timer 0 Overflow Handler
41
inc PWMCount ; den PWM Zähler von 0 bis
42
cpi PWMCount, 128 ; 127 zählen lassen
43
brne WorkPWM
44
clr PWMCount
45
46
WorkPWM:
47
ldi temp, 0b11000000 ; 0 .. Led an, 1 .. Led aus
48
49
OneOn: cp PWMCount, ocr_1 ; Ist der Grenzwert für Led 2 erreicht
50
brlo TwoOn
51
ori temp, $02
52
53
TwoOn: cp PWMCount, ocr_2 ; Ist der Grenzwert für Led 3 erreicht
54
brlo ThreeOn
55
ori temp, $04
56
57
ThreeOn:cp PWMCount, ocr_3 ; Ist der Grenzwert für Led 4 erreicht
58
brlo SetBits
59
ori temp, $08
60
61
SetBits: ; Die neue Bitbelegung am Port ausgeben
62
out PORTB, temp
63
64
reti
Wenn ich nun den auskommentierten Teil im loop-Teil mit assembliere, so
würde ich erwarten, dass jedes mal wenn ich die Taste1 drücke, Rot ein
paar Schritte heller wird. (Taster ist noch nicht entprellt, 128 ist
aufgrund der Transistorschaltung LED-aus, 0 ist voll an) Das passiert
aber nicht, die roten LED nehmen nach dem Reset irgend einen
Helligkeitswert (immer der gleiche) an, und bei Tastendruck passiert
nichts.
Was ich mir vorstellen könnte, dass der Mega zu schnell läuft um den
Tastendruck sinnvoll auszuwerten. Deswegen hatte ich nach dem dec ocr_1
auch schon mehrere nops testweise eingebaut, das hat nichts genutzt.
Vielleicht liegt es aber auch an etwas anderem? Kann mir hier jemand
einen Tip geben? Das würde mich freuen. Auch über Tips zur Machbarkeit
des Fadings würde ich mich freuen.
Freundliche Grüße,
bendd
Ben D. schrieb:> Wenn ich nun den auskommentierten Teil im loop-Teil mit assembliere, so> würde ich erwarten, dass jedes mal wenn ich die Taste1 drücke, Rot ein> paar Schritte heller wird.
Falsch.
So wie das programmiert ist, wird der OCR Wert verringert - SOLANGE der
Taster gedrückt ist. Und bei dieser Länge (Anzahl Takten und damit
Ausführungszeit eines Durchlaufs durch die Schleife) des Schleifencodes
...
1
loop:
2
sbis PortD, 0
3
rjmp loop
4
dec ocr_1
5
nop
6
rjmp loop
... dekremtiert dein µC den ocr1_1 Wert pro Zehntel Sekunde (und recht
viel kürzer kannst du deine Taste als Mensch gar nicht drücken, egal wie
sehr du dich bemühst) und paar Zehntausend mal.
Ben D. schrieb:> loop:> sbis PortD, 0> rjmp loop> /* dec ocr_1> nop*/> rjmp loop
Das sbis betrifft die I/O Register der internen Hardware (zBsp. EEWE )
also lieber mit IN den Registerwert einlesen und dann vergleichen/prüfen
und nicht vergessen den Port (Portbit) auf Eingang schalten .
siehe auch tutorial ;-)
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten
Gruß
Wenn das hier gegeben ist
> ldi temp, 0x00> out DDRD, temp ; PortD auf Eingang> ldi temp, 0xFF ; PortB auf Ausgang> out DDRB, temp> out DDRD, temp ;PullUp an PortD
Dann hast du am D-Port die Taster als active Low verschaltet, was
grundsätzlich in Ordnung ist. (Taster schaltet nach Masse)
D.h. der Tasterpin ist auf 1, wenn der Taster nicht gedrückt ist und auf
0, wenn der Taster gedrückt ist.
Dann kann das hier
1
loop:
2
sbis PortD, 0
3
rjmp loop
4
....
nicht stimmen.
Da steht:
Skip (also überspringe) die nächste INstruktion, If pin Is Set (wenn der
Pin also auf 1 ist).
Der Pin ist aber genau dann auf 1, wenn der Taster nicht gedrückt ist.
D.h. solange der Taster nicht gedrückt ist, wird die nächste Instruktion
(welche der rjmp nach loop ist) übersprungen und es geht mit
1
dec ocr_1
2
nop
3
rjmp loop
weiter. Das ist aber nicht so ganz das, was du eigentlich wolltest. Du
wolltest doch den Dekrement nur dann machen, wenn die Taste gedrückt
ist. Gedrückt bedeutet aber der Portpin liegt auf 0.
Pastor Braune schrieb:> Ben D. schrieb:>> loop:>> sbis PortD, 0>> rjmp loop>> /* dec ocr_1>> nop*/>> rjmp loop>> Das sbis betrifft die I/O Register der internen Hardware (zBsp. EEWE )>> also lieber mit IN den Registerwert einlesen und dann vergleichen/prüfen> und nicht vergessen den Port (Portbit) auf Eingang schalten .
Quatsch.
Sobald du an ein Register mit IN/OUT ran kommst, kannst du auch einen
sbis oder eine andere der Operationen sbi, cbi, sbis, sbic drauf
anwenden.
Hi
> ldi temp, 0xFF ; PortB auf Ausgang> out DDRB, temp> out DDRD, temp ;PullUp an PortD
Mit dem letzten Befehl schaltest du PortD auf Ausgang und nicht die
Pull-Ups ein.
MfG Spess
Karl Heinz Buchegger schrieb:> Quatsch.> Sobald du an ein Register mit IN/OUT ran kommst, kannst du auch einen> sbis oder eine andere der Operationen sbi, cbi, sbis, sbic drauf> anwenden.
Ja mag sein - macht es aber einem Anfänger auch nicht leichter,wenn in
den Tutorials der NORMALE Weg beschrieben wird.
Und wie ist es mit den neueren Chips die ihr I/O im memory space haben
>$3f ?
mfg
Danke für die schnellen Antworten.
Karl Heinz Buchegger schrieb:> ... dekremtiert dein µC den ocr1_1 Wert pro Zehntel Sekunde (und recht> viel kürzer kannst du deine Taste als Mensch gar nicht drücken, egal wie> sehr du dich bemühst) und paar Zehntausend mal.
Das leuchtet ein. Also brauche ich zur Auswertung der Taster eine
Flankenerkennung, denke ich mal.
Karl Heinz Buchegger schrieb:> Du> wolltest doch den Dekrement nur dann machen, wenn die Taste gedrückt> ist. Gedrückt bedeutet aber der Portpin liegt auf 0.
Stimmt, mein Fehler, da bin ich wohl mit High und Low
durcheinandergekommen. Habe es im Code geändert, allerdings bringt das
keine Veränderung, da ja noch keine Flankenerkennung dabei ist.
spess53 schrieb:> Mit dem letzten Befehl schaltest du PortD auf Ausgang und nicht die> Pull-Ups ein.
Auch das stimmt, habe das im Code geändert, danke für den Hinweis.
Nun werde ich mich wohl mal an die Flankenerkennung setzen müssen, diese
werde ich ja dann für alle 4 Tasten einbauen müssen, wenn es erstmal
funktioniert.
Freundliche Grüße, bendd.
Pastor Braune schrieb:> Karl Heinz Buchegger schrieb:>> Quatsch.>> Sobald du an ein Register mit IN/OUT ran kommst, kannst du auch einen>> sbis oder eine andere der Operationen sbi, cbi, sbis, sbic drauf>> anwenden.>> Ja mag sein - macht es aber einem Anfänger auch nicht leichter,wenn in> den Tutorials der NORMALE Weg beschrieben wird.
Der normale Weg, gerade für einen Anfänger IST die Benutzung der CBI,
SBI, SBIC und SBIS Anweisungen.
Und sie werden im Tutorial benutzt. Von Ànfang an.
> Und wie ist es mit den neueren Chips die ihr I/O im memory space haben>>$3f ?
Wir reden hier von einem Mega8
Konstuiert keine Sonderfälle wo keine sind. Damit hilfst du dem TO
nicht.
Habe mir jetzt mal den Artikel zur Flankenerkennung zu Gemüte geführt.
Bis zu einem bestimmten Punkt scheint mir auch alles logisch, dann
steigt der Kopf aber irgendwie nicht mehr durch. Habe das Codebeispiel
soweit ich es verstanden habe angepasst auf:
1
loop:
2
3
in E1, PinD ; Erkennen der Flanke
4
neg E1 ; E1 negieren, da Taster active Low
5
neg M2 ; Vorbereiten für log. Vgl.
6
and M2, E1 ; M1=E1 AND NOT M2
7
mov M1, M2 ; M2 nach M1 schreiben
8
9
mov M2, E1 ; E1 in M2 Schreiben
10
11
sbrc M1, S1 ; Skip if bit(S1) in Register(M1) is cleared
12
; (cleared wegen active Low)
13
rjmp loop ;Sprung wenn M1 nicht gesetzt
14
15
dec ocr_1
16
rjmp loop
17
.
18
.
19
.
Und da verließen sie Ihn. Es passiert auf alle Fälle schonmal was.
Drücke ich den Taster, werden die LED angeschaltet. Allerdings werden
sie beim Loslassen wieder dunkler oder gehen ganz aus. Dabei habe ich
doch oben nur die Erkennung der Steigenden Flanke programmiert, oder
irre ich? Eventuell liegt das am Prellen des Tasters? Was ich noch nicht
ganz verstehe: Wenn erstmalig eine Flanke erkannt wurde, müssten danach
nicht E1, M1 und M2 wieder zurückgesetzt werden?
Über weitere Hinweise würde ich mich freuen.
Freundliche Grüße, bendd
Ben D. schrieb:> Und da verließen sie Ihn.
2 Dinge
* Dir ist aber schon klar, dass dein Ocr Wert nur im Bereich 0 bis 128
Gültigkeit hat. Wenn du zu oft dekrementierst, dann wird der Wert
irgendwann 1, dann 0, dann 255 und damit bist du ausserhalb des
zulässigen Bereichs.
* Simulier dein Programm doch mal im Simulator durch. Gerade
Tastendrücke lassen sich doch recht gut simulieren, indem man zu
beliebigen Zeitpunkten am Simulator-Pin Port den entsprechenden Eingang
ein bzw,. ausschaltet. Immer im Hinterkopf haben, dass die I/O Sache
wesentlich langsamer als der µC von statten geht. D.h. du schaltest im
Simulator den Pin von 1 auf 0 und drehst dann mit F10 im
Einzelschrittbetrieb ein paar Runden durch die Hauptschleife, ehe du
dann im Simulator die 'Taste wieder loslässt' indem du den Pin wieder
von 0 auf 1 zurückschaltest und wieder mit F10 ein paar Runden durch die
Hauptschleife drehst und dir ansiehst, was da warum passiert.
Die Vorgabe ist recht eindeutig: Wenn du den Pin von 1 auf 0 änderst
dann muss einmalig der Codeteil mit dem Dekrement durchlaufen werden.
Aber wirklich nur einmalig, wenn du mit dieser Eingangspinstellung
weitere Runden durch die Hauptschleife drehst, dann darf das nicht mehr
passieren. Und wenn du dann den Pin von 0 wieder zurück auf 1 stellst,
dann darf ebenfalls nichts passieren. Erst beim nächsten mal umstellen
von 1 auf 0 gehts dann wieder beim Dekrement durch. Sicherheitshalber
nach jeder Pin-Umstellaktion immer ein paar Runden durch die
Hauptschleife drehen lassen, damit du auch siehst, ob sich das auch
wirklich so entwickelt. Denn dein µC macht das ja auch: In der Zeit, in
der du den Taster niederdrückst, arbeitet er die Hauptschleife ein paar
Hunderttausendmal ab.
> irre ich? Eventuell liegt das am Prellen des Tasters?
So ein Taster prellt ein paar mal. Bei halbwegs unverbrauchten Tastern
bewegt sich das mit einiger SIcherheit im Zahlenraum kleiner als 10.
D.h. du wirst deine gewünschte Helligkeit vielleicht nicht punktgenau
einstellen können, aber zumindest so einigermassen. Ob dein Ocr wert um
3 oder 4 zu hoch/niedrig ist, spielt bis auf die ganz kleinen Werte (und
bei Overflows/Underflows) in der Helligkeit der LED nicht so sehr die
große Rolle.
Ich kann dir nur empfehlen im Simulator ein paar Runden zu drehen und da
mal simulierte Tastendrücke durch Manipulation des PIN Registers
'einzuspielen' und dir anzusehen ob das was du über Flankenerkennung
gelernt hast mit dem übereinstimmt, was du programmiert hast. Daraus
beziehst du deinen Haupt-Lerneffekt und nicht durch das reine Lesen
eines Tutoriums-Artikels. Der Artikel ist schon gut - aber nichts geht
über zurücklehnen; nachdenken was man gelesen hat; nachdenken darüber
was die Grundidee vom Code ist; diese Idee dann programmieren und
verifizieren, dass man die Idee sowohl richtig verstanden als auch
richtig umgesetzt hat.
Ja, das ist mühsam. Aber den olympischen Marathonlauf gewinnen zu wollen
erfordert ebenfalls mühsame Vorbereitung.
Und noch was: Ehe es ans simulieren geht musst du eine ganz klare
Vorstellung davon haben, wie der Prozess eigentlich laufen soll, welche
Bits in welchem Register sich eigentlich wie ändern sollen damit das
Endergebnis entsteht.
Hallo Ben
Ich habe sowas schonmal für einen ATtiny2313 geschrieben. Es werden die
RGB Leds mittels 10Bit Sofware PWM angesteuert. Über einen Encoder mit
integriertem Taster können 4 Modi ausgewält werden. Die Modi werden je
mit einer LED angezeigt.
TASTE | Einstellung | ENCODER
--------------------------------------------------
MODE0: |AUTO Farbwechsel | [ENC: +/- Fadetime]
MODE1: |Helligkeit (V) | [ENC: +/- Helligkeit]
MODE2: |Sättigung (S) | [ENC: +/- Sättigung]
MODE3: |Farbe (H) | [ENC: +/- Farbe]
Ich könnte das mit ein wenig Anpassungen auch in einen ATmega8
implementieren. Statt des Encoders kann man auch Tasten verwenden.
Welche Ports und Pins benutzt du?
Gruß Steffen
Ben D. schrieb:> Nun werde ich mich wohl mal an die Flankenerkennung setzen müssen
Die Entprellung/Flankenerkennung hättest Du zuerst machen müssen.
Man schreibt Programme nicht in einem Rutsch, sondern Aufgabe für
Aufgabe. D.h. man unterteilt die Gesamtfunktion zuerst mal in einzelne
Teilaufgaben.
Vergiß also alles bisher gemachte.
Nimmm eine Taste + eine LED und programiere es so, daß jeder Tastendruck
die LED an/aus wechselt. Und das zuverlässig, egal, wie lange man
drückt.
Und wenn das läuft, dann für 2 Tasten + 2 LEDs unabhängig. D.h. auch
wenn man eine Taste gedrückt hält, muß die andere trotzdem ihre LED
wechseln.
Karl Heinz Buchegger schrieb:> * Dir ist aber schon klar, dass dein Ocr Wert nur im Bereich 0 bis 128> Gültigkeit hat. Wenn du zu oft dekrementierst, dann wird der Wert> irgendwann 1, dann 0, dann 255 und damit bist du ausserhalb des> zulässigen Bereichs.
Das ist richtig und das ist mir auch bewusst. Das sollte ja aber nach
dem ersten Betätigen eigentlich noch nicht passieren.
Werde, wenn ich heute Nachmittag daheim bin mich wieder ransetzen und
das in Ruhe ansehen, im Simulator.
Steffen H. schrieb:> Ich könnte das mit ein wenig Anpassungen auch in einen ATmega8> implementieren. Statt des Encoders kann man auch Tasten verwenden.>> Welche Ports und Pins benutzt du?
Das wäre super und würde mir sehr helfen. Die LED sind (R-G-B) an PB
1-2-3, die Taster an PD0-3. Gerne würde ich aber trotzdem verstehen wie
das alles funktioniert, keine Frage.
Peter Dannegger schrieb:> D.h. man unterteilt die Gesamtfunktion zuerst mal in einzelne> Teilaufgaben.
Das ist mir bekannt, habe vorher schon etwas programmiert, nur nicht so
viel in ASM. Hatte mir die PWM als erstes großes Teilstück vorgenommen,
die läuft ja auch. Jetzt geht es noch an die Auswertung der Taster und
dann an das Fading Programm. Das klingt vielleicht etwas blauäugig, aber
mir scheint das durchaus logisch.
Peter Dannegger schrieb:> Vergiß also alles bisher gemachte.> Nimmm eine Taste + eine LED und programiere es so, daß jeder Tastendruck> die LED an/aus wechselt. Und das zuverlässig, egal, wie lange man> drückt.
Ungerne würde ich alles vergessen, aber ich verstehe worauf du
hinauswillst. Wenn ich wieder daheim bin werde ich mir das mal so
vornehmen, ohne den restlichen Code. Das kann ja später zusammengebaut
werden.
Danke für die vielen Hinweise,
freundliche Grüße, bendd
Ben D. schrieb:>> D.h. man unterteilt die Gesamtfunktion zuerst mal in einzelne>> Teilaufgaben.> Das ist mir bekannt, habe vorher schon etwas programmiert, nur nicht so> viel in ASM. Hatte mir die PWM als erstes großes Teilstück vorgenommen,> die läuft ja auch. Jetzt geht es noch an die Auswertung der Taster und> dann an das Fading Programm. Das klingt vielleicht etwas blauäugig, aber> mir scheint das durchaus logisch.
Peter hat recht.
Leg die PWM erst mal zur Seite. Das ist im Moment noch zuviel Code, der
dir die Sicht aufs momentan Wesentliche verstellt.
Jetzt geht es erst mal nur darum, in einem eigenen Programm zu lernen,
wie man Tastendrücke auswertet.
Gleich in einem einzigen Programm 5 Techniken erlernen zu wollen, geht
meistens schief. Lern erst mal losgelöst von deinem eigentlichen Ziel
wie eine bestimmte Technik funktioniert. In einem Programm, dessen
Hauptfokus nur auf dieser einen Technik liegt. Damit ein wenig
rumspielen, bis du das Gefühl hast, du hast auch im Detail verstanden,
wie diese Technik funktioniert und wie man sie aufbaut.
> vornehmen, ohne den restlichen Code. Das kann ja später zusammengebaut> werden.
Genau.
Das machen Profis auch nicht anders. Ehe ich eine komplexe Technik im
Produktionscode einsetze, verschaffe ich mir erst mal einen Überblick in
einem eigenen Testprogramm, in dem ich mit dem neuen Teil spiele, mir
ansehe wie das alles funktioniert, ob es überhaupt funktioniert, wo die
Stärken, wo die Schwächen liegen, welches die Knackpunkte sind. Und da
kann es dann auch schon mal sein, dass ich 3 oder 4 Testprogramme mache
um Verfahren zu vergleichen.
Programme schreibt man nicht, indem man Codezeile an Codezeile pappt.
Programme schreibt man indem man Module bzw. Programmideen bzw.
komplette Bausteine auf konzeptioneller Ebene miteinander kombiniert.
Die Codezeilen sind dann nur Abfallprodukt, aber am Anfang stehen die
Bausteine, die Building Blocks. Und die muss man intim kennen.
Hallo, Danke für die Beiträge. Kam die letzten Tage nicht dazu, habe
mich eben erst wieder rangesetzt, rausgekommen ist das (erstmal nur für
eine Farbe)
1
loop:
2
sbic PinB, LED_R ; skip wenn LED aus ist
3
rjmp rot_aus ; sonstzu rot_aus, um bei Tastendruck auszuschalten
4
sbic PinD, T0 ; skip, wenn Taster gedrückt wird
5
rjmp loop ; ansonsten springe zu loop
6
sbi PortB, LED_R ; LED an
7
rjmp loop ; von vorne
8
9
rot_aus:
10
sbic PinD, T0 ; skip wenn Taste gedrückt
11
rjmp rot_aus ; ansonsten wieder abfragen
12
cbi PortB, LED_R ; LED aus
13
rjmp loop ; ganz nach oben
Klappt im Prinzip, nur nicht zuverlässig. Liegt vermutlich wieder daran,
dass der µC schneller rennt als ich drücken kann. Wie kann ich das
umgehen? Geht das wirklich nur durch das Einführen der 3
"Statusregister" (E1, M1,M2) wie im Tutorial? So richtig leuchtet mir
das nicht ein.
Das als kleiner Fortschrittsbericht, werde mir das mit E1,... nun
nochmal ansehen. Über Hinweise freue ich mich.
Freundliche Grüße, bendd.
Ben D. schrieb:> Klappt im Prinzip, nur nicht zuverlässig. Liegt vermutlich wieder daran,> dass der µC schneller rennt als ich drücken kann. Wie kann ich das> umgehen?
Indem du realisierst, dass ein Tastendrück aus 2 Teilen besteht. Im
ersten Teil wird die Taste niedergedrückt, im zweiten Teil wird die
Taste losgelassen.
Du programmierst immer noch:
wenn die Taste gedrückt IST
(und kämpfst damit, dass für deinen µC die Taster tatsächlich bei 10000
Überprüfungen in der Sekunde sich als gedrückt präsentiert. Das ist nun
mal so. Wir Menschen sind schnarchlangsam aus Sicht des µC)
und nicht: wurde die Taste niedergedrückt (hat sie ihren Zustand
verändert).
Oder alternativ: wenn eine Taste niedergedrückt IST dann mach was, aber
danach warten wir erst mal darauf dass der Benutzer die Patschehändchen
auch wieder von der Taste runter nimmt - denn erst dann ist dieser
Tastendruck vollständig und erst dann gilt es wieder was, wenn die Taste
das nächste mal gedrückt ist.
(wenn da nicht das leidige Tastenprellen wäre)
> Geht das wirklich nur durch das Einführen der 3 "Statusregister"> (E1, M1,M2) wie im Tutorial?
Wo hast du dieses E, M1, M2 eigentlich her?
Aus unserem Tastentutorial? hmm. kommt mir komisch vor. An E, M1 oder M2
kann ich mich nicht erinnern. Und wichtige "Variablen" würde ich auch
nicht so benennen.
AVR-Tutorial: Tasten
Karl Heinz Buchegger schrieb:> Du programmierst immer noch:> wenn die Taste gedrückt IST
Ich glaube jetzt hats Klick gemacht. Danke.
Karl Heinz Buchegger schrieb:> Wo hast du dieses E, M1, M2 eigentlich her?
Das hatte ich aus dem Artikel mit der Flankenerkennung
(http://www.mikrocontroller.net/articles/Flankenerkennung), habe eben es
mit dem Beispiel aus dem Artikel Tasten versucht, das sieht schon viel
besser aus, werde den jetzt mal bis zum Schluss durcharbeiten und hoffe,
dass ich dann ein Stück näher am Ziel bin...muss mich aber sicher
nochmal melden.
Danke für die Hilfe bisher!
Na da hätte ich auch eher mal Lesen können, dann hätte ich mir einiges
an unnötigen Versuchen gespart. Also die Auswertung der Taster klappt
jetzt auf der Grundlage des Tasten Tutorials
einwandfrei.(http://www.mikrocontroller.net/articles/AVR-Tutorial:_Tasten#Einfache_Tastenentprellung_und_Abfrage)
Wenn mein Gehirnschmalz wieder nachgelaufen ist versuche ich mich dann
daran, die PWM einzubinden.
Dafür würde ich dann zusätzlich den zweiten 8Bit Timer anwerfen wollen,
und die PWM von oben entsprechend anpassen. In der Main müssten dann die
Tastendrücke so ausgewertet werden, dass je nach Druck auf die entspr.
Taste für R/G/B jeweils der OCR Wert für R/G/B geändert wird.
Die Interrupt-Routinen für die Tastenauswertung und die PWM würden dann
"nebeneinanderher" laufen.
Stimmt das soweit, kann das klappen?
Über Antworten und Hinweise würde ich mich freuen,
freundliche Grüße, bendd.
Hallo!
Habe nun versucht die beiden für sich laufenden Codes miteinander
zusammenzuführen. Das ist noch nicht komplett fertig gelungen. Habe den
Code der Tastenauswertung um die Definitionen für die PWM erweitert, die
Initialisierung soweit angepasst, und den Timer2 konfiguriert.
Dann hatte ich testweise in die Main eine Abfrage in der Art wie es bei
dem Tastenprogramm auch war, um eines der OCR zu dekrementieren, hat
erwartungsgemäß nicht geklappt, keine Reaktion auf Tastendrücke. Die PWM
an sich läuft aber, man kann nur keine Änderungen der OCR vornehmen, was
dafür spricht dass wenn die PWM läuft die Tastenauswertung nicht mehr
funktioniert.
Dann habe ich alles so gelassen, nur den Timer2 stillgelegt und die Main
wieder zurückgeändert, dann lief es wieder wie vorher, also Taste1x
Licht an, nochmal drücken Licht aus. Ich vermute also, dass sich die
Timer ins Gehege kommen mit ihren Interruptroutinen. Habe auch mit den
Prescalern gespielt, das brachte keine Änderung. Habt ihr einen Tip, wie
ich das nebeneinanderher zum Laufen bekomme? Über Hinweise würde ich
mich freuen,
freundliche Grüße, bendd
Jetzt ist die Datei 2mal reingerutscht, kann das aber irgendwie nicht
rückgängig machen.
Ben D. schrieb:> Dann hatte ich testweise in die Main eine Abfrage in der Art wie es bei> dem Tastenprogramm auch war, um eines der OCR zu dekrementieren
Wo genau machst du das in deinem Code - also das Dekrementieren?
> erwartungsgemäß nicht geklappt, keine Reaktion auf Tastendrücke. Die PWM> an sich läuft aber, man kann nur keine Änderungen der OCR vornehmen, was> dafür spricht dass wenn die PWM läuft die Tastenauswertung nicht mehr> funktioniert.> Dann habe ich alles so gelassen, nur den Timer2 stillgelegt und die Main> wieder zurückgeändert, dann lief es wieder wie vorher, also Taste1x> Licht an, nochmal drücken Licht aus. Ich vermute also, dass sich die> Timer ins Gehege kommen mit ihren Interruptroutinen.
Im
timer2_overflow:
solltest du auf jeden Fall das SREG sichern und am Ende
wiederherstellen. Das SREG muss man praktisch so gut wie immer in einer
Interrupt Routine behandeln. Alle anderen Register nur dann, wenn es
Überschneidungen mit gleichen Registern in anderen Funktionsblöcken
gibt. Aber SREG ist ein muss.
In deinem zu allererst geposteten Beispiel kann man nur deshalb auf
diese Behandlung verzichten, weil hier
1
loop:
2
sbis PortD, 0
3
rjmp loop
4
/* dec ocr_1
5
nop*/
6
rjmp loop
nichts davon abhängt, dass zum Beispiel das Zero-Flag seinen Wert nicht
ändert. Den Fall hast du aber hier
1
main:
2
cli ;
3
mov temp1, key_press ; Einen ev. Tastendruck merken
4
clr key_press ; und zurücksetzen
5
sei
6
cpi temp1, 0 ; Tastendruck auswerten. Wenn eine der Tasten
7
breq main ; gedrückt worden wäre, wäre ein entsprechendes
8
; Bit in key_press gesetzt gewesen
9
....
10
11
rjmp main
hier nicht mehr. Da sind Compare und BREQ drinnen. Und die hängen davon
ab, dass der BREQ auch wirklich das Ergebnis vom CPI sieht und nicht
zwischendurch ein Interrupt die entsprechenden Flags zerstört hat.
setzen, damit sicher gestellt ist, dass der tatsächliche Code dann nicht
in der Interrupt Tabelle steht und du nicht von einer falschen
Reihenfolge der Sprungbefehle in der Angabe der rjmp gebissen wirst
1
.org OVF2addr
2
rjmp timer2_overflow ; Timer2 Overflow Handler
3
.org OVF0addr
4
rjmp timer0_overflow ; Timer0 Overflow Handler
5
6
.org INT_VECTORS_SIZE
7
8
timer2_overflow:
9
inc PWMCount ; den PWM Zähler von 0 bis
10
cpi PWMCount, 128 ; 127 zählen lassen
11
brne WorkPWM
12
....
Du musst ein bischen mehr tun als einfach nur Codeteile
zusammenzukopieren. Grundsätzlich halte ich von dieser Vorgehensweise
nicht sehr viel. Der IMHO richtige Denkansatz wäre, dass man ein neues
Programm schreibt und das komplett frisch aufbaut. Man kann sich
einzelne Codeteile per Copy&Paste reinkopieren, aber die müssen IMMER
angepasst werden. Copy&Paste ist also als Erleichterung zur Einsparung
von Tipparbeit zu sehen. Aber einfach nur Codeteile in ein File
zusammenzukopieren und dann zu denken, das muss doch funktionieren ...
das geht meistens schief. Gerade in Assembler, in dem man sich noch viel
mehr als in C um den ganzen Kleinkram kümmern muss. Assembler erfordert
sehr viel Disziplin und Beachtung von Details. Mit 'einfach mal so
zusammenkopieren' ist in Assembler nicht viel.
Danke für die schnelle Antwort!
Karl Heinz Buchegger schrieb:> Wo genau machst du das in deinem Code - also das Dekrementieren?
Das hatte ich dann erstmal wieder rausgeschmissen, weil es so nicht
geklappt hatte. In dieser Version ist es wieder da.
Karl Heinz Buchegger schrieb:> solltest du auf jeden Fall das SREG sichern und am Ende> wiederherstellen. Das SREG muss man praktisch so gut wie immer in einer> Interrupt Routine behandeln. Alle anderen Register nur dann, wenn es> Überschneidungen mit gleichen Registern in anderen Funktionsblöcken> gibt. Aber SREG ist ein muss.
Danke für diesen Hinweis. Ich hatte da sowas im Hinterkopf, darum habe
ich auch drauf geachtet, dass die beiden Routinen unterschiedliche
Register benutzen. An das SREG hab ich dann aber nicht mehr gedacht.
Karl Heinz Buchegger schrieb:> Du musst ein bischen mehr tun als einfach nur Codeteile> zusammenzukopieren.
Das ist mir bewusst. Ich bin froh inzwischen gaanz langsam
durchzusteigen, und zu verstehen was passiert, und was beachtet werden
muss. Das ist trotzdem ziemlich schwierig für mich und irgendwie traue
ich mir nicht zu dass das klappt, wenn ich jetzt von ganz vorne anfange.
Vielleicht ist das dämlich, aber ich denke wenn ich was habe was bereits
klappt, dann müsste das ja einfacher ineinander integrierbar sein, als
das alles komplett neuzumachen.
Karl Heinz Buchegger schrieb:> Man kann sich> einzelne Codeteile per Copy&Paste reinkopieren, aber die müssen IMMER> angepasst werden.
Das sehe ich genauso, und das habe ich bisher auch so versucht. Leider
eben nicht so erfolgreich, weil es schwierig ist, alles zu bedenken was
zu bedenken ist, einfach aus Mangel an Erfahrung.
Karl Heinz Buchegger schrieb:> Aber einfach nur Codeteile in ein File> zusammenzukopieren und dann zu denken, das muss doch funktionieren ...
Im Leben nicht, das hab ich nie gedacht. Ich hab bisher versucht alles
was ich gelesen habe sogut wie möglich auf mein Projekt anzupassen. Bei
Problemen erhoffe ich mir Hinweise von Leuten, die mehr Erfahrung und
Ahnung von der Sache haben, als ich.
Dieser Austausch klappt ja auch gut, und dafür bin ich auch wirklich
dankbar, kann mir aber durchaus vorstellen dass solche Anfänger nervig
sind.
Ich habe deine Tips nachvollzogen und eingebaut, diesmal auch den
Decrement des OCR, leider passiert da trotzdem nichts.
Vielleicht mag nochmal jemand drüber schauen, ich lass mich gerne mit
dem Zaunspfahl belehren;)
Freundliche Grüße, bendd
Also ich hab nochmal intensiver nachgedacht, und ich kann es mir nicht
so recht erklären, wieso das nicht klappt.
Die PWM läuft, denn wenn ich einen beliebigen Wert <128 händisch in ein
ocr schreibe, werden die LED entsprechend gedimmt. Nun nehme ich mir den
Code aus der Main her, so wie er in der Tastenauswertung funktioniert
hat:
1
main:
2
cli ;
3
mov temp1, key_press ; Einen ev. Tastendruck merken
4
clr key_press ; und zurücksetzen
5
sei
6
cpi temp1, 0 ; Tastendruck auswerten. Wenn eine der Tasten
7
breq main ; gedrückt worden wäre, wäre ein ; entsprechendes
8
; Bit in key_press gesetzt gewesen
9
eor leds, temp1 ; Die zur Taste gehörende Led umschalten
10
11
out led_port, leds
12
rjmp main
So wie ich das nun sehe, steht im Register temp1 nun immer nach
Ausführen der Interruptroutine von Timer0, wenn eine Taste als gedrückt
erkannt wurde an deren Position eine 1. Das müsste ich doch nun nutzen
können, um anstatt diese 1 direkt auf den LED Port zu geben, das
jeweilige ocr zu dekrementieren.
Dafür hab ich die letzten drei Zeilen genommen und wie folgt geändert
1
sbrc temp1, 0
2
dec ocr_r
3
rjmp main
Sprich: Überspringe den nächsten Befehl, wenn Bit0 in temp1 0 ist. Ist
das Bit=1, dann dekrementiere den auf 128 stehenden ocr_r 1mal. (so wie
sonst der LED-Port 1x umgeschalten wurde. Also sollte dann die LED zu
leuchten beginnen, wenn auch nur schwach. Es wäre sichtbar, das hab ich
probiert.
Um Denkfehler bzgl. low und high auszuschließen habe ich auch sbrs
temp1, 0 getestet, da passiert auch nichts.
Langsam verzweifle ich wirklich. Liegt hier in der Main ein Denkfehler,
oder hängt es woanders?
Der Interrupt für die Tastenauswertung tritt alle 1024*255=261120 Takte
auf. Der für die PWM alle 255 Takte, wenn ich mich nicht irre. Da müsste
doch genug Zeit dazwischen sein? So richtigen Rat weiß ich nicht mehr.
Von ganz vorne zu programmieren, ich hab es vorhin nochmal versucht,
aber bin dann auch nur wieder dabei gelandet, die Codes die ich schon
habe ineinander zu schachteln, was ja auch wieder auf obigen
herausläuft.
Wenn ich irgendeinen Hinweis bekommen könnte, was noch verquer sein
könnte, wäre ich sehr froh.
Freundliche Grüße, bendd
Das ist böse, denn hier im gepostetem Forum-Text machst du es richtig:
Ben D. schrieb:> Dafür hab ich die letzten drei Zeilen genommen und wie folgt geändert
1
sbrc temp1, 0
2
dec ocr_r
3
rjmp main
>Sprich: Überspringe den nächsten Befehl, wenn Bit0 in temp1 0 ist. Ist> das Bit=1, dann dekrementiere den auf 128 stehenden ocr_r 1mal. (so wie> sonst der LED-Port 1x umgeschalten wurde. Also sollte dann die LED zu> leuchten beginnen, wenn auch nur schwach. Es wäre sichtbar, das hab ich> probiert.> Um Denkfehler bzgl. low und high auszuschließen habe ich auch sbrs> temp1, 0 getestet, da passiert auch nichts.> Langsam verzweifle ich wirklich. Liegt hier in der Main ein Denkfehler,> oder hängt es woanders?
Und in deinem gepostetem Code leider falsch.. Kein wunder dass du da am
verzweifeln bist.
Wär ich übrigens auch ;-)
Steffen
Danke für deine Antwort! So ganz verstehe ich aber nicht, worauf du
hinaus willst. Das Bit das ich auswerte, hängt ja vom Taster ab, welchen
ich abfragen will. Da ich an Pin0-3 Taster angeschlossen habe, ist es
also theoretisch egal, welches der 4 Bits ich abfrage, ich muss nur den
zugehörigen Taster treffen. Und ich habe in jeder Version die ich
getestet habe alle Taster durchprobiert und auf Reaktion gehofft...
Oder meinst du was ganz anderes und wir reden aneinander vorbei?
Freundliche Grüße, bendd
Karl Heinz Buchegger schrieb:> gratuliere. Du hast dir soeben das zuvor gesetzt TOIE0 Bit im TIMSK> gelöscht.
Der Wahnsinn. Da Bin ich froh das mit den Timern kapiert zu haben,
schlag extra nochmal im DB nach welches Bit wie und wofür, stelle an den
Prescalern rum, und merke das nicht. Man nennt das wohl betriebsblind.
Auf alle Fälle funktioniert jetzt das was ich bisher habe so wie es
soll.
Vielen Dank! Nächster Schritt (und das genügt dann wohl für heute) sind
die anderen beiden Farben, und dann wird ergründet wie das mit der
Nichtlinearität des menschlichen Auges lösbar ist. Einen riesen Dank für
alle Tips und Hinterkopfschläge bisher!
Grüße, bendd.
Wow, den hab ich auch nicht entdeckt. Hab den TOV0 im Simulator immer
manuell ausgelöst, weil ich nicht immer so lange warten wollte.
Der Karl Heinz.. , super - gratuliere.
Gruß Steffen
Hallo!
Nur ein kurzer Status, viel hab ich nicht geschafft. Also das Regeln der
3PWM Kanäle über die Taster klappt einwandfrei, das motiviert schonmal.
Nun habe ich mich ein wenig in die Thematik mit der Helligkeit und dem
menschlichen Auge eingelesen und es eröffnet sich mir die Frage, wie ich
das am günstigsten umsetzen kann. Habe eine Exceltabelle, welche mir die
Helligkeitsstufen ausrechnet, so rein zum Verständnis. Jetzt stellt sich
mir die Frage wie ich das dem µC beibringe. 2 Möglichkeiten fallen mir
ein, einmal die Berechnung "live" auf Grundlage des aktuellen
PWM-Wertes, und einmal eine Tabelle im Speicher. Was ist da günstiger?
Bin nicht sicher, aber was ich so gelesen habe kann ich mit ASM nicht
potenzieren, bzw. nur schwierig. Also geht die Tendenz in Richtung
Tabelle.
Woher aber weiß dann meine jeweilige PWM Stufe, welchen Wert sie aus dem
Speicher holen soll? Für eine leuchtet mir das noch ein: wird ein
Tastendruck registriert, wird der Z-Pointer um eins erhöht und der
gespeicherte Wert geladen und ins OCR geschrieben, fertig. Jetzt gibt es
hier ja aber 3 möglicherweise verschieden helle PWM-Stufen und nur einen
Pointer. Wie kriege ich das gelöst?
Ich stelle mir gerade 3 weitere Zählregister dafür vor, die sich jeweils
den Wert des Z-Pointers merken, und dann anstelle des eigentlichen
Pointers erhöht werden, um dann in den Pointer geladen werden zu konnen.
Da eröffnet sich gleich das nächste Problem, ich bräuchte hier ja 2
Register, da der Pointer 16 Stellen haben will. Das würde schon 6
Register benötigen, und die werden dann ziemlich knapp.
Verrenne ich mich hier und denke viel zu kompliziert? Wie wird das
üblicherweise gemacht?
Wenn das mit der Linearisierung fertig ist und ich es für notwendig
erachte, würde ich gerne noch eine "Halte-Funktion" der Tasten einbauen,
dazu habe ich im Tasten-Tutorial auch schon gelesen. Aber eins nach dem
anderen...
Über Hinweise und Tips würde ich mich freuen. Das ist alles recht neu
für mich und deshalb schwierig, verzeiht mir daher bitte, falls ich sehr
dumme Fragen stelle zu Dingen, die man besser wissen sollte.
Ben D. schrieb:> Also geht die Tendenz in Richtung> Tabelle.
Tabelle ist sicherlich einfacher.
> Woher aber weiß dann meine jeweilige PWM Stufe, welchen Wert sie aus dem> Speicher holen soll? Für eine leuchtet mir das noch ein: wird ein> Tastendruck registriert, wird der Z-Pointer um eins erhöht und der> gespeicherte Wert geladen und ins OCR geschrieben, fertig.
Nein.
Was du brauchst sind 3 neue Werte. level_r, level_g, level_b. Diese
Werte erhöhst du bzw. verringerst du durch Tastendruck.
Mithilfe diese level_x Wertes gehst du dann in die Tabelle und holst dir
den zum jeweiligen level gehörenden ocr Wert, der dann in das zugehörige
ocr_x Register kommt und so die PWM einstellt.
> Ich stelle mir gerade 3 weitere Zählregister dafür vor, die sich jeweils> den Wert des Z-Pointers merken,
Nein.
Den Z-Pointer benutzt du nur temporär um aus dem level Wert den
jeweiligen ocr Wert über die Tabelle zu bestimmen.
Ein Farbkanal besteht also aus 2 Registern.
level_r ocr_r
level_g ocr_g
level_b ocr_b
die level_x Register sind die, die der Benutzer verändert. Die ocr_x
Register sind die, die für die PWM genommen werden. Der Zusammenhang
besteht darin, dass du mithilfe des level Registers und der Tabelle den
Wert für das ocr Register bestimmst (wenn der Benutzer eine Taste
gedrückt hat).
Nach einiger Zeit der Untätigkeit hab ich heute endlich mal wieder Zeit
gefunden, mich heranzusetzen. Der Ansatz von oben leuchtet mir ein, ist
um einiges sinnvoller als meine Idee...
Nun habe ich, wie soll es anders sein, mit der Umsetzung ein paar
Schwierigkeiten. Zuerst habe ich mir die "Tabelle" zurechtgeschnitten
und eingefuügt, schließlich den Z-Pointer drauf gesetzt. Auch die
level_x sind eingeführt, haben aber noch keine Funktion, außer, dass sie
bei Tastendruck erhöht werden. Um zu sehen ob das mit dem Z-Pointer so
klappt, habe ich den level auch erstmal vernachlässigt. Nun soll beim
Druck der Rot-Taste der erste Wert ins ocr_r geladen werden. einmal habe
ich das versucht mit
1
lpm
2
mov ocr_r, r0
3
...
und einmal mit
1
lpm ocr_r, Z+
Beide Male Dunkelheit bei Tastendruck, klappt also nicht. Dann dachte
ich mir, ist ja logisch, bei 128 im ocr ist die LED ja aus. Also habe
ich die 111 als ersten Wert der Tabelle platziert, bei beiden o.g.
Versuchen aber das gleiche Ergebnis, es passiert nichts. Was mach ich
verkehrt?
Und wenn das läuft, wie bekomme ich die level_x mit dem Z-Pointer
verknüpft? Simples Addieren ist wohl zu einfach...
Über Hinweise würde ich mich freuen,
freundliche Grüße, bendd
Dämlich, darauf habe ich gar nicht geachtet, danke für den Hinweis. Hab
die betreffende Stelle mal schnell rausgenommen, jetzt klappts vom
Prinzip. Werde das mal umstricken. Danke!
Dank des Tips oben bin ich ein ganzes Stück weitergekommen, freut mich
ungemein. Habe es hinbekommen alle 3Farben ungefähr gemäß der Kennline
des menschl. Auges erhellen zu lassen, klappt wunderbar.
Ich frage mich nur ob das auch eleganter gegangen wäre als in meinem
Code. Ein Manko hier ist auch, dass sobald eines der level_x größer als
16 war, die anderen beiden level mit zurückgesetzt werden. Das
erschließt sich mir noch nicht ganz wieso das passiert, bin wohl wieder
betriebsblind. Eventuell seht ihr hier was? Werde erstmal wieder
Hirnschmalz aufsammeln, dann mach ich weiter. Nächstes großes Ziel ist
dann das Fading, das wird sicher nochmal ein Akt.
Allein hätte ich es sicher nichtmal bis hierher geschafft, danke dafür!
Karl Heinz Buchegger schrieb:> Sobald du an ein Register mit IN/OUT ran kommst, kannst du auch einen> sbis oder eine andere der Operationen sbi, cbi, sbis, sbic drauf> anwenden.
Das stimmt einfach nicht. IN/OUT geht von Registeradresse 0 bis 63
($3f). SBIS und Konsorten hingegen funktionieren nur für
Registeradressen von 0 bis 31 ($1f), also gerade mal die Hälfte des
Bereiches, der mit IN bzw. OUT zu erreichen ist.
Typisches Beispiel: SREG. Dessen IO-Adresse ist bei (nahezu?) allen
Megas $3f. Läßt sich per IN/OUT lesen und schreiben, aber nicht per
SBIC/SBIS/SBI/CBI.
Empfehlenswerte Lektüre: "AVR Instruction Set Reference". Downloadbar
bei Atmel. Da steht u.a. sowas drin.
Hallo!
Habe mir meinen Code nochmal in Ruhe angesehen, aber kann mir immer noch
nicht erklären warum alle Lichter ausgehen, sobald eine der
Over-/Underflow-Routinen aufgerufen wird. Es ist doch so (am Beispiel
von B und G)
Angenommen B steht auf level=2, also ocr_b=123. Dann will ich G
dazumischen und drücke ausversehen 1x zu viel. Level_g wird=16 und
infolgedessen wird level_g_uf aufgerufen. Hier wird level_g=0 und der
Pointer wieder auf den Anfang der Tabelle gesetzt, damit beim nächsten
Aufruf der Main nach Tastendruck der Pointer nicht ins Leere zeigt. Die
anderen beiden Levelwerte werden hier nicht angerührt, trotzdem scheinen
sie mit zurückgesetzt zu werden, wieso?
Theoretisch müsste durch den Rücksprung in die Main doch der Rest
weiterlaufen wie vorher? An der Berechnung der Speicheradresse des
jeweiligen Wertes ändert sich ja durch das Rücksetzen des Pointers
nichts? Das mache ich doch in der Main auch nicht anders?
Kann mir hier jemand weiterhelfen? Das würde mich freuen.
Freundliche Grüße, bendd
Hi!
Habe mich nun, da die manuelle Farbeinstellung läuft, mal dem
Fadingprogramm gewidmet. Hier hätte ich wieder 2 mögliche Ansätze. Der
erste ist vom Code her vermutlich einfacher:
Ich baue eine Schleife die hintereinander die verschiedenen Farben und
Mischfarben durchfadet. Problem ist hier, dass ich den Controller sehr
bremsen muss, um überhaupt sichtbares Fading zu erhalten. Also immer
wieder Warteschleifen einbauen, nicht sehr effizient und während der
Warteschleife kann der Chip nichts anderes machen...
Bisher habe ich das auch so versucht und bin auf obige Probleme
gestoßen. Konkret funktioniert die Schleife offenbar, aber ich sehe eben
nur eine Art weiß.
Ansatz 2: Ich aktiviere den dritten Timer und löse mit dem Interrupt den
jeweiligen Farbwechsel aus. Hier beginnt der Spaß, da ich nun nicht
einfach die Schleife von oben in den Timer packen kann. Viel mehr muss
ich jetzt mit jeder Menge Vergleichen arbeiten, um die Funktion so zu
bekommen wie ich will. Das stelle ich mir etwa so vor, normales
Rainbowfading vorrausgesetzt (Rot=ocr_r, Gelb, Grün=ocr_g, Türkis,
Blau=ocr_b, Lila)
Beginn mit ocr_r=128, also LED aus.
solange ocr_r>0, wird dekrementiert
ist ocr_r=0, wird begonnen, ocr_g von 128 bis 0 zu dekrementieren
und soweiter, also gleiches Prinzip, aber immer nur 1 Schritt in der
Helligkeit pro Durchlauf der ISR.
Kann das so was werden? Oder geht das geschickter und einfacher?
Rein rechnerisch mit Prescaler 1024 habe ich dann alle
65535*1024=67107840 Takte einen Aufruf der Routine, bei 8MHZ also schon
tendenziell eher zu langsam, den Prescaler kann man ja aber
runtersetzen.
Aber zum Prinzip, ist das der richtige Weg?
Anbei noch das File mit der Schleife aus Ansatz 1.
Über Hinweise würde ich mich freuen.
Freundliche Grüße, bendd
Hi!
Also bin mit Ansatz 2 ein ganzes Stück weitergekommen. Der Timer läuft,
Faden klappt vom Prinzip her auch. Aber ich komme nicht über die ersten
2 Farben hinaus:
1
timer1_overflow:
2
3
cpi ocr_r, 0 ; Vergleiche ocr_r mit 0
4
breq gruen_auf ; wenn rot voll an (=0), soll gruen auffaden
5
cpi ocr_g, 0 ; wenn gruen voll an
6
breq rot_ab ; soll rot abfaden
7
//reti
8
rjmp rot_auf ; ansonsten weiter rot auffaden
9
rot_auf:
10
dec ocr_r
11
reti
12
gruen_auf:
13
dec ocr_g
14
reti
15
rot_ab:
16
inc ocr_r
17
reti
18
19
reti
Nun komme ich aber nicht weiter, weil als nächstes eigentlich grün
kommen sollte. D.h.: Rot ausfaden. Aber wie komme ich da hin?
Das
1
cpi ocr_g, 0 ; wenn gruen voll an
2
breq rot_ab ; soll rot abfaden
klappt nicht so ganz, da hab ich wieder einen Knoten im Hirn. Leg das
ganze jetzt erstmal weg für heute, aber vielleicht sieht jemand von euch
hier gleich eine Lösung. Da würde ich mich über Hinweise freuen. Sonst
muss ich (so oder so) weiter dran tüfteln später.
Freundliche Grüße, bendd
Hallo!
Also ganz logisch, um das so hinzubekommen wie ich mir das vorstelle,
muss ich 2 Bedingungen verknüpfen, so wie bspw. bei C und Konsorten
üblich. Da Assembler das von Haus aus offenbar nicht kann, bin ich auf
folgendes Makro gestoßen:
http://www.mikrocontroller.net/articles/AVR_Assembler_Makros#SAM_.28Structured_Assembly_Macros.29
Soweit sogut. Dort habe ich in der Beispielsammlung auch dem für mich
passenden Fall gefunden: ; 2 or more conditions are anded together (all
conditions must be true)
mit folgendem Code:
1
.dseg
2
buffer: .BYTE 400
3
end_buffer:
4
.cseg
5
cpi zh,high(end_buffer)
6
ifeq_and end_buffer_reached
7
cpi zl,low(end_buffer)
8
ifeq end_buffer_reached
9
ldi zh,high(buffer) ;wrap buffer
10
ldi zl,low(buffer)
11
end end_buffer_reached
12
ld r0,z+ ;read buffer
Leider ist das sehr spartanisch dokumentiert, und so wie es da steht,
erklärt es sich mir leider überhaupt nicht. Das Einbinden der Datei
bekomme ich denke ich hin, aber wie nutze ich das Ganze in meinem Sinn?
Hat hiermit schon jemand Erfahrungen gesammelt? Ebenfalls wurde ich auf
das HLASM von IBM aufmerksam, dort verstehe ich sogar die Syntax, das
kost nur leider was. Und für dieses eine Mal würde ich das ungerne auf
mich nehmen...
Bin ich hier auf dem komplett falschen Weg, oder gehts nicht anders?
Über Antworten würde ich mich freuen, bendd
Ben D. schrieb:> Faden klappt vom Prinzip her auch. Aber ich komme nicht über die ersten> 2 Farben hinaus:
1
timer1_overflow:
2
3
cpi ocr_r, 0 ; Vergleiche ocr_r mit 0
4
breq gruen_auf ; wenn rot voll an (=0), soll gruen auffaden
5
cpi ocr_g, 0 ; wenn gruen voll an
6
breq rot_ab ; soll rot abfaden
7
//reti
8
rjmp rot_auf ; ansonsten weiter rot auffaden
9
rot_auf:
10
dec ocr_r
11
reti
12
gruen_auf:
13
dec ocr_g
14
reti
15
rot_ab:
16
inc ocr_r
17
reti
18
19
reti
> Nun komme ich aber nicht weiter, weil als nächstes eigentlich grün> kommen sollte. D.h.: Rot ausfaden. Aber wie komme ich da hin?
Tja, ich könnt wetten du bekommst zwar rot auf '0' gefaded und danach
grün aber dann springt dir grün wieder auf voll an und begunnt wieder
runter zu faden.
Ist aber auch klar warum..
Du müsstest jetzt in deiner 'grün_auf' auch noch überprüfen, bevor du
decrementierst ob 'ocr_g' schon 0 ist. wenn ja, dann musst du den
nächsten Wert faden oder so.
Wieviel PWM hst du denn schon? Nur ROT und GRUEN?
Gruß Steffen
Nachtrag: Ich weiß nicht was du mit diesen Macro Rotienen für den 32bit
SAM willst.
Hallo!
Steffen H. schrieb:> Tja, ich könnt wetten du bekommst zwar rot auf '0' gefaded und danach> grün aber dann springt dir grün wieder auf voll an und begunnt wieder> runter zu faden.
Ja so ähnlich war es, das Gelb ging wieder aus und Rot sprang im
gleichen Moment an.
Steffen H. schrieb:> Du müsstest jetzt in deiner 'grün_auf' auch noch überprüfen, bevor du> decrementierst ob 'ocr_g' schon 0 ist. wenn ja, dann musst du den> nächsten Wert faden oder so.
Da hatte ich es gestern noch soweit gebracht:
1
timer1_overflow:
2
3
cpi ocr_r, 0 ; Vergleiche ocr_r mit 0
4
breq gruen_auf_1 ; wenn rot voll an (=0), soll gruen auffaden
5
rjmp rot_auf_1 ; ansonsten weiter rot auffaden
6
7
reti
8
9
rot_auf_1:
10
cpi ocr_r, 0 ; wenn rot voll an
11
breq fade_main ; springe in die main
12
dec ocr_r ; ansonsten auffaden
13
reti
14
gruen_auf_1:
15
cpi ocr_g, 0 ; wenn grün voll an
16
breq rot_ab_1 ; springe zu rot_ab_1
17
dec ocr_g ; ansonsten auffaden
18
reti
19
rot_ab_1:
20
cpi ocr_r, 128 ; wenn rot aus
21
breq fade_main ; springe in die main
22
inc ocr_r ; ansonsten rot abfaden
23
reti
Damit blendet es Gelb auf und bleibt stehen, Rot wird nicht mehr
ausgeblendet. Das erschloss sich mir nicht ganz, darum habe ich nach
einem "einfacheren" Weg gesucht und geschaut ob man einfach 2
Bedingungen verknüpfen kann, wie in den Höheren Sprachen. Dabei stieß
ich dann auf das offenbar ungeeignete Macro.
Was übersehe ich? Freundliche Grüße, bendd
Das stimmt, an sowas habe ich gar nicht gedacht, habe mich eben ein
wenig zu dem Thema (aus der ISR springen und nicht zurück) belesen und
das leuchtet eigentlich auch ein.
Aber: Solange eine der Bedingungen die ich stelle erfüllt ist, springt
der Controller ja nicht in die Main, d.h. theoretisch sollte doch rot
wieder ausfaden und der Controller erst dann stehen bleiben?
Wenn das Farbwechsel-Konstrukt fertig ist, sollte es ja so sein, dass
der Controller nie die Fälle von breq fade_main wahrnehmen muss, sondern
immer gleich zur nächsten Dekrement/Inkrementschleife springt.
Momentan passiert das:
1
Durchlauf rot_auf_1 --> gruen_auf_1 --> rot_ab_1
2
ocr_r 0 0 0
3
ocr_g 128 0 0
4
ocr_b 128 128 128
Erwarten würde ich aber das:
1
Durchlauf rot_auf_1 --> gruen_auf_1 --> rot_ab_1
2
ocr_r 0 0 128
3
ocr_g 128 0 0
4
ocr_b 128 128 128
(Zustand jeweils nach Eintritt der Anfangsbedingung)
Offenbar nehme ich jeden Fehler den man machen kann mit. Wenn mir jemand
erklären kann wieso das so passiert wie oben beschrieben wäre ich
dankbar.
Noch ein Gedanke: Wenn rot erfolgreich runtergeblendet wurde
(ocr_r=128), würde das Ganze ja hier schon wieder von vorne beginnen.
Wie kann ich das vermeiden? Der Fall rot war_ an und grün _ist noch an
muss abgefragt werden um hier weiterzukommen. Kriegt man das mit
verschachtelten Bedingungen hin, oder sollte ich besser ein
Statusregister dafür einführen und das ganze in die Richtung umstricken?
Freundliche Grüße, bendd
Ben D. schrieb:> Das stimmt, an sowas habe ich gar nicht gedacht, habe mich eben ein> wenig zu dem Thema (aus der ISR springen und nicht zurück) belesen und> das leuchtet eigentlich auch ein.
vergiss dieses Springen!
Die Interrupt Routine wird dadurch betreten, dass der Timer den
Interrupt generiert und damit den impliziten CALL macht. Und die
Interrupt Routine wird (auf allen Pfaden durch sie!) mit dem RETI
beendet.
Alles andere ist ein 'Ask for trouble'
Genau das ist die Gefahr bei der Assembler-Programmierung: Das man auf
Biegen und Brechen wie wild im Programm von einer Stelle zu einer
anderen Stelle rumspringt. Das führt unweigerlich zur berüchtigten
'Spaghetti-Programmierung', bei der die Sprünge irgendwann so dermassen
wild durcheinander verlaufen, dass kein Mensch mehr entwirren kann, wann
und zu welchem Zeitpunkt welcher Sprung wohin führt.
Die Abhilfe dagegen sind Programmstrukturen, die im wesentlichen dem
entsprechen, was man in Hochsprachen hat: systematische Sprünge, die
Verzweigungen bilden bzw. Schleifen bilden. Aber alles mit Mass und
Ziel. Wilder Rumspringen ist auch in Assembler verboten (selbst wenn es
möglich ist), weil die entstehenden Programmstrukturen nicht mehr
handhabbar sind.
D.h. für dich:
deine komplette Logik wie und in welcher Reihenfolge welches Faden
passiert, findet komplett innerhalb der Interrupt Routine statt: Sie
wird durch den Call vom Timer betreten und sie wird durch den RETI
wieder verlassen. Und dazwischen steht die von dir gewünschte Logik.
Im übrigen:
simulier dein Programm im Simulator! Genau dazu hast du ihn. Du kannst
dort Port-Bits verändern (und so dem Programm einen Input vorgaukeln)
und du kannst auch Timerwerte verändern. Wenn es gar nicht anders geht,
dann setzt man halt zur SImulation zb einen Timer-Vorteiler auf 1, damit
man nicht so lange warten muss, bis in der Simulation dann die
Timer-Bedingung erfüllt ist.
Oder du setzt dir einen Breakpoint auf den Anfang der ISR und lässt den
Simulator mit F5 eigenständig laufen. Oder du machst eine Kombination
aus alle dem. Aber simulier dir die Dinge durch! Dann siehst du live und
in Farbe, welche Register welche Werte haben, wie sie sich verändern und
was du in deiner gedachten Logik übersehen hast.
Auch mit dem Simulator muss man üben, damit man seine Möglichkeiten
einsetzen kann. Irgendwann musst du damit anfangen. Was ja schon mal
sehr sehr gut ist, das ist dass du eine Vorstellung davon hast, was
eigentlich passieren sollte. Jetzt geht es darum herauszufinden, warum
es nicht passiert - wo du deinen Denkfehler in der Umsetzung im Programm
hast.
Karl Heinz Buchegger schrieb:> Die Interrupt Routine wird dadurch betreten, dass der Timer den> Interrupt generiert und damit den impliziten CALL macht. Und die> Interrupt Routine wird (auf allen Pfaden durch sie!) mit dem RETI> beendet.
Ich vergass:
Es ist oft eine ziemlich gute Idee, wenn man den RETI an einer einzigen
Stelle macht. Also nicht so wie hier ...
1
timer1_overflow:
2
3
cpi ocr_r, 0 ; Vergleiche ocr_r mit 0
4
breq gruen_auf_1 ; wenn rot voll an (=0), soll gruen auffaden
5
rjmp rot_auf_1 ; ansonsten weiter rot auffaden
6
7
reti
8
9
rot_auf_1:
10
cpi ocr_r, 0 ; wenn rot voll an
11
breq fade_main ; springe in die main
12
dec ocr_r ; ansonsten auffaden
13
reti
14
gruen_auf_1:
15
cpi ocr_g, 0 ; wenn grün voll an
16
breq rot_ab_1 ; springe zu rot_ab_1
17
dec ocr_g ; ansonsten auffaden
18
reti
19
rot_ab_1:
20
cpi ocr_r, 128 ; wenn rot aus
21
breq fade_main ; springe in die main
22
inc ocr_r ; ansonsten rot abfaden
23
reti
... 4 verschiedene RETI.
Der Grund ist ganz einfach: diene 'Haushaltsführung'.
Denn: noch hast du dich ja zb um das SREG überhaupt nicht gekümmert! Das
musst du aber noch machen! D.h. deine ISR wird so etwas wie eine
Einleitungssequenz haben, in der das SREG gesichert wird. Und es wird so
etwas wie eine Abschlussequenz haben, in der das SREG dann aus der
Sicherung wieder hergestellt werden muss. Das ist allerdings
ungeschickt, wenn du dir diese Dinge (in deinem Fall 4 mal) über den
Code verstreust. Daher: An einer Stelle zusammenfassen und wenn du aus
der ISR raus willst entsprechend anspringen.
1
timer1_overflow:
2
3
push r0
4
in r0, SREG
5
push r0
6
7
cpi ocr_r, 0 ; Vergleiche ocr_r mit 0
8
breq gruen_auf_1 ; wenn rot voll an (=0), soll gruen auffaden
Und da du letzten Endes 3 Farbkanäle umdimmen willst, würde ich dir
folgende Vorgehensweise nahelegen.
Stell dir ein Register ab, in welchem du festhältst, auf welcher
'Fadestrecke' du dich gerade befindest. Einfach eine Zahl von 0 bis (in
dem Fall jetzt) 3
0 bedeutet Rot von 128 auf 0 faden
1 bedeutet Grün von 128 auf 0 faden
2 bedeutet Grün von 0 auf 128 faden
3 bedeutet Rot von 0 auf 128 faden
je nachdem, welche Zahl in diesem Register vorliegt, verweigst du
innerhalb der ISR zum jeweiligen Teil, machst die einzelne Aktion und
stellst dann noch fest ob damit die Fadestrecke vollständig abgearbeitet
wurde, und wenn ja, weißt du diesem Register einen anderen Wert aus dem
Wertebereich 0 bis 3 zu, so dass beim nächsten ISR Aufruf dann eben eine
andere Fadeaktion durchgeführt wird.
Da du im Endeffekt 3 Farbe koordinieren musst und dazu eine
'Choreographie' der einzelnen Fading-Aktionen in der richtigen
Reihenfolge brauchst, ist das die einfachste Möglichkeit, das
hinzukriegen.
timer1_overflow:
push r0
in r0, SREG
push r0
cpi do_fade, 0
breq rot_auf
cpi do_fade, 1
breq gruen_auf
cpi do_fade, 2
breq gruen_ab
cpi do_fade, 3
breq rot_ab
jmp exit_timer1_overflow
rot_auf:
dec ocr_r ; rot verringern
brne exit_timer1_overflow ; 0 erreicht? Nein -> fertig
ldi do_fade, 1 ; als nächstes Grün dimmen
jmp exit_timer1_overflow
gruen_auf:
dec ocr_g ; grün verringern
brne exit_timer1_overflow ; 0 erreicht? Nein -> fertig
ldi do_fade, 2 ; als nächstes Grün wieder im Wert
erhöhen
jmp exit_timer1_overflow
gruen_auf:
inc ocr_g ; grüen erhöhen
cpi ocr_g, 128 ; 128 erreicht
brne exit_timer1_overflow ; nein -> fertig
ldi do_fade, 3
jmp exit_timer1_overflow
rot_auf:
inc ocr_r ; rot erhöhen
cpi ocr_r, 128 ; 128 erreicht
brne exit_timer1_overflow ; nein -> fertig
ldi do_fade, 0
jmp exit_timer1_overflow
exit_timer1_overflow:
pop r0,
out SREG, r0
pop r0
reti
Hallo, danke für die Antworten!
Karl Heinz Buchegger schrieb:> D.h. für dich:> deine komplette Logik wie und in welcher Reihenfolge welches Faden> passiert, findet komplett innerhalb der Interrupt Routine statt: Sie> wird durch den Call vom Timer betreten und sie wird durch den RETI> wieder verlassen. Und dazwischen steht die von dir gewünschte Logik.
Alles klar, sowas hatte ich dunkel vermutet.
Karl Heinz Buchegger schrieb:> Aber simulier dir die Dinge durch! Dann siehst du live und> in Farbe, welche Register welche Werte haben, wie sie sich verändern und> was du in deiner gedachten Logik übersehen hast.
Das ist ein Punkt den ich schon wieder komplett verdrängt hatte, Danke
fürs Erinnern, wird gemacht.
Karl Heinz Buchegger schrieb:> Der Grund ist ganz einfach: diene 'Haushaltsführung'.> Denn: noch hast du dich ja zb um das SREG überhaupt nicht gekümmert!
Richtig, das hatte ich ganz zu Anfang, als sonst noch gar nichts in der
ISR war bedacht, und dann wegen der reti wieder rausgenommen, da das so
nicht gepasst hat, der Hinweis mit der Ausgangssprungmarke ist das,
worauf ich an der Stelle nicht gekommen bin...danke dafür.
Karl Heinz Buchegger schrieb:> Da du im Endeffekt 3 Farbe koordinieren musst und dazu eine> 'Choreographie' der einzelnen Fading-Aktionen in der richtigen> Reihenfolge brauchst, ist das die einfachste Möglichkeit, das> hinzukriegen.
Das hatte ich mit der Einführung eines Statusregister für den
Fadevorgang gemeint, jetzt weiß ich, dass das die Lösung ist, vielen
Dank.
Werde mich nochmal ransetzen und die neuen Ratschläge einbauen, hoffe
dass das dann zum Erfolg führt. Danke!
Hallo!
Vernehmt die freudige Botschaft, es klappt. Das war eine schwere Geburt,
heut Nachmittag traf mich dann der richtige Gedanke wie der Blitz. Hatte
wieder viel zu kompliziert gedacht, wollte die Reihenfolge des Fadings
erst im Speicher ablegen, dabei geht das ja auch so:
1
timer1_overflow:
2
push r0
3
in r0, SREG
4
push r0
5
6
cpi fadestatus, 1
7
breq rot_auf
8
cpi fadestatus, 2
9
breq gruen_auf
10
cpi fadestatus, 3
11
breq rot_ab
12
cpi fadestatus, 4
13
breq blau_auf
14
cpi fadestatus, 5
15
breq gruen_ab
16
cpi fadestatus, 6
17
breq rot_auf
18
cpi fadestatus, 7
19
breq blau_ab
20
21
rot_auf:
22
cpi ocr_r, 0
23
breq set_status
24
dec ocr_r
25
rjmp exit_timer1_ovf
26
gruen_auf:
27
cpi ocr_g, 0
28
breq set_status
29
dec ocr_g
30
rjmp exit_timer1_ovf
31
blau_auf:
32
cpi ocr_b, 0
33
breq set_status
34
dec ocr_b
35
rjmp exit_timer1_ovf
36
rot_ab:
37
cpi ocr_r, 128
38
breq set_status
39
inc ocr_r
40
rjmp exit_timer1_ovf
41
gruen_ab:
42
cpi ocr_g, 128
43
breq set_status
44
inc ocr_g
45
rjmp exit_timer1_ovf
46
blau_ab:
47
cpi ocr_b, 128
48
breq set_status
49
inc ocr_b
50
rjmp exit_timer1_ovf
51
52
set_status:
53
cpi fadestatus, 7
54
breq reset_status
55
inc fadestatus
56
rjmp exit_timer1_ovf
57
58
reset_status:
59
ldi fadestatus, 1
60
rjmp exit_timer1_ovf
61
62
exit_timer1_ovf:
63
pop r0
64
out SREG, r0
65
pop r0
66
reti
Die Freude ist groß, das sieht schon richtig nach was aus. Als nächstes
ist hier noch die Linearisierung dran, da muss wohl bisschen probiert
werden mit den Werten, brauche ja vermutlich einige mehr als 16 damits
flüssig aussieht. Aber ich bin sehr zuversichtlich, das hat ja bei der
manuellen Mischung auch schon geklappt.
Also an dieser Stelle einen riesen Dank an alle hilfreichen Tip-Geber!
Wenn das Programm dann fertig ist und ich alles zusammengebaut habe und
es bis dahin keine Fragen mehr gibt (was ich noch bezweifle) Werde ich
die fertige Software natürlich auch hier hinstellen.
Grüße, bendd
Hi
>Dein Code bisher:>...>Und so sparst du dir den cpi Vergleich:>...
Hat aber nicht das gleiche Verhalten. Was passiert bei deinem Code, wenn
ocr_r schon Null ist?
MfG Spess
Spess53 schrieb:> Hat aber nicht das gleiche Verhalten. Was passiert bei deinem Code, wenn> ocr_r schon Null ist?
Dann wird ja das Label nicht mehr angesprungen, da sich der Fadestatus
geändert hat.
Gruß Steffen
Hi
>Dann wird ja das Label nicht mehr angesprungen, da sich der Fadestatus>geändert hat.
Der originale Code verhindert aber weiteres decrementieren bis
irgendwann wieder ein Wert >0 durch das Hauptprogramm nach ocr_r
geschrieben wird.
Bei deinem Code macht der Controller beim nächsten Interrupt mit $FF
weiter, auch wenn das Hauptprogramm das gar nicht will.
MfG Spess
Liebe Freunde der Elektronik!
Lange hab ich nichts mehr hören lassen, was nicht heißen soll dass ich
gänzlich untätig war. Die maximale Stromaufnahme hab ich bestimmt, um
eine entsprechend dimensionierte Sicherung einbauen zu können. Außerdem
habe ich das Anschlusskonzept nochmal modifiziert, da es nihirgends
Buchsenleisten in 2x2 zu finden gibt. Jetzt ist ein Mini-Din Stecker
verbaut, sieht auch gleich viel hübscher aus.
Die Linearisierung für das Fading habe ich nach einigen Tests verworfen,
da diese keine wirkliche Verbesserung gegenüber der normalen 128 Stufen
PWM geliefert hatten. Das könnte schlicht und einfach an der zu geringen
Auflösung liegen.
Geplant ist es wie folgt: Beim Einschalten startet der µC im manuellen
Modus. Man kann nun nach belieben eine Farbe einstellen, oder eben durch
drücken der Taste 4 in das Fading Programm wechseln.
Im Fading sollen dann noch verschiedene Geschwindigkeiten einstellbar
sein, dazu wird der Prescaler des Timers entsprechend gesetzt.
Aus dem Fading Programm sollte man dann natürlich auch wieder
rauskommen, durch drücken einer Taste also den Timer anhalten und wieder
in die main springen.
Um zu verhindern, dass der Timer gleich wieder losläuft nachdem er
gestoppt wurde, habe ich zum Stoppen eine andere Taste gewählt als die
zum Starten.
Nach einiger Bastelei klappt das auch einwandfrei. Könnte mir jetzt noch
vorstellen eine Speicherung der manuellen Werte zu implementieren, da
die aber vermutlich eh weniger genutzt werden, spare ich mir das
erstmal.
Anbei also der finale Beta-Code, eventuell interessiert es irgendwann
mal irgendjemanden. Sollten euch Verbesserungsvorschläge einfallen,
nehme ich mir diese auch gerne zu Herzen. An dieser Stelle also einen
Riesendank an alle Hinweis- und Hilfestellungsgeber, Mitdenker und
Helfer, ohne euch wäre das wohl nichts geworden. Danke!
Kuriose Geschichte am Rande: Als die fade_main noch nicht komplett
angepasst war, wurde nach Einschalten und manuellem voll-durchschalten
von Rot eine feste Farbe ausgegeben, die sich dann auch nicht mehr
ändern ließ. Das passierte nicht immer, sondern nur, wenn der µC etwas
länger aus war. Kurios finde ichs deswegen, weil im manuellen Modus die
fade_main ja gar nicht betreten wird...Auf alle Fälle trat der Fehler
nicht mehr auf, seit sie so ist wie sie jetzt ist, und am Rest habe ich
nichts geändert...mh.