Icm20948Transport.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. * ________________________________________________________________________________________________________
  3. * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved.
  4. *
  5. * This software, related documentation and any modifications thereto (collectively “Software”) is subject
  6. * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
  7. * and other intellectual property rights laws.
  8. *
  9. * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
  10. * and any use, reproduction, disclosure or distribution of the Software without an express license agreement
  11. * from InvenSense is strictly prohibited.
  12. *
  13. * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
  14. * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  15. * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
  16. * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
  17. * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
  18. * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  19. * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  20. * OF THE SOFTWARE.
  21. * ________________________________________________________________________________________________________
  22. */
  23. #include "Icm20948Transport.h"
  24. #include "Icm20948Serif.h"
  25. #include "Icm20948.h"
  26. struct inv_icm20948 * icm20948_instance;
  27. int inv_icm20948_read_reg(struct inv_icm20948 * s, uint8_t reg, uint8_t * buf, uint32_t len)
  28. {
  29. return inv_icm20948_serif_read_reg(&s->serif, reg, buf, len);
  30. }
  31. int inv_icm20948_write_reg(struct inv_icm20948 * s, uint8_t reg, const uint8_t * buf, uint32_t len)
  32. {
  33. return inv_icm20948_serif_write_reg(&s->serif, reg, buf, len);
  34. }
  35. void inv_icm20948_sleep_100us(unsigned long nHowMany100MicroSecondsToSleep) // time in 100 us
  36. {
  37. inv_icm20948_sleep_us(nHowMany100MicroSecondsToSleep * 100);
  38. }
  39. long inv_icm20948_get_tick_count(void)
  40. {
  41. return (long)inv_icm20948_get_time_us();
  42. }
  43. /* driver transport function */
  44. #include "Icm20948Defs.h"
  45. #include "Icm20948DataBaseDriver.h"
  46. #include "Icm20948DataBaseControl.h"
  47. void inv_icm20948_transport_init(struct inv_icm20948 * s)
  48. {
  49. s->lastBank = 0x7E;
  50. s->lLastBankSelected = 0xFF;
  51. }
  52. static uint8_t check_reg_access_lp_disable(struct inv_icm20948 * s, unsigned short reg)
  53. {
  54. switch(reg){
  55. case REG_LP_CONFIG: /** (BANK_0 | 0x05) */
  56. case REG_PWR_MGMT_1: /** (BANK_0 | 0x06) */
  57. case REG_PWR_MGMT_2: /** (BANK_0 | 0x07) */
  58. case REG_INT_PIN_CFG: /** (BANK_0 | 0x0F) */
  59. case REG_INT_ENABLE: /** (BANK_0 | 0x10) */
  60. case REG_FIFO_COUNT_H: /** (BANK_0 | 0x70) */
  61. case REG_FIFO_COUNT_L: /** (BANK_0 | 0x71) */
  62. case REG_FIFO_R_W: /** (BANK_0 | 0x72) */
  63. return inv_icm20948_ctrl_get_batch_mode_status(s);
  64. case REG_FIFO_CFG: /** (BANK_0 | 0x76) */
  65. case REG_MEM_BANK_SEL: /** (BANK_0 | 0x7E) */
  66. case REG_BANK_SEL: /** 0x7F */
  67. case REG_INT_STATUS: /** (BANK_0 | 0x19) */
  68. case REG_DMP_INT_STATUS: /** (BANK_0 | 0x18) */
  69. return 0;
  70. break;
  71. default:
  72. break;
  73. }
  74. return 1;
  75. }
  76. /**
  77. * @brief Set up the register bank register for accessing registers in 20630.
  78. * @param[in] register bank number
  79. * @return 0 if successful.
  80. */
  81. static int inv_set_bank(struct inv_icm20948 * s, unsigned char bank)
  82. {
  83. int result;
  84. //if bank reg was set before, just return
  85. if(bank==s->lastBank)
  86. return 0;
  87. else
  88. s->lastBank = bank;
  89. result = inv_icm20948_read_reg(s, REG_BANK_SEL, &s->reg, 1);
  90. if (result)
  91. return result;
  92. s->reg &= 0xce;
  93. s->reg |= (bank << 4);
  94. result = inv_icm20948_write_reg(s, REG_BANK_SEL, &s->reg, 1);
  95. return result;
  96. }
  97. /* the following functions are used for configuring the secondary devices */
  98. /**
  99. * @brief Write data to a register on MEMs.
  100. * @param[in] Register address
  101. * @param[in] Length of data
  102. * @param[in] Data to be written
  103. * @return 0 if successful.
  104. */
  105. int inv_icm20948_write_mems_reg(struct inv_icm20948 * s, uint16_t reg, unsigned int length, const unsigned char *data)
  106. {
  107. int result = 0;
  108. unsigned int bytesWrite = 0;
  109. unsigned char regOnly = (unsigned char)(reg & 0x7F);
  110. unsigned char power_state = inv_icm20948_get_chip_power_state(s);
  111. if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
  112. result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
  113. if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be disabled
  114. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0); //Disable LP_EN
  115. result |= inv_set_bank(s, reg >> 7);
  116. while (bytesWrite<length)
  117. {
  118. int thisLen = min(INV_MAX_SERIAL_WRITE, length-bytesWrite);
  119. result |= inv_icm20948_write_reg(s, regOnly+bytesWrite,&data[bytesWrite], thisLen);
  120. if (result)
  121. return result;
  122. bytesWrite += thisLen;
  123. }
  124. if(check_reg_access_lp_disable(s, reg)) //Enable LP_EN since we disabled it at begining of this function.
  125. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1);
  126. return result;
  127. }
  128. /**
  129. * @brief Write single byte of data to a register on MEMs.
  130. * @param[in] Register address
  131. * @param[in] Data to be written
  132. * @return 0 if successful.
  133. */
  134. int inv_icm20948_write_single_mems_reg(struct inv_icm20948 * s, uint16_t reg, const unsigned char data)
  135. {
  136. int result = 0;
  137. unsigned char regOnly = (unsigned char)(reg & 0x7F);
  138. unsigned char power_state = inv_icm20948_get_chip_power_state(s);
  139. if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
  140. result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
  141. if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be disabled
  142. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0); //Disable LP_EN
  143. result |= inv_set_bank(s, reg >> 7);
  144. result |= inv_icm20948_write_reg(s, regOnly, &data, 1);
  145. if(check_reg_access_lp_disable(s, reg)) //Enable LP_EN since we disabled it at begining of this function.
  146. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1);
  147. return result;
  148. }
  149. /**
  150. * @brief Read data from a register on MEMs.
  151. * @param[in] Register address
  152. * @param[in] Length of data
  153. * @param[in] Data to be written
  154. * @return 0 if successful.
  155. */
  156. int inv_icm20948_read_mems_reg(struct inv_icm20948 * s, uint16_t reg, unsigned int length, unsigned char *data)
  157. {
  158. int result = 0;
  159. unsigned int bytesRead = 0;
  160. unsigned char regOnly = (unsigned char)(reg & 0x7F);
  161. unsigned char i, dat[INV_MAX_SERIAL_READ];
  162. unsigned char power_state = inv_icm20948_get_chip_power_state(s);
  163. if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
  164. result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
  165. if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be disabled
  166. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0); //Disable LP_EN
  167. result |= inv_set_bank(s, reg >> 7);
  168. while (bytesRead<length)
  169. {
  170. int thisLen = min(INV_MAX_SERIAL_READ, length-bytesRead);
  171. if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
  172. result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &dat[bytesRead], thisLen);
  173. } else {
  174. result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &data[bytesRead],thisLen);
  175. }
  176. if (result)
  177. return result;
  178. bytesRead += thisLen;
  179. }
  180. if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
  181. for (i=0; i< length; i++) {
  182. *data= dat[i];
  183. data++;
  184. }
  185. }
  186. if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be enabled
  187. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); //Enable LP_EN
  188. return result;
  189. }
  190. /**
  191. * @brief Read data from a register in DMP memory
  192. * @param[in] DMP memory address
  193. * @param[in] number of byte to be read
  194. * @param[in] input data from the register
  195. * @return 0 if successful.
  196. */
  197. int inv_icm20948_read_mems(struct inv_icm20948 * s, unsigned short reg, unsigned int length, unsigned char *data)
  198. {
  199. int result=0;
  200. unsigned int bytesWritten = 0;
  201. unsigned int thisLen;
  202. unsigned char i, dat[INV_MAX_SERIAL_READ] = {0};
  203. unsigned char power_state = inv_icm20948_get_chip_power_state(s);
  204. unsigned char lBankSelected;
  205. unsigned char lStartAddrSelected;
  206. if(!data)
  207. return -1;
  208. if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
  209. result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
  210. if(check_reg_access_lp_disable(s, reg))
  211. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0);
  212. result |= inv_set_bank(s, 0);
  213. lBankSelected = (reg >> 8);
  214. if (lBankSelected != s->lLastBankSelected)
  215. {
  216. result |= inv_icm20948_write_reg(s, REG_MEM_BANK_SEL, &lBankSelected, 1);
  217. if (result)
  218. return result;
  219. s->lLastBankSelected = lBankSelected;
  220. }
  221. while (bytesWritten < length)
  222. {
  223. lStartAddrSelected = (reg & 0xff);
  224. /* Sets the starting read or write address for the selected memory, inside of the selected page (see MEM_SEL Register).
  225. Contents are changed after read or write of the selected memory.
  226. This register must be written prior to each access to initialize the register to the proper starting address.
  227. The address will auto increment during burst transactions. Two consecutive bursts without re-initializing the start address would skip one address. */
  228. result |= inv_icm20948_write_reg(s, REG_MEM_START_ADDR, &lStartAddrSelected, 1);
  229. if (result)
  230. return result;
  231. thisLen = min(INV_MAX_SERIAL_READ, length-bytesWritten);
  232. /* Write data */
  233. if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
  234. result |= inv_icm20948_read_reg(s, REG_MEM_R_W, &dat[bytesWritten], thisLen);
  235. } else {
  236. result |= inv_icm20948_read_reg(s, REG_MEM_R_W, &data[bytesWritten], thisLen);
  237. }
  238. if (result)
  239. return result;
  240. bytesWritten += thisLen;
  241. reg += thisLen;
  242. }
  243. if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
  244. for (i=0; i< length; i++) {
  245. *data= dat[i];
  246. data++;
  247. }
  248. }
  249. //Enable LP_EN if we disabled it at begining of this function.
  250. if(check_reg_access_lp_disable(s, reg))
  251. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1);
  252. return result;
  253. }
  254. /**
  255. * @brief Write data to a register in DMP memory
  256. * @param[in] DMP memory address
  257. * @param[in] number of byte to be written
  258. * @param[out] output data from the register
  259. * @return 0 if successful.
  260. */
  261. int inv_icm20948_write_mems(struct inv_icm20948 * s, unsigned short reg, unsigned int length, const unsigned char *data)
  262. {
  263. int result=0;
  264. unsigned int bytesWritten = 0;
  265. unsigned int thisLen;
  266. unsigned char lBankSelected;
  267. unsigned char lStartAddrSelected;
  268. unsigned char power_state = inv_icm20948_get_chip_power_state(s);
  269. if(!data)
  270. return -1;
  271. if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
  272. result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
  273. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0);
  274. result |= inv_set_bank(s, 0);
  275. lBankSelected = (reg >> 8);
  276. if (lBankSelected != s->lLastBankSelected)
  277. {
  278. result |= inv_icm20948_write_reg(s, REG_MEM_BANK_SEL, &lBankSelected, 1);
  279. if (result)
  280. return result;
  281. s->lLastBankSelected = lBankSelected;
  282. }
  283. while (bytesWritten < length)
  284. {
  285. lStartAddrSelected = (reg & 0xff);
  286. /* Sets the starting read or write address for the selected memory, inside of the selected page (see MEM_SEL Register).
  287. Contents are changed after read or write of the selected memory.
  288. This register must be written prior to each access to initialize the register to the proper starting address.
  289. The address will auto increment during burst transactions. Two consecutive bursts without re-initializing the start address would skip one address. */
  290. result |= inv_icm20948_write_reg(s, REG_MEM_START_ADDR, &lStartAddrSelected, 1);
  291. if (result)
  292. return result;
  293. thisLen = min(INV_MAX_SERIAL_WRITE, length-bytesWritten);
  294. /* Write data */
  295. result |= inv_icm20948_write_reg(s, REG_MEM_R_W, &data[bytesWritten], thisLen);
  296. if (result)
  297. return result;
  298. bytesWritten += thisLen;
  299. reg += thisLen;
  300. }
  301. //Enable LP_EN since we disabled it at begining of this function.
  302. result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1);
  303. return result;
  304. }
  305. /**
  306. * @brief Write single byte of data to a register on MEMs with no power control
  307. * @param[in] Register address
  308. * @param[in] Data to be written
  309. * @return 0 if successful.
  310. */
  311. int inv_icm20948_write_single_mems_reg_core(struct inv_icm20948 * s, uint16_t reg, const uint8_t data)
  312. {
  313. int result = 0;
  314. unsigned char regOnly = (unsigned char)(reg & 0x7F);
  315. result |= inv_set_bank(s, reg >> 7);
  316. result |= inv_icm20948_write_reg(s, regOnly, &data, 1);
  317. return result;
  318. }