]> www.fi.muni.cz Git - bike-lights.git/blob - firmware/pwmled.c
pwm.c: publish only PWM_MAX as non-internal value
[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_steady;
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_steady = 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 #define PWMLED_PROBE_STEADY_COUNT 10
119
120 static inline unsigned char pwmled_probed_ok(unsigned char n, uint16_t old_pwm)
121 {
122         pwmled_t *led = pwmleds + n;
123
124         if (led->pwm == old_pwm) {
125                 if (led->probe_steady < PWMLED_PROBE_STEADY_COUNT)
126                         led->probe_steady++;
127         } else {
128                 led->probe_steady = 0;
129         }
130
131         if (led->probe_steady < PWMLED_PROBE_STEADY_COUNT
132                 && old_pwm <= led->pwm)
133                 return 0;
134
135         // probed OK
136         led->mode_pwm[led->mode - 1] = led->pwm;
137         led->err_sums[led->mode - 1] = 0;
138
139         // next mode to probe?
140         if (led->mode < N_PWMLED_MODES) {
141                 led->probe_steady = 0;
142                 led->err_sum = 0;
143
144                 led->mode++;
145                 led->target = adc_vals[n*N_PWMLED_MODES+led->mode-1];
146
147                 return 0;
148         } else {
149                 unsigned char i;
150
151                 led->state = ST_OFF;
152                 pwm_off(n);
153
154                 log_byte(0xF0);
155                 log_byte(n);
156                 log_word(jiffies);
157
158                 for (i = 0; i < N_PWMLED_MODES; i++)
159                         log_word(led->mode_pwm[i]);
160
161                 log_flush();
162
163                 pattern_reload();
164
165                 return 1;
166         }
167 }
168
169 static inline void pwmled_err(unsigned char n)
170 {
171         pwmleds[n].state = ST_DISABLED;
172         pwm_off(n);
173
174         log_byte(0xF1);
175         log_byte(n);
176         log_word(jiffies);
177         log_flush();
178 }
179
180
181 void pwmled_adc(unsigned char n, uint16_t adcval)
182 {
183         pwmled_t *led = pwmleds + n;
184         uint16_t old_pwm;
185         int32_t sum;
186         unsigned char shift;
187
188         if (!ST_IS_ON(led->state))
189                 return;
190
191         // FIXME: test for maximum adcval value (adc_max[n])
192
193         old_pwm = led->pwm;
194
195         shift = led->state == ST_PROBING ? 3 : 5;
196
197         sum = ((int32_t)led->pwm << shift)
198                 + led->err_sum + led->target - adcval;
199
200         if (sum < 0)
201                 sum = 0;
202
203         led->pwm = sum >> shift;
204         sum -= led->pwm << shift;
205         led->err_sum = sum;
206
207         if (led->pwm >= PWM_MAX) {
208                 pwmled_err(n);
209                 return;
210         }
211
212         if (led->state == ST_PROBING)
213                 if (pwmled_probed_ok(n, old_pwm))
214                         return;
215
216         if (led->pwm == old_pwm)
217                 return;
218
219         pwm_set(n, led->pwm);
220 }
221