icm20608.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * Copyright (c) 2006-2018, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2018-09-03 Ernest Chen the first version
  9. */
  10. #include <rtthread.h>
  11. #include <rthw.h>
  12. #include <rtdevice.h>
  13. #include <finsh.h>
  14. #include <string.h>
  15. #include "icm20608.h"
  16. #ifdef PKG_USING_ICM20608
  17. #define DBG_ENABLE
  18. #define DBG_SECTION_NAME "icm20608"
  19. #define DBG_LEVEL DBG_LOG
  20. #define DBG_COLOR
  21. #include <rtdbg.h>
  22. // Register map for icm20608
  23. #define ICM20608_CONFIG_REG 0x1A // configuration:fifo, ext sync and dlpf
  24. #define ICM20608_GYRO_CONFIG_REG 0x1B // gyroscope configuration
  25. #define ICM20608_ACCEL_CONFIG1_REG 0x1C // accelerometer configuration
  26. #define ICM20608_ACCEL_CONFIG2_REG 0x1D // accelerometer configuration
  27. #define ICM20608_INT_ENABLE_REG 0x38 // interrupt enable
  28. #define ICM20608_ACCEL_MEAS 0x3B // accelerometer measurements
  29. #define ICM20608_GYRO_MEAS 0x43 // gyroscope measurements
  30. #define ICM20608_PWR_MGMT1_REG 0x6B // power management 1
  31. #define ICM20608_PWR_MGMT2_REG 0x6C // power management 2
  32. #define ICM20608_ADDR 0x68 /* slave address, ad0 set 0 */
  33. /**
  34. * This function burst writes sequences, include single-byte.
  35. *
  36. * @param bus the pointer of iic bus
  37. * @param reg the address regisgter
  38. * @param len the length of bytes
  39. * @param buf the writing value
  40. *
  41. * @return the writing status of accelerometer, RT_EOK reprensents setting successfully.
  42. */
  43. static rt_err_t write_regs(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
  44. {
  45. struct rt_i2c_msg msgs[2];
  46. msgs[0].addr = ICM20608_ADDR;
  47. msgs[0].flags = RT_I2C_WR;
  48. msgs[0].buf = &reg;
  49. msgs[0].len = 1;
  50. msgs[1].addr = ICM20608_ADDR;
  51. msgs[1].flags = RT_I2C_WR | RT_I2C_NO_START;
  52. msgs[1].buf = buf;
  53. msgs[1].len = len;
  54. if (rt_i2c_transfer(bus, msgs, 2) == 2)
  55. {
  56. return RT_EOK;
  57. }
  58. else
  59. {
  60. LOG_E("Writing command error");
  61. return -RT_ERROR;
  62. }
  63. }
  64. static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
  65. {
  66. struct rt_i2c_msg msgs[2];
  67. msgs[0].addr = ICM20608_ADDR;
  68. msgs[0].flags = RT_I2C_WR;
  69. msgs[0].buf = &reg;
  70. msgs[0].len = 1;
  71. msgs[1].addr = ICM20608_ADDR;
  72. msgs[1].flags = RT_I2C_RD;
  73. msgs[1].buf = buf;
  74. msgs[1].len = len;
  75. if (rt_i2c_transfer(bus, msgs, 2) == 2)
  76. {
  77. return RT_EOK;
  78. }
  79. else
  80. {
  81. LOG_E("Reading command error");
  82. return -RT_ERROR;
  83. }
  84. }
  85. static rt_err_t reset_sensor(icm20608_device_t dev)
  86. {
  87. rt_uint8_t value = 0;
  88. RT_ASSERT(dev);
  89. return write_regs(dev->i2c, ICM20608_PWR_MGMT1_REG, 1, &value);
  90. }
  91. static rt_err_t icm20608_sensor_init(icm20608_device_t dev)
  92. {
  93. rt_err_t result = -RT_ERROR;
  94. RT_ASSERT(dev);
  95. result = rt_mutex_take(dev->lock, RT_WAITING_FOREVER);
  96. if (result != RT_EOK)
  97. {
  98. goto __exit;
  99. }
  100. result = reset_sensor(dev);
  101. if (result != RT_EOK)
  102. {
  103. goto __exit;
  104. }
  105. rt_thread_mdelay(50);
  106. /* open 3 accelerometers and 3 gyroscope */
  107. result = icm20608_set_param(dev, ICM20608_PWR_MGMT2, 0);
  108. if (result != RT_EOK)
  109. {
  110. goto __exit;
  111. }
  112. /* set gyroscope range, default 250 dps */
  113. result = icm20608_set_param(dev, ICM20608_GYRO_CONFIG, 0);
  114. if (result != RT_EOK)
  115. {
  116. goto __exit;
  117. }
  118. /* set accelerometer range, default 2g */
  119. result = icm20608_set_param(dev, ICM20608_ACCEL_CONFIG1, 0);
  120. if (result != RT_EOK)
  121. {
  122. goto __exit;
  123. }
  124. /* ACCEL_FCHOICE_B = 0 and A_DLPF_CFG[2:0] = 1 */
  125. result = icm20608_set_param(dev, ICM20608_ACCEL_CONFIG2, 1);
  126. if (result != RT_EOK)
  127. {
  128. goto __exit;
  129. }
  130. __exit:
  131. if (result != RT_EOK)
  132. {
  133. LOG_E("This sensor initializes failure");
  134. }
  135. rt_mutex_release(dev->lock);
  136. return result;
  137. }
  138. /**
  139. * This function calibrates original gyroscope and accelerometer, which are bias, in calibration mode
  140. *
  141. * @param dev the pointer of device driver structure
  142. * @param times averaging times in calibration mode
  143. *
  144. * @return the getting original status, RT_EOK reprensents getting gyroscope successfully.
  145. */
  146. rt_err_t icm20608_calib_level(icm20608_device_t dev, rt_size_t times)
  147. {
  148. rt_int32_t accel[3] = {0, 0, 0};
  149. rt_int32_t gyro[3] = {0, 0, 0};
  150. rt_size_t i;
  151. rt_err_t result;
  152. RT_ASSERT(dev);
  153. result = rt_mutex_take(dev->lock, RT_WAITING_FOREVER);
  154. if (result == RT_EOK)
  155. {
  156. for (i = 0; i < times; i++)
  157. {
  158. rt_int16_t x, y, z;
  159. /* read the sensor digital output */
  160. result = icm20608_get_accel(dev, &x, &y, &z);
  161. if (result == RT_EOK)
  162. {
  163. accel[0] += x;
  164. accel[1] += y;
  165. accel[2] += z;
  166. }
  167. else
  168. {
  169. break;
  170. }
  171. result = icm20608_get_gyro(dev, &x, &y, &z);
  172. if (result == RT_EOK)
  173. {
  174. gyro[0] += x;
  175. gyro[1] += y;
  176. gyro[2] += z;
  177. }
  178. else
  179. {
  180. break;
  181. }
  182. }
  183. if (result == RT_EOK)
  184. {
  185. dev->accel_offset.x = (accel[0] / (int)times);
  186. dev->accel_offset.y = (accel[1] / (int)times);
  187. dev->accel_offset.z = (accel[2] / (int)times) - 0x3fff; //default full range
  188. dev->gyro_offset.x = (int16_t)(gyro[0] / (int)times);
  189. dev->gyro_offset.y = (int16_t)(gyro[1] / (int)times);
  190. dev->gyro_offset.z = (int16_t)(gyro[2] / (int)times);
  191. }
  192. }
  193. if (result == RT_EOK)
  194. {
  195. rt_mutex_release(dev->lock);
  196. }
  197. else
  198. {
  199. LOG_E("Can't calibrate the sensor");
  200. }
  201. return result;
  202. }
  203. /**
  204. * This function initializes icm20608 registered device driver
  205. *
  206. * @param dev the name of icm20608 device
  207. *
  208. * @return the icm20608 device.
  209. */
  210. icm20608_device_t icm20608_init(const char *i2c_bus_name)
  211. {
  212. icm20608_device_t dev;
  213. RT_ASSERT(i2c_bus_name);
  214. dev = rt_calloc(1, sizeof(struct icm20608_device));
  215. if (dev == RT_NULL)
  216. {
  217. LOG_E("Can't allocate memory for icm20608 device on '%s' ", i2c_bus_name);
  218. rt_free(dev);
  219. return RT_NULL;
  220. }
  221. dev->i2c = rt_i2c_bus_device_find(i2c_bus_name);
  222. if (dev->i2c == RT_NULL)
  223. {
  224. LOG_E("Can't find icm20608 device on '%s'", i2c_bus_name);
  225. rt_free(dev);
  226. return RT_NULL;
  227. }
  228. dev->lock = rt_mutex_create("mutex_icm20608", RT_IPC_FLAG_FIFO);
  229. if (dev->lock == RT_NULL)
  230. {
  231. LOG_E("Can't create mutex for icm20608 device on '%s'", i2c_bus_name);
  232. rt_free(dev);
  233. return RT_NULL;
  234. }
  235. /* init icm20608 sensor */
  236. if (icm20608_sensor_init(dev) != RT_EOK)
  237. {
  238. LOG_E("Can't init icm20608 device on '%s'", i2c_bus_name);
  239. rt_free(dev);
  240. rt_mutex_delete(dev->lock);
  241. return RT_NULL;
  242. }
  243. return dev;
  244. }
  245. /**
  246. * This function releases memory and deletes mutex lock
  247. *
  248. * @param dev the pointer of device driver structure
  249. */
  250. void icm20608_deinit(icm20608_device_t dev)
  251. {
  252. RT_ASSERT(dev);
  253. rt_mutex_delete(dev->lock);
  254. rt_free(dev);
  255. }
  256. /**
  257. * This function gets accelerometer by icm20608 sensor measurement
  258. *
  259. * @param dev the pointer of device driver structure
  260. * @param accel_x the accelerometer of x-axis digital output
  261. * @param accel_y the accelerometer of y-axis digital output
  262. * @param accel_z the accelerometer of z-axis digital output
  263. *
  264. * @return the getting status of accelerometer, RT_EOK reprensents getting accelerometer successfully.
  265. */
  266. rt_err_t icm20608_get_accel(icm20608_device_t dev, rt_int16_t *accel_x, rt_int16_t *accel_y, rt_int16_t *accel_z)
  267. {
  268. rt_err_t result = -RT_ERROR;
  269. rt_uint8_t value[6];
  270. rt_uint8_t range = 0;
  271. RT_ASSERT(dev);
  272. result = rt_mutex_take(dev->lock, RT_WAITING_FOREVER);
  273. if (result == RT_EOK)
  274. {
  275. result = icm20608_get_param(dev, ICM20608_ACCEL_CONFIG1, &range); // default 2g
  276. if (range < 4 && result == RT_EOK)
  277. {
  278. result = read_regs(dev->i2c, ICM20608_ACCEL_MEAS, 6, &value[0]); //self test x,y,z accelerometer;
  279. if (result != RT_EOK)
  280. {
  281. LOG_E("Failed to get accelerometer value of icm20608");
  282. }
  283. else
  284. {
  285. *accel_x = (value[0] << 8) + value[1] - dev->accel_offset.x;
  286. *accel_y = (value[2] << 8) + value[3] - dev->accel_offset.y;
  287. *accel_z = (value[4] << 8) + value[5] - dev->accel_offset.z;
  288. }
  289. }
  290. else
  291. {
  292. LOG_E("Failed to get accelerometer value of icm20608");
  293. }
  294. rt_mutex_release(dev->lock);
  295. }
  296. else
  297. {
  298. LOG_E("Failed to get accelerometer value of icm20608");
  299. }
  300. return result;
  301. }
  302. /**
  303. * This function gets gyroscope by icm20608 sensor measurement
  304. *
  305. * @param dev the pointer of device driver structure
  306. * @param gyro_x the gyroscope of x-axis digital output
  307. * @param gyro_y the gyroscope of y-axis digital output
  308. * @param gyro_z the gyroscope of z-axis digital output
  309. *
  310. * @return the getting status of gyroscope, RT_EOK reprensents getting gyroscope successfully.
  311. */
  312. rt_err_t icm20608_get_gyro(icm20608_device_t dev, rt_int16_t *gyro_x, rt_int16_t *gyro_y, rt_int16_t *gyro_z)
  313. {
  314. rt_err_t result = -RT_ERROR;
  315. rt_uint8_t range = 0;
  316. rt_uint8_t value[6];
  317. RT_ASSERT(dev);
  318. result = rt_mutex_take(dev->lock, RT_WAITING_FOREVER);
  319. if (result == RT_EOK)
  320. {
  321. result = icm20608_get_param(dev, ICM20608_GYRO_CONFIG, &range); // default 250dps
  322. if (range < 4 && result == RT_EOK)
  323. {
  324. result = read_regs(dev->i2c, ICM20608_GYRO_MEAS, 6, &value[0]); //self test x,y,z gyroscope;
  325. if (result != RT_EOK)
  326. {
  327. LOG_E("Failed to get gyroscope value of icm20608");
  328. }
  329. else
  330. {
  331. *gyro_x = (value[0] << 8) + value[1] - dev->gyro_offset.x;
  332. *gyro_y = (value[2] << 8) + value[3] - dev->gyro_offset.y;
  333. *gyro_z = (value[4] << 8) + value[5] - dev->gyro_offset.z;
  334. }
  335. }
  336. else
  337. {
  338. LOG_E("Failed to get gyroscope value of icm20608");
  339. }
  340. rt_mutex_release(dev->lock);
  341. }
  342. else
  343. {
  344. LOG_E("Failed to get gyroscope value of icm20608");
  345. }
  346. return result;
  347. }
  348. /**
  349. * This function sets parameter of icm20608 sensor
  350. *
  351. * @param dev the pointer of device driver structure
  352. * @param cmd the parameter cmd of device
  353. * @param value for setting value in cmd register
  354. *
  355. * @return the setting parameter status,RT_EOK reprensents setting successfully.
  356. */
  357. rt_err_t icm20608_set_param(icm20608_device_t dev, icm20608_set_cmd_t cmd, uint8_t value)
  358. {
  359. rt_err_t result = -RT_ERROR;
  360. RT_ASSERT(dev);
  361. switch (cmd)
  362. {
  363. case ICM20608_GYRO_CONFIG:
  364. {
  365. rt_uint8_t args;
  366. if (!(value == ICM20608_GYROSCOPE_RANGE0 || value == ICM20608_GYROSCOPE_RANGE1 || value == ICM20608_GYROSCOPE_RANGE2 || value == ICM20608_GYROSCOPE_RANGE3))
  367. {
  368. LOG_E("Setting gyroscope range is wrong, please refer gyroscope range");
  369. return -RT_ERROR;
  370. }
  371. result = read_regs(dev->i2c, ICM20608_GYRO_CONFIG_REG, 1, &args);
  372. if (result == RT_EOK)
  373. {
  374. args &= 0xE7; //clear [4:3]
  375. args |= value << 3;
  376. result = write_regs(dev->i2c, ICM20608_GYRO_CONFIG_REG, 1, &args);
  377. }
  378. break;
  379. }
  380. case ICM20608_ACCEL_CONFIG1:
  381. {
  382. rt_uint8_t args;
  383. if (!(value == ICM20608_ACCELEROMETER_RANGE0 || value == ICM20608_ACCELEROMETER_RANGE1 || value == ICM20608_ACCELEROMETER_RANGE2 || value == ICM20608_ACCELEROMETER_RANGE3))
  384. {
  385. LOG_E("Setting als accelerometer range is wrong, please refer accelerometer range");
  386. return -RT_ERROR;
  387. }
  388. result = read_regs(dev->i2c, ICM20608_ACCEL_CONFIG1_REG, 1, &args);
  389. if (result == RT_EOK)
  390. {
  391. args &= 0xE7; //clear [4:3]
  392. args |= value << 3;
  393. result = write_regs(dev->i2c, ICM20608_ACCEL_CONFIG1_REG, 1, &args);
  394. }
  395. break;
  396. }
  397. case ICM20608_ACCEL_CONFIG2:
  398. {
  399. result = write_regs(dev->i2c, ICM20608_ACCEL_CONFIG2_REG, 1, &value);
  400. break;
  401. }
  402. case ICM20608_PWR_MGMT2:
  403. {
  404. result = write_regs(dev->i2c, ICM20608_PWR_MGMT2_REG, 1, &value);
  405. break;
  406. }
  407. case ICM20608_INT_ENABLE:
  408. {
  409. result = write_regs(dev->i2c, ICM20608_INT_ENABLE_REG, 1, &value);
  410. break;
  411. }
  412. default:
  413. {
  414. LOG_E("This cmd'%2x' can not be set or supported", cmd);
  415. return -RT_ERROR;
  416. }
  417. }
  418. return result;
  419. }
  420. /**
  421. * This function gets parameter of icm20608 sensor
  422. *
  423. * @param dev the pointer of device driver structure
  424. * @param cmd the parameter cmd of device
  425. * @param value to get value in cmd register
  426. *
  427. * @return the getting parameter status,RT_EOK reprensents getting successfully.
  428. */
  429. rt_err_t icm20608_get_param(icm20608_device_t dev, icm20608_set_cmd_t cmd, rt_uint8_t *value)
  430. {
  431. rt_err_t result = -RT_ERROR;
  432. RT_ASSERT(dev);
  433. switch (cmd)
  434. {
  435. case ICM20608_GYRO_CONFIG:
  436. {
  437. rt_uint8_t args;
  438. result = read_regs(dev->i2c, ICM20608_GYRO_CONFIG_REG, 1, &args);
  439. *value = (args >> 3) & 0x3;
  440. break;
  441. }
  442. case ICM20608_ACCEL_CONFIG1:
  443. {
  444. rt_uint8_t args;
  445. result = read_regs(dev->i2c, ICM20608_ACCEL_CONFIG1_REG, 1, &args);
  446. *value = (args >> 3) & 0x3;
  447. break;
  448. }
  449. case ICM20608_ACCEL_CONFIG2:
  450. {
  451. rt_uint8_t args;
  452. result = read_regs(dev->i2c, ICM20608_ACCEL_CONFIG2_REG, 1, &args);
  453. break;
  454. }
  455. case ICM20608_PWR_MGMT2:
  456. {
  457. result = read_regs(dev->i2c, ICM20608_PWR_MGMT2_REG, 1, value);
  458. break;
  459. }
  460. case ICM20608_INT_ENABLE:
  461. {
  462. result = read_regs(dev->i2c, ICM20608_INT_ENABLE_REG, 1, value);
  463. break;
  464. }
  465. default:
  466. {
  467. LOG_E("This cmd'%2x' can not be get or supported", cmd);
  468. return -RT_ERROR;
  469. }
  470. }
  471. return result;
  472. }
  473. void icm20608(int argc, char *argv[])
  474. {
  475. static icm20608_device_t dev = RT_NULL;
  476. if (argc > 1)
  477. {
  478. if (!strcmp(argv[1], "probe"))
  479. {
  480. if (argc > 2)
  481. {
  482. /* initialize the sensor when first probe */
  483. if (!dev || strcmp(dev->i2c->parent.parent.name, argv[2]))
  484. {
  485. /* deinit the old device */
  486. if (dev)
  487. {
  488. icm20608_deinit(dev);
  489. }
  490. dev = icm20608_init(argv[2]);
  491. }
  492. }
  493. else
  494. {
  495. rt_kprintf("icm20608 probe <dev_name> - probe sensor by given name\n");
  496. }
  497. }
  498. else if (!strcmp(argv[1], "read"))
  499. {
  500. if (dev)
  501. {
  502. if (argc > 2 && atoi(argv[2]) > 0)
  503. {
  504. rt_int16_t x, y, z;
  505. rt_uint32_t i;
  506. for (i = 0; i < atoi(argv[2]); i++)
  507. {
  508. /* read the sensor digital output */
  509. icm20608_get_accel(dev, &x, &y, &z);
  510. rt_kprintf("accelerometer: X%6d Y%6d Z%6d\n", x, y, z);
  511. icm20608_get_gyro(dev, &x, &y, &z);
  512. rt_kprintf("gyroscope : X%6d Y%6d Z%6d\r\033[1A", x, y, z);
  513. }
  514. rt_kprintf("\n\n");
  515. }
  516. else if (argc == 2)
  517. {
  518. rt_int16_t x, y, z;
  519. /* read the sensor digital output */
  520. icm20608_get_accel(dev, &x, &y, &z);
  521. rt_kprintf("accelerometer: X%6d Y%6d Z%6d\n", x, y, z);
  522. icm20608_get_gyro(dev, &x, &y, &z);
  523. rt_kprintf("gyroscope : X%6d Y%6d Z%6d\n", x, y, z);
  524. }
  525. else
  526. {
  527. rt_kprintf("[times] should be set bigger than 1\n");
  528. }
  529. }
  530. else
  531. {
  532. rt_kprintf("Please using 'icm20608 probe <dev_name>' first\n");
  533. }
  534. }
  535. else if (!strcmp(argv[1], "calib"))
  536. {
  537. if (dev)
  538. {
  539. /* calibration icm20608 zero level */
  540. icm20608_calib_level(dev, 10);
  541. rt_kprintf("accel_offset: X%6d Y%6d Z%6d\n", dev->accel_offset.x, dev->accel_offset.y, dev->accel_offset.z);
  542. rt_kprintf("gyro_offset : X%6d Y%6d Z%6d\n", dev->gyro_offset.x, dev->gyro_offset.y, dev->gyro_offset.z);
  543. }
  544. else
  545. {
  546. rt_kprintf("Please using 'icm20608 probe <dev_name>' first\n");
  547. }
  548. }
  549. else
  550. {
  551. rt_kprintf("Unknown command. Please enter 'icm20608' for help\n");
  552. }
  553. }
  554. else
  555. {
  556. rt_kprintf("Usage:\n");
  557. rt_kprintf("icm20608 probe <dev_name> - probe sensor by given name\n");
  558. rt_kprintf("icm20608 calib - calibrate sensor in calibration status\n");
  559. rt_kprintf("icm20608 read [times] - read sensor icm20608 data\n");
  560. }
  561. }
  562. MSH_CMD_EXPORT(icm20608, icm20608 sensor function);
  563. #endif /* PKG_USING_ICM20608 */