Forum: Mikrocontroller und Digitale Elektronik PWM Werte aus Array laden


von Nils W. (darky9312)


Angehängte Dateien:

Lesenswert?

Hallo ersteinmal.
Ich habe folgendes Problem: Ich mache für meine Freundin ein Herz aus 
Leds, die dann unterschiedlich hell leuchten und Muster und sowas machen 
sollen...
Als Controller habe ich einen Attiny2313, der zum einen einen PNP 
transistor ansteuert, der am PWM Ausgang OC0A sitzt. Zum anderen steuert 
er ein 4067 IC an, welches zwar eigentlich ein Multiplex IC ist, sich 
aber auch super eignet wenn man mal mehr als 4 Leds hat und nicht so 
viele Beinchen verschwenden möchte. Ich habe mich gegen ein 
Schieberegister entschieden, da ich die Leds ja einzeln ansteuern 
wollte.
Das Programm sollte später folgendes machen: Nach immer der gleichen 
Zeit die Led wechseln und dann in der ihr zugehörigen Helligkeit 
leuchten lassen. Ich wollte zuerst für das Ledwechseln den Timer1 des 
tiny nehmen, aber der war doch langsamer als erwartet, weswegen ich in 
der Mein eine einfache Zählschleife habe, auch wenn ich weiß, dass das 
ziemlich stümperhaft ist... Ich glaube das ginge ja eventuell doch, wenn 
jemand einen tipp hat, wei ich das doch über den TImer1 machen könnte, 
bin ich ganz Ohr :) .Aber zurück zu meinem eigentlichen Problem: Nachdem 
ich es dann endlich geschafft hatte, die PWM einzustellen, da es mein 
erstes Mal ist, dass ich mit PWM Arbeite, und die Leds auch alle 
leuchteten, wollte ich die Werte aus meinem Array in OCR0A laden, damit 
dann jede Led in ihrer eigenen helligkeit leuchtet. Ich weiß, dass mein 
Code ziemlich schlecht ist und man das bestimmt hätte besser schreiben 
können, aber icih bin halt noch ziemlicher Anfänger meiner Meinung nach, 
deswegen macht mich in der Hinsicht bitte nicht allzu sehr fertig :D 
Aber anstatt dass dann jede Led in der ihr zugehörigen Helligkeit 
leuchtet, blinken diese nur, wenn ich das Programm so stehen habe, wie 
ihr es vorliegen habt. Ich habe schon probiert, beim Ledwechseln einen 
Zähler zu haben der dann jedes 100te mal den Wert in OC0A erhöht um die 
Leds langsam angehen zu lassen, allerdings alle auf einmal und das hat 
funktioniert... Lange Rede kurzer Sinn... Kann mir jemand helfen? Ich 
versuche schon seit 2 Tagen, das auf die Reihe zu bekommen :( Wer es bis 
hier unten geschafft hat, dem danke ich fürs lesen und ich hoffe auf 
baldige Hilfe :)
Liebe Grüße Nils

von troll (Gast)


Lesenswert?

Ohne alles gelesen zu haben, das ist schon mal Unsinn:
uint8_t LED [1] [14]=
{           ^^^
  {100,255,1,255,1,255,1,255,1,255,1,255,1,255},

};

OCR0A = LED [1] [LEDZaehler];
            ^^^

error: out of bound access

Brauchst du eventuell ein C-Buch? Warum überhaupt ein zweidimensionales 
Array?

von Nils W. (darky9312)


Lesenswert?

Weil ich nicht wusste, dass man ein Array auch eindimensional machen 
kann... Aber wo du es sagst, scheint es ziemlich sinnvoll und wurde auch 
direkt geändert facepalm mit anchließendem Facedesk

von Nils W. (darky9312)


Angehängte Dateien:

Lesenswert?

Ich habe das Array nun geändert und ein paar Sachen rausgenommen, die 
ich zu Testzwecken drin hatte aber es funktioniert nach wie vor nicht. 
Weiß einer vielleicht wieso?

von Karl H. (kbuchegg)


Lesenswert?

> Aber anstatt dass dann jede Led in der ihr zugehörigen Helligkeit
> leuchtet, blinken diese nur, wenn ich das Programm so stehen habe, wie
> ihr es vorliegen habt.

Schon mal ein bischen gerechnet?

Mit welcher Taktfrequenz betreibst du deinen µC?

(Wenn du mehr als 1 LED gedimmt ansteuern willst, ist die Hardware PWM 
nicht mehr ideal, da du beim Umschalten mal kurz die PWM an die falsche 
LED weitergibst. So gesehen war deine Entscheidung gegen die 
Schieberegister keine weise Entscheidung.)

von Nils W. (darky9312)


Lesenswert?

nein gerechten habe ich noch nicht. Der µC läuft an einem 10 MHz Quartz. 
Um das mit der falschen PWM vorzubeugen habe ich den Inhibit Pin des 
4067 an den µC angeschlossen und normalerweise soltle das mit dem 
ausgeben so aussehen:
1
  PORTB |= (1<<Inhibit);              //Leds ausschalten
2
  Multiplexer = LEDZaehler;              //entsprechende LED schalten
3
  OCR0A = LED [LEDZaehler];            //PWM Wert aus Array laden
4
  PORTB &= (0<<Inhibit);              //Leds wieder einschalten
aber das habe ich ebenfalls zum ausprobieren weggehabt...

von Nils W. (darky9312)


Lesenswert?

Ich habe gerade einmal ausprobiert die Leds RICHTIG langsam zu schalten, 
und dann funktioniert es wunderbar... nur jede zweite Led leuchtet. Aber 
das verstehe ich nicht, könnte es sein, dass der Multiplexer zu lange 
braucht um abzuschalten und in der Zwischenzeit schon die nächste zum 
leuchten gebracht wird? Wobei es komisch ist, da wenn die Leds leuchten, 
sie nicht wirklich leuchten sondern nur merkwürdig flimmern und das 
machen alle und nicht nur die, die leuchten sollten.

von Karl H. (kbuchegg)


Lesenswert?

Nils Wiechert schrieb:
> nein gerechten habe ich noch nicht. Der µC läuft an einem 10 MHz Quartz.

Und der ist auch aktiv?

> Um das mit der falschen PWM vorzubeugen habe ich den Inhibit Pin des
> 4067 an den µC angeschlossen und normalerweise soltle das mit dem
> ausgeben so aussehen:
>
1
>   PORTB |= (1<<Inhibit);              //Leds ausschalten
2
>   Multiplexer = LEDZaehler;              //entsprechende LED schalten
3
>   OCR0A = LED [LEDZaehler];            //PWM Wert aus Array laden
4
>   PORTB &= (0<<Inhibit);              //Leds wieder einschalten
5
>
> aber das habe ich ebenfalls zum ausprobieren weggehabt...

NUr das dir das nichts hilft.
ZUm einen, weil dein Inhibit-Bit löschen nicht das macht was du 
eigentlich willst, zum zweiten, weil die Timer-PWM irgendwann in 
nächster Zeit beginnt (je nachdem, welcher Zählerstand gerade vorliegt) 
und nicht zum Zeitpunkt an dem du an OCR0A zuweist. ABer seis drum. Den 
kleinen Glitsch beim Umschalten wird man nicht sehen, wenn mans nicht 
weiß (denk ich mal)

von Nils W. (darky9312)


Lesenswert?

Aber was macht es denn dann? Ich dachte, das würde gehen, da der 
Inhibit-Pin am Multiplexer ja alle Ausgänge Potentialfrei schaltet und 
die Leds ja dann theoretisch aus sein müssten oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Nils Wiechert schrieb:
> Aber was macht es denn dann?

Einen einzelnen Pin schaltet man so auf 0

   PORTB &= ~(1<<Inhibit);


was dein Code effektiv macht: Er schaltet den kompletten Port, alle 
Bits, auf 0.

> Ich dachte, das würde gehen, da der
> Inhibit-Pin am Multiplexer ja alle Ausgänge Potentialfrei schaltet und
> die Leds ja dann theoretisch aus sein müssten oder nicht?

Den Inhibit kannst du dir allerdings auch sparen.
Denn im Grund müsstest du das Umschalten der LED mit deiner Hardware-PWM 
synchronisieren. Du kannst nicht einfach hergehen und mit nichts dir 
nichts, mitten drinnen einfach den PWM-Ausgang auf eine andere LED 
legen.´Denn der Timer macht ja seinen PWM-Zyklus mit dem vorhergehenden 
Wert erst noch fertig, ehe dann die geänderte OCR0A Einstellung wirksam 
wird.

von Nils W. (darky9312)


Lesenswert?

Achso, jetzt weiß ich auch wie du das gemeint hast ^^ Aber woran liegt 
es denn dann, dass alle Leds mit der gleichen Helligkeit leuchten, bzw 
flimmern und nicht mit der, die im Array steht?

von Nils W. (darky9312)


Lesenswert?

Oh... Okay danke für den Hinweis. Werd ich mir merken :)

von Michael S. (rbs_phoenix)


Lesenswert?

1
void wechseln (void)
2
{
3
  LEDZaehler++;
4
  if (LEDZaehler == 15)
5
  {
6
    LEDZaehler = 0;
7
  }
8
  Multiplexer = LEDZaehler;              //entsprechende LED schalten
9
  OCR0A = LED [LEDZaehler];            //PWM Wert aus Array laden
10
}

Hier setzt du zurück, wenn der Zähler 15 ist. Wenn er aber 14 ist nicht. 
Somit hast du dann OCR0A = LED[14], und das geht, nicht, da du nur von 
0-13 Elemente (=14 an der Zahl) hast. Ich hätte geschrieben 
"if(LEDZaehler >= 14)".

von Programmierer (Gast)


Lesenswert?

PORTB |= (1<<Inhibit);              //Leds ausschalten
  Multiplexer = LEDZaehler;              //entsprechende LED schalten
  OCR0A = LED [LEDZaehler];            //PWM Wert aus Array laden
  PORTB &= (0<<Inhibit);              //Leds wieder einschalten


Was tust du da in der letzten Codezeile? Du möchtest wahrscheinlich den 
"inhibit" Pin rücksetzen, nur steht da was anderes.

von Nils W. (darky9312)


Lesenswert?

Ja hatte ich vor, wurde hier aber bereits eines besseren belehrt :) und 
danke Michael für den Hinweis :)

von Karl H. (kbuchegg)


Lesenswert?

Michael Skropski schrieb:

> 0-13 Elemente (=14 an der Zahl) hast. Ich hätte geschrieben
> "if(LEDZaehler >= 14)".

Ich hätts mit sizeof gemacht.
Aber das ist erst mal nicht sein Problem.
Sein Problem ist, dass er nicht einfach den PWM Puls nach Lust und Laune 
an die LEDs verteilen kann. Die Umschaltung muss schon geordnet 
erfolgen, am besten dann, wenn der Timer wieder auf 0 zurückgeht. Aber 
das geht nicht so einfach.

von Nils W. (darky9312)


Lesenswert?

Also so wie ich das jetzt verstanden habe, geht der PWM Puls anscheinend 
so lange, dass er die nächste oder sogar noch weitere Leds mit 
beeinflusst oder?
Ist natürlich Mist... daran habe ich garnicht gedacht

von Karl H. (kbuchegg)


Lesenswert?

Ähm.

Ein 4067 ist ein Analog-Multiplexer!

Kann es sein, dass du denkst, da kommt aus dem PWM Pin eine analoge 
SPannung raus, deren Höhe du einstellen kannst?

Dem ist nicht so! Da kommen Pulse raus, deren Länge variiert. Deine LED 
wird also nicht deswegen gedimmt, weil die SPannung gedimmt wird, 
sondern weil sie von (hausnummer) 1 Millisekunden nur 1/10 der Zeit 
brennt und die restliche Zeit aus ist. Deswegen erscheint die dunkler.
Und deswegen musst du die Multiplexer Umschaltung genau in den 
Pulspausen machen. Was, wenn du die PWM ausreizen willst (*), gar nicht 
so einfach ist.


Der 4067 war summa summarum keine gute Idee. Ganz und gar nicht gut.

(*) d.h. PWM voll aufgedreht, da kommt Puls auf Puls rauf - de facto 
eine Gleichspannung mit keiner Pause dazwischen. Du bräuchtest aber die 
Pause, weil da drinnen die Multiplexer umschaltung erfolgen muss.

von Nils W. (darky9312)


Lesenswert?

Könnte man nicht einfach wieder den Wert 0 in das Zählregister vom 
Timer0 schreiben? dann wäre der Zähler ja wieder zurückgesetzt und würde 
erst bei der aktuellen Led wieder anfangen zu zählen.

von Nils W. (darky9312)


Lesenswert?

Nein das denke ich nicht, wie eine PWM funktioniert ist mir klar. AN dem 
PWM Pin hängt ja auch der PNP Transistor, der die SPannung der Leds 
schaltet. An dem Multiplexer hängen weitere NPN Transitroren, die nach 
Masse geschaltet sind und jeweils eine eigene Led zugeordnet haben. Der 
Inhibit Pin ist ja wiederum ein anderer am µC

von Sascha W. (sascha-w)


Lesenswert?

@Nils,

zeig doch mal einen Schaltplan ...

des weitern solltes du bedenken, das bei deiner Vorgehensweise die LEDs 
nur maximal 1/15 der Zeit leuchten können, selbst wenn die PWM bei 100% 
ist. Und das müsstest du auch so timen, das an jeder LED einige (100) 
PWM-Zyklen anliegen, bevor du zur nächsten weiterschaltest - und das 
wird mit dem Zäher in main sicher nichts.
Du wirst wohl besser zu einer Software-PWM übergehen.


Sascha

von Karl H. (kbuchegg)


Lesenswert?

Nils Wiechert schrieb:
> Könnte man nicht einfach wieder den Wert 0 in das Zählregister vom
> Timer0 schreiben?

Könnte man.
Allerdings musst du dich auch um den Pin kümmern. Nur dadurch, dass du 
den Zähler auf 0 zurücksetzt wird der PWM Ausgangspin ja nicht 
beeinflusst. Der reagiert auf Zählereignisse und nicht auf Zählerstände.
Wobei ich sogar denke, man müsste ihn auf 0xFF setzen, damit der 
Overflow den PWM-Zyklus in Gang bringt. Da bin ich mir aber nicht 
sicher, das müsste man ausprobieren.

De facto läuft es darauf hinaus, dass du bei LED-Wechsel einen PWM 
Zyklus brauchst, in dem keine LED leuchtet (du darfst ja auch die 
vorhergehende nicht einfach mitten im Zyklus abwürgen). In diesem Zyklus 
wird die Umschaltung gemacht und erst mit dem nächsten geht die LED dann 
wieder "Online".

ALles in allem: knifflig

von Nils W. (darky9312)


Lesenswert?

also mit
1
PORTB |= ~(1<<Inhibit);              //Leds ausschalten
2
  Multiplexer = LEDZaehler;              //entsprechende LED schalten
3
  OCR0A = LED [LEDZaehler];            //PWM Wert aus Array laden
4
  TCNT0 = 0xFF;
5
  PORTB &= ~(1<<Inhibit);              //Leds wieder einschalten
funktioniert es schon sehr gut, jetzt leuchten nur die Leds hinterher 
ein ganz kleines bisschen nach :)

von Nils W. (darky9312)


Lesenswert?

Auf eine ähnliche Weise könnte ich doch auch den Timer1 noch als Timer 
für das Ledwechseln nehmen oder? wenn ich nach einem Overflow einen 
entsprechend hohen Wert wieder in den TImer lade

von Sascha W. (sascha-w)


Lesenswert?

du kannst auch den Timer 0 zum wechseln nehmen, nutze den OV_INT und 
wechsle den Ausgang jedes n'te Mal. Damit brauchst du nur einen Timer 
und bist beim wechseln schon syncron zum PWM (brauchst den TCNT0 also 
nicht zurücksetzen).

Sascha

von Nils W. (darky9312)


Lesenswert?

Das ist eine Super Idee. WUsste garnicht, dass das geht, aber es 
funktioniert wunderbar :) danke

von Karl H. (kbuchegg)


Lesenswert?

Was ich tun würde:

Ich würde mich in den PWM Werten auf maximal 200 (statt 256) 
beschränken. Der optische Unterschied von 200 zu 256 ist maginal und 
kaum zu merken, wenn man es nicht weißt.

Dafür würde ich mir:
Mit dem noch freien OCR0B Compare Match einen Interrupt aufbauen, der 
bei 201 zuschlägt und die Daten für die nächste LED ins OCR0A Register 
lädt und den Multiplexer umschaltet. Zum Zeitpunkt dieses Compare Match 
Interrupts ist (durch die Selbstbeschränkung auf 200) sicher gestellt, 
dass die gerade aktuelle LED auf jeden Fall bereits ausgeschaltet ist 
und der PWM-Zyklus für die nächste LED noch nicht begonnen hat.

Auf die Art hab ich mir 56 Takte 'erkauft', in denen die Umkonfiguration 
statt finden kann, ohne dass es Auswirkungen auf die LEDs hat. Die 56 
sind einfach nur eine Zahl, von der ich annehme, dass sie auf jeden Fall 
ausreicht um das gewünschte im Compare Match Interrupt durchzuführen und 
so gewählt, dass sich für die Maximalhelligkeit eine 'gerade' Zahl 
ergibt (die 200).

Damit sollte sich dann ein 1A Multiplexen der LEDs machen lassen, ohne 
dass es zu irgendwelchen Beeinflussungen kommt.

von Nils W. (darky9312)


Lesenswert?

Alles klar danke :) Werde ich versuchen Vielen Danke für eure Hilfe :)

von Nils W. (darky9312)


Angehängte Dateien:

Lesenswert?

Ich verstehe garnichts mehr... Es tut mir echt leid, ich glaub ich nerve 
langsam, da es bestimmt alles total doofe Fehler sind, aber ich habe es 
jetzt so gemacht, wie  Karl Heinz Buchegger es gesagt hat, habe OCR0B 
auf 201 eingestellt und frage auf einen Interrupt ab, aber jetzt bleibt 
es bei der ersten LED stehen -.- Kann mir bitte nochmal jemand helfen?

von Sascha W. (sascha-w)


Lesenswert?

falsch
1
TIMSK |= (1<<TOIE0)|(OCIE0B);
richtig
1
TIMSK |= (1<<TOIE0)|(1<<OCIE0B);

ist schon spät

Sascha

von Nils W. (darky9312)


Lesenswert?

OH MEIN GOTT... Das darf nicht wahr sein... ist echt peinlich, ich danke 
dir :) Ich sollte vielleicht schluss machen für heute...

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.