Forum: Mikrocontroller und Digitale Elektronik SW-Lösungsansatz gesucht für "Tunable White Dimmer"


von Markus (Gast)


Lesenswert?

Hallo Zusammen,

ich versuche, einen 2-kanaligen Dimmer zu programmieren, für eine 
"Tunable White"-Funktion. Also Kanal 1 Warmweisse Led, Kanal 2 
Kaltweisse Led.

Die Helligkeit und das Farbverhältniss soll über 2 Potentiometer 
eingestellt werden:
P1 stellt das Farbverhältnis ein: Rechtsanschlag (0V) nur Warmweiss, 
Linksanschlag (3,3V) nur Kaltweiss. Dazwischen soll linear zwischen den 
Farben hinundhergeblendet werden.
P2 stellt die Helligkeit des Farbgemisches ein: Rechtsanschlag (0V) 
dunkel, Linksanschlag (3,3V) volle Helligkeit.

Die Hardware ist soweit aufgebaut und funktionert (Attiny841). Die 
beiden 10-Bit-Pwm-Timer / Mostfet-Ausgänge funktionieren. Auch das 
Einlesen der Potentiometer / Adc's funktioniert bestens.
Die Helligkeit wird über eine PWM-Tabelle modifiziert, damits "linear" 
wird fürs Auge (10-Bit PWM, 128 Steps).

Mein grosses Problem ist nun, wie ich nun die Software aufbaue / 
schreibe, damit das Hinundherblenden zwischen den beiden Farben 
funktioniert.
Die Helligkeit alleine konnte ich schon so programmieren dass das 
Funktioniert hat, aber sobald ich versuche den Farbwert zu ändern gibts 
Chaos...
Bin da schon relativ lange am rumprobieren, komme aber auf keine Lösung, 
mir fehlt einfach der logische Ansatz wie das gelöst werden soll.
Deswegen sieht der Code momentan noch total Mager aus (macht m.M.n. 
keinen Sinn einen überhaupt nicht funktionierenden Ansatz zu posten).

Wie gehe ich die beiden Abschnitte nun am besten an? Habt ihr einen 
Denkanstoss für mich, wie ich das Lösen kann? (erwarte natürlich keinen 
fertigen Code, mehr ein kleines Beispiel / denkansatz damit ich selbst 
weiter dran arbeiten kann).
Wahrscheinlich ist es total simpel und ich denke viel zu kompliziert 
(wie immer).

Falls ihr weitere Infos möchtet oder mein planloses rumgecode in den 
Abschnitten "farbe berechnen" und "helligkeit berechnen" doch sehen 
wollt, bitte melden!

Ich danke euch schon für eure Hilfe!

Beste Grüsse,
Markus
1
int main(void)
2
{
3
  uint8_t helligkeit = 0;    // 0 dunkel, 127 = 100% 
4
  uint8_t farbe = 0;       // 0 = warmweiss, 127 = kaltweiss
5
  uint8_t dimmlevel_cw = 0;  // dimmwert kaltweiss
6
  uint8_t dimmlevel_ww = 0;  // dimmwert warmweiss
7
  
8
  
9
  init_io;
10
  init_timer;
11
  init_adc;
12
  
13
  sei();
14
    
15
  while(1)
16
  {
17
    // potis über 10bit adc einlesen und durch 8 teilen so dass max. wert = 127
18
    farbe = read_adc(p1)/8;  
19
    helligkeit = read_adc(p2)/8;
20
    
21
    
22
    // farbe berechnen
23
    
24
    
25
    // helligkeit berechnen
26
27
    
28
    
29
    // pwm-timer / ausgänge setzen m. gammakorrektur aus pwm-tabelle
30
    OCR2B = pgm_read_word(&dimm_step[dimmlevel_cw]);
31
    OCR1A = pgm_read_word(&dimm_step[dimmlevel_ww]);
32
33
  }
34
}

von Wolfgang (Gast)


Lesenswert?

Markus schrieb:
> P2 stellt die Helligkeit des Farbgemisches ein: Rechtsanschlag (0V)
> dunkel, Linksanschlag (3,3V) volle Helligkeit.

Über die Zuordnung würde ich nochmal nachdenken. Potis werden gewöhnlich 
andersrum benutzt.

Rechne dir das Helligkeitsverhältnis anhand des Farbtemperaturpotis aus 
und multipliziere die sich daraus ergebende Grundhelligkeit mit der 
Helligkeit, die sich aus der Stellung des Helligkeitspotis ergibt.

von Markus (Gast)


Lesenswert?

Hallo Wolfgang,

vielen Dank für deinen Hinweis, habe dadurch nun eine funktionierende 
Software schreiben können. Unten der ist komplette Code angehängt. 
Eigentlich ziemlich simpel wenn man mal auf der richtigen Fähre ist. 
Kommentare zum Code sind erwünscht, was man besser machen könnte etc 
(die Verrechnung habe ich absichtlich in mehrere Schritte aufgeteilt, 
zum einfacheren debuggen).

Auf die Gammakorrektur per PWM-Tabelle habe ich schlussendlich 
verzichtet, der Regelweg passt so fast besser.

bez. den potis, ich hab beim schreiben l/r vertauscht... ^^

Gruss und vielen Dank!
Markus
1
// MCU: ATTINY841
2
3
#ifndef F_CPU
4
#define F_CPU 8000000UL
5
#endif
6
7
#include <avr/io.h>
8
9
uint16_t read_adc(uint8_t channel);
10
11
int main(void)
12
{    
13
  uint16_t helligkeit = 0;    // 0 dunkel, 127 = 100%
14
  uint16_t farbe = 0;        // 0 = warmweiss, 127 = kaltweiss
15
  uint16_t dimmlevel_cw = 0;    // dimmwert kaltweiss
16
  uint16_t dimmlevel_ww = 0;    // dimmwert warmweiss
17
    float  ratio_cw = 0;      //
18
  float  ratio_ww = 0;      //
19
20
  // init i/o's
21
  DDRA = (1<<PORTA1) | (1<<PORTA2);  // set outputs
22
  PUEA = (1<<PORTA3);          // set inputs
23
  DDRB = 0x00;            // set outputs
24
  PUEB = (1<<PORTB0);          // set inputs
25
  
26
  // init timer prescaler
27
  CCP = 0xD8;      // enable configuration change to change clock prescaler.
28
  CLKPR = 0x00;    // set clock prescaler to 1 from 1
29
  
30
  // init timer 1 fast-pwm 10bit // PIN3 PA2 TOCC1
31
  TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (1<<WGM10);
32
  TCCR1B = (0<<ICNC1)  | (0<<ICES1)  | (0<<WGM13)  | (1<<WGM12)  | (0<<CS12)  | (1<<CS11) | (0<<CS10);
33
  TCCR1C = (0<<FOC1A)  | (0<<FOC1B);
34
  TOCPMSA0 |= (0<<TOCC1S1) | (1<<TOCC1S0); // i/o pin assignment
35
  TOCPMSA1 |= 0x00;             // i/o pin assignment
36
  TOCPMCOE |= (1<<TOCC1OE);         // enable/disable output pin
37
  TIMSK1 = (0<<ICIE1) | (0<<OCIE1B) | (0<<OCIE1A) | (0<<TOIE1);
38
  TCCR1A |= (1<<COM1A1);
39
  
40
  // init timer 2 fast-pwm 10bit // PIN4 PA1 TOCC0
41
  TCCR2A = (1<<COM2A1) | (0<<COM2A0) | (1<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (1<<WGM20);
42
  TCCR2B = (0<<ICNC2)  | (0<<ICES2)  | (0<<WGM23)  | (1<<WGM22)  | (0<<CS22)  | (1<<CS21) | (0<<CS20);
43
  TCCR2C = (0<<FOC2A)  | (0<<FOC2B);
44
  TOCPMSA0 |= (1<<TOCC0S1) | (0<<TOCC0S0); // i/o pin assignment
45
  TOCPMSA1 |= 0x00;             // i/o pin assignment
46
  TOCPMCOE |= (1<<TOCC0OE);         // enable/disable output pin
47
  TIMSK2 = (0<<ICIE2) | (0<<OCIE2B) | (0<<OCIE2A) | (0<<TOIE2);
48
  TCCR2A |= (1<<COM2B1);  
49
  
50
  // init adc
51
  ADCSRA |= ((1<<ADEN) |(1<<ADPS2) | (1<<ADPS1));  // Enable ADC, set prescaler to 16
52
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung
53
  while (ADCSRA & (1<<ADSC));
54
  (void) ADCW;
55
  
56
  while (1)
57
  {      
58
    // potis über 10bit adc einlesen
59
    farbe = read_adc(3);    // PA3 ADC3
60
    helligkeit = read_adc(11);  // PB0 ADC11
61
    
62
    // farbeverhältnis berechnen
63
    ratio_cw = (float)farbe/511.5;
64
    ratio_ww = 2-ratio_cw; 
65
    
66
    // helligkeit berechnen (für 50/50-verhältnis)
67
    dimmlevel_cw = helligkeit/2;
68
    dimmlevel_ww = helligkeit/2;
69
    
70
    // helligkeit anhand farbverhälgnis verrechnen
71
    dimmlevel_cw = dimmlevel_cw * ratio_cw;
72
    dimmlevel_ww = dimmlevel_ww * ratio_ww;
73
    
74
    // pwm-timer / ausgänge setzen
75
    OCR2B = dimmlevel_cw;
76
    OCR1A = dimmlevel_ww;
77
  }
78
}
79
80
uint16_t read_adc(uint8_t channel)
81
{
82
  #define    ADC_AVRG  2    // number of ad-conversions
83
  #define    ADC_OFFSET  0    // adc offset-value (empiric)
84
  
85
  uint32_t  ADValue  = 0;    // saves sum of ad-conversions
86
87
  ADMUXA = (ADMUXA & ~(0x1F)) | (channel & 0x1F);  // select adc channel
88
    
89
  for(uint8_t i = 0; i < ADC_AVRG; i++)      // read adc values
90
  {  ADCSRA |= (1<<ADSC);            // start the first conversion
91
    while(ADCSRA &(1<<ADSC));          // wait until conversion is finished
92
    ADValue += ADCW;              // write adc value in variable
93
  }
94
  
95
  return (uint16_t) ((ADValue / ADC_AVRG) + ADC_OFFSET);
96
}

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.