Forum: Mikrocontroller und Digitale Elektronik ATmega8 untere Dimmstufen zu groß


von Stas S. (stas0)


Lesenswert?

Guten Tag.
Ich hab mir mal ein 0-10v in PWM Dimmer gebaut für meine LED Leisten.
Das PWM kommt aus einem ATmega8 das Programm hab ich aus diesem Forum.

Ich bin mit den unteren Dimmstufen nicht zufrieden, die Sprünge sind zu 
groß.
Das liegt definitiv am ATmega und nicht an meinen 0-10v.

So jetzt hab ich hier im Forum ein Thread gefunden, wo genau das Problem 
besprochen wurde und da ist sogar ein Lösungsvorschlag.
Leider kenn ich mich selber nur sehr schlecht mit dem ATmega aus und 
deswegen bitte ich euch, mir zu helfen.
Könnte jemand mir helfen diesen Lösungsvorschlag in meinen Code 
einzufügen.

Hier der Lösungsvorschlag von AndiH 
(Beitrag "Re: AVR LED Dimmen mit Logarithmus")
1
Die Beste Lösung, die ich bisher hinbekommen habe, ist folgende:
2
3
* 8-Bit-Timer mit Compare-Match-IRQ
4
* zunächst sehr kleinen Compare-Match Wert, der aber bei jedem IRQ
5
Aufruf verlängert wird.
6
* Ein Regsiter als "PWM-Zähler"
7
* Bei jedem Aufruf des Timers LED-Helligkeits-Wert mit "PWM-Zähler"
8
vegleichen und entsprechend die LED ein oder ausschalten
9
10
Der Trick dabei ist nämlich der, daß bei sehr kleinen Helligkeitswerten
11
die Zeitdauer des IRQ-Aufrufes (und damit das ausschalten der LEDs) sehr
12
kurz ist, mit jedem erneuten Aufruf (=Helligkeits-schritt) aber
13
verlängert wird. Sozusagen eine pwm, bei dem die Zeitspanne der
14
einelnen Schritte unterschiedlich lang ist.
15
16
Ich hoffe, man versteht, was ich meine? Ich kanns schlecht erklären...
17
18
Hier mal ein kleiner Auszug der Assembler-IRQ-Routine:
19
20
-----CUT ---------
21
  ;****** IRQ TIMER 1 Compare Match ****
22
timer1irqA:
23
24
        ; *** Register-Werte sichern ***
25
    in itemp, SREG    ; Status-Register...
26
    push itemp      ; ...auf dem Stack sichern
27
28
              ;** Dauer bis nächsten aufruf verlängern
29
    ldi itemp2, mpwm      ; letzter durchlauf? (mpwm = maxpwm, s.o.: 
30
equ)
31
    cp pwm, itemp2      ; vergleichen
32
    brne t1alonger      ; ...wenn nicht - sprung. Sonst:
33
    clr pwm          ; ...pwm wieder auf 0 setzen, und
34
    ldi ZL, $10        ; ...Z-Register...
35
    clr ZH          ; ... auf Anfangswert setzen
36
    rjmp t1aset
37
38
t1alonger:            ; noch nicht letzter durchlauf, dann:
39
    adiw ZH:ZL, $10      ; wert hinzuaddieren
40
41
t1aset:
42
43
    out OCR1AH, ZH      ; Timer 1A Compare HighByte
44
    out OCR1AL, ZL      ; Timer 1A Compare LoByte
45
46
led1:
47
    cp pwm, led1      ; wenn led1...
48
    brlo led1an        ; ...größer als pwm, dann led einschalten
49
    brsh led1aus      ; ...gleich oder größer pwm, dann ausschalten
50
    rjmp led2        ; ...sonst weiter mit led2
51
led1an:
52
    cbi portd,0        ; LED0 einschalten (lo-aktiv, clear bit)
53
    rjmp led2        ; ...sonst weiter mit led2
54
led1aus:
55
    sbi portd,0        ; LED0 ausschalten (hi-inaktiv, set bit)
56
57
led2:
58
(......)
59
-----CUT ---------
60
Bei diesem Codebeispiel wird bei jedem Aufruf der Timer-Compare-Wert um
61
$10 verlängert, insgesamt 16mal (mpwm-Wert wird per equ. im Programmkopf
62
festgelegt), dann wieder zurückgesetzt auf den Anfangswert (=kleineste
63
Helligkeitsstufe).
64
65
So hab ich es geschafft, mit 16 (oder auch mehr, = mpwm-Wert)
66
Helligkeitsstufen halbwegs gleichmäßig zu dimmen, besonders der
67
Einschaltmoment ist schön "weich", Stufe 1 glimmt nur.
68
69
Zeitdiagramm:
70
71
|--1--|-----2----|-------3-------|----------4--------|---....


Hier mein jetziger Code:
1
#define    PC1          1
2
#define    PC0          0
3
#include <avr/io.h>
4
#include <stdio.h>
5
6
unsigned char adc_read8(char channel);
7
8
int main(void)
9
{
10
11
  //PC4,5 output
12
  DDRB = (1<<DDB1) | (1<<DDB2);
13
14
15
  //TIMER 1
16
  ICR1 = 255;
17
  TCCR1A = (1<<COM1A1) | (1<<COM1B1) | (1<<COM1A0) | (1<<COM1B1) | (1<< WGM11);   //Fast PWM, TOP = ICR1, Clear on Compare Match
18
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11) | (1<<CS10);  //Prescaler 64  //Fast PWM, Prescaler 256
19
  
20
21
  //ADC
22
  ADMUX = (1<<REFS0) | (1<<ADLAR);                    //VRef = AVCC
23
  ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADEN);       // 125 kHz @ 8 Mhz, ADC enabled
24
  ADCSRA |= (1<<ADSC);
25
  while (ADCSRA & (1<<ADSC));
26
27
  unsigned int i = 0;
28
  while(1)
29
  {
30
    if (~i)
31
    {
32
      OCR1A = 255-adc_read8(PC0);
33
      OCR1B = 255-adc_read8(PC1);;
34
35
    }
36
    i++;
37
  }
38
}
39
40
unsigned char adc_read8(char channel)
41
{
42
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
43
  ADCSRA |= (1<<ADSC);
44
  while (ADCSRA & (1<<ADSC));
45
  return ADCH;
46
}
Danke im voraus

von Karl H. (kbuchegg)


Lesenswert?

das Beispiel in Assembler bringt dich nicht wirklich weiter.

Was dich ev. weiter bringen würde, wäre die Tabelle weiter vorne im 
verlinkten Thread
1
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 25,
2
28, 30, 33, 36, 39, 42, 46, 49, 53, 56, 60, 64, 68, 72, 77, 81, 86, 90,
3
95, 100, 105, 110, 116, 121, 127, 132, 138, 144, 150, 156, 163, 169,
4
176, 182, 189, 196, 203, 210, 218, 225, 233, 240, 248, 255
die 64 Dimmstrufen auf einer 8 Bit PWM abbildet.

Willst du aber 255 Dimmstufen haben, dann geh einfach mit der PWM höher. 
Auf 10 Bit, oder 12. Deine PWM ist sowieso auf den Top Wert in ICR1 
eingestellt. D.h. du schreibst da 1023 rein (für 10 Bit) oder 4095 (für 
12 Bit) und gehst ev. mit dem Vorteiler für den Timer etwas runter, 
wenns dann flackert.

Und dann rechnest du dir eben im Excel eine neue Tabelle aus, die dir 
265 Dimmstufen in den Werten von 0 bis 1023 realisiert.

Der Rest bleibt im Grunde wie er ist. Mit dem ADC Wert gehst du in diese 
Tabelle und kriegst daraus den Wert raus, der ins OCR1A Register zu 
schreiben ist.

Den erstrebten, fürs Auge gleichmässigen Helligkeitsverlauf, kriegst du 
dann durch die Werte in der Tabelle, die die Korrektur für die 
logarithmische Kennlinie des Auges machen.

von Karl H. (kbuchegg)


Lesenswert?

Du kannst dich hier orientieren

http://www.mikrocontroller.net/articles/LED-Fading

von Oldie (Gast)


Lesenswert?

Da ist nicht der ATmega dran Schuld, sondern die PWM-
Auflösung mit nur 8 Bit. Da es nun mal RELATIV größere Sprünge
bei den kleinen Pulsweiten gibt, fällt es beim Dimmen auf.

Von 255/256 zu 254/256 hast du eine Änderung um 0,04.
Von 2/256 zu 1/256 hast du eine Änderung um 0,5.

Ob man kleinere Helligkeitsunterschiede damit erzeugt, dass
man nicht von 2/256 auf 1/256 schaltet, sondern als
Zwischenstufe noch abwechelnd 2/256 und 1/256 o.ä. ausgibt,
ist Geschmackssache - ich würde eine 16-Bit PWM bevorzugen.

Bei den 8-Bit Zwischenstufen könnte dich auch bald ein Flackern
stören, da die PWM damit effektiv langsamer wird.

Nimm eine 16-Bit-PWM und alles ist gut.

von Stas S. (stas0)


Lesenswert?

Mein Problem ist, dass ich mich gar nicht auskenne damit.
Hab eben http://www.mikrocontroller.net/articles/LED-Fading mir 
angeschaut. Scheint genau das Richtige zu sein weis aber gar nicht wo 
ich das einfügen soll und was noch geändert werden muss.
Könnte nicht jemand bitte mein Code so bearbeiten, damit es geht.

Vielen dank für eure Hilfe

von Michael_ (Gast)


Lesenswert?

Oldie schrieb:
> Ob man kleinere Helligkeitsunterschiede damit erzeugt, dass
> man nicht von 2/256 auf 1/256 schaltet, sondern als
> Zwischenstufe noch abwechelnd 2/256 und 1/256 o.ä. ausgibt,
> ist Geschmackssache - ich würde eine 16-Bit PWM bevorzugen.

Den Unterschied braucht und sieht kein Mensch!
64 Stufen reichen dicke aus.

von Blinder (Gast)


Lesenswert?

Wenn man blind ist natürlich nicht..

Oo

von Michael_ (Gast)


Lesenswert?

Oh entschuldige! Das man hier auf Blinde (Im Gehirn) Rücksicht nehmen 
muß, wußte ich nicht.

Karl Heinz schrieb:
> Was dich ev. weiter bringen würde, wäre die Tabelle weiter vorne im
> verlinkten Thread0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
> 18, 20, 22, 25,
> 28, 30, 33, 36, 39, 42, 46, 49, 53, 56, 60, 64, 68, 72, 77, 81, 86, 90,
> 95, 100, 105, 110, 116, 121, 127, 132, 138, 144, 150, 156, 163, 169,
> 176, 182, 189, 196, 203, 210, 218, 225, 233, 240, 248, 255
> die 64 Dimmstrufen auf einer 8 Bit PWM abbildet.

Wie kommt man nur auf so eine dämliche Tabelle?
Eine sinnvolle Helligkeit erhält man erst ab etwa 30% der Tabelle.
Und wenn man schon mit einer Tabelle arbeitet, dann sollte die 
logarithmisch sein. Das Auge will es so.

von Michael K. (Gast)


Lesenswert?

Michael_ schrieb:
> Den Unterschied braucht und sieht kein Mensch!
> 64 Stufen reichen dicke aus.

Aha ?
Alles unter 10bit hauen meine Kunden mir um die Ohren und das zu Recht.
Es sieht einfach billig aus wenn sich Helligkeit & Farbort in Sprüngen 
ändern.

Michael_ schrieb:
> Und wenn man schon mit einer Tabelle arbeitet, dann sollte die
> logarithmisch sein. Das Auge will es so.

Man kann mit einem 8bit Sollwert auf eine 10bit Tabelle refenzieren.
Das ändert aber wenig daran das 8bit PWM sichtbare Sprünge mit sich 
bringt.

Stas S. schrieb:
> Könnte nicht jemand bitte mein Code so bearbeiten, damit es geht.

Das wäre aber schade wenn das jemand tut, denn dann würdest Du nie diese 
tiefe Befredigung erfahren nach langer nervenaufreibender Arbeit ein 
gutes Ergebniss zu erhalten.
Ich denke das Du hier schon ganz wertvolle Tips erhalten hast wo das 
Problem liegt und wie Du weitermachen solltest.
Wenn Du mehr möchtest fällt das in den Bereich 'Auftragsentwicklung' und 
damit verdiene nicht nur ich mein Geld.

von Stas S. (stas0)


Lesenswert?

Also ich hab mal versucht die Tabelle mit 16bit von 
http://www.mikrocontroller.net/articles/LED-Fading
in mein Code einzufügen.
Leider funktioniert das noch nicht wo sind meine Fehler?

Mein jetziger Code:
1
/*
2
 * GccApplication6.c
3
 *
4
 * Created: 23.07.2014 12:37:51
5
 *  Author: PC
6
 */ 
7
8
#include <avr/io.h>
9
#include <avr/pgmspace.h>
10
11
#define F_CPU 8000000L
12
#include <util/delay.h>
13
14
#define INVERT_PWM (1 << COM1A0)
15
// non-inverted PWM on OC1A
16
17
#define    PC1          1
18
#define    PC0          0
19
#include <stdio.h>
20
21
unsigned char adc_read8(char channel);
22
23
const uint16_t pwmtable_16[256] PROGMEM =
24
{
25
  0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
26
  3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
27
  7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15,
28
  15, 16, 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
29
  31, 32, 33, 35, 36, 38, 40, 41, 43, 45, 47, 49, 52, 54, 56, 59,
30
  61, 64, 67, 70, 73, 76, 79, 83, 87, 91, 95, 99, 103, 108, 112,
31
  117, 123, 128, 134, 140, 146, 152, 159, 166, 173, 181, 189, 197,
32
  206, 215, 225, 235, 245, 256, 267, 279, 292, 304, 318, 332, 347,
33
  362, 378, 395, 412, 431, 450, 470, 490, 512, 535, 558, 583, 609,
34
  636, 664, 693, 724, 756, 790, 825, 861, 899, 939, 981, 1024, 1069,
35
  1117, 1166, 1218, 1272, 1328, 1387, 1448, 1512, 1579, 1649, 1722,
36
  1798, 1878, 1961, 2048, 2139, 2233, 2332, 2435, 2543, 2656, 2773,
37
  2896, 3025, 3158, 3298, 3444, 3597, 3756, 3922, 4096, 4277, 4467,
38
  4664, 4871, 5087, 5312, 5547, 5793, 6049, 6317, 6596, 6889, 7194,
39
  7512, 7845, 8192, 8555, 8933, 9329, 9742, 10173, 10624, 11094,
40
  11585, 12098, 12634, 13193, 13777, 14387, 15024, 15689, 16384,
41
  17109, 17867, 18658, 19484, 20346, 21247, 22188, 23170, 24196,
42
  25267, 26386, 27554, 28774, 30048, 31378, 32768, 34218, 35733,
43
  37315, 38967, 40693, 42494, 44376, 46340, 48392, 50534, 52772,
44
  55108, 57548, 60096, 62757, 65535
45
};
46
void my_delay (uint16_t milliseconds)
47
{
48
  for (; milliseconds > 0; milliseconds--)
49
  _delay_ms (1);
50
}
51
void pwm_up_down (const uint16_t pwm_table[], int16_t size, uint16_t delay)
52
{
53
  int16_t tmp;
54
  
55
  for (tmp = 0; tmp < size; tmp++)
56
  {
57
    OCR1A = pgm_read_word (& pwm_table[tmp]);
58
    my_delay (delay);
59
  }
60
  
61
  for (tmp = size-1; tmp >= 0; tmp--)
62
  {
63
    OCR1A = pgm_read_word (& pwm_table[tmp]);
64
    my_delay (delay);
65
  }
66
}
67
void pwm_16_256 (uint16_t delay)
68
{
69
  // 16 Bit Fast PWM
70
  TCCR1A = 0x82 | INVERT_PWM;
71
  // stop timer
72
  TCCR1B = 0;
73
  // TOP for PWM, full 16 Bit
74
  ICR1 = 0xFFFF;
75
  // prescaler 1 -> ~122 Hz PWM frequency
76
  TCCR1B = (1 << WGM12) | (1 << WGM13) | 1;
77
  
78
  pwm_up_down (pwmtable_16, 256, delay);
79
}
80
81
int main (void)
82
{
83
  //PC4,5 output
84
  DDRB = (1<<DDB1) | (1<<DDB2);
85
86
  int8_t i;
87
  // delay in milliseconds for one fading step
88
  int16_t step_time = 400;
89
  
90
   
91
    //ADC
92
    ADMUX = (1<<REFS0) | (1<<ADLAR);                    //VRef = AVCC
93
    ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADEN);       // 125 kHz @ 8 Mhz, ADC enabled
94
    ADCSRA |= (1<<ADSC);
95
    while (ADCSRA & (1<<ADSC));
96
     
97
    while (1)
98
    {
99
      if (~i)
100
      {
101
        OCR1A = 255-adc_read8(PC0);
102
        OCR1B = 255-adc_read8(PC1);;
103
104
      }
105
      i++;
106
    }
107
  {
108
    for (i=0; i<3; i++) pwm_16_256 (step_time/16);
109
    my_delay (1000);
110
  }
111
  
112
  return 0;
113
}
114
115
unsigned char adc_read8(char channel)
116
{
117
  ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
118
  ADCSRA |= (1<<ADSC);
119
  while (ADCSRA & (1<<ADSC));
120
  return ADCH;
121
  }

von Michael K. (Gast)


Lesenswert?

Stas S. schrieb:
> Leider funktioniert das noch nicht

Was funktioniert nicht ?

> OCR1A = pgm_read_word (& pwm_table[tmp]);
Deine Zuweisung aus der PWM Tabelle.
Wo wird die aufgerufen ?

Stas S. schrieb:
> OCR1A = 255-adc_read8(PC0);
Die wird in der main ständig durchlaufen.

von Stas S. (stas0)


Lesenswert?

Danke euch für euere Hilfe

Also wie gesagt ich kenn mich fast gar nicht aus damit hatte mir die 
Tutorials mal durchgelesen hat mich aber bei meinem Projekt nicht weiter 
gebracht.
Ist einfach zu kompliziert für mich, wenn man sich damit noch nie 
beschäftigt hatte und auf einmal so was programmieren möchte.
Also ich bin auch gern lernfähig müsst mir nur sagen, wo ich das finde, 
was ich brauch.

Den ersten Code hat mir hier einer aus dem Forum erstellt.
Hab gehofft das dieses Mal auch jemand mein Code editiert.


> Was funktioniert nicht ?
Also bei meinem letzten geposteten Code passiert einfach gar nix.

> Deine Zuweisung aus der PWM Tabelle.
Wo wird die aufgerufen?
Nirgendwo? Wo muss ich die den einfügen.

> Die wird in der main ständig durchlaufen.
Ist das so falsch? Ist doch mein ADC eingang mit meinen 0-5v.Oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Stas S. schrieb:
> Danke euch für euere Hilfe
>
> Also wie gesagt ich kenn mich fast gar nicht aus damit hatte mir die
> Tutorials mal durchgelesen hat mich aber bei meinem Projekt nicht weiter
> gebracht.
> Ist einfach zu kompliziert für mich, wenn man sich damit noch nie
> beschäftigt hatte und auf einmal so was programmieren möchte.
> Also ich bin auch gern lernfähig müsst mir nur sagen, wo ich das finde,
> was ich brauch.

Der springende Punkt ist, dass es in der Programmierung nicht diesen 
einen Punkt gibt, den du können musst und dann ist alles gut.

Das ist wie bei einem Chirurgen. Will man sich auf Herzoperationen 
spezialisieren bedeutet das nicht, das man alles andere nicht lernen 
braucht. Jeder Chirurg fängt erst mal damit an, Knoten binden zu lernen.

Und wie überall, gibt es eine untere kritische Masse an Können, die man 
erst mal überschreiten muss, ehe es Sinn macht, sich aus dem Übungsraum 
in die freie Wildbahn hinaus zu wagen.

> Den ersten Code hat mir hier einer aus dem Forum erstellt.
> Hab gehofft das dieses Mal auch jemand mein Code editiert.

So wie der aussieht hast du einfach alles irgendwie zusammenkopiert. 
Deine Beste Chance ist es, wenn dir wieder jemand das komplette Programm 
schreibt, auch wenn die Änderungen zu deinem Originalprogramm eigentlich 
recht trivial sind. Pikanterweise ist nämlich dein Originalprogramm weit 
näher an der Lösung drann, als dein letztes Konglomerat.
Allerdings: ich werds nicht sein. Aus dem Geschäft zieh ich mich mehr 
und mehr zurück. Du willst was bauen - dann lerne es. Und zwar von der 
Pieke auf, selbst wenn das ein paar Monate Lernen bedeutet. Musste ich 
ja schliesslich auch.

: Bearbeitet durch User
von Mat (Gast)


Lesenswert?

Stas S. schrieb:
>> Die wird in der main ständig durchlaufen.
> Ist das so falsch? Ist doch mein ADC eingang mit meinen 0-5v.Oder nicht?

Genau und der ist linear und nicht logarithmisch. Also muss der lineare 
Werte in die Tabelle und die spuckt dann den logarthmischen Wert zurück. 
Sonst wird die Tabelle ja garnicht verwendet.

lg

von Michael K. (Gast)


Lesenswert?

Stas S. schrieb:
> Den ersten Code hat mir hier einer aus dem Forum erstellt.
> Hab gehofft das dieses Mal auch jemand mein Code editiert.

Dein Code ?
Und genau deswegen sollte man sowas nicht tun.
Du lernst nichts und wir müssen immer weiter damit machen 'unseren' Code 
für Dich zu schreiben.

Stas S. schrieb:
> Also ich bin auch gern lernfähig müsst mir nur sagen, wo ich das finde,
> was ich brauch.

Du hast jetzt viele Schubser in die richtige Richtung bekommen.
Versuch doch wenigstens mal zu verstehen was da jede Zeile macht und 
warum manches geht und manches nicht.
Mehr Engagement !

MCU Datenblatt lesen, C Syntax lernen.

Stas S. schrieb:
> Ist einfach zu kompliziert für mich

Dann mußt Du das sein lassen

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.