PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Atmega Led Fading Software PWM



Tux12Fun
31.03.2010, 21:40
Hallo,

im Moment versuche ich gerade mit einer RGB Led und meinem ATMEGA32 ein Fading der Farben zu bekommen. Erste Gehversuche werden euch wohl sehr kompliziert vorkommen. Deshalb, meine Frage, wie könnte ich so etwas einfach Realisieren.

Mein Code sieht im Moment so aus und macht auch noch nicht ganz was er soll. Das ich bisher nur eine Farbe je heller und dunkler bringe.

Leider habe ich bisher ach noch keine Erfahrung mit den Timern im ATMEGA. Ich brauche wohl dazu noch sowas wie Threads, falls es die im ATMEGA gibt.

Mein Code:


#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <util/delay.h>

#define MCU = AVR_ATmega32
#define F_CPU 16000000

#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define DEF_MAX 3000

int main(void){


DDRC |= (1<<PC0);
DDRC |= (1<<PC1);
DDRC |= (1<<PC2);

PORTC &= ~(1<<PC0); //AUS
PORTC &= ~(1<<PC1); //AUS
PORTC &= ~(1<<PC2); //AUS

_delay_ms(5000);

while(1) {

//Fade in Red
for (int i=DEF_MAX; i > 0; i--){
PORTC &= ~(1<<PC0); //AUS
_delay_us(i);
PORTC |= (1<<PC0); //AN
_delay_us(1600);

}
_delay_ms(3000);

//Fade out Red
PORTC |= (1<<PC0); //AN
_delay_ms(3000);
for (int i=0; i < DEF_MAX +800; i++){ //Rot geht aus, evtl. Ist es heller (+800)
PORTC &= ~(1<<PC0); //AUS
_delay_us(i);
PORTC |= (1<<PC0); //AN
_delay_us(1600);

}
PORTC &= ~(1<<PC0); //AUS



//Fade in Blue
for (int i=DEF_MAX; i > 0; i--){
PORTC &= ~(1<<PC1); //AUS
_delay_us(i);
PORTC |= (1<<PC1); //AN
_delay_us(1600);

}
_delay_ms(3000);

//Fade out Blue
PORTC |= (1<<PC1); //AN
_delay_ms(3000);
for (int i=0; i < DEF_MAX; i++){
PORTC &= ~(1<<PC1); //AUS
_delay_us(i);
PORTC |= (1<<PC1); //AN
_delay_us(1600);

}
PORTC &= ~(1<<PC0); //AUS



//Fade in green
for (int i=DEF_MAX; i > 0; i--){
PORTC &= ~(1<<PC2); //AUS
_delay_us(i);
PORTC |= (1<<PC2); //AN
_delay_us(1600);

}
_delay_ms(3000);

//Fade out green
PORTC |= (1<<PC2); //AN
_delay_ms(3000);
for (int i=0; i < DEF_MAX; i++){
PORTC &= ~(1<<PC2); //AUS
_delay_us(i);
PORTC |= (1<<PC2); //AN
_delay_us(1600);

}
PORTC &= ~(1<<PC2); //AUS



}

return 0;
}


Meine Beschaltung. ATMEGA Minimalverkabelung mit Quarz.
An PDC0 - 3 hängt die RGB Led und an Minus über einen Widerstand.
An jedem + - Pärchen der Led hängt ein 220µF Elko

PCD0 = rot
PDC1 = blau
PDC2 = grün

PDC0 -------------- Elko+ ---------- LED+
MINUS ------------- Elko- ----------- LED -

Ich hoffe ich konnte meinen bisherigen Aufbau ein wenig rüber bringen.

Danke schon im Voraus für eure Hilfe.

Bääääär
31.03.2010, 21:54
Ich hab sowas für zwei RGB-LEDs mal per Software-PWM gemacht.
=> http://johannesneumann.net/index.php?page=6-fach-soft-pwm-mit-fading
Vielleicht hilft dir das ein bisschen.

Ansonsten: Ja, Timer kannst du im Prinzip wie Threads benutzen. Du kannst beim Overflow einen Interrupt auslösen lassen, der dann alle X Prozessortakte das Programm unterbricht und eine ISR ausführt. In die kannst du deine PWM-Erzeugung packen: In jedem Durchlauf wird eine Variable hochgezählt. Hat sie den Wert der Helligkeit deiner LED erreicht, so wird der Pin auf LOW gezogen. Wenn diese Variable den Maximalwert erreicht hat, wird sie auf Null gesetzt und alle Pins auf HIGH gezogen und dann gehts von vorn los.

Das Faden kann in einer Schleife passieren, insofern du nicht was anderes abarbeiten willst, während gefadet wird. Unter dem Link oben findest du nen c-File, was genau das macht. Das Faden wird über einen anderen Timer geregelt, sodass du im Programm schon was anderes machen kannst während die LEDs ihre Farbe wechseln.


Wieso packst du da eigentlich einen Elko ran? Das ist doch sinnlos: Der läd sich auf, wenn die PWM gerade High ist und wenn sie Low geht, leuchtet die LED mit der gespeicherten Energie weiter... Damit hast du kein Dimmen mehr, sondern kontinuierliches Leuchten. Nimm die Elkos weg, dann wirds gehen.

Bääääär

[Edit] Aschso, für richtiges Dimmen wäre Gamma-Korrektur noch ne sinnvolle Sache. Siehe ebenfalls mein Link und Sourcecode.

wkrug
31.03.2010, 22:31
Im Prinzip hat der ATMEGA32 3 Hardware PWM Quellen mit dazugehörigen Ausgängen.

OCR1A, OCR1B und OCR2.
Da der Timer 2 nur ein 8Bit Timer ist, kann man damit nur 256 Helligkeiten einstellen.

Bei meinem LED Spot ( DMX ) hab ich auch den Timer 1 als 8 Bit PWM laufen lassen, da das DMX Protokoll ohnehin nur 256 Dimmstufen zulässt.

Die Gamma Korrektur könnte man über Look Up Tables für jede Helligkeit realisieren. Allerdings wird man hier nicht das volle Helligkeitspotential der LED's nutzen können, da Grüne und Rote LED's effektiver sind als Blaue.

Rot und Grün werden also auch bei Voller Helligkeit immer noch gedimmt werden müssen.

Tux12Fun
31.03.2010, 23:47
Die Elkos sorgen im Moment bei mir dafür dass die LED langsam die Helligkeit ändert. Ich schalte quasi immer einer konstante Zeit ein und eine immer länger werdende Zeit aus. Dadurch hat der Elko immer weniger Saft den die Led zieht. Ohne den Elko ist es bei mir im Moment so dass die Led zum Blinken anfängt. Zumindest wenn ich dann die Zeiten ändere.

Bääääär
01.04.2010, 10:44
Den Elko braucht man nicht, wenn die PWM Frequenz hoch genug ist. Das Auge ist echt träge in der Hinsicht.
Bei deinem Programm ist das Problem, dass du die An-Zeit unangetastet lässt und die Aus-Zeit verlängerst. Das Problem dabei ist, dass du damit de-facto die PWM-Frequenz kontinuierlich veränderst. Richtig wäre:
X Takte an, Y-X Takte aus. Wenn du die LED also einen Takt länger an hast, ist sie auch einen Takt weniger aus, sodass die Zyklus-Zeit immer gleich lang bleibt.

Hast du dir mal meinen Link zu Gemüte geführt? Da ist das genau so erledigt.

ISR(TIMER2_OVF_vect) {

TimerCounter++;
if (TimerCounter > 1023 ) {
TimerCounter = 0;
if (lr1 != 0) { n1_Port |= (1 << n1_Pin);}
if (lr2 != 0) { n2_Port |= (1 << n2_Pin);}
if (lr3 != 0) { n3_Port |= (1 << n3_Pin);}
if (lr4 != 0) { n4_Port |= (1 << n4_Pin);}
if (lr5 != 0) { n5_Port |= (1 << n5_Pin);}
if (lr6 != 0) { n6_Port |= (1 << n6_Pin);}
}
if (TimerCounter > lr1) {
n1_Port &= ~(1 << n1_Pin);
}
if (TimerCounter > lr2) {
n2_Port &= ~(1 << n2_Pin);
}
if (TimerCounter > lr3) {
n3_Port &= ~(1 << n3_Pin);
}
if (TimerCounter > lr4) {
n4_Port &= ~(1 << n4_Pin);
}
if (TimerCounter > lr5) {
n5_Port &= ~(1 << n5_Pin);
}
if (TimerCounter > lr6) {
n6_Port &= ~(1 << n6_Pin);
}
}
lr1..lr6 Das sind die Helligkeitswerte der LEDs. Diese Function wird vom Timer aufgerufen (Overflow des Timers). Was passiert sollte anhand des Codes eigentlich ersichtlich sein: Erreicht die Laufvariable 1024, so wird sie auf Null gesetzt und alle LEDs eingeschaltet. Dann läuft sie wieder hoch bis 1024. Erreicht sie dabei den Helligkeitswert einer LED so wird diese Ausgeschaltet. Die Gesamtlaufzeit dieses Zyklus ist also immer 1024 Durchläufe (also eine konstante Zeit). Das Verhältnis von An und Aus ergibt also die Helligkeit der LED.

Bääääär

Rabenauge
01.04.2010, 10:54
http://www.mikrocontroller.net/articles/LED-Fading

Hab ich erst die Tage drinnen gestöbert, da ich für mein Display auch was ähnliches machen möchte, nur so plumps, an und plumps, aus ist mir zu öde..

Tux12Fun
01.04.2010, 23:20
Hallo,

ja, ich habe deinen Quellcode gerade schon am ATMEGA bei mir laufen und studiere den Code. Danke noch für die Takterklärung.