diff options
Diffstat (limited to 'lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c')
-rw-r--r-- | lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c b/lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c new file mode 100644 index 000000000..a4ac8ec56 --- /dev/null +++ b/lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | TSL2561 for ChibiOS/RT - Copyright (C) 2016 Stephane D'Alu | ||
3 | |||
4 | Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | you may not use this file except in compliance with the License. | ||
6 | You may obtain a copy of the License at | ||
7 | |||
8 | http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | |||
10 | Unless required by applicable law or agreed to in writing, software | ||
11 | distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | See the License for the specific language governing permissions and | ||
14 | limitations under the License. | ||
15 | */ | ||
16 | |||
17 | /** | ||
18 | * Illuminance calculation code provided by www.taosinc.com | ||
19 | * DOC: http://ams.com/eng/content/download/250096/975518/143687 | ||
20 | */ | ||
21 | #define I2C_HELPERS_AUTOMATIC_DRV TRUE | ||
22 | |||
23 | #include "hal.h" | ||
24 | #include "i2c_helpers.h" | ||
25 | #include "tsl2561.h" | ||
26 | |||
27 | /*===========================================================================*/ | ||
28 | /* Driver local definitions. */ | ||
29 | /*===========================================================================*/ | ||
30 | |||
31 | // Integration time in µs | ||
32 | #define TSL2561_DELAY_INTTIME_SHORT 13700 // 13.7 ms | ||
33 | #define TSL2561_DELAY_INTTIME_MEDIUM 120000 // 120.0 ms | ||
34 | #define TSL2561_DELAY_INTTIME_LONG 450000 // 450.0 ms | ||
35 | |||
36 | |||
37 | #define TSL2561_COMMAND_BIT (0x80) | ||
38 | #define TSL2561_CLEAR_BIT (0x40) | ||
39 | #define TSL2561_WORD_BIT (0x20) | ||
40 | #define TSL2561_BLOCK_BIT (0x10) | ||
41 | |||
42 | #define TSL2561_CONTROL_POWERON (0x03) | ||
43 | #define TSL2561_CONTROL_POWEROFF (0x00) | ||
44 | |||
45 | #define TSL2561_LUX_LUXSCALE (14) | ||
46 | #define TSL2561_LUX_RATIOSCALE (9) | ||
47 | #define TSL2561_LUX_CHSCALE (10) // Scale channel values by 2^10 | ||
48 | #define TSL2561_LUX_CHSCALE_TINT0 (0x7517) // 322/11 * 2^TSL2561_LUX_CHSCALE | ||
49 | #define TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) // 322/81 * 2^TSL2561_LUX_CHSCALE | ||
50 | |||
51 | |||
52 | // I2C Register | ||
53 | #define TSL2561_REG_CONTROL 0x00 | ||
54 | #define TSL2561_REG_TIMING 0x01 | ||
55 | #define TSL2561_REG_THRESHHOLDLLOW 0x02 | ||
56 | #define TSL2561_REG_THRESHHOLDLHIGH 0x03 | ||
57 | #define TSL2561_REG_THRESHHOLDHLOW 0x04 | ||
58 | #define TSL2561_REG_THRESHHOLDHHIGH 0x05 | ||
59 | #define TSL2561_REG_INTERRUPT 0x06 | ||
60 | #define TSL2561_REG_CRC 0x08 | ||
61 | #define TSL2561_REG_ID 0x0A | ||
62 | #define TSL2561_REG_DATA0LOW 0x0C | ||
63 | #define TSL2561_REG_DATA0HIGH 0x0D | ||
64 | #define TSL2561_REG_DATA1LOW 0x0E | ||
65 | #define TSL2561_REG_DATA1HIGH 0x0F | ||
66 | |||
67 | |||
68 | // Auto-gain thresholds | ||
69 | #define TSL2561_AGC_THI_SHORT (4850) // Max value at Ti 13ms = 5047 | ||
70 | #define TSL2561_AGC_TLO_SHORT (100) | ||
71 | #define TSL2561_AGC_THI_MEDIUM (36000) // Max value at Ti 101ms = 37177 | ||
72 | #define TSL2561_AGC_TLO_MEDIUM (200) | ||
73 | #define TSL2561_AGC_THI_LONG (63000) // Max value at Ti 402ms = 65535 | ||
74 | #define TSL2561_AGC_TLO_LONG (500) | ||
75 | |||
76 | // Clipping thresholds | ||
77 | #define TSL2561_CLIPPING_SHORT (4900) | ||
78 | #define TSL2561_CLIPPING_MEDIUM (37000) | ||
79 | #define TSL2561_CLIPPING_LONG (65000) | ||
80 | |||
81 | // T, FN and CL package values | ||
82 | #define TSL2561_LUX_K1T (0x0040) // 0.125 * 2^RATIO_SCALE | ||
83 | #define TSL2561_LUX_B1T (0x01f2) // 0.0304 * 2^LUX_SCALE | ||
84 | #define TSL2561_LUX_M1T (0x01be) // 0.0272 * 2^LUX_SCALE | ||
85 | #define TSL2561_LUX_K2T (0x0080) // 0.250 * 2^RATIO_SCALE | ||
86 | #define TSL2561_LUX_B2T (0x0214) // 0.0325 * 2^LUX_SCALE | ||
87 | #define TSL2561_LUX_M2T (0x02d1) // 0.0440 * 2^LUX_SCALE | ||
88 | #define TSL2561_LUX_K3T (0x00c0) // 0.375 * 2^RATIO_SCALE | ||
89 | #define TSL2561_LUX_B3T (0x023f) // 0.0351 * 2^LUX_SCALE | ||
90 | #define TSL2561_LUX_M3T (0x037b) // 0.0544 * 2^LUX_SCALE | ||
91 | #define TSL2561_LUX_K4T (0x0100) // 0.50 * 2^RATIO_SCALE | ||
92 | #define TSL2561_LUX_B4T (0x0270) // 0.0381 * 2^LUX_SCALE | ||
93 | #define TSL2561_LUX_M4T (0x03fe) // 0.0624 * 2^LUX_SCALE | ||
94 | #define TSL2561_LUX_K5T (0x0138) // 0.61 * 2^RATIO_SCALE | ||
95 | #define TSL2561_LUX_B5T (0x016f) // 0.0224 * 2^LUX_SCALE | ||
96 | #define TSL2561_LUX_M5T (0x01fc) // 0.0310 * 2^LUX_SCALE | ||
97 | #define TSL2561_LUX_K6T (0x019a) // 0.80 * 2^RATIO_SCALE | ||
98 | #define TSL2561_LUX_B6T (0x00d2) // 0.0128 * 2^LUX_SCALE | ||
99 | #define TSL2561_LUX_M6T (0x00fb) // 0.0153 * 2^LUX_SCALE | ||
100 | #define TSL2561_LUX_K7T (0x029a) // 1.3 * 2^RATIO_SCALE | ||
101 | #define TSL2561_LUX_B7T (0x0018) // 0.00146 * 2^LUX_SCALE | ||
102 | #define TSL2561_LUX_M7T (0x0012) // 0.00112 * 2^LUX_SCALE | ||
103 | #define TSL2561_LUX_K8T (0x029a) // 1.3 * 2^RATIO_SCALE | ||
104 | #define TSL2561_LUX_B8T (0x0000) // 0.000 * 2^LUX_SCALE | ||
105 | #define TSL2561_LUX_M8T (0x0000) // 0.000 * 2^LUX_SCALE | ||
106 | |||
107 | // CS package values | ||
108 | #define TSL2561_LUX_K1C (0x0043) // 0.130 * 2^RATIO_SCALE | ||
109 | #define TSL2561_LUX_B1C (0x0204) // 0.0315 * 2^LUX_SCALE | ||
110 | #define TSL2561_LUX_M1C (0x01ad) // 0.0262 * 2^LUX_SCALE | ||
111 | #define TSL2561_LUX_K2C (0x0085) // 0.260 * 2^RATIO_SCALE | ||
112 | #define TSL2561_LUX_B2C (0x0228) // 0.0337 * 2^LUX_SCALE | ||
113 | #define TSL2561_LUX_M2C (0x02c1) // 0.0430 * 2^LUX_SCALE | ||
114 | #define TSL2561_LUX_K3C (0x00c8) // 0.390 * 2^RATIO_SCALE | ||
115 | #define TSL2561_LUX_B3C (0x0253) // 0.0363 * 2^LUX_SCALE | ||
116 | #define TSL2561_LUX_M3C (0x0363) // 0.0529 * 2^LUX_SCALE | ||
117 | #define TSL2561_LUX_K4C (0x010a) // 0.520 * 2^RATIO_SCALE | ||
118 | #define TSL2561_LUX_B4C (0x0282) // 0.0392 * 2^LUX_SCALE | ||
119 | #define TSL2561_LUX_M4C (0x03df) // 0.0605 * 2^LUX_SCALE | ||
120 | #define TSL2561_LUX_K5C (0x014d) // 0.65 * 2^RATIO_SCALE | ||
121 | #define TSL2561_LUX_B5C (0x0177) // 0.0229 * 2^LUX_SCALE | ||
122 | #define TSL2561_LUX_M5C (0x01dd) // 0.0291 * 2^LUX_SCALE | ||
123 | #define TSL2561_LUX_K6C (0x019a) // 0.80 * 2^RATIO_SCALE | ||
124 | #define TSL2561_LUX_B6C (0x0101) // 0.0157 * 2^LUX_SCALE | ||
125 | #define TSL2561_LUX_M6C (0x0127) // 0.0180 * 2^LUX_SCALE | ||
126 | #define TSL2561_LUX_K7C (0x029a) // 1.3 * 2^RATIO_SCALE | ||
127 | #define TSL2561_LUX_B7C (0x0037) // 0.00338 * 2^LUX_SCALE | ||
128 | #define TSL2561_LUX_M7C (0x002b) // 0.00260 * 2^LUX_SCALE | ||
129 | #define TSL2561_LUX_K8C (0x029a) // 1.3 * 2^RATIO_SCALE | ||
130 | #define TSL2561_LUX_B8C (0x0000) // 0.000 * 2^LUX_SCALE | ||
131 | #define TSL2561_LUX_M8C (0x0000) // 0.000 * 2^LUX_SCALE | ||
132 | |||
133 | |||
134 | /*===========================================================================*/ | ||
135 | /* Driver exported variables. */ | ||
136 | /*===========================================================================*/ | ||
137 | |||
138 | /*===========================================================================*/ | ||
139 | /* Driver local variables and types. */ | ||
140 | /*===========================================================================*/ | ||
141 | |||
142 | /*===========================================================================*/ | ||
143 | /* Driver local functions. */ | ||
144 | /*===========================================================================*/ | ||
145 | |||
146 | #define CEILING(x,y) (((x) + (y) - 1) / (y)) | ||
147 | |||
148 | static inline unsigned int | ||
149 | calculateIlluminance(TSL2561_integration_time_t integration_time, | ||
150 | TSL2561_gain_t gain, | ||
151 | uint16_t broadband, uint16_t ir, | ||
152 | unsigned int partno) { | ||
153 | unsigned long channel_1; | ||
154 | unsigned long channel_0; | ||
155 | |||
156 | /* Get value for channel scaling, and clipping */ | ||
157 | uint16_t clip_threshold = 0; | ||
158 | unsigned long channel_scale = 0; | ||
159 | switch (integration_time) { | ||
160 | case TSL2561_INTEGRATIONTIME_SHORT: | ||
161 | clip_threshold = TSL2561_CLIPPING_SHORT; | ||
162 | channel_scale = TSL2561_LUX_CHSCALE_TINT0; | ||
163 | break; | ||
164 | case TSL2561_INTEGRATIONTIME_MEDIUM: | ||
165 | clip_threshold = TSL2561_CLIPPING_MEDIUM; | ||
166 | channel_scale = TSL2561_LUX_CHSCALE_TINT1; | ||
167 | break; | ||
168 | case TSL2561_INTEGRATIONTIME_LONG: | ||
169 | clip_threshold = TSL2561_CLIPPING_LONG; | ||
170 | channel_scale = (1 << TSL2561_LUX_CHSCALE); | ||
171 | break; | ||
172 | default: | ||
173 | // assert failed | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | /* Check for saturated sensor (ie: clipping) */ | ||
178 | if ((broadband > clip_threshold) || (ir > clip_threshold)) { | ||
179 | return TSL2561_OVERLOADED; | ||
180 | } | ||
181 | |||
182 | /* Scale for gain (1x or 16x) */ | ||
183 | if (gain == TSL2561_GAIN_1X) | ||
184 | channel_scale <<= 4; | ||
185 | |||
186 | /* Scale the channel values */ | ||
187 | channel_0 = (broadband * channel_scale) >> TSL2561_LUX_CHSCALE; | ||
188 | channel_1 = (ir * channel_scale) >> TSL2561_LUX_CHSCALE; | ||
189 | |||
190 | /* Find the ratio of the channel values (Channel_1/Channel_0) */ | ||
191 | unsigned long _ratio = 0; | ||
192 | if (channel_0 != 0) | ||
193 | _ratio = (channel_1 << (TSL2561_LUX_RATIOSCALE+1)) / channel_0; | ||
194 | unsigned long ratio = (_ratio + 1) >> 1; /* round the ratio value */ | ||
195 | |||
196 | /* Find linear approximation */ | ||
197 | unsigned int b = 0; | ||
198 | unsigned int m = 0; | ||
199 | |||
200 | switch (partno) { | ||
201 | #if TSL2561_WITH_CS | ||
202 | case 0x1: // 0001 = TSL2561 CS | ||
203 | if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C)) | ||
204 | { b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C; } | ||
205 | else if (ratio <= TSL2561_LUX_K2C) | ||
206 | { b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C; } | ||
207 | else if (ratio <= TSL2561_LUX_K3C) | ||
208 | { b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C; } | ||
209 | else if (ratio <= TSL2561_LUX_K4C) | ||
210 | { b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C; } | ||
211 | else if (ratio <= TSL2561_LUX_K5C) | ||
212 | { b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C; } | ||
213 | else if (ratio <= TSL2561_LUX_K6C) | ||
214 | { b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C; } | ||
215 | else if (ratio <= TSL2561_LUX_K7C) | ||
216 | { b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C; } | ||
217 | else if (ratio > TSL2561_LUX_K8C) | ||
218 | { b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C; } | ||
219 | break; | ||
220 | #endif | ||
221 | #if TSL2561_WITH_T_FN_CL | ||
222 | case 0x5: // 0101 = TSL2561 T/FN/CL | ||
223 | if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T)) | ||
224 | { b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T; } | ||
225 | else if (ratio <= TSL2561_LUX_K2T) | ||
226 | { b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T; } | ||
227 | else if (ratio <= TSL2561_LUX_K3T) | ||
228 | { b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T; } | ||
229 | else if (ratio <= TSL2561_LUX_K4T) | ||
230 | { b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T; } | ||
231 | else if (ratio <= TSL2561_LUX_K5T) | ||
232 | { b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T; } | ||
233 | else if (ratio <= TSL2561_LUX_K6T) | ||
234 | { b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T; } | ||
235 | else if (ratio <= TSL2561_LUX_K7T) | ||
236 | { b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T; } | ||
237 | else if (ratio > TSL2561_LUX_K8T) | ||
238 | { b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T; } | ||
239 | break; | ||
240 | #endif | ||
241 | default: | ||
242 | // assert failed | ||
243 | break; | ||
244 | } | ||
245 | |||
246 | /* Compute illuminance */ | ||
247 | long ill = ((channel_0 * b) - (channel_1 * m)); | ||
248 | if (ill < 0) ill = 0; /* Do not allow negative lux value */ | ||
249 | ill += (1 << (TSL2561_LUX_LUXSCALE-1)); /* Round lsb (2^(LUX_SCALE-1)) */ | ||
250 | ill >>= TSL2561_LUX_LUXSCALE; /* Strip fractional part */ | ||
251 | |||
252 | /* Signal I2C had no errors */ | ||
253 | return ill; | ||
254 | } | ||
255 | |||
256 | static inline msg_t | ||
257 | _readChannel(TSL2561_drv *drv, uint16_t *broadband, uint16_t *ir) { | ||
258 | msg_t msg; | ||
259 | if (((msg = i2c_reg_recv16_le( | ||
260 | TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA0LOW, | ||
261 | broadband)) < MSG_OK) || | ||
262 | ((msg = i2c_reg_recv16_le( | ||
263 | TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REG_DATA1LOW, | ||
264 | ir )) < MSG_OK)) | ||
265 | return msg; | ||
266 | return MSG_OK; | ||
267 | } | ||
268 | |||
269 | /*===========================================================================*/ | ||
270 | /* Driver exported functions. */ | ||
271 | /*===========================================================================*/ | ||
272 | |||
273 | void | ||
274 | TSL2561_init(TSL2561_drv *drv, TSL2561_config *config) { | ||
275 | drv->config = config; | ||
276 | drv->gain = TSL2561_GAIN_1X; | ||
277 | drv->integration_time = TSL2561_INTEGRATIONTIME_LONG; | ||
278 | drv->state = SENSOR_INIT; | ||
279 | |||
280 | i2c_reg_recv8(TSL2561_COMMAND_BIT | TSL2561_REG_ID, | ||
281 | (uint8_t*)&drv->id); | ||
282 | } | ||
283 | |||
284 | msg_t | ||
285 | TSL2561_check(TSL2561_drv *drv) { | ||
286 | uint8_t rx; | ||
287 | |||
288 | msg_t msg; | ||
289 | if ((msg = i2c_reg_recv8(TSL2561_REG_ID, &rx)) < MSG_OK) | ||
290 | return msg; | ||
291 | if (!(rx & 0x0A)) | ||
292 | return SENSOR_NOTFOUND; | ||
293 | return MSG_OK; | ||
294 | } | ||
295 | |||
296 | msg_t | ||
297 | TSL2561_stop(TSL2561_drv *drv) { | ||
298 | struct __attribute__((packed)) { | ||
299 | uint8_t reg; | ||
300 | uint8_t conf; | ||
301 | } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL, | ||
302 | TSL2561_CONTROL_POWEROFF }; | ||
303 | |||
304 | return i2c_send((uint8_t*)&tx, sizeof(tx)); | ||
305 | } | ||
306 | |||
307 | msg_t | ||
308 | TSL2561_start(TSL2561_drv *drv) { | ||
309 | struct __attribute__((packed)) { | ||
310 | uint8_t reg; | ||
311 | uint8_t conf; | ||
312 | } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_CONTROL, | ||
313 | TSL2561_CONTROL_POWERON }; | ||
314 | |||
315 | return i2c_send((uint8_t*)&tx, sizeof(tx)); | ||
316 | } | ||
317 | |||
318 | msg_t | ||
319 | TSL2561_setIntegrationTime(TSL2561_drv *drv, | ||
320 | TSL2561_integration_time_t time) { | ||
321 | struct __attribute__((packed)) { | ||
322 | uint8_t reg; | ||
323 | uint8_t conf; | ||
324 | } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING, | ||
325 | (uint8_t)(time | drv->gain) }; | ||
326 | |||
327 | msg_t msg; | ||
328 | if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK) | ||
329 | return msg; | ||
330 | |||
331 | drv->integration_time = time; | ||
332 | |||
333 | return MSG_OK; | ||
334 | } | ||
335 | |||
336 | msg_t | ||
337 | TSL2561_setGain(TSL2561_drv *drv, | ||
338 | TSL2561_gain_t gain) { | ||
339 | struct __attribute__((packed)) { | ||
340 | uint8_t reg; | ||
341 | uint8_t conf; | ||
342 | } tx = { TSL2561_COMMAND_BIT | TSL2561_REG_TIMING, | ||
343 | (uint8_t)(drv->integration_time | gain) }; | ||
344 | |||
345 | msg_t msg; | ||
346 | if ((msg = i2c_send((uint8_t*)&tx, sizeof(tx))) < MSG_OK) | ||
347 | return msg; | ||
348 | |||
349 | drv->gain = gain; | ||
350 | |||
351 | return MSG_OK; | ||
352 | } | ||
353 | |||
354 | unsigned int | ||
355 | TSL2561_getAcquisitionTime(TSL2561_drv *drv) { | ||
356 | switch (drv->integration_time) { | ||
357 | case TSL2561_INTEGRATIONTIME_SHORT: | ||
358 | return CEILING(TSL2561_DELAY_INTTIME_SHORT , 1000); | ||
359 | case TSL2561_INTEGRATIONTIME_MEDIUM: | ||
360 | return CEILING(TSL2561_DELAY_INTTIME_MEDIUM, 1000); | ||
361 | case TSL2561_INTEGRATIONTIME_LONG: | ||
362 | return CEILING(TSL2561_DELAY_INTTIME_LONG , 1000); | ||
363 | } | ||
364 | return -1; | ||
365 | } | ||
366 | |||
367 | |||
368 | msg_t | ||
369 | TSL2561_readIlluminance(TSL2561_drv *drv, | ||
370 | unsigned int *illuminance) { | ||
371 | uint16_t broadband; | ||
372 | uint16_t ir; | ||
373 | |||
374 | /* Read channels */ | ||
375 | msg_t msg; | ||
376 | if ((msg = _readChannel(drv, &broadband, &ir)) < MSG_OK) | ||
377 | return msg; | ||
378 | |||
379 | /* Calculate illuminance */ | ||
380 | *illuminance = | ||
381 | calculateIlluminance(drv->integration_time, drv->gain, | ||
382 | broadband, ir, drv->id.partno); | ||
383 | /* Ok */ | ||
384 | return SENSOR_OK; | ||
385 | } | ||
386 | |||