]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pwmled.c
pwmled.c: save error sum between mode switches
[bike-lights.git] / firmware / pwmled.c
1 #include <avr/io.h>
2
3 #include "lights.h"
4
5 typedef struct {
6         uint16_t target, pwm;
7         int16_t err_sum;
8         unsigned char mode, state, probe_steps;
9         uint16_t mode_pwm[N_PWMLED_MODES];
10         int16_t err_sums[N_PWMLED_MODES];
11 } pwmled_t;
12
13 pwmled_t pwmleds[N_PWMLEDS];
14
15 #define PWMLED2_TESTING_WITH_350MA_LED
16
17 #define SENSE_MOHM      33      /* 0.033 Ohm */
18 #define MA_MOHM_GAIN_TO_ADC(ma, mohm, gain) (\
19         ((unsigned long)(ma))*(mohm) /* voltage at sensing resistor in uV */ \
20         /(1100000UL/gain/1024UL)     /* voltage of ADC reading == 1 */ \
21 )
22
23 static uint16_t adc_max[N_PWMLEDS] = {
24         MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM, 20),
25         MA_MOHM_GAIN_TO_ADC(  30, SENSE_MOHM, 20),
26 #ifdef PWMLED2_TESTING_WITH_350MA_LED
27         MA_MOHM_GAIN_TO_ADC( 400, SENSE_MOHM,  1)
28 #else
29         MA_MOHM_GAIN_TO_ADC(2500, SENSE_MOHM,  1)
30 #endif
31 };
32
33 static uint16_t adc_vals[N_PWMLEDS*N_PWMLED_MODES] = {
34         /* pwmled0 */
35         MA_MOHM_GAIN_TO_ADC(  20, SENSE_MOHM, 20),
36         MA_MOHM_GAIN_TO_ADC(  50, SENSE_MOHM, 20),
37         MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM, 20),
38         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM, 20),
39         /* pwmled1 */
40         16, 32, 64, 112,
41 #if 0
42         MA_MOHM_GAIN_TO_ADC(   5, SENSE_MOHM, 20),
43         MA_MOHM_GAIN_TO_ADC(  12, SENSE_MOHM, 20),
44         MA_MOHM_GAIN_TO_ADC(  16, SENSE_MOHM, 20),
45         MA_MOHM_GAIN_TO_ADC(  20, SENSE_MOHM, 20),
46 #endif
47         /* pwmled2 */
48         24, 32, 40, 48
49 #if 0
50 #ifdef PWMLED2_TESTING_WITH_350MA_LED
51         MA_MOHM_GAIN_TO_ADC( 100, SENSE_MOHM,  1),
52         MA_MOHM_GAIN_TO_ADC( 140, SENSE_MOHM,  1),
53         MA_MOHM_GAIN_TO_ADC( 250, SENSE_MOHM,  1),
54         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM,  1),
55 #else
56         MA_MOHM_GAIN_TO_ADC( 150, SENSE_MOHM,  1),
57         MA_MOHM_GAIN_TO_ADC( 350, SENSE_MOHM,  1),
58         MA_MOHM_GAIN_TO_ADC( 700, SENSE_MOHM,  1),
59         MA_MOHM_GAIN_TO_ADC(2400, SENSE_MOHM,  1),
60 #endif
61 #endif
62 };
63
64 #define ST_DISABLED 0
65 #define ST_OFF      1
66 #define ST_PROBING  2
67 #define ST_ON       3
68 // The above are constructed so that the following work:
69 #define ST_IS_ON(s)     ((s) & 0x02)
70 #define ST_CAN_SET_MODE(s)      ((s) & 0x01)
71
72 void init_pwmled()
73 {
74         unsigned char i, j;
75
76         for (i = 0; i < N_PWMLEDS; i++) {
77                 pwmled_t *led = pwmleds + i;
78                 led->err_sum = 0;
79                 led->target = adc_vals[i*N_PWMLED_MODES];
80                 led->pwm = 0;
81                 led->mode = 1;
82                 led->state = ST_PROBING;
83                 led->probe_steps = 0;
84
85                 for (j = 0; j < N_PWMLED_MODES; j++) {
86                         led->mode_pwm[j] = 0;
87                         led->err_sums[j] = 0;
88                 }
89         }
90 }
91
92 void pwmled_set_mode(unsigned char n, unsigned char mode)
93 {
94         pwmled_t *led = pwmleds + n;
95
96         if (!ST_CAN_SET_MODE(led->state))
97                 return;
98
99         if (led->mode) { // save the previous state
100                 led->mode_pwm[led->mode - 1] = led->pwm;
101                 led->err_sums[led->mode - 1] = led->err_sum;
102         }
103
104         led->mode = mode;
105
106         if (mode > 0 && mode <= N_PWMLED_MODES) {
107                 led->target = adc_vals[n*N_PWMLED_MODES + mode - 1];
108                 led->state = ST_ON;
109                 led->pwm = led->mode_pwm[mode - 1];
110                 led->err_sum = led->err_sums[mode - 1];
111                 pwm_set(n, led->pwm);
112         } else {
113                 led->state = ST_OFF;
114                 pwm_off(n);
115         }
116 }
117
118 void pwmled_adc(unsigned char n, uint16_t adcval)
119 {
120         pwmled_t *led = pwmleds + n;
121         uint16_t old_pwm;
122         int32_t sum;
123         unsigned char shift;
124
125         if (!ST_IS_ON(led->state))
126                 return;
127
128         // FIXME: test for maximum adcval value (adc_max[n])
129
130         old_pwm = led->pwm;
131
132         shift = led->state == ST_PROBING ? 3 : 5;
133
134         sum = ((int32_t)led->pwm << shift)
135                 + led->err_sum + led->target - adcval;
136
137         if (sum < 0)
138                 sum = 0;
139
140         led->pwm = sum >> shift;
141         sum -= led->pwm << shift;
142         led->err_sum = sum;
143
144         if (led->state == ST_PROBING) {
145                 if (led->pwm == old_pwm) {
146                         if (led->probe_steps < 10)
147                                 led->probe_steps++;
148                 } else {
149                         led->probe_steps = 0;
150                 }
151
152                 if (led->probe_steps >= 10 || old_pwm > led->pwm) {
153
154                         led->mode_pwm[led->mode - 1] = led->pwm;
155
156                         if (led->mode < N_PWMLED_MODES) {
157                                 led->probe_steps = 0;
158                                 led->err_sum = 0;
159
160                                 led->mode++;
161                                 led->target = adc_vals[n*N_PWMLED_MODES+led->mode-1];
162                         } else {
163                                 unsigned char i;
164
165                                 led->state = ST_OFF;
166
167                                 log_byte(0xF0);
168                                 log_byte(n);
169                                 log_word(jiffies);
170
171                                 for (i = 0; i < N_PWMLED_MODES; i++)
172                                         log_word(led->mode_pwm[i]);
173
174                                 log_flush();
175                         }
176                 }
177         }
178
179         if (led->pwm == old_pwm)
180                 return;
181
182         if (led->pwm > (PWM_MAX << PWM_STEP_SHIFT)/2) {
183                 pwm_off(n);
184                 led->state = ST_DISABLED;
185                 log_byte(0xF1);
186                 log_byte(n);
187                 log_word(jiffies);
188                 log_flush();
189         }
190
191         pwm_set(n, led->pwm);
192 }
193