aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/various/devices_lib/sensors/tsl2561.c
diff options
context:
space:
mode:
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.c386
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
148static inline unsigned int
149calculateIlluminance(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
256static 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
273void
274TSL2561_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
284msg_t
285TSL2561_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
296msg_t
297TSL2561_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
307msg_t
308TSL2561_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
318msg_t
319TSL2561_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
336msg_t
337TSL2561_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
354unsigned int
355TSL2561_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
368msg_t
369TSL2561_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