Forum: Mikrocontroller und Digitale Elektronik Mega8 Assembler RGB Fader


von Ben D. (bendd)


Lesenswert?

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:
1
.include "m8def.inc" 
2
.def temp  = r16 
3
.def PWMCount = r17 
4
.def ocr_1 = r18                      ; Helligkeitswert Led1: 0 .. 127
5
.def ocr_2 = r19                      ; Helligkeitswert Led2: 0 .. 127
6
.def ocr_3 = r20                      ; Helligkeitswert Led3: 0 .. 127
7
8
.org 0x0000
9
        rjmp    main                  ; Reset Handler
10
.org OVF0addr
11
        rjmp    timer0_overflow       ; Timer Overflow Handler
12
 
13
main:
14
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
15
        out     SPL, temp
16
        ldi     temp, HIGH(RAMEND)
17
        out     SPH, temp
18
        ldi     temp, 0x00      
19
        out     DDRD, temp    ; PortD auf Eingang
20
        ldi     temp, 0xFF    ; PortB auf Ausgang
21
        out     DDRB, temp
22
  out  DDRD, temp    ;PullUp an PortD
23
24
        ldi     ocr_1, 128
25
        ldi     ocr_2, 128
26
        ldi     ocr_3, 128
27
28
        ldi     temp, 1<<CS00         ; CS00 setzen: Teiler 1
29
        out     TCCR0, temp
30
        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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Pastor Braune (Gast)


Lesenswert?

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ß

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

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

von Pastor Braune (Gast)


Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ben D. (bendd)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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.

von Ben D. (bendd)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ben D. (bendd)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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!

von Ben D. (bendd)


Lesenswert?

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.

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Du solltest auch hier
1
.org OVF2addr
2
        rjmp    timer2_overflow       ; Timer2 Overflow Handler 
3
.org OVF0addr
4
    rjmp    timer0_overflow        ; Timer0 Overflow Handler

hinten nach noch ein
1
.org INT_VECTORS_SIZE
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.

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Ben,

Naja, du hast auch noch einen Fehler in deinem Code.

Hier funktionierts:
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


Hier funktionierts nicht:
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
  sbrc temp1, 1    ; <------- Du musst Bit0 abfragen!!!
11
  dec ocr_r
12
13
  rjmp main

Fällt dir was auf?


Gruß Steffen

von Steffen H. (avrsteffen)


Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

Ach hier ist er.
zwanzig mal drüber gelesen :-)
1
    ....
2
3
    ldi      temp1, 1<<TOIE0            ; Timer Overflow Interrupt
4
    out      TIMSK, temp1
5
//Timer2
6
    ldi     temp, 1<<CS20      ; CS20 setzen: Teiler 1
7
    out     TCCR2, temp
8
    ldi     temp, 1<<TOIE2        ; Interrupt bei Timer Overflow
9
    out     TIMSK, temp

gratuliere. Du hast dir soeben das zuvor gesetzt TOIE0 Bit im TIMSK 
gelöscht.

von Ben D. (bendd)


Lesenswert?

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.

von Steffen H. (avrsteffen)


Lesenswert?

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

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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

von spess53 (Gast)


Lesenswert?

Hi

Dir ist aber bewusst, das dein Controller hier:
1
        ...
2
  //mov ocr_r, r0
3
  cpi ocr_r, 0
4
  breq ocr_r_uf
5
6
  /*
7
  sbrc temp1, 2
8
  dec ocr_b
9
  cpi ocr_b, 0
10
  breq ocr_b_uf
11
12
  sbrc temp1, 3
13
  dec ocr_g
14
  cpi ocr_g, 0
15
  breq ocr_g_uf  
16
  rjmp main
17
*/
18
ocr_r_uf:
19
ldi ocr_r, 128
20
ret              ;<<<<<<<<<<<<<<<<
abstürzt?

MfG Spess

von Ben D. (bendd)


Lesenswert?

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!

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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!

von c-hater (Gast)


Lesenswert?

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.

von Ben D. (bendd)


Lesenswert?

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

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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

von Steffen H. (avrsteffen)


Lesenswert?

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.

von Ben D. (bendd)


Lesenswert?

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

von Steffen H. (avrsteffen)


Lesenswert?

Du springst mit
1
breq fade_main    ; springe in die main
aus einer INTERRUPT Routine ohne die Rücksprungadresse zu laden, was ja 
das "reti" für dich erledigt.

Du springst also ins Nirvana..

von Ben D. (bendd)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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
9
  rjmp   rot_auf_1     ; ansonsten weiter rot auffaden
10
  
11
  jmp    exit_timer1_overflow
12
  
13
rot_auf_1:
14
  cpi    ocr_r, 0      ; wenn rot voll an
15
  breq   exit_timer1_overflow
16
  dec    ocr_r         ; ansonsten auffaden
17
  jmp    exit_timer1_overflow
18
19
gruen_auf_1:      
20
  cpi    ocr_g, 0      ; wenn grün voll an
21
  breq   rot_ab_1      ; springe zu rot_ab_1
22
  dec    ocr_g         ; ansonsten auffaden
23
  jmp    exit_timer1_overflow
24
25
rot_ab_1:
26
  cpi    ocr_r, 128    ; wenn rot aus
27
  breq   exit_timer1_overflow
28
  inc    ocr_r         ; ansonsten rot abfaden
29
  jmp    exit_timer1_overflow
30
31
32
exit_timer1_overflow:
33
  pop    r0,
34
  out    SREG, r0
35
  pop    r0
36
  reti

von Karl H. (kbuchegg)


Lesenswert?

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

von Ben D. (bendd)


Lesenswert?

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!

von Ben D. (bendd)


Lesenswert?

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

von AVRsteffen (Gast)


Lesenswert?

Hallo Ben

Na siehst du, wird doch langsam. Du kanst dir sogar den cpi Vergleich 
auch noch sparen.
Dein Code bisher:
1
rot_auf:
2
  cpi ocr_r, 0
3
  breq set_status
4
  dec ocr_r
5
  rjmp exit_timer1_ovf
6
...

Und so sparst du dir den cpi Vergleich:
1
rot_auf:
2
  dec ocr_r
3
  breq set_status
4
  rjmp exit_timer1_ovf
5
...

Gruß Steffen

von Spess53 (Gast)


Lesenswert?

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

von Steffen H. (avrsteffen)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Ben D. (bendd)


Angehängte Dateien:

Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.