Guten Abend,
ich versuche mich gerade daran ein Moodlight zu basteln. Die Schaltung
steht soweit und jetzt ist es an der Zeit den Code dafür zu schreiben.
Was ich suche ist jetzt eine einfache Methode die Farben schön
nacheinander durchzufaden ungefähr so
1. Alles aus
2. Rot auf max Helligkeit faden
3. Grün auf max Helligkeit faden
4. Rot auf min Helligkeit faden
5. Blau auf max Helligkeit faden
6. Grün auf min Helligkeit faden
7. Rot auf max Helligkeit faden
8. Blau auf min Helligkeit faden
weiter bei 3.
Nur alles was ich mir ausdenke ist fürchterlich kompliziert. Mit x if
abfragen usw. Gibts es zufällig irgendwo so einen Quellcode in C der das
schön gelöst hat? Ich habe mir mal den Fnordliche Quellcode angeschaut.
Der ist zwar super gemacht, allerdings für meine Ansprüche schon viel zu
kompliziert mit Time Slots und allem drum und dran. Zudem hat das
Fnordlicht Low Active Leuchtdioden und ich kriege das zum Verrecken
nicht invertiert.
Grüße Mike
1. Setup für Timer und Ports finden.
2. SoftPWM für Deine LED's schreiben.
3. Linearisieren/Helligkeitsanpassung der PWM.
4. Zeitabhängig die "PWM-Regler" für die Jeweils gewünschte Farbe laufen
lassen. (Sticherwort Änderungsgeschwindigkeit)
Wo liegt Dein konkretes Problem ?
1. Setup für Timer und Ports finden.
kein Ding
2. SoftPWM für Deine LED's schreiben.
Auch kein Problem
3. Linearisieren/Helligkeitsanpassung der PWM.
Habe ich mir noch keine Gedanken zu gemacht. Wie lineasiert man denn am
besten?
4. Zeitabhängig die "PWM-Regler" für die Jeweils gewünschte Farbe laufen
lassen. (Sticherwort Änderungsgeschwindigkeit)
Ich denke hier liegt das Problem. Den Ablauf schön einfach runter zu
schreiben. Wie gesagt, ich kann mir was ausdenken, mit vielen
Verschachtelungen und if Abfragen. Allerdings denke ich bei sowas immer
zu komplizert. Das muss auch einfacher gehen und diese Lösung suche ich.
Ok,
also das Linearisieren habe ich jetzt verstanden, in dem Beispiel wird
zwar die Hardware PWM des Atmel verwendet, aber es spricht ja nichts
dagegen seine Soft PWM mit dem selben On/Off Verhältniss zu speisen.
Jetzt musst du mir bitte noch erklären, wie man das Faden elegant löst.
Du nennst das Stichwort Änderungsgeschwindigkeit. Mal gucken, ob ich was
dazu hier finde, wenn ich schon Linearisierung übersehen habe.
Grüße Mike
OK, also Du kannst jetzt für jede Gruppe/Farbe die Helligkeit unabhängig
von 0 bis 100% per PWM Regeln.
Du hast schon einen (Ablauf-) Plan !
(also dein wie dein Farbspiel sein soll)
Jetzt fehlt noch die Zeit die das Faden der jeweiligen Farbe dauern
soll.
Stell Dir vor es soll die Zeit (t) dauern eine Farbe vom Anfangswert (a)
zum Endwert (e) zu dimmen.
Dann ist doch die Änderung (d) der Helligkeit/PWM :
d=e-a
Du willst also in t eine Änderung von d.
Du hast jetzt zwei möglichkeiten die aktuelle PWM zu berechnen:
1. Du inkrementierst den aktuellen PWM-wert um d/t bis zum ende des
Fading Vorgangs.
2. Du berechnest den jeweiligen Wert der PWM.
(x) ist die bis jetzt vergangene Zeit.
PWM=a+(x/t*d) bzw
PWM=a+((e-a)/t*x)
auf Deutsch : PWM-wert =
Anfangswert+((endwertPWM-anfangswertPWM)*vergangene Zeit / Dauer für
einen durchlauf)
Wofür gilt: vergangene Zeit <= Dauer für durchlauf.
Wieder ein Teilproblem gelöst !
Du kannst jetzt eine einzelne Farbe Ein und ausfaden.
Wenn Du das jetzt nacheinander für alle weitere Farben machst,
hast Du genau das was du willst ?!
Dein Timer stellt die benötigte Zeitbasis dar...
Hmm,
so ganz verstehe ich die Formel noch nicht oder besser die Formel im
Zusammenhang mit der Erklärung. Aus der Zweiten der oberen Formel lese
ich sowas
Mist falschen Button gedrückt, ich wollte die Vorschau...
wie dem auch sei. Was mir an der Formel noch fehlt ist zum einen noch
der Bezug auf die das Array der Lineasierung und man bräuchte ja auch
noch eine Formel zum rausfaden. e und a sind fest gegeben, genau wie t,
daß einzige was man ändert ist die vergangene Zeit x oder sehe ich das
falsch?
Also ich habe jetzt vorausgesetzt das Du schon einen linearisierten
PWM-Wert benutzen kannst.
Sprich Du hast das irgendwie implementiert :
void _set_rot_brigthnes (uint brigthness);
void _set_grün_brigthnes (uint brigthness);
void _set_blau_brigthnes (uint brigthness);
Die untere Formel ist richtg.
Sorry, schau ma auf die Uhrzeit des Post...
Wegen dem aus und einfaden habe ich extra diesen a und e wert
eingeführt.
Du willst in einem Intervall T ein fading machen.
Die Zeit vergeht in x.
Du benötigst einen "Faktor" der quasi "die Zeit aufspannt".
x/T wird immer einen wert zwischen 0 und 1 haben, das entspricht 0..100%
der vergangenen Zeit.
In der Zeit T möchtest Du die PWM von einem Anfangswert zu einem Endwert
laufen lassen.
Ausgangspunkt ist also a und Du willst nach e.
e-a ergibt doch die Differenz.
Wenn Du immer ein festes Stückchen der Differenz zu a addierst, und
immer ein festes Stückchen mehr, dann wirst Du austomatisch bei e
landen.
Rate mal was d * x/T ergibt ?
x/T läuft von 0 nach 1.
0*d = ?
0.1*d = ?
0.2*d = ?
0.3*d = ?
0.4*d = ?
0.5*d = ?
0.6*d = ?
0.7*d = ?
0.8*d = ?
0.9*d = ?
1.0*d = ?
Jetzt hattest Du aber noch einen Anfangspunkt,
denn musst Du doch einfach nur dazu addieren...
Du hast jetzt wahrscheinlich nur das einfaden betrachtet ?!?!
Jetzt stell Dir mal von a=90 e=10 !
d wird also -80.
Was denkst Du was passieren wird ?
Mach mal mit dem Array langsam...
Je nach dem wie "weit" Du Abstrahieren kannst,
löse immer nur Teilprobleme.
So ein Teilproblem ist überschaubar.
Also schreib doch einfach scon mal die Funktion umd die PWMs
anzusteuern.
Dann packst Du obigen Dreisatz in eine Funktion.
Schreib das Programm ruhig erstmal nur für eine Farbe.
Die Abwechslung bringst du dann rein, OK ?
Ok, ich versuche das ganze mal in nen schönen Quellcode zu packen und
stelle den dann hier rein. Momentan kämpfe ich auch noch mit dem
Problem, daß meine UART Verbindung stellenweise hängen bleibt und ich
danch die Schaltung reseten muss. Mal sehn ob ich da was finde. Ist
eigentlich stumpf der Quellcode aus den Atmel Dokumenten.
Melde mich dann wieder
Jep tue ich. Habe mal den Code dran gehangen, noch ist er recht
übersichtlich.
In der includierten Einstellungen.h steht nix spannendes drin,
Funktionsdeklarationen und halt die Werte fürs UART
#define FOSC 16000000// Clock Speed
#define BAUD 57600
#define MYUBRR FOSC/16/BAUD-1
Ich mache mich jetzt mal dran die PWM Strucktur aufzubauen und das ganze
zu linearisieren. Gab hier ja ganz gute Beispiele in den Artikeln, muss
halt die Linearisierung nur in die Soft-PWM verpacken.
Hmmm...
IMHO:
Das gewackel an den Pins geht OK.
(aber warum ein PORTA = 0;, hängt da noch was anderes dran ?)
Aber das
1
USART_Transmit('k');
würde ich so nicht machen.
Du rufst eine Funktion auf, die in einem Interrupt Zeit "verschwendet".
(Die while schleife in USART_Transmit)
Auch wenn das in dem eher fall unkritisch ist...
QuickAndDirty kannst Du ein Flag in der While Schleife von Main
abfragen...
(volatile nicht vergessen)
Oder Du machst das ganze mit einem UART-Sende-ISR.
Geh doch nur mal zum Spaß auf 4800 oder 9600 Baud,
das sollte für Deine belange noch (dicke) reichen.
Der Fehler bei 16 MHz wird dann kleiner...
Schau mal ins Datenblatt von deinem ATMega "Examples of Baud Rate
Setting",
dort ist auch der Prozentuale Fehler beschrieben.
Benutzt Du ein Quarz ???
Falls nicht funktionert die Übertragung bei den angebebenen Baudraten
stabiler, ansonsten -> Quarz dranlöten.
Mein Atmega64 hat nen 16 Mhz Quarz dran, der sollte ausreichen um später
mal 9 bis 12 Software PWM Kanäle ausgeben zu können.
Soft PWM habe ich jetzt auch mehr oder weniger am laufen. Ich habe das
Beispiel aus dem Soft-PWM Artikel hier benutzt. Das erste war auch kein
Problem einzubinden. Momentan versuche ich mich an dem dritten Beispiel,
soll ja CPU Zeit sparen. Das klappt aber noch nicht wirklich, sobald
mehr als 2 Kanäle gesetzt werden flackert es. Mal gucken ob ich den
Fehler finden und dann muss ich mir gedanken machen, wie man da noch die
Linearisierung rein bekommt.
Ok,
der intelligente Lösungsansatz für PWM funktioniert wirklich nicht.
Fehler kann ich so keinen Finden, auch nicht, dass jetzt noch Atmega64
spezifisch angepasst werden müsste. Dennoch gibt es Kombinationen von
Helligkeitsstufen die zu einem wilden geflacker führen. Keine Ahnung
warum. Ich habe schon alles an Quellcode rausgeworfen und original den
Code aus dem Artikel hier benutzt, nur eben die Frequenz angepasst,
selber Effekt.
Bin ich denn etwa der erste der hier versucht eine linearisierte
Soft-PWM für Atmegas zu schreiben?
Bist Du nicht...
Machs Dir nicht unnötig schwer...
Bastell Dir doch eine Timer ISR nach dem Motto :
1
volatileuint16pwm_counter,// Gibt den Zähler für die PWM ab
2
// läuft bei 65535 nach 0 über
3
// das entspricht einer 16 Bit PWM
4
pwm_r,pwm_b,pwm_g;// in den Variablen werden die Grenzwerte
5
// und somit das PWM-Verhältnis gespeichert
6
7
8
9
ISRTimer_vect()
10
{
11
12
if(pwm_counter>pwm_r)Pin_r=0;//Wenn Counter > PWM_Grenzwert dann Pin aus
13
elsePin_r=1;// ansonsten Pin an
14
15
if(pwm_counter>pwm_g)Pin_g=0;
16
elsePin_r=1;
17
18
if(pwm_counter>pwm_b)Pin_b=0;
19
elsePin_r=1;
20
21
pwm_counter++;
22
23
}
So verbrauchst Du fast immer die gleiche Rechenzeit.
Blöd ist, die Spannungsquelle wird mit spitzen belastet...
Jetzt macht das ding schonmal die "echte+reine" PWM.
Wenn du die pwm_r,g,b Variablen änderst, legst du dort nur den
entsprechenden Wert aus der Tabelle rein.
1
voidsetpwm_r(unit16_tpwm)
2
{
3
pwm_r=LogLookUpTab[pwm];// Das ist jetzt Sau schwer ;)
4
}
Zum testen kannst Du zum einen die "reine" PWM also direkt in pwm_r
schreiben bzw nach einem festen intervall prm_r++;
...und Dann mit der LookUp-Tabelle.
Ach, alles nur Pseudo-code...
Ok,
so geht es auch. Allerdings gefällt mir die Version mit geringer
Rechenzeit natürlich besser, besonders wenn man später mal 12 Kanäle
nutzen will und nebenbei noch Daten durch die gegend schieben will. Und
das mit der linearisierung macht mir auch immer noch etwas
Kopfzerbrechen. Lang leben fertige Bibiotheken.
Nee, hab doch mal ein bisschen Ehrgeiz !
Wenn Du das selbst zusammengebastelt hast, ist das erfolgserlebnis viel
grösser.
Habe auch noch was nachgetragen.
Dann habe ich eine 16 Bit Soft-PWM mit 256 Schritten. Kann also 3x256
Helligkeitsstufen einstellen. Wobei die PWM eigentlich mit jeweils
anderen Obergrenzen läuft. Hier maximal 65353.
Habe mir jetzt den Artikel auch mal angeschaut... ;)
Zum ende hin hat da jemand gut nachgedacht...
Aber, versuche doch erstmal mit dem was Du kannst zu einem akzeptablen
Ergebnis zu kommen.
PS: Vergiss erstmal 16Bit PWM ;) 10 Bit ist realistischer...
Wenn du gerade einen Atmega64 da hast, kannst du ja mal das letzte der
unteren Beispiele ausprobieren. Wie gesagt, bei mir geht es nicht. Bei
machen Kombinationen für die einzelnen PWM Kanäle kommt nur noch
geblitze raus. Ansonsten bin ich momentan nicht zum Proggen gekommen,
vielleicht Morgen wieder.
Ah by the way. Angenommen ich will eine 16Bit Software PWM mit 150 Hz
Frequenz programmieren, dann gilt:
1. T=1/150Hz = 6,66 ms Periodendauer
2. 16 Bit = 65535 Stufen
3. t = 6,66ms / 65535 = 101,73 ns
Somit müsste ich also für eine 16 Bit PWM den Timer Interrupt 102 ns
ausfühen lassen. Sollte mit nem 16 MHz Quarz wohl klappen.
Aber die Timer des Atmel laufen ja anders. Da stellt man ja nicht ein,
nach wievielen Sekunden der Interrupt erfolgen soll, sondern der Timer
zählt bis 16 Bit rauf und löst dann aus.
Ah verwirrt. Wie macht man das denn jetzt? Die Auflösung der PWM sollte
doch die Anzahl der Zwischenstufen sein. Also bei 16 Bit 65535 Stufen
innerhalb einer Periode. Dann zähle ich diese Stufen rauf und toggle
meinen Pin beim erreichen der gewünschten Stufe. Beim erreichen von 16
Bit wird wieder alles zurückgesetzt und das Spiel kann von neuen
Beginnen. Nur wenn der Interrupt ja schon bis 16 Bit zählt währe das
doch doppelt gemoppelt. Der Interrupt kommt nur alle 65535 mal und
ebenso zähle ich 65535 Interrupts. Ist doch viel zu viel Rechenaufwand.
Tja, eben nicht...
Wenn Du deinen Timer aufsetzt generiert der immer nur zu einem
bestimmten Zeitpunkt einen Interrupt,
dieser interrupt endet in deiner ISR.
Bei der Hardware PWM wird TCNTx verglichen und dementsprechend ein pin
getoggeld. (OCx)
Das kostet keine Rechenzeit, es gibt aber nur eine begrenzte Menge an
OCx Pins...
Der Atmega 644 hat z.b. 6 PWM-Channels.
Der Atmega 32 hat 4 PWM-Channels.
Teilweise hängen die dann auch zusammen auf einem TCNT-Zähler.
Bei Deiner Soft-PWM Kannst Du nicht das TCNTx benutzen,
denn Du bekommst von dem Gezähle nichts mit.
Wenn Du schnellere Interrupts haben willst,
musst Du den Timer nur anders einstellen.
Die ATMegas habe im übrigen verschiede Timer die man in verschieden Modi
fahren kann. (Normal, CTC, 8/16 Bit, ISR bei überlauf oder vergleich mit
compare Register, TCNT ist vorladbar...)
-- Siehe Artikel oder Datenblatt (ich habe mir angewöhnt nur noch die
Datenblätter zu lesen).
Da nicht bei jedem Timer-Tick ein Interrupt ausgelöst wird,
kannst Du das TCNTx nicht benutzen.
Deshalb musst Du deinen eigenen Zähler benutzen.
Dein Timer stellt dir nur die Zeitbasis zur Verfügung.
Denk mal darüber nach :
1. Wie lange ein Taktzyklus ist
2. Wieviele Befehle in dieser Zeit ausgeführt werden können
3. Wieviel Zeit zwischen den einzelnen Interrupts vergehen darf
Im Soft-PWM Artikel stand was von rund 100 Takten,
dein Hauptprogramm braucht auch noch etwas Rechenzeit.
Da werden 16MHz recht wenig.
(schätze so 1-3Hz PWM)
Deswegen meinte ich was von 10 Bit PWM wäre realistischer.
Das bringt mich alles nicht weiter. Sind zwar alles nette Tips, wenn man
aber keine so richtige Idee hat wie das alles Funktioniert bringen die
auch nix.
Aber gut, rechnen wir mit 10 Bit, damit kann man immer noch ganz gut auf
256 Stufen linearisieren.
Also, ich will beispielsweise eine Frequenz von 150Hz haben, dann ist
die Periodendauer. Das sind 6,66ms. Diese sollen nun mit 10 Bit
Auflösung angesprochen werden. Sind 1024 Stufen, die zusammen 6,66 ms
ergeben müssen. Also dauert eine Stufe ungefähr 6,5µs. Somit muss mein
Timer alle 6,5ns einen Interrupt auslösen.
So welche Rolle spielt es jetzt, ob ich einen 8 oder 16 Bit Timer nehme?
Wie stelle ich den Timer am besten ein?
Gehe ich richtig in der Annahme, daß bei einem 16MHz Quarz ein Tick im
Timer 62,5ns dauert. Dann müsste der Timer überlauf ja nach 100 Ticks
geschehen?
Grüße ein inzwischen schon recht verzweifelter Mike
Hallo Mike,
alles wird gut so lange Du wild bist...
Ich habe nur versucht Dir den Zusammenhang zu vermitteln.
(Bin wohl kein guter Lehrer, könnte daran liegen das ich keiner bin ;))
t=1/f
Controllerfrequenz ist 16 MHz ein Takt sollte 62.5ns dauern, richtig !
Was für einen Timer Du bei der Soft-PWM benutzt ist grundsätzlich egal,
Du musst nur auf deine gesuchte Zeitbasis kommen.
Wenn Du eine PWM-Frequenz von 150 Hz (t=6,666) haben möchtest,
musst Du während diesem t bis 1024 (2^10) gezählt haben.
Dein Timer muss also alle 6,666ms/1024=6,51µs einen Interrupt auslösen,
das geschieht also mit ca 153,6kHz -- das ist schon ordentlich.
Wie kommst Du jetzt auf diese 6,5ns ?
Du musst Dir jetzt einen Timer aussuchen,
je nach dem was verfügbar ist, ob Du die 8 oder 16 lieber magst oder
vielleicht nach Deiner Sockengrösse.
Alles egal Hauptsache Du kommst mit dem Timer auf die 153,6kHz.
Jetzt wirst Du in das Datenblatt von deinem µC schauen müssen und einen
Timer konfigurieren...
Alles klar ?
Wenn nicht schick mir deine Festnetznummer... ;)
Ok, verschrieben
die Zeit ist 6,51us. Ich habe mir jetzt mal die Software PWM Nr 2 aus
dem Artikel hier eingebaut. Die läuft soweit, nur kriege ich die nicht
auf 10 Bit erweitert. Wird ja eigentlich alles von alleine berechnet.
Tipt man aber mehr als 256 ein gibt es einen Integer Overflow. Ok 8 Bit
sind ja 256 aber selbst das Ändern der Variablen auf 16 Bit hilft da
nicht. Lässt man die Berechnung für den Timer Reload Wert weg und tippt
direkt die 156 ein gibts den Fehler nicht. Komisches Ding.
Was auch nicht klappt ist das Linearisieren. Ich habe mal den Code
drangehangen, vielleicht fällt einem ja was ein. Das Problem ist
folgendes. Ich habe ja ein Array definiert und weise meiner PWM dann in
einer Schleife alle Werte zu. Wobei ich eigentlich am Maximum aufhöhre.
Somit müsste bereits nach wenigen momenten die volle Helligkeit erreicht
sein, alles was ich aber kriege ist nen leichtes geflimmer und weniger
Leuchtkraft.
Grüße Mike
Wie lange dauert ein durchlauf der Schleife ?
Wie schnell änderst Du den Helligkeitswert ?
Wie schnell ist das komplette Array durchgelaufen ?
Was kannst Du den µC machen lassen ?
Super Thomas,
vielen Dank, daß du dir die Mühe machst und für mich die Forensuche
kreisen lässt. Ich denke den Fehler mit der Schleife habe ich selber
gefunden. Die Schleife wird ja permanent ausgeführt, läuft bis 32 hoch,
bricht ab. Nun ginge es ansich weiter im Programm. Hier schlägt die
while Schleife zu, startet die for Schleife wider neu. I wird auf Null
gesetzt und das Spiel geht von vorne los.
Sehe ich das richtig, daß der Prescaler bei ner Software PWM nicht
wirklich was bringt? Er teilt ja einfach nur den Systemtakt runter,
somit werden weniger Interrups in der selben Zeit durch den Timer
ausgelöst und man verliert an Auflösung in der PWM.
Ansonsten währe es jetzt vielleicht mal ganz interessant zum Problem mit
dem Fading zu kommen und wie man die ganz oben genannte Reihenfolge
möglichst elegant programmieren kann.
Grüße Mike
Hmmmm....
Auch wenn ich ein kleiner Misanthrop bin,
gehöre ich doch irgendwie zu den guten ;)
Hast Du denn mal ein delay (ob mit der delay.h oder über deinen
Interrupt) in die Schleife eingebaut ???
Quasi um zu schauen ob dein Fremdcode mit deinem Frendcode (TM)
Funktioniert ?
Wenn das ganze Funktioniert kannst Du gerne mal zwei dieser Schleifen
hintereinander in die while Schleife packen.
(Die zweite für eine andere Farbe..)
Wie Du sicherlich weisst,
kann dein Controller mit einer Schleife auch wieder schön nach unten
zählen.
Denke für deine Anwendung brauchst Du keine grossartige Ablaufsteuerung.
;)
Hast Du mal im Datenblatt zu deinem Controller nachgeschaut wie die
Timer Funktionieren ?
Wie geschrieben.
Der Interrupt den dein Timer auslöst dient nur als Zeitbasis für deine
PWM.
Der Prescaler des Timers hat durchaus einen Sinn !
Denn wenn der Zähler der im Timer hochläuft nicht mehr ausreicht um den
Quarz-Takt runterzuteilen,
dann nutzt man den Prescaler um diesen Takt vor zu teilen.
(Prescaler == Vorteiler)
Rechenbeispiel 16MHz / 16Bit Timer = 244Hz
Du kannst mit dem 16Bit Timer und dem Prescaler=1 keinen Takt kleiner
als 244Hz erzeugen.
Bei einem 8Bit Timer kommst Du nicht unter 62,745 KHz...
>Ansonsten währe es jetzt vielleicht mal ganz interessant zum Problem mit>dem Fading zu kommen und wie man die ganz oben genannte Reihenfolge>möglichst elegant programmieren kann.
Die Antwort ist : "42"
Was willst Du wissen ?
Nun im ersten Posting hatte ich ne beispielhafte Reihenfolge für den
Farbwechsel gepostet. Da ich beim Proggen immer zu kompliziert denke
würde ich halt gerne ne einfache Lösung finden. Ich hätte ja mit x
if-Abfragen die ganzen Zustände ausprogrammiert. Quasi eine
Ablaufsteuerung
if rot = 0 und blau = max dann fade rot rein
ist rot = max und blau = max, dann fade blau raus
usw.
Ich wette da gibt es auch eine unkomplizeret Lösung zu.
Wenn ich mich einmischen darf.
Mad Mike ist auf der Suche nach einer Art Tabellensteuerung mit der er
die Farbwechsel 'progammieren' kann.
Ist machbar. Vor allem, weil er ja mit dem Timer eh schon einen
Basistakt hat. Aber mein Vorschlag wäre: Brings erst mal so zum laufen
und dann such nach alternativen Methoden, wie du die Farbwechsel
codieren kannst.
So, klink mich wieder aus. Thomas ist ja eh drann.
Ich sage ja ich mache es mir immer viel zu kompliziert g
Vom Quellcode her sieht es jetzt eigentlich gut aus. PWM ist da,
Linearisierung ist da und auch das Fading ist schön einfach
programmiert. Jetzt muss ich nur noch rauskriegen, warum es nicht
richtig klappt :P
Alles was ich kriege ich nen hübsches geflacker und irgendwann bleibt
mein Programm dann bei einer Farbe stehen. Zumindest scheint es kein
Geschwindigkeitsproblem zu sein, denn der Fehler ist auch bei 50 und
sogar bei nur 20 Herz PWM Frequenz präsent. Ist die Frage, ob mein
Atmega64 zum 32er kompatibel ist. Beide haben als Timer1 einen 16 Bit
Timer und dieser wird ja in der Soft-PWM verwendet. Ansonsten kann ich
in den Datenblättern so keinen Unterschied feststellen. Der Atmega103
Kompatibilitätsmodus ist auch deaktiviert. Als Quelle habe ich für
meinen 16 MHz Quarz ext.Chrystal/Resonator High-Freq 16K CK + 64ms
eingestellt. Damit sollte man ja definitiv auf der sicheren Seite sein
und der Quarz sauber einschwingen.
Hmmpf moren nochmal auf meine Hardware gucken, an irgendwas muss es ja
liegen.
...wollte gerade schreiben das ich nicht weiss was im moment überhaupt
funktioniert.
Poste doch bitte nochmal schnell Deinen Source.
Wäre doch gelacht...
Jo,
hier der Quellcode. Ist schon komisch, wenn ich manuell die Werte für
die PWM zuweise klappt es ohne flackern. Setzte ich aber alle von Hand
auf den Höchstwert, also im Fall meiner Lookup-Table 255 bleibt die LED
dunkel. Scheint also fast nen Problem mit dem Maximalwert der PWM zu
sein. Das könnte das Geflacker erklären. Kannst ja mal drüber gucken,
vielleicht fällt dir noch spontan was ein.
Grüße Mike
@kbuchegg
Wie bereits geschrieben,
hatte noch keine Erfolgsmeldung.
...und für einen fest vorgegebenen Ablauf gleich mit Kanonen auf Spatzen
zu schiessen, ich weiss nicht.
Wenn das sein Wunsch ist, gehts halt weiter. ;)
Aber jetzt muss erstmal das Fading funktionieren !
PS: Das war doch keine Einmischung und wenn dann ist sie sehr
willkommen.
Hmmm...
- Der Prolog mit den 8MHz stimmt nicht mehr, oder ?
- In main() fehlt eine Klammer (die vom While)
Du sagst wenn Du ohne pwmtable, also die reale PWM, langsam hochfährst,
geht das ganze ohne geflackere ?
Das Array der LookUp Tabelle wird durch das Schlüsselwort PROGMEM im
Flash deines Controllers angelegt,
die Funktion pgm_read_word aus progmem.h liefert den Wert der
entsprechenden Speicherzelle zurück.
Hier könnte man sofort ein wenig Optimieren.
Die LookUp Tabelle beinhaltet keinen Wert grösser 255.
Dafür kann man auch den Datentyp uint8_t nehmen (verbraucht nur noch die
hälfte des Speichers :)).
Die Funktion zum Auslesen heisst dann : pgm_read_byte
Wenns klappt, gehen wir an deine Ablaufsteuerung.
Jein,
wenn ich die Werte von Hand setzt und sie unter 200 für die On-Time
bleiben geht es recht zuverlässig. Allerdings auch nicht immer und
gelegentlich scheint das Programm abzustürzen. Ich werde morgen mal
deinen Tip für das Einlesen einbauen. Dann kann ich vorher nochmal die
Hardware kontrollieren.
Was die Klammer angeht, fehlte die wohl nur im Moment des speicherns
dieser Version. Sonst hätte der Compiler meckern müssen. Im Quelltext wo
ich den Inhalt der While-Schleife auskommentiert habe und die Werte von
Hand setzt ist sie jedenfalls drin.
Mist,
lässt einen doch keine Ruhe sowas. Ich habe jetzt deine korrektur
eingebaut. Damit geht das ganze schon wesentlich besser. Richtig klappen
tut es aber immer noch nicht. Früher oder später gerät das System aus
den Fugen. Am Anfang fadet es noch recht hübsch, dann stellt es nach
lust und laune Farben ein. Geht mal auf volle Helligkeit, blinkt und
blitzt, fängt sich irgendwann vielleicht auch wieder. Wirklich komisch
das ganze.
Wie gesagt morgen mal Hardware checken.
Anbei sonst der Quellcode wie ich den aktuell compiliert habe.
Das mit dem Wert von unter 200 meinst in der pwm_settings,
oder über die LookUp-Tabelle ?
Vielleicht kannst Du den Wert einmal Statisch, ohne hoch oder
runterzählen, ausserhalb der while-Schleife setzen...
Damit würden wir den Fehler auf die Interrupt-Routine eingrenzen.
OCR1A+=(uint8_t)T_PWM;// Was macht der Cast an dieser stelle ? Ist doch 16Bittig
6
7
if(pwm_setting[0]>pwm_cnt)tmp|=(1<<0);
8
if(pwm_setting[1]>pwm_cnt)tmp|=(1<<1);
9
if(pwm_setting[2]>pwm_cnt)tmp|=(1<<2);
10
if(pwm_setting[3]>pwm_cnt)tmp|=(1<<3);
11
PWM_PORT=tmp;// PWMs aktualisieren
12
if(pwm_cnt==(uint8_t)(PWM_STEPS-1))
13
pwm_cnt=0;
14
else
15
pwm_cnt++;
16
}
Das l_value (Register OCR1A) ist zwar 16 Bitig,
aber das r_value wird durch den Type-Cast ((unit8_t)) auf 8 Bit
reduziert.
Solch ein Type-Cast wird zur Typumwandlung benutzt,
um zb float nach int zu bekommen.
Hier macht er genau das,
aber die errechneten 625 für T_PWM passen nicht in den Datentyp uint8_t
(0..255).
Dein Compiler schneidet jetzt 8 Bit ab -> der Wert stimmt also nicht.
Glaube ich.
PS: Du hast doch nur "3 Farben",LED-ketten oder so an dem Controller !?
Dann kannst Du die Abfrage pwm_setting[3] rauswerfen.
Also nix für ungut wenn ich mich hier einmische :-)
Mein Ding ist eher Bascom & Assembler.
Aber hier denke ich es ist gar keine gute Idee, eine Typumwandlung mit
(uint8_t)T_PWM zu machen, wenn der T_PWM Wert laut Rechnung einen Wert
von 625 enthält. Das gibt Murks.
Warum eigentlich nicht den CTC Mode & Prescaler nehmen ? Dann muss man
OCR1A nicht immer nachladen.
Das ist doch ein aus einem Artikel geklauter Sourcecode.
Warscheinlich hatte der Schreiber von Anfang an vor die Variante 3 zu
Programmieren.
Da ist es doch sinnvoll, oder ?
Soft-PWM
Aber Pssst... soll keiner Wissen.
Hm, ja...
Aber auch im Originalartikel wird immer der OCR1A mit T_PWM aufaddiert.
Wobei das selbst da keinen Sinn macht, die Anweisung ist unnötig wenn
der richtige CTC verwendet wird. Diese Addition kostet 102400 Zyklen/s,
0,64% der Rechenleistung und hat für den gegebenen Zweck keinerlei
Nutzen.
Hmmm... naja was soll ich sagen...
Der Schreiber von dem Artikel macht es bei allen 3 Varianten.
Warum weiss ich auch nicht...
Wer hat das denn geschrieben, dann fragen wir einfach mal nach ?
(sehe dort weder einen Namen noch ein erklärendes Kommentar)
@MWS :
Man merkt gleich das Du Assembler Programmierst ;)
Was passiert eigentlich wenn OCR1A überläuft ?
Mir kam es Suspect vor, habe es auch ehrlich nicht kapiert aber es
scheinte zu funktionieren.
Für mich mache ich nur das was ich auch verstehe.
Bin da ein Eigenbrödler... ;)
Du meinst, was passiert wenn nach einer Addition mehr als 65535
rauskommt ? Das Gleiche wie beim Überlauf eines unsigned int, Bits über
15 werden einfach abgeschnitten, im Register steht dann der Rest des
Überlaufs.
Prinzipiell funktioniert die Routine schon (außer der zuaddierte Wert
wird so klein, daß der Zähler bereits weiter als der neue Wert in OCR1A
ist).
Das Ganze ist aber wie gesagt programmiertechnisch zweckfrei, bzw.
schädlich solange geeignetere Modi zur Verfügung stehen.
Vielleicht habe ich es deshalb nicht verstanden... ;)
Ich denke aber mal das es für die 3. Soft-PWM Variante doch gut
passt.
Da muss während der Timer läuft modifiziert werden.
Stelle mir das im Normal oder CTC - Modus eher Problematisch vor.
Ja genau, beim dritten Beispiel ist es notwendig. Aber das zweite
Beispiel argumentiert mit Laufzeitersparnis durch geschicktere Anordnung
des Codes. Da fällt natürlich auf, wenn dort ein unnötiger Befehl
drinsteht, der die Ausführungszeit verlängert statt verkürzt.
>Autor: Thomas W. (wagneth)>Datum: 21.01.2009 22:30>Das ist doch ein aus einem Artikel geklauter Sourcecode.>Warscheinlich hatte der Schreiber von Anfang an vor die Variante 3 zu>Programmieren.>Da ist es doch sinnvoll, oder ?>Soft-PWM>Aber Pssst... soll keiner Wissen.
^--- Das hatte ich in meinem Post weiter oben gemeint...
Du hast natürlich recht, aber Mad Mike hat sich für Copy&Paste
entschieden.
Was soll ich tun, ausser seinen Code zu "DeBugen". :)
Ich bin da eher für selber schreiben.
Nur so bekommt man ein Gefühl für die Sprache.
Auch Fehler helfen einem beim lernen.
Aber das ganze soll erstmal funktionieren.
Jetzt bin ich OffTopic...
Wenn Mad Mike das alles lesen muss wird er wirklich noch mad ;)
Gute Nacht !
Da hast Du recht, es ist ungemein hilfreich, wenn man weis, was man
macht :-)
Gute Nacht.
@Mad Mike
Also ich würd' den CTC-Modus nehmen, bei:
...
TCCR1B = 1; // Timer läuft mit vollem Systemtakt
TIMSK |= (1<<OCIE1A); // Interrupt freischalten
--> OCR1A = (uint16_t)T_PWM; // Obere Grenze des Timers einstellen
...
Dafür OCR1A += (uint8_t)T_PWM aus der ISR löschen
Dann in die main:
...
--> TCCR1B = 1 | CTC1; // Timer läuft mit vollem Systemtakt &
// wird bei Erreichen von OCR1A zurückgesetzt
TIMSK |= (1<<OCIE1A); // Interrupt freischalten
....
Dann noch in die for/next Schleifen 31 statt 10 reinschreiben, denn die
pwmtable hat 32 Werte, und so wird's nie richtig hell.
Viel Spass bei der Erleuchtung ;-)
@MWS
Naja, ich denke das Programm würde auch ohne CTC laufen.
Den Fehler mit dem Cast hatte ich eine Minute vor Dir gepostet,
der Zugriff auf den Flash sollte jetzt auch Funktionieren.
Aber:
Jetzt kann er schon wieder mit Copy&Paste Arbeiten.
Ich glaube nicht das er daraus viel lernt.
Less Dir den Thread mal von vorne durch...
(er weiss immer noch nicht was der CTC-Modus ist)
Sorry.
@ Mad Mike: Warte auf Deine Erfolgsmeldung... :)
Yup. Kannst recht haben.
Hab' oft den Eindruck hier daß lernen nicht sooo gefragt ist, eher die
fertige Lösung zum Problem ohne viel Aufwand. Man könnte ein Sticky
machen mit der Nachricht: RTFM
Natürlich funktioniert auch das Orginalbeispiel mit Aufaddieren des
OCR1A, und mit der Korrektur des falschen Typecast sollte das Ding jetzt
auch laufen.
Sollte von mir nur eine Anregung sein, sich über die Funktion des Timers
und des CTC-Modus Gedanken zu machen, außerdem ist schon noch etwas
Kreativität gefragt, meine Zeilen an passender Stelle einzufügen :-)
Neee, war doch auch nicht böse gemeint.
Will doch nur das er sich selbst mit der Lösung seines Problems
beschäftigt.
Dann geht das nächste Projekt schon ganz alleine von der Hand. :)
IMHO:
Es liegt nicht nur an dem Fragesteller,
auch der Gefragte trägt zum Verlauf bzw der "Selbstständigkeit" (oder
wie man das auch immer nennen will) des TEs bei.
Ich erwarte nur das der Fragende mit seiner Problematik sich aktiv
auseinandersetzt und Fragen formuliert.
Oft finde ich beim ausformulieren einer Frage bzw. der Möglichkeiten
schon eine/mehrer Lösung des Problems.
Also wenn ich antworte will ich auch helfen. :)
Sehr gut, weiter so g
Ich verkrümele mich gleich mal hinter mein Oszi und checke die Hardware.
Denn ansich sollte man ja erwarten können, daß hier quasi als
Musterlösung präsentierter Quellcode auch funktioniert. Das es bei allen
3 irgendwie flackert ist schon komisch. Deshalb checke ich nochmal meine
Hardware, daß kann ich wenigstens im vergleich zum programmieren. Danach
gehe ich mal die ganzen Tips hier durch und melde mich dann wieder. Aber
zur allgemeinen Beruhigung, daß mit dem OCR1A aufaddieren war mir auch
aufgefallen und ich fragte mich nach dem Sinn. Da kommt dann aber wieder
der Punkt mit dem Mustercode. Das steht da, also muss es funktionieren
g
So,
es wird immer besser. Jetzt fadet er schon ein paar mal sauber rauf und
runter bevor das wilde Geflacker wieder anfängt. Ich habe den Quellcode
mal nach MSW Vorschlag abgeändert. Wobei seine Zeile
TCCR1B = 1 | CTC1;
nicht laufen wollte, CTC1 sei unbekannt. Ich habe das Register dann mit
einer 9 geladen, sollte das selbe Ergebniss sein. Was das Geflacker
angeht, so habe ich auch schon einen Verdacht. Das könnte wirklich an
der Hardware liegen. Wenn ich den Port ändere und weg von meiner
High-Power LED mit Treiber IC gehe kann ich immer schön die PWM messen.
Gleich mal ein paar normale LEDs an den Port klemmen und gucken was
passiert. Wobei nen Hardwarefehler im LED Chip währe komisch, den wenn
der nebenbei auf voller Power läuft klappt die Schaltung auch. Sehr
misteriös. Also ihr kümmert euch im die Software und ich nehme mir die
Hardware zur brust g
Anbei die letzte Software Version. Melde mich wieder wenn ich die
Hardware zerlegt habe.
@Mad Mike,
ja CTC1 heist auch WGM12 und ist Bit3 des TCCR1B, somit ist 9 richtig.
Sehe keinen Fehler mehr, einfach mal verschiedene Haltepunkte in die
for/next Schleifen reinsetzen um zu sehen, ob die while Schleife
wenigstens einmal komplett durchläuft, oder wo's sonst klemmt.
Ansonsten HP LED mit weniger Saft betreiben, sehen ob's an
Spannungseinbrüchen liegt. Was ist das für ein LED Treiber ?
Schaltregler ?
Moinsen,
es scheint wirklich an der Hardware zu liegen. Ich habe das ganze jetzt
mal auf einen Atmega8 geproggt und der macht PWM Kanäle ohne Probleme.
Nur ganz aus gehen die LEDs nicht. Das liegt aber wohl an der
for-Schleife.
for(i=31;i>0;i--)
geht ja nie wirklich auf Null, da Null zum Abbruch der Schleife führt
und nicht mehr geladen wird, schreibe ich jedoch
for(i=31;i>=0;i--)
fängt die LED beim erreichen von 0 wieder an zu flackern und es geht
nicht weiter. Dabei ist die 0 in der Look Up Table ja sauber definiert.
Verstehe ich gerade nicht so ganz. Kann der Wert für i vielleicht nicht
negativ werden, so dass die Abbruchbedingung nie erfüllt würde?
Was die High-Power LED angeht, so werde ich mal versuchen die
Spannungsversorgung zu puffern. Ich denke diese 350mA Sprünge pro Kanal
können nicht schnell genug geliefert werden
Software sollte OK sein (habs natürlich nicht auf der Hardware
getestet).
Jedenfalls sehe ich keine Fehler mehr.
Wenn Du nur 3 Farben/Ausgänge verwendest kannst Du
Was ist das für ein Treiber den Du verwendest ?
By the way: Du kannst das Array pwm_settings eins kleiner
Dimensionieren, und auch die if-Abfrage in der ISR rausnehmen.
Ändere doch auch mal den Prolog mit den 8MHz ;)
In Main.c :
1
// Timer 1 OCRA1, als variablem Timer nutzen
2
OCR1A+=(uint16_t)T_PWM;
3
TCCR1B=9;// Timer l�uft mit vollem Systemtakt und CTC1
4
TIMSK|=(1<<OCIE1A);// Interrupt freischalten
Was machst Du denn da mit dem OCR1A ?
Wieso Addierst Du, mach doch nur eine Zuweisung,
das wirkt unsauber.
Weiss jetzt grad nicht ob OCR1A mit 0 Vorinitialisiert wird oder schon
einen Wert != 0 enthalten kann.
Dabei kannst Du gleich in das (1<<WGM12) ersetzen.
Mach doch mal einen Schaltplan von dem was Du aufgebaut hast.
(Multiple-Bugs g)
Äh ja das += gehört da gar nicht hin, blödes copy und paste.
Schaltplan gibt es zu der Schaltung natürlich schon längst, auch ne
geätzte Platine ist vorhanden, sonst ist Löten von Fine Pitch immer ne
qual. Das Treiber IC ist ein AS3691
http://www.austriamicrosystems.com/eng/Products/Lighting-Management/LED-Drivers/AS3691
Quasi ein 4 Fach Linearregler mit max. 400mA pro Kanal. Ist ansich ein
ganz einfacher Baustein, sollte aber Reflow gelötet werden, da
Kühlfläche auf der Unterseite. Ich schmeiß den gleich mal unter die
Röntenkamera bei uns im Labor und gucke ob die Reflow Lötung geklappt
hat. Toll wenn man das gesammte Labor seiner HW Abteilung zur Verfügung
hat, gelle g
Ich sitze bei EADS in der Hardwareentwickelung,
habe mit Software also eigentlich so rein gar nichts am Hut. Mache das
ganze jetzt zum privaten Vergnügen.
Also der Chip ansich sieht gut verlötet aus. Jeder Kanal für sich
funktioniert auch wunderbar, erst wenn man 2 zusammen benutzt gerät das
System aus den Fugen. Vielleicht ist der Chip intern zerstört, mal sehn
ob ich einen neuen Auflöte, die sind schwer wieder runter zu kriegen.
Mal an einen anderen Port ausprobieren, was der mit normalen LEDs macht.
Dann hätte ich den Fehler definitiv auf die Hardware begrenzt.
Grüße Mike
Das mit der Software ist kein Problem...
Du hast sicherlich schon die Spannungen an den wichtigen Punkten
Oszilloskopiert ?
Vielleicht sind die Pulse etwas viel für Deine Spannungsquelle ?
Welchen Chip meinst Du, µC oder Treiber ?
(wahrscheinlich beide)
"Typische Sachen" wären nicht alle der 3xGND/2xVCC/1xAVCC Pins
verbunden.
Laut Datenblatt ATMega64 ist AVCC allerdings nur die Spannungsversorgung
für PortF, sollte aber trotzdem verbunden werden. (Datasheet Seite 7)
Ist die Spannung am Reset-Pin stabil bei 5V ?
Was für einen Programmer benutzt Du ?
Also ich habe es jetzt auf einen Hardwarefehler runterbrechen können.
Mit Low Current LEDs an einem anderen Port tut das ganze einwandfrei.
Tja, da muss ich wohl noch mal ne runde Datenblätter lesen.
Die Versorgungsspannung jedenfalls ist stabil. Es ist ehr der Treiber IC
der Probleme macht. Na ja, wird schon werden. Die Software PWM mit
Linearisierung tut jedenfalls. Jetzt könnte man noch überlegen, wie man
schöne verläufe hinbekommt.
Mike
Ok,
es wird echt zu spät. Wenn ich die Farben nicht fade sondern fest
einstelle, macht die PWM keine Probleme. Ist es vielleicht doch kein
Hardware fehler, aber warum geht die Software dann Problemlos auf einem
anderen Port oder uC? Das spricht für Hardwarefehler, dass bei fester
Farbe die Lampe geht spricht für Software.
Man ey, so was einfaches und ich finde den Fehler nicht, zum kotzen. Na
ja morgen mal gucken. Jetzt wird erstmal ne Runde mit dem Laser gespielt
^^
...schade um die schöne Platine !
Nur interessehalber, wie hast Du gelötet ?
Neee, Laser-Reflow ?!
Also wenn es direkt mit einem anderen Port funktioniert sollte es die
Hardware sein.
Entweder der Port des µC oder Dein Treiber.
Wenn es mit einem Kanal geht, würde ich eher auf Spikes oder
schwächelnde Versorgung zu wenig Blockkondensatoren oder sowas tippen.
OK, Mike liebt also die Abwechslung...
Zuerst müssen wir planen wie das ganze aussehen soll.
Soll immer nur eine Farbe gefadet werden ?
Also reicht es abwechselnd Farben ein und auszublenden ?
Überlegen wir mal was wir alles brauchen...
Jemand der Regie führt -- Eine Tabelle in der Zeiten und Helligkeiten
abgelegt werden.
Eine Zeitbasis -- Wir wollen etwas in Abhängigkeit der Zeit verändern.
Was muss jetzt alles in einen Datensatz rein ?
1. Welche Farbe
2. Dauer,
3. Anfangswert,
4. Endwert, des Fadings
5. Pausenzeit
Solch einen Datensatz kann man in einem Struct kombinieren.
Nun kann man ein Array von diesem Struct erzeugen.
In dieser Struktur kann man nun auf einfache weise den gesamten Ablauf
speichern (Für Fading nacheinander).
Mad Mike,
die Seite über den Led Treiber gibt nicht genug Info über die Specs,
bist Du sicher, daß Du diesen Treiber im Rahmen seiner Specs betreibst ?
Probier doch mal folgendes, ist nicht viel Arbeit im Falle es nicht
geht:
...
if (pwm_cnt==(uint8_t)(PWM_STEPS-1))
{
pwm_cnt=0;
--> sync_now = TRUE
else
pwm_cnt++;
...
...
uint8_t i=0;
--> static int8_t sync_now = FALSE
while(1)
{
for(i=0;i<32;i++)
{
--> while(! sync_now); // Auf Sync warten (1/100 sec)
pwm_setting[0]=pgm_read_byte(&pwmtable[i]);
sync_now = FALSE;
// _delay_ms raus !
}
usw...
...
Nagel mich nicht auf die Syntax fest, kann's nicht testen.
Möglicherweise gibt's in C ein Boolean, dann nimm den anstatt des int
für den sync_now.
Klar was das macht ? Jedesmal wenn ein PWM Zyklus zu Ende ist wird das
sync_now gesetzt, die while Schleife vor der for(i=0... wartet bis das
geschehen ist und schreibt dann erst einen neuen Wert in die
pwm_setting, damit findet kein Wechsel während des PWM Zyklus statt, ist
also nun synchron dazu. Möglicherweise zickt Dein Treiber bei
unregelmässiger PWM. Allerdings wärst Du mit dieser Methode auf maximal
die F_PWM festgelegt, mit dem Du die Werte hochzählst. Gegenüber Deiner
_delay_ms(10) Version würde sich aber nix ändern, denn 1/100 Hz = 10ms.
Nochmal nachgedacht, bin nicht mehr sicher, ob mein Ansatz Sinn macht,
aber probier's immerhin aus.
Noch etwas, hab' ich gerade nachgelesen: In ISR & main verwendete
Variablen in C müssen volatile deklariert werden.
Also static int8_t sync_now = FALSE nicht in die ISR, sondern als
volatile int8_t sync_now unter globale Variablen.
Hmmm...
Denke der Knackpunkt ist nicht der Zugriff auf den Flash,
es kann ja nur an der Zuweisung liegen !
Aber ein Byte überträgt der AVR doch in einem Rutsch, oder ?
(8Bit als 8 Bitter ?!!?)
@MWS : Wie sähe das denn in Assembler aus ? (die Zuweisung)
Ob der neue PWM-Wert ein oder zwei PWM Perioden später aktualisiert wird
dürfte nur bei sehr niedrigen Werten bis garnicht auffallen...
Denke das ist es nicht.
Zumal das Programm an einem anderen Port mit anderer Beschaltung
Funktioniert.
Ich Tippe auf Hardware/Versorgung.
(http://de.wikipedia.org/wiki/Ockhams_Rasiermesser)
Ein nicht ganz frischer Wagneth.
PS: Das Datenblatt ist wirklich nicht so "auskünftig"...
Also ich gehe eigentlich auch von einem Hardwareproblem aus, nur ist es
irgend so ein kleiner fieser Fehler, der sich nicht finden lässt. Aber
das kriege ich noch raus, ich kann nen Flugzeug fliegen lasse aber keine
Lampe zum leuchten bringen, währe doch gelacht.
Die Lötstellen sahen sowohl unter dem Mikroskop, als auch unter der
Röntgenkamera gut aus. Das interne Chip Bonding weißt auch keine Fehler
auf. Thermalkamera zeigt keine ungewöhnliche Wärmeverteilung. Analyse
der Spannungsversorgung zeigte Schwankungen beim Schalten der Frequenzen
im Bereich von max 0,2V. Stromanalyse und analyse auf Oberwellen in der
Spannungsversorgung bin ich noch nicht zu gekommen, aber die LED mit nem
Elko zu puffern schien das ganze ehr noch schlimmer zu machen. Als
nächstes werde ich mal den Belastungsgrad des Treibers ändern. Da er ja
mit einem Kanal und 350mA wunderbar läuft, könnte er mit 3x30mA
vielleicht auch tun, wenn ja ist es definitiv ein Problem in der
Spannungsversorgung.
Was ich auch noch testen könnte, wie sich der Treiber IC verhält, wenn
ich ihn mit nem Frequenzgenerator ansteuer. Da haben wir ein paar schöne
programmierbare hier, mit denen kann man auch PWMs durchfaden g
Aber das muss bis nach der Arbeit warten, jetzt muss ich erstmal wieder
meinen Chef glücklich machen.
PS hier ein Link zum vollständigen Datenblatt des AS3691
http://www.datasheetarchive.com/pdf-download/Datasheet-025/DSA00435991.html
Welche Zuweisung ? pgm_read_byte(&pwmtable[i]) ? Die schaut aber gut
aus, genauso wie's sein soll. Und klar werden da nicht Bits einzeln
übertragen, der neue Wert steht entweder bereits in der pwm_setting wenn
die ISR zuschlägt, oder eben auch nicht, dann wird der alte Wert
verwendet.
Wie in Assembler ? Da gibt's nur Befehle für Zugriffe entweder auf den
normalen Speicherbereich oder auf die IO Register. Sowas wie Progmem
gibt's da nicht, das ist speziell eine C Anweisung.
Wenn ein schneller Wechsel der PWM Werte stattfindet und der mit etwa,
aber eben nicht genau mit der F_PWM, kann's schon ungewohnte Effekte
geben. Um das auszuschließen mein Vorschlag zur Synchronisierung.
Würd' auch um den Fehler einzukreisen Zug um Zug solche Programmteile
wie die Anpassung rausnehmen und einfach schreiben
for(i=255;i>0;i--), pwm_setting[2]= i, usw.
@Mad Mike, Datenblatt für den Treiber hab' ich gefunden, ist die
Versorgung des MC bei Deiner Schaltung von der Versorgung des Treibers /
der LED entkoppelt ?
Ich wollte auf den Grund raus, warum das Sync da rein sollte ?!
Ich meinte das funktion pgm_read keine Probleme damit hat,
höchstens die anschliessende Zuweisung.
Ein Byte wird entweder vor dem nächsten Interrupt Zugewiesen,
oder danach. Das sollte nicht schaden.
Probelem gäbe es ja zb nur wenn er 16Bit übertragen würde...
Dann könnte es vielleicht sein das nur Low oder High - Byte Übertragen
würden.
Das würde zum Fehlerfall führen, ev. sein Flackern.
Ich dachte da könntest Du uns mit deinen Assembler-Kenntnissen
aufklären...
Aber er überträgt immer nur ein Byte. Da dauert die Zuweisung doch
bestimmt nur einen Takt, oder ?
Das war die Frage !
Drücke ich meine Gedanken unverständlich aus ?
PS: Ich schrieb Byte, und bezog mich auf den Grund warum da ein Sync
rein muss/sollte. Habe Dich so interpretiert als ob es bei der Zuweisung
zu Problemen kommt. Aber bei einen einzelnen Byte kann der Interrupt
doch nicht sazwischen kommen !? Sorry.
@MSC
Die Chips sind mit den üblichen Maßnahmen, wie Blockkondensatoren,
sauberer VCC und Masseführung usw. ausreichend von einander entkoppelt.
Die Spannungsversorgung für den AS3691 ist auch nicht so spannend.
Besser ist die Frage nach der Vorsorgung der LED. Die hängt zwar am
selben Netzteil, wird aber bis zur LED über eine eigene Leitung direkt
versorgt. Wie gesagt, daß VCC Signal schwankt max. um 0,2V und das kommt
einfach durch die sprunghafte Belastung der Stromquelle.
Pufferkondensator könnte abhilfe schaffen, tut er aber nicht so ganz.
Ich denke inzwische, daß der AS3691 vielleicht nicht sauber auf die
Groundplane gelötet ist und somit nen Strom über 350mA nicht abführen
kann. Ich werde später mal den AS mit Frequenzgeneratoren ansteuern,
dann herscht Klarheit über den µC als Fehlerquelle.
@Thomas
Ich habe inzwischen den Grund gefunden, warum die for-Schleife nicht
tut, wenn man sie auf >=0 setzt. Schleifen werden erst immer
inkrementiert/dekrementiert, bevor die neue Bedingung getestet wird. Bei
mir ist die Zählervariable aber als unsigned integer definiert und das
kann nicht negativ werden. Ist die Schleife also bei 0 angekommen ist
die Prüfbedigung gültig, i müsste nun auf -1 gesetzt werden, damit bei
der nächsten Prüfung die Schleife abbricht. -1 Ist für uint halt aber
nicht zulässig.
Nö, hab' Dich schon verstanden, während des Interrupts kommt sicher nix
dazwischen, außer man macht das absichtlich so. Bei einer 16 Bit
Zuweisung würde man den Interrupt disablen und nachher wieder enablen.
Das ist auch nicht mein Punkt. Aber ein Zyklus dauert 256 Schritte , die
werden 100mal in der Sekunde durchlaufen, jetzt nimm mal an der PWM Wert
wird in der {main} 300mal pro Sekunde geändert, dann würde Änderungen
innerhalb dieser 256 Schritte vollzogen. Wie denkst Du würde das PWM
Signal am Port aussehen ?
Das würde natürlich davon beeinflusst. Jetzt sagt mir mein Verständnis
vom Programmablauf, daß das im Ergebnis nichts machen dürfte, aber lass
doch nur Spikes auf dem Port entstehen und der Treiber fängt dann an zu
zicken. Das ist meine Gedanke dahinter.
Ist Dir übrigens aufgefallen, daß diese PWM eine Schwachstelle hat ? Der
Port kann zwar komplett auf Low gehen, aber nie auf High, er wird selbst
beim PWM Wert 255 Spikes von 39us nach Low haben. Die Bedingung
pwm_setting[x] > pwm_cnt kann nämlich für pwm_cnt 255 nie zutreffen, der
Port geht da immer auf Low, was er eigentlich nicht dürfte. Und daraus,
zusammen mit dem asynchronen Zuweisen der PWM Werte könnten besagte
Probleme im Treiber entstehen, während eine normale LED das wegsteckt.
Zu synchronisieren ist möglicherweise auch nur ein Schuss ins Blaue,
aber wie gesagt, ist wenig Arbeit diese Sync reinzuschreiben. Ich geh so
vor, wenn ich nicht weiterweis, daß ich solche Tests anstelle und dann
schau was passiert.
Mad Mike,
ich glaube man wäre auf der sicheren Seite wenn man dem Atmel eine
eigenen Spannunsregler mit Glättung verpassen würde. Aber das muss nicht
der Grund sein. Probier mal testweise, was ich zur Software geschrieben
habe.
So,
fehler gefunden würde ich mal sagen. Der Chip war nicht sauber mit
seiner Massefläche auf GND verbunden. Das hat dann wohl dazu geführt,
daß er bei hohen Strömen, also meheren Farben nicht mitgespielt hat. Das
ganze Teil nochmal in den Reflow Oven gepackt und ordentlich warm
gemacht, kurz mit ner Pinzette runtergedrückt und abkühlen lassen. Jetzt
läuft die Lampe. Die Farbwechsel sind stellenweise noch etwas "hart".
Ich werde mal die Snychronisation einbauen, vielleicht bringt das was
verbesserung.
Prima, gratuliere, freut mich das es geht.
Ob eine Synchronisation den Farbwechsel verbessert, würd' ich
bezweifeln. Aber probier's aus und sag' uns das Ergebnis.
Denke es könnte mehr Sinn machen, die Funktionen für den Farbwechsel zu
ändern (statt for/next Schleifen)
Grüsse
Magic White Smoke
Jup,
danke danke. Also die Snychronisation hat soweit nix gebracht.
Interessanter währe jetzt vielleich dein Einwand daß die PWM nie völlig
Hight ist. Könnte man doch recht einfach lösen, indem man die PWM nur
mit 254 Schritten macht, oder?
@Thomas
struct fade
{
uint8_t farbe;
uint8_t anfang;
uint8_t ende;
uint8_t dauer;
uint8_t pause
};
struct fade this[x];
das sollte deinem Vorschlag entsprechen. Ist die Frage, legt man sich
auf eine maximale Anzahl Farbwechsel fest oder macht man es dynamisch
mit malloc. Dann müsste man sich auch noch um die Speicherverwaltung
kümmern, sauber einketten, ausketten, löschen und vielleicht auch noch
wo der µC es speichert. Denn die Zellen im Eeprom darf man ja nicht so
oft beschreiben.
Ja das geht, ist zwar wie ich denke nur ein Schönheitsfehler und hat
nichts mit dem gleich- oder ungleichmässigen Faden zu tun.
if (pwm_cnt==(uint8_t)(PWM_STEPS-2))
...
Viel mehr zu tun hat damit, daß Du eine PWM mit 256 Schritten durch die
Anpassung über die pwm_table auf 32 Schritte reduzierst. Da siehst Du
halt dann die Stufen. Entweder eine größere pwm_table verwenden, oder
mal eine Formel stricken, welche die table nachahmt, kostet halt
Rechenzeit. Aber noch hast Du. Schätze mal, bei 8 MHz bleiben für das
Gefade noch 60-70% Prozessorzeit übrig.
Klasse mit dem Fehler. :)
--
Der PWM-Fehler
Der Fehler Beträgt ca 0.392 % bei 8 Bit.
Bei 10Bit ca 0,098 %
Naja der Vergleich in der ISR sieht so aus :
Setting > Counter = ?
255 > 0 = 1
255 > 1 = 1
255 > 2 = 1
...
255 > 253 = 1
255 > 254 = 1
255 > 255 = 0
256 > 255 = 1 // Spinnerei aus meinem Hirn.
Da hilft auch die Schrittweite nicht wirklich weiter.
Als evil-fix könntest Du am Anfang ein Setting++; machen.
Das Tastet die Schrittweite nicht an, Du verschwendest nicht mehr
Rechenpower mit einer weiteren Abfrage innerhalb der ISR und bei 0
ändert sich nichts.
Kostet allerdings Speicher für dickere Variablen.
...und Du musst die PWM-Werte über eine Funktion ändern,
oder das ganze zusammenlegen.
Aber, Du hast deine 0% und 100 %. Wobei die 0% da schon wichtiger sind
;)
Wenn es Funktioniert wie ich es mir denke.
Untestet :
1
voidSet_PWM(uint8_tch,pwm_set;)
2
{
3
if(pwm_set>0)pwm_settings[ch]=++pwm_set;
4
elsepwm_settings[ch]=0;
5
return;
6
}
--
Wenn das Fading sauber werden soll musst Du mit dem PWM_steps hochgehen,
momentan benutzt Du eine 8Bit-PWM.
Versuche doch mal die 10Bit Variante.
Leider musst Du alle beteiligten Variablen und die LookUp-Tabelle
ändern.
Laut Artikel machen die 2 Bit nicht viel aus, aber man kann es mal
Probieren.
Die 16Bit Variante dürfte mit der Rechenzeit nicht mehr hinkommen.
(2 Takte für die ISR ist def. zu kurz)
Vielleicht kannst Du mit den Werten der LookUp ein bisschen vermitteln.
Das könnte das ganze ein wenig "verschmieren".
---
An PortA kann man auch keine Hardware-PWM betreiben :(
Das wären PortE+B, dann hättest Du mit der Hardw.PWM Rechenzeit ohne
ende.
---
Für Farbe hätte ich vielleicht ein enum genommen.
Aber eigentlich egal...
1
typedefstruct{
2
enum{
3
rot=0,
4
blau=1,
5
gruen=2
6
}farbe;
7
uint16_tdauer,
8
pause;
9
uint8_tstartPWM,
10
stopPWM;
11
12
}_FADEREC;
13
_FADERECFaderAr[10];
Wenn Du ansonsten keine Anforderungen hast auf zum Nächsten Punkt.
Willst Du im laufenden Betrieb per UART, Taster o.ä. den Ablauf
verändern ?
Bei einem festen Muster würde ich das ganz im Flash unterbringen.
(genauso wie die LookUp-Tabelle)
Wenn das ganze per UART o.ä. nachgeladen werden soll bleibt Dir nur noch
EPROM oder RAM.
Im EPROM würde die Fad-Daten auch nach einem Reset noch liegen.
(immerhin würde das für ca 256 Fadings reichen)
100.000 Schreibzyklen sind schon ein wenig. Da musst Du viel mit
spielen.
Auswerten würde ich das Array mit einem Zähler und einem switch-case
gerippe.
Edit : PWM_Table ist von den Stufen eigentlich schon am Limit.(die
vielen doppelten Werte...)
Hardware PWM fällt aus, da das Teil eigentlich für 12 Kanäle gedacht
war. Das ist quasi der Prototyp und später mache ich ne neue Platine wo
dann alles fertig drauf kommt. Man muss ja erstmal gucken, ob man mit
dem Treiber zurecht kommt.
Ansonsten hast die Platine einen FTDI drauf der die Lampe dann per UART
steuern können sollte. Andere Dinge, die möglich währen. DMX, Touchpad,
IR Fernbedienung, Sound2Light. Was es halt so an netten Spielerreien
gibt. Aber in erster Linie währe Fading mit PC Anschluss schon mal toll.
Ich gehe jetzt mal hin und schraube die PWM auf 10Bit rauf und mache die
Look Up Table größer.
Morgen Mike,
hab im Anhang mal eine bisschen rumgespielt.
Vielleicht reicht Dir so ein einfaches Konstrukt.
(Du musst daran aber noch richtig Arbeiten)
Wenn Du gerne mal mit dem PC ein neues Schema einspielen willst, würde
ich es erstmal im RAM des Controllers ablegen.
(zumal der 2x so gross wie das EPROM ist)
Mit einem "Befehl" per UART könnte man das ganze in das EPROM
übertragen.
Überlege Dir doch mal wie lange die Pausen bzw Überblendzeiten sein
sollen !?
Den demnach würde ich einen Timer auslegen so das daß uint8_t für die
Maximale Dauer ausreicht...
---
Solch ein switch-case mit einem kleinen Ringpuffer eignet sich auch für
das auswerten von deinem UART-Datenstrom.
Nach dem Motto :
Befehl "a" heisst neuen Datensatz emfpangen,
in Zeile 3 überschreiben,
Befehl "rot",
Dauer 100,
StartPWM = 0,
EndPWM = 9999 ...
oder
Befehl "r" heisst vielleicht Daten von EPROM nach RAM übertragen.
Befehl "e" heisst vielleicht Daten von RAM nach EPROM übertragen.
Befehl "p" heisst vielleicht Play.
...
Jetzt bist Du gefordert... :)
Grüße
Thomas
PS: Erinnerst Du dich noch an die Formel vom Anfang ?
Man Thomas,
ich glaube wenn das Teil richtig läuft, musst du mir mal dein Adresse
geben. Dann kriegst von mir auch so ne Platine, wenn du schon den halben
Quellcode dafür schreibst.
Was den Timer angeht, so würde ich mal sagen 10 Sekunden sind da mehr
als ausreichend. Fading im 10 Sekunden takt ist ja schon sehr gemütlich.
Somit währe laut der Rechnung in deinem Quelltext die Interrupt dauer
auf ungefähr 40ms festzulegen. Sollte mit einem Prescaler wohl machbar
sein. Bedeutet aber auch, daß das schnellste Fading 40ms dauert. So, ein
Tick dauert 62,5ns. Ich will mit nem 8 Bit Timer auf 40ms kommen, kann
also nur bis 256 zählen. 40ms/256 = 156,25µs / 62,5ns = 2500. Ok passt
nicht, bei nem 16 Bit Counter würde ein Prescaler von 10 reichen, könnte
man also mit 8 machen.
Siehst Du einen Unterschied bei der 10Bit PWM ?
Hmmm...
40ms entsprechen 25Hz.
Mit Timer0 kommen wir laut :
t= (Prescaler*TimerTicks)/Fclk
t=16,32ms
Zählbar mit 8Bit bedeutet ca 4,16s
Zählbar mit 16Bit bedeutet ca 1069s ^= 17 Minuten
Irgendwie ist das blöd.
Ausserdem würden wir Rechenzeit für die zusätzliche ISR "verschwenden"
(rein und raus aus der ISR -- weiss aber nicht wieviel das ausmacht)
---
Neuer Ansatz:
Die vorhandene ISR Arbeitet mit 25,6 KHz.
Aber die PWM Arbeitet doch mit 100Hz das entspricht 10ms...
Wenn wir mit 255 zählen kommen wir auf 2,55s
Wenn wir mit 65535 zählen kommen wir auf 655,35s
Angenommen die PWM würde nur mit 60Hz laufen...
Da kommen wir mit einem von Zähler 255 auf 4,25s
Da kommen wir mit einem von Zähler 65535 auf 1092s
Schlüssel wäre also diese Stelle :
1
...
2
if(pwm_setting[0]>pwm_cnt)tmp|=(1<<0);
3
if(pwm_setting[1]>pwm_cnt)tmp|=(1<<1);
4
if(pwm_setting[2]>pwm_cnt)tmp|=(1<<2);
5
if(pwm_setting[3]>pwm_cnt)tmp|=(1<<3);
6
PWM_PORT=tmp;// PWMs aktualisieren
7
if(pwm_cnt==(uint8_t)(PWM_STEPS-1))
8
pwm_cnt=0;//Hier kann man die 100Hz abgreifen
9
else
10
pwm_cnt++;
11
}
Es dürfte also geschickter sein mit einer 16Bit-Variable zu Timern...
1
volatileunit16_tmood_timer;// Zähler ist Global
2
3
...
4
PWM_PORT=tmp;// PWMs aktualisieren
5
if(pwm_cnt==(uint8_t)(PWM_STEPS-1))
6
{
7
pwm_cnt=0;
8
mood_timer++;
9
}
10
else
11
pwm_cnt++;
12
}
Eine andere Lösung wäre noch verrückter:
1
uint8_tmood_timer;
2
3
ISR(TIMER1_COMPA_vect){
4
staticuint8_tpwm_cnt=0;
5
staticuint8_tmood_prescaler=0;// ein Prescaler !
6
7
uint8_ttmp=0;
8
9
if(pwm_setting[0]>pwm_cnt)tmp|=(1<<0);
10
if(pwm_setting[1]>pwm_cnt)tmp|=(1<<1);
11
if(pwm_setting[2]>pwm_cnt)tmp|=(1<<2);
12
if(pwm_setting[3]>pwm_cnt)tmp|=(1<<3);
13
14
PWM_PORT=tmp;// PWMs aktualisieren
15
16
if(pwm_cnt==(uint8_t)(PWM_STEPS-1))
17
{
18
pwm_cnt=0;
19
mood_prescaler++;
20
21
if(mood_prescaler>=3)//wir zählen von 0..3 = 4 * 10ms
22
{
23
mood_prescaler=0;// Unser "prescaler" faktor 4 wird hier zurückgesetzt
24
mood_timer++;//...und unser Timer "aufgeladen" somit haben wir ca 4*10ms pro step.
25
}
26
27
}elsepwm_cnt++;
28
}
Denke das wäre eine passable Lösung :)
Bei der in der Tabelle 16-8 Bit "gespart" blieben.
Wenn Du jetzt das Timing ändern willst, hängt aber alles andere mit
dran.
PS: Bräuchte Deine EMail Adresse...
Hmm,
ich krige die PWM nicht vernünftig auf 10 Bit umgebaut. Habe zwar die
passenden Variablen auf 16Bit erhöht, aber bei den höheren Stufen der
PWM (also die mit mehr On-Zeit) fängt es an zu flackern. Mal gucken, ob
ich rauskriege für welche PMW Werte da anfangen problematisch zu werden.
Jap,
nur zu dumm, dass es nichts gebracht hat. Bei 10 Bit läuft die PWM nicht
sauber durch und einen wirklichen Fehler kann ich nicht erkennen. Am
Anfang dimmt sie noch schön rein und bei größerer On-Time tickt sie dann
aus.
Ok, vergess es. Ich habe den Fehler gefunden. Man sollte auch sicher
sein, daß man wirklich alle benötigten Werte auf 16 Bit umgestellt hat.
Jetzt habe ich eine wirklich schöne 10 Bit PWM.
Ok, also zurück zum Thema. Ausprogrammieren der Farbverläufe.
Thomas hast mail
Mad Mike,
wenn Du dann einmal Deine ganzen Farbverläufe hast und feststellst daß
Dir die Rechenleistung ausgeht, (Du verwendest momentan ca. 50% davon)
dann kannst Du den Lösungsansatz verwenden den ich hier programmiert
habe. Allerdings in Basic, sollte aber verständlich sein.
Dir wird aufgefallen sein, daß zwischen den 64 Werten für die Anpassung
auf die Kennlinie des Auges und den 1024 Schritten Deiner PWM eine
ziemliche Verschwendung von Rechenleistung liegt.
Mein Programm macht das Gleiche wie Deines, hat auch eine Dynamik von
10bit und 64 Schritte, verwendet allerdings bereits 8 Kanäle und braucht
geschätzt 1/20 der Rechenleistung Deiner einfachen Lösung, ohne daß es
recht kompliziert ist.
Der Assemblerteil ist so effizient wie möglich, sollte Dein C-Compiler
da mehr Code für die ISR erzeugen, so wäre die Einsparung noch etwas
größer.
Ich bin scho umgestiegen auf die intelligente Software-PWM Lösung. Die
dürfte selbst bei den 10 Bit auf denen sie momentan läuft nur gute 2%
Rechenzeit brauchen
Seh' gerade noch, daß ich durch copy/paste Fehler in meinem Quelltext
habe, Du hast das sicher schon korrigiert:
ORI R21,&b00001000 ab Label NoPAB3 muss natürlich auch die
restlichen Ports ansprechen, also
ORI R21,&b00010000
...
ORI R21,&b00100000
...
usw.
Hatte die PWM ursprünglich auf 4 Kanäle geschrieben und für Dich auf 8
Kanäle erweitert - und schon ist der Bug drin :-)