lis302dl.c 17 KB

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