aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/ex/devices/ST/lis302dl.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/ex/devices/ST/lis302dl.c')
-rw-r--r--lib/chibios/os/ex/devices/ST/lis302dl.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/lib/chibios/os/ex/devices/ST/lis302dl.c b/lib/chibios/os/ex/devices/ST/lis302dl.c
new file mode 100644
index 000000000..f1e03cacc
--- /dev/null
+++ b/lib/chibios/os/ex/devices/ST/lis302dl.c
@@ -0,0 +1,554 @@
1/*
2 ChibiOS - Copyright (C) 2016..2019 Rocco Marco Guglielmi
3
4 This file is part of ChibiOS.
5
6 ChibiOS is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 ChibiOS is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21/**
22 * @file lis302dl.c
23 * @brief LIS302DL MEMS interface module code.
24 *
25 * @addtogroup LIS302DL
26 * @ingroup EX_ST
27 * @{
28 */
29
30#include "hal.h"
31#include "lis302dl.h"
32
33/*===========================================================================*/
34/* Driver local definitions. */
35/*===========================================================================*/
36
37/*===========================================================================*/
38/* Driver exported variables. */
39/*===========================================================================*/
40
41/*===========================================================================*/
42/* Driver local variables and types. */
43/*===========================================================================*/
44
45/*===========================================================================*/
46/* Driver local functions. */
47/*===========================================================================*/
48
49#if (LIS302DL_USE_SPI) || defined(__DOXYGEN__)
50/**
51 * @brief Reads a generic register value using SPI.
52 * @pre The SPI interface must be initialized and the driver started.
53 *
54 * @param[in] spip pointer to the SPI interface
55 * @param[in] reg starting register address
56 * @param[in] n number of adjacent registers to write
57 * @param[in] b pointer to a buffer.
58 */
59static void lis302dlSPIReadRegister(SPIDriver *spip, uint8_t reg, size_t n,
60 uint8_t* b) {
61 uint8_t cmd;
62 (n == 1) ? (cmd = reg | LIS302DL_RW) : (cmd = reg | LIS302DL_RW | LIS302DL_MS);
63 spiSelect(spip);
64 spiSend(spip, 1, &cmd);
65 spiReceive(spip, n, b);
66 spiUnselect(spip);
67}
68
69/**
70 * @brief Writes a value into a generic register using SPI.
71 * @pre The SPI interface must be initialized and the driver started.
72 *
73 * @param[in] spip pointer to the SPI interface
74 * @param[in] reg starting register address
75 * @param[in] n number of adjacent registers to write
76 * @param[in] b pointer to a buffer of values.
77 */
78static void lis302dlSPIWriteRegister(SPIDriver *spip, uint8_t reg, size_t n,
79 uint8_t* b) {
80 uint8_t cmd;
81 (n == 1) ? (cmd = reg) : (cmd = reg | LIS302DL_MS);
82 spiSelect(spip);
83 spiSend(spip, 1, &cmd);
84 spiSend(spip, n, b);
85 spiUnselect(spip);
86}
87#endif /* LIS302DL_USE_SPI */
88
89/**
90 * @brief Return the number of axes of the BaseAccelerometer.
91 *
92 * @param[in] ip pointer to @p BaseAccelerometer interface.
93 *
94 * @return the number of axes.
95 */
96static size_t acc_get_axes_number(void *ip) {
97 (void)ip;
98
99 return LIS302DL_ACC_NUMBER_OF_AXES;
100}
101
102/**
103 * @brief Retrieves raw data from the BaseAccelerometer.
104 * @note This data is retrieved from MEMS register without any algebraical
105 * manipulation.
106 * @note The axes array must be at least the same size of the
107 * BaseAccelerometer axes number.
108 *
109 * @param[in] ip pointer to @p BaseAccelerometer interface.
110 * @param[out] axes a buffer which would be filled with raw data.
111 *
112 * @return The operation status.
113 * @retval MSG_OK if the function succeeded.
114 * @retval MSG_RESET if one or more I2C errors occurred, the errors can
115 * be retrieved using @p i2cGetErrors().
116 * @retval MSG_TIMEOUT if a timeout occurred before operation end.
117 */
118static msg_t acc_read_raw(void *ip, int32_t axes[]) {
119 LIS302DLDriver* devp;
120 uint8_t i, tmp;
121 msg_t msg = MSG_OK;
122
123 osalDbgCheck((ip != NULL) && (axes != NULL));
124
125 /* Getting parent instance pointer.*/
126 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
127
128 osalDbgAssert((devp->state == LIS302DL_READY),
129 "acc_read_raw(), invalid state");
130
131#if LIS302DL_USE_SPI
132#if LIS302DL_SHARED_SPI
133 osalDbgAssert((devp->config->spip->state == SPI_READY),
134 "acc_read_raw(), channel not ready");
135
136 spiAcquireBus(devp->config->spip);
137 spiStart(devp->config->spip,
138 devp->config->spicfg);
139#endif /* LIS302DL_SHARED_SPI */
140
141 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++) {
142 lis302dlSPIReadRegister(devp->config->spip,
143 LIS302DL_AD_OUT_X + (i * 2), 1, &tmp);
144 axes[i] = (int32_t)((int8_t)tmp);
145 }
146
147#if LIS302DL_SHARED_SPI
148 spiReleaseBus(devp->config->spip);
149#endif /* LIS302DL_SHARED_SPI */
150#endif /* LIS302DL_USE_SPI */
151 return msg;
152}
153
154/**
155 * @brief Retrieves cooked data from the BaseAccelerometer.
156 * @note This data is manipulated according to the formula
157 * cooked = (raw * sensitivity) - bias.
158 * @note Final data is expressed as milli-G.
159 * @note The axes array must be at least the same size of the
160 * BaseAccelerometer axes number.
161 *
162 * @param[in] ip pointer to @p BaseAccelerometer interface.
163 * @param[out] axes a buffer which would be filled with cooked data.
164 *
165 * @return The operation status.
166 * @retval MSG_OK if the function succeeded.
167 * @retval MSG_RESET if one or more I2C errors occurred, the errors can
168 * be retrieved using @p i2cGetErrors().
169 * @retval MSG_TIMEOUT if a timeout occurred before operation end.
170 */
171static msg_t acc_read_cooked(void *ip, float axes[]) {
172 LIS302DLDriver* devp;
173 uint32_t i;
174 int32_t raw[LIS302DL_ACC_NUMBER_OF_AXES];
175 msg_t msg;
176
177 osalDbgCheck((ip != NULL) && (axes != NULL));
178
179 /* Getting parent instance pointer.*/
180 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
181
182 osalDbgAssert((devp->state == LIS302DL_READY),
183 "acc_read_cooked(), invalid state");
184
185 msg = acc_read_raw(ip, raw);
186 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++) {
187 axes[i] = (raw[i] * devp->accsensitivity[i]) - devp->accbias[i];
188 }
189 return msg;
190}
191
192/**
193 * @brief Set bias values for the BaseAccelerometer.
194 * @note Bias must be expressed as milli-G.
195 * @note The bias buffer must be at least the same size of the
196 * BaseAccelerometer axes number.
197 *
198 * @param[in] ip pointer to @p BaseAccelerometer interface.
199 * @param[in] bp a buffer which contains biases.
200 *
201 * @return The operation status.
202 * @retval MSG_OK if the function succeeded.
203 */
204static msg_t acc_set_bias(void *ip, float *bp) {
205 LIS302DLDriver* devp;
206 uint32_t i;
207 msg_t msg = MSG_OK;
208
209 osalDbgCheck((ip != NULL) && (bp != NULL));
210
211 /* Getting parent instance pointer.*/
212 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
213
214 osalDbgAssert((devp->state == LIS302DL_READY),
215 "acc_set_bias(), invalid state");
216
217 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++) {
218 devp->accbias[i] = bp[i];
219 }
220 return msg;
221}
222
223/**
224 * @brief Reset bias values for the BaseAccelerometer.
225 * @note Default biases value are obtained from device datasheet when
226 * available otherwise they are considered zero.
227 *
228 * @param[in] ip pointer to @p BaseAccelerometer interface.
229 *
230 * @return The operation status.
231 * @retval MSG_OK if the function succeeded.
232 */
233static msg_t acc_reset_bias(void *ip) {
234 LIS302DLDriver* devp;
235 uint32_t i;
236 msg_t msg = MSG_OK;
237
238 osalDbgCheck(ip != NULL);
239
240 /* Getting parent instance pointer.*/
241 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
242
243 osalDbgAssert((devp->state == LIS302DL_READY),
244 "acc_reset_bias(), invalid state");
245
246 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
247 devp->accbias[i] = LIS302DL_ACC_BIAS;
248 return msg;
249}
250
251/**
252 * @brief Set sensitivity values for the BaseAccelerometer.
253 * @note Sensitivity must be expressed as milli-G/LSB.
254 * @note The sensitivity buffer must be at least the same size of the
255 * BaseAccelerometer axes number.
256 *
257 * @param[in] ip pointer to @p BaseAccelerometer interface.
258 * @param[in] sp a buffer which contains sensitivities.
259 *
260 * @return The operation status.
261 * @retval MSG_OK if the function succeeded.
262 */
263static msg_t acc_set_sensivity(void *ip, float *sp) {
264 LIS302DLDriver* devp;
265 uint32_t i;
266 msg_t msg = MSG_OK;
267
268 /* Getting parent instance pointer.*/
269 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
270
271 osalDbgCheck((ip != NULL) && (sp != NULL));
272
273 osalDbgAssert((devp->state == LIS302DL_READY),
274 "acc_set_sensivity(), invalid state");
275
276 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++) {
277 devp->accsensitivity[i] = sp[i];
278 }
279 return msg;
280}
281
282/**
283 * @brief Reset sensitivity values for the BaseAccelerometer.
284 * @note Default sensitivities value are obtained from device datasheet.
285 *
286 * @param[in] ip pointer to @p BaseAccelerometer interface.
287 *
288 * @return The operation status.
289 * @retval MSG_OK if the function succeeded.
290 * @retval MSG_RESET otherwise.
291 */
292static msg_t acc_reset_sensivity(void *ip) {
293 LIS302DLDriver* devp;
294 uint32_t i;
295 msg_t msg = MSG_OK;
296
297 osalDbgCheck(ip != NULL);
298
299 /* Getting parent instance pointer.*/
300 devp = objGetInstance(LIS302DLDriver*, (BaseAccelerometer*)ip);
301
302 osalDbgAssert((devp->state == LIS302DL_READY),
303 "acc_reset_sensivity(), invalid state");
304
305 if(devp->config->accfullscale == LIS302DL_ACC_FS_2G)
306 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
307 devp->accsensitivity[i] = LIS302DL_ACC_SENS_2G;
308 else if(devp->config->accfullscale == LIS302DL_ACC_FS_8G)
309 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
310 devp->accsensitivity[i] = LIS302DL_ACC_SENS_8G;
311 else {
312 osalDbgAssert(FALSE,
313 "acc_reset_sensivity(), accelerometer full scale issue");
314 return MSG_RESET;
315 }
316 return msg;
317}
318
319/**
320 * @brief Changes the LIS302DLDriver accelerometer fullscale value.
321 * @note This function also rescale sensitivities and biases based on
322 * previous and next fullscale value.
323 * @note A recalibration is highly suggested after calling this function.
324 *
325 * @param[in] devp pointer to @p LIS302DLDriver interface.
326 * @param[in] fs new fullscale value.
327 *
328 * @return The operation status.
329 * @retval MSG_OK if the function succeeded.
330 * @retval MSG_RESET otherwise.
331 */
332static msg_t acc_set_full_scale(LIS302DLDriver *devp, lis302dl_acc_fs_t fs) {
333 float newfs, scale;
334 uint8_t i, cr;
335 msg_t msg;
336
337 osalDbgCheck(devp != NULL);
338
339 osalDbgAssert((devp->state == LIS302DL_READY),
340 "acc_set_full_scale(), invalid state");
341 osalDbgAssert((devp->config->spip->state == SPI_READY),
342 "acc_set_full_scale(), channel not ready");
343
344 /* Computing new fullscale value.*/
345 if(fs == LIS302DL_ACC_FS_2G) {
346 newfs = LIS302DL_ACC_2G;
347 }
348 else if(fs == LIS302DL_ACC_FS_8G) {
349 newfs = LIS302DL_ACC_8G;
350 }
351 else {
352 msg = MSG_RESET;
353 return msg;
354 }
355
356 if(newfs != devp->accfullscale) {
357 /* Computing scale value.*/
358 scale = newfs / devp->accfullscale;
359 devp->accfullscale = newfs;
360
361#if LIS302DL_USE_SPI
362#if LIS302DL_SHARED_SPI
363 spiAcquireBus(devp->config->spip);
364 spiStart(devp->config->spip,
365 devp->config->spicfg);
366#endif /* LIS302DL_SHARED_SPI */
367
368 /* Getting data from register.*/
369 lis302dlSPIReadRegister(devp->config->spip, LIS302DL_AD_CTRL_REG1, 1, &cr);
370
371#if LIS302DL_SHARED_SPI
372 spiReleaseBus(devp->config->spip);
373#endif /* LIS302DL_SHARED_SPI */
374#endif /* LIS302DL_USE_SPI */
375
376 cr &= ~(LIS302DL_CTRL_REG1_FS_MASK);
377 cr |= fs;
378
379#if LIS302DL_USE_SPI
380#if LIS302DL_SHARED_SPI
381 spiAcquireBus(devp->config->spip);
382 spiStart(devp->config->spip,
383 devp->config->spicfg);
384#endif /* LIS302DL_SHARED_SPI */
385
386 /* Getting data from register.*/
387 lis302dlSPIWriteRegister(devp->config->spip, LIS302DL_AD_CTRL_REG1, 1, &cr);
388
389#if LIS302DL_SHARED_SPI
390 spiReleaseBus(devp->config->spip);
391#endif /* LIS302DL_SHARED_SPI */
392#endif /* LIS302DL_USE_SPI */
393
394 /* Scaling sensitivity and bias. Re-calibration is suggested anyway. */
395 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++) {
396 devp->accsensitivity[i] *= scale;
397 devp->accbias[i] *= scale;
398 }
399 }
400 return msg;
401}
402
403static const struct LIS302DLVMT vmt_device = {
404 (size_t)0,
405 acc_set_full_scale
406};
407
408static const struct BaseAccelerometerVMT vmt_accelerometer = {
409 sizeof(struct LIS302DLVMT*),
410 acc_get_axes_number, acc_read_raw, acc_read_cooked,
411 acc_set_bias, acc_reset_bias, acc_set_sensivity, acc_reset_sensivity
412};
413
414/*===========================================================================*/
415/* Driver exported functions. */
416/*===========================================================================*/
417
418/**
419 * @brief Initializes an instance.
420 *
421 * @param[out] devp pointer to the @p LIS302DLDriver object
422 *
423 * @init
424 */
425void lis302dlObjectInit(LIS302DLDriver *devp) {
426 devp->vmt = &vmt_device;
427 devp->acc_if.vmt = &vmt_accelerometer;
428
429 devp->config = NULL;
430
431 devp->accaxes = LIS302DL_ACC_NUMBER_OF_AXES;
432
433 devp->state = LIS302DL_STOP;
434}
435
436/**
437 * @brief Configures and activates LIS302DL Complex Driver peripheral.
438 *
439 * @param[in] devp pointer to the @p LIS302DLDriver object
440 * @param[in] config pointer to the @p LIS302DLConfig object
441 *
442 * @api
443 */
444void lis302dlStart(LIS302DLDriver *devp, const LIS302DLConfig *config) {
445 uint32_t i;
446 uint8_t cr[2] = {0, 0};
447 osalDbgCheck((devp != NULL) && (config != NULL));
448
449 osalDbgAssert((devp->state == LIS302DL_STOP) || (devp->state == LIS302DL_READY),
450 "lis302dlStart(), invalid state");
451
452 devp->config = config;
453
454 /* Control register 1 configuration block.*/
455 {
456 cr[0] = LIS302DL_CTRL_REG1_XEN | LIS302DL_CTRL_REG1_YEN |
457 LIS302DL_CTRL_REG1_ZEN | LIS302DL_CTRL_REG1_PD |
458 devp->config->accoutputdatarate |
459 devp->config->accfullscale;
460 }
461
462 /* Control register 2 configuration block.*/
463 {
464#if LIS302DL_USE_ADVANCED || defined(__DOXYGEN__)
465 if(devp->config->hpmode != LIS302DL_HPM_BYPASSED)
466 cr[1] = devp->config->acchighpass;
467#endif
468 }
469
470#if LIS302DL_USE_SPI
471#if LIS302DL_SHARED_SPI
472 spiAcquireBus((devp)->config->spip);
473#endif /* LIS302DL_SHARED_SPI */
474 spiStart((devp)->config->spip, (devp)->config->spicfg);
475
476 lis302dlSPIWriteRegister(devp->config->spip, LIS302DL_AD_CTRL_REG1,
477 2, cr);
478
479#if LIS302DL_SHARED_SPI
480 spiReleaseBus((devp)->config->spip);
481#endif /* LIS302DL_SHARED_SPI */
482#endif /* LIS302DL_USE_SPI */
483
484 /* Storing sensitivity information according to full scale value */
485 if(devp->config->accfullscale == LIS302DL_ACC_FS_2G) {
486 devp->accfullscale = LIS302DL_ACC_2G;
487 if(devp->config->accsensitivity == NULL)
488 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
489 devp->accsensitivity[i] = LIS302DL_ACC_SENS_2G;
490 else
491 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
492 devp->accsensitivity[i] = devp->config->accsensitivity[i];
493 }
494 else if(devp->config->accfullscale == LIS302DL_ACC_FS_8G) {
495 devp->accfullscale = LIS302DL_ACC_8G;
496 if(devp->config->accsensitivity == NULL)
497 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
498 devp->accsensitivity[i] = LIS302DL_ACC_SENS_8G;
499 else
500 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
501 devp->accsensitivity[i] = devp->config->accsensitivity[i];
502 }
503 else {
504 osalDbgAssert(FALSE, "lis302dlStart(), accelerometer full scale issue");
505 }
506
507 /* Storing bias information according to user setting */
508 if(devp->config->accbias != NULL)
509 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
510 devp->accbias[i] = devp->config->accbias[i];
511 else
512 for(i = 0; i < LIS302DL_ACC_NUMBER_OF_AXES; i++)
513 devp->accbias[i] = LIS302DL_ACC_BIAS;
514
515 /* This is the Accelerometer transient recovery time */
516 osalThreadSleepMilliseconds(10);
517
518 devp->state = LIS302DL_READY;
519}
520
521/**
522 * @brief Deactivates the LIS302DL Complex Driver peripheral.
523 *
524 * @param[in] devp pointer to the @p LIS302DLDriver object
525 *
526 * @api
527 */
528void lis302dlStop(LIS302DLDriver *devp) {
529 uint8_t cr1;
530 osalDbgCheck(devp != NULL);
531
532 osalDbgAssert((devp->state == LIS302DL_STOP) ||
533 (devp->state == LIS302DL_READY),
534 "lis302dlStop(), invalid state");
535
536 if (devp->state == LIS302DL_READY) {
537#if LIS302DL_USE_SPI
538#if LIS302DL_SHARED_SPI
539 spiAcquireBus((devp)->config->spip);
540 spiStart((devp)->config->spip,
541 (devp)->config->spicfg);
542#endif /* LIS302DL_SHARED_SPI */
543 /* Disabling all axes and enabling power down mode.*/
544 cr1 = 0;
545 lis302dlSPIWriteRegister(devp->config->spip, LIS302DL_AD_CTRL_REG1, 1, &cr1);
546 spiStop((devp)->config->spip);
547#if LIS302DL_SHARED_SPI
548 spiReleaseBus((devp)->config->spip);
549#endif /* LIS302DL_SHARED_SPI */
550#endif /* LIS302DL_USE_SPI */
551 }
552 devp->state = LIS302DL_STOP;
553}
554/** @} */