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