www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR gesteuerte Kerze für bis zu 8 LEDs


Autor: Orikson (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

es Weihnachtet schon sehr, diese Woche hab ich im Aldi Prospekt schon 
Beleuchtung etc. gesehen und da ist mir wieder die Idee gekommen, dass 
ich auch noch was brauche. Aber natürlich nichts gekauftes, dafür aber 
umso dekorativer. Unsere Krippe hat nämlich letztes Jahr schon eine 
Feuerstelle dazu bekommen, allerdings noch keine bzw. nur statische 
Beleuchtung. Das wollte ich letztes Jahr schon in Angriff nehmen, was 
aber aus Zeitmangel scheiterte. Dieses Jahr soll das nun nachgeholt 
werden, weshalb ich heute schon mal etwas am Code gebastelt habe. 
Hardwareseitig kommt einfach an jeden Port des µC eine LED mit 
Vorwiderstand, die Tatkung übernimmt der interne Oscillator, welchen ich 
auf 4Mhz gestellt habe. Als Controller hab ich hier jetzt einen Mega8 
verwendet, jeder andere (bis runter zum ATiny 13 sollte aber auch 
gehen).

Die LEDs werden über Software-PWM gedimmt, den Code dazu habe ich von 
dieser Seite (http://www.mikrocontroller.net/articles/Soft-PWM) hier 
übernommen. Dahinter habe ich eigentlich nur noch einen kleinen 
Zufallsgenerator für jede LED gehängt, und diesen Wert vom aktuellen 
abziehen lassen. Somit vermeide ich hässliche Helligkeitssprünge, was ja 
auch bei normalen Kerzen eher selten vorkommt. Außerdem flackert jede 
LED unabhängig von der anderen, und nicht alle gleich.

Allerdings würde ich gerne noch eine Kleinigkeit in meinem Code 
verändern. Ich habe unten 8 mal if-Abragen mit welcher ich die 
Zufallszahlen für die Helligkeit der LEDs erzeuge. Das ganze 
funktioniert zwar, ist aber nicht sonderbar schön und wenn man einen 
Wert ändern will muss man das 8 mal machen... Ich hatte da an eine 
for-Schleife gedacht (for (x=1,x=PWM_CHANNELS,x++)), allerdings weiß ich 
nicht wie ich das in den if-Abfragen dann umschreiben müsste. Die Zahlen 
hinter i müssten dann aus der Variable x erzeugt werden, aber ix 
anstelle von i1, i2 etc. geht nicht! Wie kann ich das noch lösen?

Außerdem muss ich gestehen, dass ich nicht ganz weiß, wie die Ausgabe 
mit diesem Soft-PWM Code gedacht ist. Der Code der Testanwendung 
funktioniert aber ganz gut ;-P


#define F_CPU         4000000L           // Systemtakt in Hz
#define F_PWM         100L               // PWM-Frequenz in Hz
#define PWM_PRESCALER 8                  // Vorteiler für den Timer
#define PWM_STEPS     256                // PWM-Schritte pro Zyklus(1..256)
#define PWM_PORT      PORTB              // Port für PWM
#define PWM_DDR       DDRB               // Datenrichtungsregister für PWM
#define PWM_CHANNELS  8                  // Anzahl der PWM-Kanäle
 
// ab hier nichts ändern, wird alles berechnet
 
#define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
//#define T_PWM 1   //TEST
 
#if ((T_PWM*PWM_PRESCALER)<(111+5))
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden
#endif
 
#if ((T_PWM*PWM_STEPS)>65535)
    #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER erhöhen.   
#endif
// includes
 
#include <stdint.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
// globale Variablen
 
uint16_t pwm_timing[PWM_CHANNELS+1];          // Zeitdifferenzen der PWM Werte
uint16_t pwm_timing_tmp[PWM_CHANNELS+1];      
uint8_t  pwm_mask[PWM_CHANNELS+1];            // Bitmaske für PWM Bits, welche gelöscht werden sollen
uint8_t  pwm_mask_tmp[PWM_CHANNELS+1];        
uint8_t  pwm_setting[PWM_CHANNELS];           // Einstellungen für die einzelnen PWM-Kanäle
uint8_t  pwm_setting_tmp[PWM_CHANNELS+1];     // Einstellungen der PWM Werte, sortiert
 
volatile uint8_t pwm_cnt_max=1;               // Zählergrenze, Initialisierung mit 1 ist wichtig!
volatile uint8_t pwm_sync;                    // Update jetzt möglich
 
// Pointer für wechselseitigen Datenzugriff
 
uint16_t * isr_ptr_time  = pwm_timing;
uint16_t * main_ptr_time = pwm_timing_tmp;
uint8_t *  isr_ptr_mask  = pwm_mask;
uint8_t *  main_ptr_mask = pwm_mask_tmp;
 
// Zeiger austauschen
// das muss in einem Unterprogramm erfolgen,
// um eine Zwischenspeicherung durch den Compiler zu verhindern
 
void tausche_zeiger(void) {
    uint16_t * tmp_ptr16;
    uint8_t * tmp_ptr8;
 
    tmp_ptr16 = isr_ptr_time;
    isr_ptr_time = main_ptr_time;
    main_ptr_time = tmp_ptr16;
    tmp_ptr8 = isr_ptr_mask;
    isr_ptr_mask = main_ptr_mask;
    main_ptr_mask = tmp_ptr8;
}
 
// PWM Update, berechnet aus den PWM Einstellungen
// die neuen Werte für die Interruptroutine
 
void pwm_update(void) {
    uint8_t i, j, k, min;
    uint8_t tmp;
 
    // PWM Maske für Start berechnen
    // gleichzeitig die Bitmasken generieren und PWM Werte kopieren
 
    tmp=0;
    j = 1;
    for(i=1; i<=(PWM_CHANNELS); i++) {
        main_ptr_mask[i]=~j;                        // Maske zum Löschen der PWM Ausgänge
        pwm_setting_tmp[i] = pwm_setting[i-1];
        if (pwm_setting_tmp[i]!=0) tmp |= j;        // Maske zum setzen der IOs am PWM Start
        j <<= 1;
    }
    main_ptr_mask[0]=tmp;                           // PWM Start Daten 
 
    // PWM settings sortieren; Einfügesortieren
 
    for(i=1; i<=PWM_CHANNELS; i++) {
        min=255;
        k=i;
        for(j=i; j<=PWM_CHANNELS; j++) {
            if (pwm_setting_tmp[j]<min) {
                k=j;                                // Index und PWM-setting merken
                min = pwm_setting_tmp[j];
            }
        }
        if (k!=i) {
            // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
            tmp = pwm_setting_tmp[k];
            pwm_setting_tmp[k] = pwm_setting_tmp[i];
            pwm_setting_tmp[i] = tmp;
            tmp = main_ptr_mask[k];
            main_ptr_mask[k] = main_ptr_mask[i];
            main_ptr_mask[i] = tmp;
        }
    }
 
    // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
 
    k=PWM_CHANNELS;             // PWM_CHANNELS Datensätze
    i=1;                        // Startindex
 
    while(k>i) {
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (k>i) ) {
 
            // aufeinanderfolgende Werte sind gleich und können vereinigt werden
            // oder PWM Wert ist Null
            if (pwm_setting_tmp[i]!=0)
                main_ptr_mask[i+1] &= main_ptr_mask[i];        // Masken vereinigen
 
            // Datensatz entfernen,
            // Nachfolger alle eine Stufe hochschieben
            for(j=i; j<k; j++) {
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
                main_ptr_mask[j] = main_ptr_mask[j+1];
            }
            k--;
        }
        i++;
    }
    
    // letzten Datensatz extra behandeln
    // Vergleich mit dem Nachfolger nicht möglich, nur löschen
    // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
    if (pwm_setting_tmp[i]==0) k--;
 
    // Zeitdifferenzen berechnen
    
    if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind
        main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2;
        main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2;
        k=1;
    }
    else {
        i=k;
        main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
        j=pwm_setting_tmp[i];
        i--;
        for (; i>0; i--) {
            main_ptr_time[i]=(uint16_t)T_PWM*(j-pwm_setting_tmp[i]);
            j=pwm_setting_tmp[i];
        }
        main_ptr_time[0]=(uint16_t)T_PWM*j;
    }
 
    // auf Sync warten
 
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
    while(pwm_sync==0);
 
    // Zeiger tauschen
    cli();
    tausche_zeiger();
    pwm_cnt_max = k;
    sei();
}
 
// Timer 1 Output COMPARE A Interrupt
 
ISR(TIMER1_COMPA_vect) {
    static uint8_t pwm_cnt;
    uint8_t tmp;
 
    OCR1A += isr_ptr_time[pwm_cnt];
    tmp    = isr_ptr_mask[pwm_cnt];
    
    if (pwm_cnt == 0) {
        PWM_PORT = tmp;                         // Ports setzen zu Begin der PWM
        pwm_cnt++;
    }
    else {
        PWM_PORT &= tmp;                        // Ports löschen
        if (pwm_cnt == pwm_cnt_max) {
            pwm_sync = 1;                       // Update jetzt möglich
            pwm_cnt  = 0;
        }
        else pwm_cnt++;
    }
}
 
int main(void) {
 
    PWM_DDR = 0xFF;         // Port als Ausgang
    
    // Timer 1 OCRA1, als variablem Timer nutzen
    TCCR1B = 2;             // Timer läuft mit Prescaler 8
    TIMSK |= (1<<OCIE1A);   // Interrupt freischalten
    sei();                  // Interrupts gloabl einschalten
 
  int i1,i2,i3,i4,i5,i6,i7,i8;

    //for (x=1,x=PWM_CHANNELS,x++)
  while (1)
  {

    if (i1 > rand()%235)
  {
    i1 = i1 - rand()%20;
  }
  else
  {
      i1 = i1 + rand()%20;
  };    
  
  if (i2 > rand()%235)
  {
    i2 = i2 - rand()%20;
  }
  else
  {
      i2 = i2 + rand()%20;
  };
  
  if (i3 > rand()%235)
  {
    i3 = i3 - rand()%20;
  }
  else
  {
      i3 = i3 + rand()%20;
  };

  if (i4 > rand()%235)
  {
    i4 = i4 - rand()%20;
  }
  else
  {
      i4 = i4 + rand()%20;
  };
  
  if (i5 > rand()%235)
  {
    i5 = i5 - rand()%20;
  }
  else
  {
      i5 = i5 + rand()%20;
  };
  
  if (i6 > rand()%235)
  {
    i6 = i6 - rand()%20;
  }
  else
  {
      i6 = i6 + rand()%20;
  };
  
  if (i7 > rand()%235)
  {
    i7 = i7 - rand()%20;
  }
  else
  {
      i7 = i7 + rand()%20;
  };
  
  if (i8 > rand()%235)
  {
    i8 = i8 - rand()%20;
  }
  else
  {
      i8 = i8 + rand()%20;
  };




  const uint8_t t1[8] = {i1, i2, i3, i4, i5, i6, i7, i8};
    memcpy(pwm_setting, t1, 8);
    pwm_update();
  _delay_ms(rand()%50+50);


  };

    return 0;
}

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Orikson (Gast)

>Allerdings würde ich gerne noch eine Kleinigkeit in meinem Code
>verändern. Ich habe unten 8 mal if-Abragen mit welcher ich die
>Zufallszahlen für die Helligkeit der LEDs erzeuge. Das ganze

Kleiner Tip: Netiquette.
Lange Quelltexte bitte als Anhang.

>funktioniert zwar, ist aber nicht sonderbar schön und wenn man einen
>Wert ändern will muss man das 8 mal machen... Ich hatte da an eine
>for-Schleife gedacht (for (x=1,x=PWM_CHANNELS,x++)), allerdings weiß ich
>nicht wie ich das in den if-Abfragen dann umschreiben müsste. Die Zahlen
>hinter i müssten dann aus der Variable x erzeugt werden, aber ix
>anstelle von i1, i2 etc. geht nicht! Wie kann ich das noch lösen?

Wie du schon richtig sagtest, mit einer Schleife. Und einem Array.

my_delay(uint16_t ms) {
  for(; ms>0; ms--) _delay_ms(1);
}

int main(void) {
 
    PWM_DDR = 0xFF;         // Port als Ausgang
    
    // Timer 1 OCRA1, als variablem Timer nutzen
    TCCR1B = 2;             // Timer läuft mit Prescaler 8
    TIMSK |= (1<<OCIE1A);   // Interrupt freischalten
    sei();                  // Interrupts gloabl einschalten
 
  uint8_t zufall[8];
  uint8_t i;

  while (1) {
     for (i=0; x<PWM_CHANNELS; i++) {
       if (zufall[i] > rand()%235) {
         zufall[i] -= rand()%20;
       }
       else {
         zufall[i] += rand()%20;
       };
     }
     memcpy(pwm_setting, zufall, 8);
     pwm_update();
     my_delay(rand()%50+50);
  };

    return 0;
}


>Außerdem muss ich gestehen, dass ich nicht ganz weiß, wie die Ausgabe
>mit diesem Soft-PWM Code gedacht ist. Der Code der Testanwendung
>funktioniert aber ganz gut ;-P

Du solltest dich mal mit ein paar Grundlagen befassen, Arrays und so. 
Und der _delay_ms() Funktion, die mag keine variablen Parameter.

http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

MFG
Falk

Autor: Orikson (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe mir jetz mal die Schaltung mit einem Tiny2313 aufgebaut 
(http://pic.leech.it/i/3a475/bb60de9bild1.png), aber leider noch ein 
kleines Problem...

Zwar flackert die LED (hab erst 2 Widerstände und LEDs dran, der Rest 
kommt erst noch) schon, aber die geht sehr oft auch komplett aus und 
wieder an, auch mehrmals hinter einander! Der Tiny2313 läuft mit 
internen 4 Mhz, das erhöhen auf 8 MHz bringt aber auch keinen 
Unterschied, außer dass alles schneller abläuft, dass die LED aus ist 
sieht man aber trotzdem noch.

Woran kann das liegen? Programierkabel ist abgezogen, Spannung passt und 
Kurzschluss oder so hab ich auch nicht. Habe auch schon im Programm die 
PWM Kanäle runter, die PWM-Frequenz hoch und runter und den Prescaler 
verändert, alles ohne Abhilfe...

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Orikson (Gast)

>(http://pic.leech.it/i/3a475/bb60de9bild1.png), aber leider noch ein
>kleines Problem...

Das sollte auf jeden Fall ein 100nF NAH an den AVR. 100nF am Reset 
schaden auch nicht, sind aber nicht zwingend.

MFG
Falk

Autor: Orikson (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Falk Brunner: Nah genug ;-P
http://pic.leech.it/i/1412e/876a671fimgp0798.jpg

Ne, das war es nicht. Kann das an der Software liegen oder ist das n 
Hardwareproblem?

Autor: Chris W. (squid1356)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die Platine sieht sehr "unsauber" aus.

erstens sind die tonerreste (war doch direkttonermethode ?!) nicht 
sorgfältig entfernt, und es sind auf den ersten blick ein paar unsaubere 
lötstellen zu erkennen.

besonders die dritte von oben rechts sieht "kalt" aus. am besten nochmal 
alles mit aceton säubern, evtl. fluxen und nachlöten.
an der schaltung scheint es sonst nicht zu liegen.

software habe ich noch nicht angesehen...

Autor: Orikson (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Platine ist ok, habs auch nochmal überprüft und einige Sachen noch 
weggekratzt. Allerdings hat das nicht wirklich geholfen.

Dafür konnte ich den Fehler im Code finden, verstehen tu ich ihn aber 
nicht...

Der Fehler tritt auf, sobald ein zufälliger Wert an die Funktion 
my_delay übergeben wird. Wenn man ihn fest auf einen Wert über 30 setzt 
tritt das Problem auch auf. Wenn man ihn fest auf einen Wert unter 30 
setzt geht es ohne blinken! Wenn man den Wert aber per Zufall unterhalb 
von 30 erzeugt blinkt es wieder!

Das selbe tritt auch auf, wenn ich direkt _delay_ms verwende oder ein 
void vor der my_delay Funktion ergänze (an dem meckert er eh rum).

Wodurch kann der Fehler also verursacht werden? Vor allem läuft das ja 
z.B. im Mega8!

Autor: Herr Jemine (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du nimmst aber viel Lötzinn! Man braucht eigentlich gar kein Aceton, ich 
bürste den Toner mit einem Stahlwolle-Topfreiniger ab. Hoffentlich wird 
das im Klärwerk irgendwie rausgeholt.

Autor: Chris W. (squid1356)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Herr Jemine wrote:
> Du nimmst aber viel Lötzinn! Man braucht eigentlich gar kein Aceton, ich
> bürste den Toner mit einem Stahlwolle-Topfreiniger ab. Hoffentlich wird
> das im Klärwerk irgendwie rausgeholt.

aceton hinterlässt halt nicht die hässlichen kratzer im kupfer. finde 
ich sauberer und ist bei feinen leitungen sicherer.

Autor: Orikson (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Herr Jemine: Jein, ich habe nur keinen 1 mm Bohrer (darüh 0,8mm) für 
größere Löcher. Deshalb nimm ich zur Zeit noch so nen Spitzen Fräser 
fürn Dremel, allerdings werden dann manche Löcher hald etwas größere. 
Und damit ich vernünftig Kontakt habe kommt da ordentlich Lötzinn drauf 
hust

Die Tonervariante ist eigentlich nicht mein Ding, hab die jetz nur mal 
ausprobiert, weil ich so miese und billige Fotoplatinen gekauft habe, 
die sich beschi**** belichten lassen...

Keiner ne Ahnung, was das mit dem Fehler sein könnte?

Autor: martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo ich habe letzte winter mir ein LED Lagerfeuer und Schweißlicht 
selber gebaut...
Es sollte so klein wie möglich sein und am ende kam eine kleine Platine 
raus,
mit der man 8 Leds unterschiedlich flackern lassen kann... sieht aus wie 
echt.

Abmaße der Platine sind ca 3x4cm

Die daten findet ihr unter folgendem Link: 
http://bluematrixi.bl.funpic.de/html/meinwebzeugs/...

Autor: R. Max (rmax)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
martin schrieb:

> Die daten findet ihr unter folgendem Link:
> 
http://bluematrixi.bl.funpic.de/html/meinwebzeugs/...

Schön wär's aber da kommt leider nur 404.

Autor: martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sorry hatte noch ne kleine Änderung in der Seite...
der Link ist: 
http://bluematrixi.bl.funpic.de/index.php/elektron...

oder eben nur die Seite und dann im Menu finden: 
http://bluematrixi.bl.funpic.de

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.