Forum: Mikrocontroller und Digitale Elektronik LEDs langsam auf- und wieder abdimmen


von A. Z. (sunmoon)


Angehängte Dateien:

Lesenswert?

Hallo,
ich bin Anfänger im Programmieren in C. Ich möchte  LEDs langsam auf- 
und wieder abdimmen. Die Idee dabei ist, eine pwm zu erzeugen deren 
Pulsbreite langsam zunimmt. Dazu soll der timer alle 5 ms den Wert einer 
Variablen verändern der dann in das Register OCR1A eingelesen wird. 
Leider funktioniert es nicht. Wer kann mir helfen?
sunmoon

von Hubert G. (hubertg)


Lesenswert?

Funktioniert nicht ist etwas dürftig.

von MaWin (Gast)


Lesenswert?

> Leider funktioniert es nicht

Wenn ich mir dein Programm ansehen, wundert das nicht,
da steht keinerlei Code zum auf/ab dimmen drin.

von DocMartin (Gast)


Lesenswert?

Zwei Anmerkungen:

C-Quellen lassen sich als solche einbinden.

und

Du änderst zwar die Variable "lauf" sehr schön, schreibst sie aber nur 
einmal, nämlich im "init" nach OCR1A, danach nicht mehr. Ich würde also 
erwarten, das PWM immer auf Maximum oder Minimum bleibt.
Richtig?

Ahoi, Martin

von Klaus Dieter (Gast)


Lesenswert?

Und bevor du dich wunderst, dass das Ganze nicht schön langsam auf und 
ab geht, guck dir folgenden Artikel mal an:
http://www.mikrocontroller.net/articles/LED-Fading

von A.Z. (Gast)


Lesenswert?

Richtig,
es bleibt immer entweder auf Minimum oder Maximum! Die Änderungen der 
Variable "lauf" werden nicht sichtbar.

von A.Z. (Gast)


Angehängte Dateien:

Lesenswert?

Klaus Dieter schrieb:
> Und bevor du dich wunderst, dass das Ganze nicht schön langsam auf und
> ab geht, guck dir folgenden Artikel mal an:
> http://www.mikrocontroller.net/articles/LED-Fading

danke! das ist kompliziert aber könnte helfen. Allerdings meckert meine 
Programmierumgebung daran herum.
Aber nochmal zu meiner eigenen Programmierung: Die ISR sollte die 
Variable "lauf" Schritt für Schritt verringern, tut sie aber nicht. 
Warum?
Wenn ich "lauf" per hand ändere geht die LED je nachdem an oder aus oder 
ist nur halb hell, das funktioniert also.-
Ich habe damit nochmal herumbrobiert, siehe txt- Datei, leider 
funktioniert es immernoch nicht.
Grüße
A

von Wolfgang (Gast)


Lesenswert?

>   if (lauf==0); lauf=10200;

Was meinst du, was das erste Semikolon bewirkt? Der Compiler übersetzt 
leider immer was da steht und nicht, was der Programmierer gemeint hat.

Hast du im Simulator mal geguckt, ob alles so abläuft, wie du dir das 
gedacht hast?

von Hubert G. (hubertg)


Lesenswert?

1
#ifndef F_CPU
2
#define   F_CPU   4000000
3
#endif
4
#include <avr\io.h>
5
#include <avr\interrupt.h>
6
volatile uint16_t lauf=10200;
7
volatile uint8_t isrlauf=0;
8
uint8_t laufup, laufdown;
9
//--------------------------------------------------------------------
10
// TIMER2_COMP_vect - Timer2 Interrupt bei Vergleichswert
11
// aktuelle Einstellung:  200.321 Hz  4.992 ms
12
//--------------------------------------------------------------------
13
ISR(TIMER2_COMP_vect)  //(vorher:   timer2_comp_vect)
14
{
15
//   lauf--;
16
//  if (lauf==0){
17
//     lauf=10200;
18
//  }
19
  isrlauf=1;
20
}
21
//------------------------------------------------------------------------
22
// Initialisierungen
23
//------------------------------------------------------------------------
24
void init()
25
{
26
   // Ports initialisieren
27
   DDRB |= _BV(1);   // PORTB1 auf Ausgang
28
   DDRB |= _BV(2);   // PORTB2 auf Ausgang
29
   DDRD &= ~_BV(2);   // PORTD2 auf Eingang mit PullUp
30
   PORTD |= _BV(2);
31
   //--- Timer 2 initialisieren ---
32
   TCCR2= (1<<CS20)|(1<<CS21);   // Teiler 1/32
33
   TCCR2|=0x08;   // Modus: Zählen bis Vergleichswert (WGM21=1)
34
   OCR2=56;   // Vergleichswert speichern
35
   TIMSK|=0x80;   // Interrupt bei Vergleichswert
36
   
37
   TCCR1A = (1<<COM1A0)|(1<<COM1A1)|(1<<COM1B0)|(1<<COM1B1)|(1<<WGM11);  //
38
  
39
  TCCR1B = (1<<CS10)|(1<<WGM13);
40
41
  ICR1 = 10200;  
42
  OCR1A=lauf;
43
  
44
  laufup=1;
45
  laufdown=0;
46
47
   //--- Interrupts erlauben ---
48
   sei();
49
}
50
51
/////////////////////////////////////////////////////////////////////////////
52
// Main-Funktion
53
/////////////////////////////////////////////////////////////////////////////
54
int main(void)
55
{
56
   init();   // Initialisierungen
57
   for (;;)    // Mainloop-Begin
58
   {
59
    if(isrlauf){
60
61
62
      if(lauf==0){
63
        laufup=1;
64
        laufdown=0;
65
      }
66
67
      if(lauf==10200){
68
        laufdown=1;
69
        laufup=0;
70
      }
71
72
      if(laufup){
73
        lauf++;
74
      }
75
76
      if(laufdown){
77
        lauf--;
78
      }
79
80
        
81
       OCR1A=lauf;
82
      isrlauf=0;
83
    }
84
   } // Mainloop-Ende
85
}
Ist auch nicht schön, aber funktioniert.
Ist mit dem AVR-studio compiliert, sollte daher keine großen Probleme 
machen.

von Bool (Gast)


Lesenswert?

Hubert G. schrieb:
> Ist auch nicht schön

Stimmt! Besonders bemerkenswert ist die Speicherung der Bool Information 
für Up/Down, denn bei Dir gilt immer laufup == !laufdown.

von A.Z. (Gast)


Lesenswert?

Hubert G. schrieb:
> st auch nicht schön, aber funktioniert.
> Ist mit dem AVR-studio compiliert, sollte daher keine großen Probleme
> machen.

Hallo Hubert,
danke, das funktioniert auf Anhieb!
Ob schön oder nicht ist mir weniger wichtig.
jetzt noch eine Frage: ist es irgendwie möglich das auf- und abfaden 
auch auf andere Ausgänge des Microcontrollers zu übertragen, oder geht 
das jetzt nur auf dem Ausgang OC1A ??
Grüße
Anando

von Wolfgang (Gast)


Lesenswert?

A.Z. schrieb:
> jetzt noch eine Frage: ist es irgendwie möglich das auf- und abfaden
> auch auf andere Ausgänge des Microcontrollers zu übertragen, oder geht
> das jetzt nur auf dem Ausgang OC1A ??

Dann mal raus mit der Sprache, was soll das werden?
Möchtest du das Signal auf einem anderen Pin habe? Dazu sagt das 
Datenblatt, dass der ATmega8 auch den OC1B bedienen kann.
Wenn du zwei verschiedene PWM Signale auf OC1A und B erzeugen willst, 
überleg' dir, was du gegenüber dem jetzigen Programmablauf ändern mußt.
Falls du mehr als die beiden PWM Signale brauchst, siehst du aus dem 
Datenblatt, dass dafür keine Hardwareunterstützung im µC vorhanden ist. 
Dann heißt es also: Software-PWM, anderer Prozessor oder externes Dimmer 
IC, wie z.B. die PCA953x Serie, die du dann über I2C/TWI mit dem µC 
steuern kannst.

von A.Z. (Gast)


Lesenswert?

Hallo Wolfgang,
was ich will ist folgendes: ich bin Künstler und steuere meine Objekte 
mit Microcontrollern. Zur Zeit baue ich an einem Objekt, daß interaktiv 
sein soll, also die Betrachter können über Taster und evtl anderes (z.B. 
lichtempfindliche Widerstände) in die Steuerung eingreifen. In dem 
Objekt gibt es mehrere Gruppen von LEDs, die langsam auf- und abgedimmt 
werden sollen. Das soll durch ein Programm einerseits automatisch 
ablaufen, andererseits sollen die unterschiedlichen Funktionen durch die 
Betrachter ausgelöst werden können.
Dazu wäre es praktisch, wenn ich das pwm-Signal zu jedem beliebigen 
Zeitpunkt auf jeden beliebigen Port legen könnte.
Das Ganze muss jedoch nicht auf einem Microcontroller programmiert sein, 
sondern ich könnte mir vorstellen, daß mehrere Microcontroller dafür 
zusammenarbeiten.Auch, weil auf dem AT Mega 8, den ich immer benutze, 
nicht genügend PORTs zur Verfügung stehen.
Grüße
Anando

Wolfgang schrieb:
> Dann mal raus mit der Sprache, was soll das werden?
> Möchtest du das Signal auf einem anderen Pin habe? Dazu sagt das
> Datenblatt, dass der ATmega8 auch den OC1B bedienen kann.
> Wenn du zwei verschiedene PWM Signale auf OC1A und B erzeugen willst,
> überleg' dir, was du gegenüber dem jetzigen Programmablauf ändern mußt.
> Falls du mehr als die beiden PWM Signale brauchst, siehst du aus dem
> Datenblatt, dass dafür keine Hardwareunterstützung im µC vorhanden ist.
> Dann heißt es also: Software-PWM, anderer Prozessor oder externes Dimmer
> IC, wie z.B. die PCA953x Serie, die du dann über I2C/TWI mit dem µC
> steuern kannst.

von Hubert G. (hubertg)


Lesenswert?

Es ist kein Problem den PWM-Ausgang mit einem anderen Pin zu verbinden.
Z.B. Taste X gedrückt, PWM-Signal auch an PORTC0.
Wenn du nicht genügend I/O hast, kannst du auf Portexpander wie PCF8574 
zurückgreifen oder einen Mega16 mit 32 I/O verwenden.

von Wolfgang (Gast)


Lesenswert?

A.Z. schrieb:
> Dazu wäre es praktisch, wenn ich das pwm-Signal zu jedem beliebigen
> Zeitpunkt auf jeden beliebigen Port legen könnte.

Dann ist für deinen Zweck vielleicht die PCA963x Serie von NCA noch 
besser geeignet. Mit dem PCA9633 kannst du 4, mit dem PCA9634 8 und mit 
dem PCA9635 sogar 16 LEDs unabhängig in der Helligkeit einstellen. Dem 
Baustein schickst du über I2C jeweils einen Helligkeitswert. Um die 
Erzeugung des PWM Signals kümmert sich dann der Baustein ohne weiteres 
Zutun. Am Prozessor brauchst du dann für die LED-Steuerung nur zwei Pins 
und kannst mit dem Konzept im Extremfall 2048 LEDs unabhängig steuern. 
Ob du das voll ausschöpfst, hängt von deinem Objekt ab.

Der Einstieg in die I2C-Steuerung ist sicher etwas aufwändiger, aber 
dann hättest du alle Möglichkeiten. Zu Soft-PWM als Alternative gibt es 
hier im Forum einen Artikel mit einem gan pfiffigen Ansatz:
http://www.mikrocontroller.net/articles/Soft-PWM

von A.Z. (Gast)


Lesenswert?

Hallo Wolfgang,
danke für die Tips! Das ist allerdings viel, das muss ich mir erst 
einmal in Ruhe ansehen.
Grüße und Dank
Anando

Wolfgang schrieb:
> Mit dem PCA9633 kannst du 4, mit dem PCA9634 8 und mit
> dem PCA9635 sogar 16 LEDs unabhängig in der Helligkeit einstellen.

Wolfgang schrieb:
> Zu Soft-PWM als Alternative gibt es
> hier im Forum einen Artikel mit einem gan pfiffigen Ansatz:
> http://www.mikrocontroller.net/articles/Soft-PWM

von A.Z. (Gast)


Lesenswert?

Hallo Wolfgang,
Eine einfache Lösungsidee: wäre folgendes möglich?
Der Microcontroller1 produziert die auf-und abdimmende pwm an OC1A. 
Diese pwm wird an Microcontroller2 in einen Eingang gegeben, sagen wir 
PORTC,2. Das Signal wir dann im Microcontroller 2 auf beliebige Pins an 
den Ports verteilt.
z.B. so:
loop_until_bit_is_set(<PINC><2>);sbi(<PORTX><x>);loop_until_bit_is_clear 
(<PINC><2>);cbi(<PORTX><x>)
Per Interrupt wird (<PORTX><x>) dann in (<PORTY><y>)etc verändert.
Damit die Phase der pwm stimmt, die leds also dunkel anfangen und 
langsam heller werden wird durch ein gesetztes Bit in einem Ausgang von 
Microcontroller 2 (oder evtl µC3) ein reset an µC1 ausgelöst.
Ich denke das könnte gehen!
Den Artikel über Soft pwm habe ich mir angesehen. Leider meckert meine 
Programmierumgebung heftig daran herum.
Grüße
A

von Hubert G. (hubertg)


Angehängte Dateien:

Lesenswert?

Du musst gar nicht so kompliziert arbeiten.
Auf PB0 hast du jetzt die gleiche PWM wie auf PB1.
Das dann abhängig von irgendwelchen Taster oder sonstigen Eingaben 
machen ist kein Problem.
Bei einem Mega16 hast du vier PWM und 32IO.

von A.Z. (Gast)


Lesenswert?

Auf dem Mega 8, den ich jetzt benutze habe ich die  auf- und abdimmende 
pwm eigenartigerweise nur auf dem PB1, und nicht - wie ich denke, daß es 
sein sollte - auch auf PB2 (OC1B). Auf PB2 habe ich ein gleichmäßiges 
Signal, das die led gleichmäßig bei mittlerer Helligkeit leuchten lässt. 
Ich frage mich woran das liegt.

Hubert G. schrieb:
> Du musst gar nicht so kompliziert arbeiten.
> Auf PB0 hast du jetzt die gleiche PWM wie auf PB1.
> Das dann abhängig von irgendwelchen Taster oder sonstigen Eingaben
> machen ist kein Problem.
> Bei einem Mega16 hast du vier PWM und 32IO.

von Hubert G. (hubertg)


Lesenswert?

Damit OC1B auch als PWM-Ausgang funktioniert musst du das schon 
programmieren.
PB0 in meinem Programm ist nur parallel zu OC1A geschaltet.

von A.Z. (Gast)


Lesenswert?

wie schaltest du 2 Ausgänge parallel?

Hubert G. schrieb:
> Damit OC1B auch als PWM-Ausgang funktioniert musst du das schon
> programmieren.
> PB0 in meinem Programm ist nur parallel zu OC1A geschaltet.

von Hubert G. (hubertg)


Lesenswert?

Die sind nicht elektrisch parallel geschaltet, sonder mittels SW.
Auf diese Weise kannst du Leds faden lassen wenn du eine Taste drückst.
In meiner SW geschieht das hier:
1
    if (PINB &(1<<PINB1)){
2
      PORTB|=(1<<PB0);
3
    }
4
    else {
5
      PORTB &= ~(1<<PB0);
6
    }
Wenn du es mit Taste willst musst du vorher noch den Tasteneingang 
abfragen.

von A.Z (Gast)


Lesenswert?

Hubert G. schrieb:
> Die sind nicht elektrisch parallel geschaltet, sonder mittels SW.


Danke, das hat mir sehr geholfen! damit kann ich das Signal auf 
beliebige Ports weiterleiten.
Jetzt versuche ich Folgendes, per Interrupt wird die Variable lauf bei 
Tastendruck auf 10200 gesetzt, und damit das Signal auf 0. Das 
funktioniert. Gleichzeitig soll die Variable schalt von 0 bis 5 und dann 
wieder nach 0 weitergeschaltet werden:

ISR (INT0_vect)
{
  waitMs(10); //entprellen

      if(schalt==5){
        schalt=0;
      }
      lauf=10200;
      schalt++;
}
schalt soll dann je nach Zustand das Signal auf verschiedene Ausgänge 
legen, das funktioniert leider noch nicht:

int main(void)
{
   init();   // Initialisierungen


if (schalt = 0)
   {
   if (PINB & 0x02)
  {
   PORTB |=(1<<PB0);
   PORTB |=(1<<PB7);
   }
   else{
   PORTB &= ~(1<<PB0);
   PORTB &= ~(1<<PB7);
  }
   }
//----------------
  else if (schalt==1)
   {
   if (PINB & 0x02)
  {
   PORTB |=(1<<PB0);

   }
   else{
   PORTB &= ~(1<<PB0);

  }
   }
 else if (etc)
}
Wenn du dazu eine Idee hättest, würde mir das sehr helfen.
Grüße

von Hubert G. (hubertg)


Angehängte Dateien:

Lesenswert?

Das funktioniert recht brauchbar, Verbesserungsmöglichkeiten gibt es 
sicher noch.

von A.Z. (Gast)


Lesenswert?

Super! das funktioniert bestens.
Vielen Dank, Grüße
Anando

von Simon K. (simon) Benutzerseite


Lesenswert?

Boooor, formatiere den Code gescheit! Der sieht aus wie Kraut und Rüben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.