Hallo, ich habe hier ein kleines Problem, bei dem ich nicht weiterkomme - vielleicht hat ja einer einen Denkanstoß für mich. Also, ich will eine RGB-Led so richtig schön mit Übergängen ansteuern und habe mir dafür an Timer2 und Timer3 je an die OCRs einen FET mit jeweiligen Farbe drangebaut. Die Timer betreibe ich mit 8 Bit (der Timer2 kann eh nicht mehr), normaler FastPWM, die OCR sind auch gemappt. Wenn ich einen Wert in das OCR-Register schreibe, kommt aus der Hardware auch sauber die PWM raus. Dann habe ich mir eine Interpolatorroutine gebaut, die zwischen zwei RGB-Werten linear interpoliert und die ich alle 20ms aufrufe. Den Interpolationswert schreibe ich dann in die OCR-Register. Das kann ich am Simulator wunderbar nachvollziehen - es fällt der richtige Wert aus der Routine raus, die OCR2 bzw. OCR3AL und OCR3BL stehen richtig und wandern auch brav bei jedem Durchlauf mit. Lade ich aber das in die Hardware, kommt immer Fullscale raus. Zum Gegencheck habe ich mal das Rechenergebnis einfach mit 7F maskiert - ja, dann kommt halt 7F raus, maskiere ich anders, ja es kommt so raus. Auch die 20ms habe ich überprüft - alles okay - es ist einfach das Rechenergebnis nicht mit dem Simulator konform. Ach ja, Data Space (Stack) ist noch genug da. (AVR GCC, 37% Data usage) Irgendwie begreife ich das nicht. Im Simulator ist alles paletti, die Hardware funktioniert (ich kann 'zu Fuß' PWM-Werte ausgeben), aber das Interpolationsergebnis scheint immer 0xFF zu sein. Gibt es da ev. Errata, die man kennen sollte? Den Code poste ich mal noch nicht, ist etwas umfangreicher. Frustriert ... Wolfgang
>Den Code poste ich mal noch nicht, ist etwas umfangreicher.
Dann lass es halt bleiben und such selber den Fehler.
Okay, dann beginnen wir mal, hier init und Hardwarezugriff:
1 | void init_rgb_timer(void) |
2 | {
|
3 | // Init Timer2 as Fast PWM with a CLKDIV (prescaler) of 64
|
4 | |
5 | #define T2_PRESCALER 64 // may be 1, 8, 32, 64, 128, 256, 1024
|
6 | #if (T2_PRESCALER==1)
|
7 | #define T2_PRESCALER_BITS ((0<<CS22)|(0<<CS21)|(1<<CS20))
|
8 | #elif (T2_PRESCALER==8)
|
9 | #define T2_PRESCALER_BITS ((0<<CS22)|(1<<CS21)|(0<<CS20))
|
10 | #elif (T2_PRESCALER==32)
|
11 | #define T2_PRESCALER_BITS ((0<<CS22)|(1<<CS21)|(1<<CS20))
|
12 | #elif (T2_PRESCALER==64)
|
13 | #define T2_PRESCALER_BITS ((1<<CS22)|(0<<CS21)|(0<<CS20))
|
14 | #elif (T2_PRESCALER==128)
|
15 | #define T2_PRESCALER_BITS ((1<<CS22)|(0<<CS21)|(1<<CS20))
|
16 | #elif (T2_PRESCALER==256)
|
17 | #define T2_PRESCALER_BITS ((1<<CS22)|(1<<CS21)|(0<<CS20))
|
18 | #elif (T2_PRESCALER==1024)
|
19 | #define T2_PRESCALER_BITS ((1<<CS22)|(1<<CS21)|(1<<CS20))
|
20 | #else
|
21 | #error void value T2_PRESCALER
|
22 | #endif
|
23 | |
24 | TCCR2 = (0 << FOC2) // Timer2: fastpwm |
25 | | (1 << WGM20) // wgm = 00: normal mode, top=0xff |
26 | | (1 << COM21) // COMx1,COMx0 = 00: normal mode, pin operates as usual |
27 | | (0 << COM20) // COMx1,COMx0 = 10: compare match: set OC2 at TOP. clear at OCR |
28 | // COMx1,COMx0 = 11: compare match: clear OC2 at TOP. set at OCR
|
29 | //
|
30 | | (1 << WGM21) // |
31 | | T2_PRESCALER_BITS; |
32 | |
33 | TCNT2 = 0; |
34 | |
35 | // Init Timer3 as Fast PWM with a CLKDIV (prescaler) of 64
|
36 | |
37 | #define T3_PRESCALER 64 // may be 1, 8, 16, 32, 64, 256, 1024
|
38 | #if (T3_PRESCALER==1)
|
39 | #define T3_PRESCALER_BITS ((0<<CS32)|(0<<CS31)|(1<<CS30))
|
40 | #elif (T3_PRESCALER==8)
|
41 | #define T3_PRESCALER_BITS ((0<<CS32)|(1<<CS31)|(0<<CS30))
|
42 | #elif (T3_PRESCALER==16)
|
43 | #define T3_PRESCALER_BITS ((1<<CS32)|(1<<CS31)|(0<<CS30))
|
44 | #elif (T3_PRESCALER==32)
|
45 | #define T3_PRESCALER_BITS ((1<<CS32)|(1<<CS31)|(1<<CS30))
|
46 | #elif (T3_PRESCALER==64)
|
47 | #define T3_PRESCALER_BITS ((0<<CS32)|(1<<CS31)|(1<<CS30))
|
48 | #elif (T3_PRESCALER==256)
|
49 | #define T3_PRESCALER_BITS ((1<<CS32)|(0<<CS31)|(0<<CS30))
|
50 | #elif (T3_PRESCALER==1024)
|
51 | #define T3_PRESCALER_BITS ((1<<CS32)|(0<<CS31)|(1<<CS30))
|
52 | #else
|
53 | #error void value T3_PRESCALER
|
54 | #endif
|
55 | |
56 | // FastPWM, 8 Bit = Mode 5: WGM3 = 0101
|
57 | |
58 | TCCR3A = (1 << COM3A1) // compare match A |
59 | | (0 << COM3A0) // |
60 | | (1 << COM3B1) // compare match B |
61 | | (0 << COM3B0) |
62 | | (0 << FOC3A) |
63 | | (0 << FOC3B) |
64 | | (0 << WGM31) |
65 | | (1 << WGM30); |
66 | TCCR3B = (0 << ICNC3) |
67 | | (0 << ICES3) |
68 | | (0 << WGM33) |
69 | | (1 << WGM32) |
70 | | (T3_PRESCALER_BITS); // clkdiv |
71 | |
72 | TCNT3 = 0; |
73 | }
|
74 | |
75 | unsigned char RED; // mirror, fuer den Simulator |
76 | unsigned char GREEN; |
77 | unsigned char BLUE; |
78 | |
79 | // hardware access, values given from 0..255
|
80 | |
81 | void set_R(unsigned char red_value) |
82 | {
|
83 | OCR3AL = red_value; |
84 | RED = red_value; |
85 | }
|
86 | |
87 | void set_G(unsigned char green_value) |
88 | {
|
89 | OCR2 = green_value; |
90 | GREEN = green_value; |
91 | }
|
92 | |
93 | void set_B(unsigned char blue_value) |
94 | {
|
95 | OCR3BL = blue_value; |
96 | BLUE = blue_value; |
97 | }
|
Und dann die Routine, welche die Interpolation macht:
1 | #define SIZE_RGB_FADE 24 // number of entries (triples) in one control
|
2 | |
3 | typedef struct // fade values, normalized [0..255] |
4 | {
|
5 | unsigned char time; |
6 | unsigned char red; |
7 | unsigned char green; |
8 | unsigned char blue; |
9 | } t_rgb_point; |
10 | |
11 | t_rgb_point farbkreis[] PROGMEM = |
12 | {
|
13 | // time, r, g, b
|
14 | { 0 , 255 , 0 , 0 }, |
15 | { 1 , 255 , 255 , 0 }, |
16 | { 2 , 0 , 255 , 0 }, |
17 | { 3 , 0 , 255 , 255 }, |
18 | { 4 , 0 , 0 , 255 }, |
19 | { 5 , 255 , 0 , 255 }, |
20 | { 6 , 255 , 0 , 0 }, |
21 | { 0 , 0 , 0 , 0 }, // end of list: time = 0 |
22 | };
|
23 | |
24 | t_rgb_point *pre_def_fades[] = |
25 | {
|
26 | farbkreis, // 0: farbkreis |
27 | };
|
28 | |
29 | t_rgb_point rgb_fade[SIZE_RGB_FADE]; |
30 | |
31 | typedef struct |
32 | {
|
33 | unsigned char redmax; // upper limit [0..255] |
34 | |
35 | unsigned char greenmax; // upper limit [0..255] |
36 | |
37 | unsigned char bluemax; // upper limit [0..255] |
38 | |
39 | unsigned char control; |
40 | |
41 | unsigned char repeat; // if REPEAT: this is the number of repeats to do |
42 | |
43 | unsigned char fade_index; // points to actual target in curve |
44 | |
45 | t_rgb_point *fade; // fading curve, normalized [0..255] |
46 | |
47 | unsigned int active_time; // runtime: relative time to start point |
48 | // 0 = restart Servos
|
49 | // 0xffff = finished
|
50 | |
51 | unsigned char time_ratio; // ratio between runtime and curve time |
52 | } t_rgb_ctrl; |
53 | |
54 | t_rgb_ctrl rgb_ctrl = |
55 | { 255, // unsigned char redmax; |
56 | 255, // unsigned char greenmax; |
57 | 255, // unsigned char bluemax; |
58 | 0, // unsigned char control; |
59 | 0, // unsigned char repeat; |
60 | 1, // unsigned char fade_index; |
61 | rgb_fade, // pointer to curve |
62 | 0, // active time |
63 | 1, // ratio of curve |
64 | };
|
65 | |
66 | |
67 | //------------------------------------------------------------------------------
|
68 | //
|
69 | #define CALC_GAIN 128L // 2 bis 128; 128 ist obere Grenze wegen möglichen Überlauf
|
70 | |
71 | void calc_rgb_next_val(void) |
72 | {
|
73 | unsigned char myindex; // index in curve |
74 | int16_t posi; |
75 | int16_t dt, delta_t; // time delta always > 0 |
76 | int32_t posl; |
77 | int16_t delta_pos; |
78 | unsigned char end_of_list_reached = 0; |
79 | |
80 | if (rgb_ctrl.active_time == 0xFFFF) return; // inactive - do nothing |
81 | |
82 | rgb_ctrl.active_time++; |
83 | |
84 | // check, if next curve point is reached
|
85 | myindex = rgb_ctrl.fade_index; |
86 | |
87 | if ((rgb_ctrl.fade[myindex].time * rgb_ctrl.time_ratio) == rgb_ctrl.active_time) |
88 | {
|
89 | myindex++; |
90 | // new curve point reached, how to proceed?
|
91 | if (rgb_ctrl.fade[myindex].time == 0) |
92 | {
|
93 | // end of list
|
94 | end_of_list_reached = 1; |
95 | myindex--; // stay on last curve point |
96 | }
|
97 | else
|
98 | {
|
99 | rgb_ctrl.fade_index = myindex; // save index |
100 | }
|
101 | }
|
102 | |
103 | // now calc linear interpolation
|
104 | // val = val_prev + (dt / delta_t) * (val - val_prev)
|
105 | |
106 | dt = rgb_ctrl.active_time - (int)rgb_ctrl.fade[myindex-1].time * rgb_ctrl.time_ratio; // int |
107 | |
108 | delta_t = (int)(rgb_ctrl.fade[myindex].time - rgb_ctrl.fade[myindex-1].time) // int |
109 | * rgb_ctrl.time_ratio; |
110 | |
111 | if (delta_t == 0) delta_t = 1; // avoid div0 (this is dirty) |
112 | |
113 | //----> make red
|
114 | |
115 | delta_pos = ((int)rgb_ctrl.fade[myindex].red |
116 | - (int)rgb_ctrl.fade[myindex-1].red) |
117 | * CALC_GAIN; |
118 | posl = (int32_t)delta_pos * dt; |
119 | |
120 | posl = posl / delta_t; |
121 | posl = posl + (int)rgb_ctrl.fade[myindex-1].red * CALC_GAIN; |
122 | |
123 | // scale this fade value according to max volume
|
124 | // scaled = interpolated * max
|
125 | |
126 | posl = posl * rgb_ctrl.redmax; |
127 | |
128 | // now scale to timer
|
129 | |
130 | posl = posl / 256; |
131 | posi = posl / CALC_GAIN; |
132 | // range 0 ... 256
|
133 | set_R(posi); |
134 | // die anderen Farben schenke ich mir hier
|
135 | }
|
Und der Aufruf ist dann:
1 | void do_rgb_fade(void) |
2 | {
|
3 | rgb_ctrl.redmax = my_eeprom_read_byte(&CV.REDmax); |
4 | rgb_ctrl.greenmax = my_eeprom_read_byte(&CV.GREENmax); |
5 | rgb_ctrl.bluemax = my_eeprom_read_byte(&CV.BLUEmax); |
6 | |
7 | rgb_copy_fade(my_eeprom_read_byte(&CV.RGB_profile)); |
8 | rgb_ctrl.time_ratio = my_eeprom_read_byte(&CV.RGB_time); |
9 | rgb_ctrl.fade_index = 1; |
10 | rgb_ctrl.active_time = 0; |
11 | rgb_ctrl.repeat = my_eeprom_read_byte(&CV.RGB_repeat); |
12 | rgb_ctrl.control = 0; |
13 | set_R(0); // red_value |
14 | set_G(0); // green_value; |
15 | set_B(0); // blue_value; |
16 | }
|
Und dann wird alle 20ms calc_rgb_next_val(); aufgerufen. Servus Wolfgang
libm eingebunden? du machst hier Rechnungen mit Multiplikationen und Divisionen ... und falls jetzt kommt da sind keine floats drin: #define CALC_GAIN 128L ist ein float ... gruss
soundso schrieb: > libm eingebunden? > > du machst hier Rechnungen mit Multiplikationen und Divisionen ... > > und falls jetzt kommt da sind keine floats drin: > > #define CALC_GAIN 128L ist ein float ... Seit wann? @Wolfgang EEPROM hast du gebrannt? Ansonsten: Konzentrier dich auf die 20ms. Die Berechnungen laufen auf dem µC auch nicht anders als auf dem Simulator. Du hast doch in beiden Fällen identische Optimizer-Einstellungen? Dem Simulator sind deine Berechnungen wurscht, der arbeitet nur Assembler Instruktionen ab, genauso wie der µC. Da kommt im Simulator dasselbe raus, wie auf dem µC. Timing ist aber eine andere Sache, das kann abweichen, zb wenn im Simulator falsche Takteinstellungen gemacht sind.
Hallo, ich ziehe
1 | #include <stdlib.h> |
2 | #include <stdbool.h> |
3 | #include <inttypes.h> |
rein. 128L ist doch ein Long, oder? Servus Wolfgang
Hallo, > Ansonsten: Konzentrier dich auf die 20ms. Hatte ich auch schon in Verdacht, habe daher einfach mal set_R(my_static_i++) reingehängt - und die LED dimmt in 5sec einmal rum; 5 sec = 256/20ms, paßt. > Du hast doch in beiden Fällen identische Optimizer-Einstellungen? Ja, und EEPROM ist auch geladen. Das werde ich aber nochmal zur Probe rücklesen und das eep file genau ansehen. Servus Wolfgang
Hi >Ja, und EEPROM ist auch geladen. Das werde ich aber nochmal zur Probe >rücklesen und das eep file genau ansehen. Und EESAVE-Fuse setzen. MfG Spess
Hast du irgendeine Möglichkeit für die Ausgabe? LCD, UART?
Hallo, LCD und UART ist keiner dran, die Zielplatine ist recht minimalistisch und klein. UART könnte ich mal nachverdrahten und über ein FTDI-Kabel mal ein bischen printf machen - Danke für den Hinweis. Servus Wolfgang
Ansonsten fällt mir nur noch ein; Abspecken. Du hast einen Fehler gemacht: Zuviel Code geschrieben ohne ihn auf der realen Hardware zwischendurch immer wieder zu testen.
Hallo, und heute des Rästels Lösung: der ISP war einen Tick zu schnell - ich habe den AVRmkII auf langsamere Speed gestellt, et voila, es dimmt. Danke nochmals für die Denkanstöße. Übrigens, obige Rumrechnerei habe ich bereits bei der Servoansteuerung drin - um mit Integer etwas Auflösung hinzuzaubern, muß man das einfach ein bischen hochskalieren. Servus Wolfgang, www.opendcc.de
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.