Ich versuche gerade in paar LEDs zu dimmen. Das geht aber nicht so recht. Das ganze soll PWM-gesteuert sein. Die 3 LED sind aber immer voll an. Egal welchen Wert "value" hat. Hier mal meine Code. Vorübergehend sind mal 3 Ports geschalten. Alles über einen ULN2003AN. void abled_init(unsigned char value) { ABLED_PORT_DDR |= 1<<PD4 | 1<<PD5 | 1<<PD6; /* Port auf Ausgang */ ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6); /* Low ausgeben */ cli(); /* IRQs abschalten */ TCCR0 = 1<<WGM00 | 1<<WGM01 | 1<<CS02 | 1<<CS00; /* Timer 0: Fast PWM, clk/1024, bei 8 MHz */ TCNT0 = value; /* Startwert setzen */ OCR0 = 0; /* Output compare auf 0 setzen */ TIMSK |= 1<<OCIE0; /* Output compare -> IRQ */ sei(); /* IRQs einschalten */ return; } void abled_change(unsigned char value) { cli(); /* IRQs abschalten */ TCNT0 = value; /* Startwert neu setzen */ sei(); /* IRQs einschalten */ return; } SIGNAL(SIG_OUTPUT_COMPARE0) { ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6; /* High ausgeben */ } SIGNAL(SIG_OUTPUT_OVERFLOW0) { ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6); /* Low ausgeben */ }
Von dem her was ich grad im datasheet gesehen hab, ist timer0 nicht für pwm vorgesehen und es gibt gar kein compare register und auch keinen compare interrupt. Du müsstes das alles im overflow machen und bei jedem overflow tcnt0 neu setzen um so ne art pwm zu bekommen. cu Flo
Hmm .. ich hatte natürlich vergessen zu erwähnen, um welchen Controller es sich handelt. Ich rede hier vom ATmega16. Der hat natürlich diese Funktionalität.
OK. Sieht so aus als hättest du dein ocr0 auf null stehen. tcnt0 brauchst bzw darfst du auf keinen fall ändern wenn du was änderst dan ocr0 damit definierst du dann die helligkeit. TCNT0 steht blos der Wert drinn den der Timer grad hat, da würd ich ned drann rumspielen. Bei jedem inkrement von tcnt0 durch den timer wird tcnt0 mit ocr0 verglichen und der entsprechende interrupt ausgelöst. Bei jedem overflow wird der overflow ausgelöst. Da nun OCR0 auf null ist wird beim overflow deine leds ausgemacht und sofort wieder ein, weil dein ocr0= 0 ist. Deswegen leuchten die led andauernd hell. cu Flo
Upsa .. einfach mal was verwechselt. :) Der gröbste Fehler, war jedoch der IRQ-Vektor. Da habe ich den falschen angegeben. Jetzt läuft es auch soweit. Nur das die LEDs schon bei einem Wert von 100 mit etwa voller Helligkeit leuchten. Schöner wäre natürlich, wenn man das prozentual von 0 - 254 aufteilen könnte. Dazu müssten aber die Timings genau auf die LED bzw. den ULN2003A zugeschnitten sein. Oder wie kann man das verbessern. Eine andere Frage stellt sich mir jetzt erst. Wie kann ich da mehrere LEDs verschieden hell leuchten lassen. Ich habe ja nicht unedlich viele Timer zur Verfügung, wo ich das Tastverhältnis einstellen kann. Da der Overflow ja immer zu gleichen Zeit kommt könnte ich mir vorstellen, die Auschaltzeit im "Compare"-IRQ für jede LED zu verändern. Das würde aber heißen, dass das Compare-Register ständig in der IRQ-Routine angepasst werden müsste bzw. es garnicht mehr über einen PWM-Timer gemacht wird, sondern einen der eben bei jedem Overflow aufgerufen wird, welches ich dann zeitlich durch 255 teilen müsste. Wie würdet ihr das machen? Hier nochmal der fehlerbereinigte Code: #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include "abled.h" unsigned char abled_table_rgb[3][3] = { { 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF } }; void abled_init(unsigned char value) { if (value) { ABLED_PORT_DDR |= 1<<PD4 | 1<<PD5 | 1<<PD6; /* Port auf Ausgang */ ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6; /* High ausgeben */ cli(); /* IRQs abschalten */ TCCR0 = 1<<WGM00 | 1<<WGM01 | 1<<CS02; /* Timer 0: Fast PWM, clk/256, bei 8 MHz */ TCNT0 = 0; /* Zähler auf 0 setzen */ OCR0 = value; /* Output compare setzen */ TIMSK |= 1<<OCIE0 | 1<<TOIE0; /* Overflow + Output compare -> IRQ */ sei(); /* IRQs einschalten */ } return; } void abled_change(unsigned char value) { cli(); /* IRQs abschalten */ OCR0 = value; /* Output compare neu setzen */ sei(); /* IRQs einschalten */ return; } SIGNAL(SIG_OUTPUT_COMPARE0) { ABLED_PORT_OUT &= ~(1<<PD4 | 1<<PD5 | 1<<PD6); /* Low ausgeben */ } SIGNAL(SIG_OVERFLOW0) { ABLED_PORT_OUT |= 1<<PD4 | 1<<PD5 | 1<<PD6; /* High ausgeben */ }
Hmm der msp430f149 hat 10 ausgängen für pwm;) Der 16bit timer vom avr hat glaub ich auch mehrer compare-register. Du musst die leds ja auch ned mit maximal möglicher geschwindigkeit dimmen sondern es reicht ja wenn du ne pwm von 100Hz hast(also timer einmal pro 100tel sekunde voll durchgelaufen) das müsste sich dann in software aufsetzend auf dem timerinterrupt auch für mehrer leds machen lassen. Ich glaub das Problem das deine leds ned voll regelbar sind sind die schaltzeiten probier mal nen anderen prescaler --> langsamer cu Flo
Die 10 würde wahrscheinlich immernoch nicht reichen. ;) Das mit den einzelnen LEDs klappt zumindest mit 3 Stück schon so halbwegs. Ich setze jedes mal in der IRQ-Routine einen neuen OCR0, der dann der nächsthöhere aus einer Tabelle mit den Helligkeitswerten ist. Die PWM mache ich jetzt mi 8MHz/256. Wenn ich auf /1024 gehe flackert der ganze Kram. Dafür gibt es allerdings andere Probleme, die ich in einem neuen Thread setze. Irgendwas haut da mit C selbst nicht hin. Danke für deine Hilfe. Wenn die Routine mal fertig ist, werde ich die in die Codesammlung posten. Vielleicht braucht ja jemand sowas noch.
Wie viele LEDs willst Du denn steuern? Das hört sich danach an, als würde der Controller weiter nichts tun als die LEDs anzusteuern, dann kann man sicher genauso die PWMs in Software schreiben. Da kannst Du dann so viele davon haben, wie Du freie Portpins hast (zumal die Genauigkeit in dem Falle eher eine untergeordnete Rolle spielen dürfte).
Ich dachte da so an ca. 12 LEDs ... Die Genauigkeit ist natürlich relativ uninteressant. Viel mehr soll der Controller nacher wirklich nicht zu tun haben. Da kommen nur noch 2 - 3 Taster ran. Die man natürlich durch die wenigen Funktionen, die man benötigt, u.U. auch doch IRQs abfragen könnte. Aber das ist relativ egal.
Ich würde mir eine Zeitgeberfunktion schreiben (bzw. hab' ich schon :-), die timer queues verwaltet. Für jeden Kanal läßt man dann das An- und Abschalten durch einen software timer vornehmen. Braucht einen einzigen hardware timer mit entsprechender Auflösung. Wenn man die Auflösung zu hoch ansetzt, wird die Interruptlast sicher nicht zu verachten sein, andererseits wirst Du vermutlich kaum mehr als 50 Helligkeitsstufen unterscheiden können müssen, und 50 Hz Flimmerfrequenz müßten auch genug sein. Das bedeutet eine Zeitgeberrate von 2500 Hz bzw. 400 µs. Wenn Du nur 1 MHz Taktfrequenz hast, kann das schon bißchen knapp werden (Du hast dann maximal 400 ausführbare Befehle zwischen den Interrupts), bei höherem Takt ist das aber sicher kein Problem mehr.
Naja wenn ich meine LEDs mal alle habe, werde ich das ganze verdrahten und dann kann ich mich ja an die wirkliche Programmierung machen. Die Hardwareseite ist ja nicht so das Problem. Aber dauern wird das sicher dennoch ein weilchen. Allerdings ist es erstmal schln zu wissen, wie man das hinbekommt. Und im Prinzip funktuniert ja meine Lösung schon mehr oder weniger. Zeig doch mal deine Queue her. ;)
> Zeig doch mal deine Queue her. ;) OK, ich habe die Möglichkeit, timertick() aus einem Interruptkontext direkt zu rufen, noch in den Hauptzweig meiner CVS-Version zurückgepatcht. Wenn Du die Bibliothek mit -DTMR_INTPROTECT compilierst, darfst Du timertick() entsprechend direkt aus der Timer-ISR heraus rufen (dafür wird mehr Code gebraucht). Normalerweise sollte das aber eher so sein volatile unsigned char timer_ticked; SIGNAL(SIG_OUTPUT_COMPARExx) { /* timer running in CTC mode, just trigger timertick() */ timer_ticked = 1; } ... int main(void) { ... for (;;) { if (timer_ticked) { timer_ticked = 0; timertick(); } ... } } http://www.sax.de/~joerg/timer.tar.gz Die union timeoutarg erlaubt es, 16-bit (int) Werte direkt zu übergeben, aber auf saubere Weise stattdessen einen Zeiger auf einen größeren Datenbereich an die nach dem Timeout gerufene Funktion zu übergeben.
Hmm .. letztendlich will ich ja dennoch PWM nutzen und das geht ja im CTC-mode nicht. Ich denke aber ich werde einfach deinen Tipp mit der Queue nutzen. Einfach eine Queue aufbauen. Alle Ports und Zeiten reinqueuen und dann den Timer starten. Somit ist das eine sehr flexible Bibliothelk, die halt nur auf gewünschte Ports ein geünschtes Software-PWM zur Verfügung stellt. Die Zeiten könnte man sich dann idealerweise in der Initialisierung ausrechnen. So das dann nachher nur noch der Pointer zur Queue und die Zeiten übertragen werden. Programmiertechnisch ist das allerdings ein recht hoher Aufwand. Da muss ich wirklich überlegen, ob ich mir die Mühe machen will. :)
Da mich der Spaß selbst interessiert hat, hier mal eine Implementierung des ganzen. 8 Software-PWM-Kanäle an Port B, die LEDs sind low-aktiv. Getestet auf STK500/502 (also mit ATmega128), sollte sich einfach für andere Controller ändern lassen. Was mir beim Spielen aufgefallen ist: die Helligkeit der LEDs zumindest am STK500 ändert sich bei geringen Tastverhaltnissen drastisch, bei großen Tastverhältnissen kaum noch. Zwischen 50 und 100 % kaum noch eine optische Änderungen. Darum bin ich erstens von 50 auf 250 PWM-Stufen gegangen (sonst waren die Sprünge im unteren Bereich zu groß) und habe zweitens in der kleinen Spielerei, die die PWM-Werte verändert, bei 50 % aufgehört und fange von vorn an. Möglicherweise ist dieser Effekt aber bei anderen LEDs nicht so stark ausgeprägt.
@Jörg: Habe den Effekt auch schon beobachtet. Nachdem das schon > 10 Jahre her ist, nehme ich an, dass es auf so ziemlich alle LEDs zutrifft ;-) Optisch passt die Ausgabe, wenn man einen quadratischen Zusammenhang programmiert: PWM-Einzeit(16 Bit) = Helligkeit(8 Bit) * Helligkeit(8 Bit) Stefan
War bei meinen Tests aber auch so. Daher scheint das völlig normal zu sein. So kann man sich nacher noch überlegen, wo die 100% erreicht sind. Überhaupt steigt die Helligkeit nicht linear an, was man gut bei einer LED sieht, die man langsam voll aufdreht und dann wieder langsam runter dreht. Und das eben ständig wiederholt. Ich denke das Grundprinzip funktioniert erstmal. Deshalb werde ich mal sehen, dass ich meine Schaltung komplett aufbaue und dann erst richtig anfange mit programmieren.
Hallo, ein sehr alter Thread, aber trotzdem! Hast Du mittlerweile eine laufende Versin der RGB Ansteuerung? Würde mich auch mal intressieren, deinen Code zu sehen. Ebenso die abled.h. Gruß Toby
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.