ICM42688P.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include "ICM42688P_regs.h"
  2. #include "ICM42688P.h"
  3. #define TAG "ICM42688P"
  4. #define ICM42688P_TIMEOUT 100
  5. struct ICM42688P {
  6. const FuriHalSpiBusHandle* spi_bus;
  7. const GpioPin* irq_pin;
  8. float accel_scale;
  9. float gyro_scale;
  10. };
  11. static const struct AccelFullScale {
  12. float value;
  13. uint8_t reg_mask;
  14. } accel_fs_modes[] = {
  15. [AccelFullScale16G] = {16.f, ICM42688_AFS_16G},
  16. [AccelFullScale8G] = {8.f, ICM42688_AFS_8G},
  17. [AccelFullScale4G] = {4.f, ICM42688_AFS_4G},
  18. [AccelFullScale2G] = {2.f, ICM42688_AFS_2G},
  19. };
  20. static const struct GyroFullScale {
  21. float value;
  22. uint8_t reg_mask;
  23. } gyro_fs_modes[] = {
  24. [GyroFullScale2000DPS] = {2000.f, ICM42688_GFS_2000DPS},
  25. [GyroFullScale1000DPS] = {1000.f, ICM42688_GFS_1000DPS},
  26. [GyroFullScale500DPS] = {500.f, ICM42688_GFS_500DPS},
  27. [GyroFullScale250DPS] = {250.f, ICM42688_GFS_250DPS},
  28. [GyroFullScale125DPS] = {125.f, ICM42688_GFS_125DPS},
  29. [GyroFullScale62_5DPS] = {62.5f, ICM42688_GFS_62_5DPS},
  30. [GyroFullScale31_25DPS] = {31.25f, ICM42688_GFS_31_25DPS},
  31. [GyroFullScale15_625DPS] = {15.625f, ICM42688_GFS_15_625DPS},
  32. };
  33. static bool icm42688p_write_reg(const FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t value) {
  34. bool res = false;
  35. furi_hal_spi_acquire(spi_bus);
  36. do {
  37. uint8_t cmd_data[2] = {addr & 0x7F, value};
  38. if(!furi_hal_spi_bus_tx(spi_bus, cmd_data, 2, ICM42688P_TIMEOUT)) break;
  39. res = true;
  40. } while(0);
  41. furi_hal_spi_release(spi_bus);
  42. return res;
  43. }
  44. static bool icm42688p_read_reg(const FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* value) {
  45. bool res = false;
  46. furi_hal_spi_acquire(spi_bus);
  47. do {
  48. uint8_t cmd_byte = addr | (1 << 7);
  49. if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break;
  50. if(!furi_hal_spi_bus_rx(spi_bus, value, 1, ICM42688P_TIMEOUT)) break;
  51. res = true;
  52. } while(0);
  53. furi_hal_spi_release(spi_bus);
  54. return res;
  55. }
  56. static bool
  57. icm42688p_read_mem(const FuriHalSpiBusHandle* spi_bus, uint8_t addr, uint8_t* data, uint8_t len) {
  58. bool res = false;
  59. furi_hal_spi_acquire(spi_bus);
  60. do {
  61. uint8_t cmd_byte = addr | (1 << 7);
  62. if(!furi_hal_spi_bus_tx(spi_bus, &cmd_byte, 1, ICM42688P_TIMEOUT)) break;
  63. if(!furi_hal_spi_bus_rx(spi_bus, data, len, ICM42688P_TIMEOUT)) break;
  64. res = true;
  65. } while(0);
  66. furi_hal_spi_release(spi_bus);
  67. return res;
  68. }
  69. bool icm42688p_accel_config(
  70. ICM42688P* icm42688p,
  71. ICM42688PAccelFullScale full_scale,
  72. ICM42688PDataRate rate) {
  73. icm42688p->accel_scale = accel_fs_modes[full_scale].value;
  74. uint8_t reg_value = accel_fs_modes[full_scale].reg_mask | rate;
  75. return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_ACCEL_CONFIG0, reg_value);
  76. }
  77. float icm42688p_accel_get_full_scale(ICM42688P* icm42688p) {
  78. return icm42688p->accel_scale;
  79. }
  80. bool icm42688p_gyro_config(
  81. ICM42688P* icm42688p,
  82. ICM42688PGyroFullScale full_scale,
  83. ICM42688PDataRate rate) {
  84. icm42688p->gyro_scale = gyro_fs_modes[full_scale].value;
  85. uint8_t reg_value = gyro_fs_modes[full_scale].reg_mask | rate;
  86. return icm42688p_write_reg(icm42688p->spi_bus, ICM42688_GYRO_CONFIG0, reg_value);
  87. }
  88. float icm42688p_gyro_get_full_scale(ICM42688P* icm42688p) {
  89. return icm42688p->gyro_scale;
  90. }
  91. bool icm42688p_read_accel_raw(ICM42688P* icm42688p, ICM42688PRawData* data) {
  92. bool ret = icm42688p_read_mem(
  93. icm42688p->spi_bus, ICM42688_ACCEL_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData));
  94. return ret;
  95. }
  96. bool icm42688p_read_gyro_raw(ICM42688P* icm42688p, ICM42688PRawData* data) {
  97. bool ret = icm42688p_read_mem(
  98. icm42688p->spi_bus, ICM42688_GYRO_DATA_X1, (uint8_t*)data, sizeof(ICM42688PRawData));
  99. return ret;
  100. }
  101. bool icm42688p_write_gyro_offset(ICM42688P* icm42688p, ICM42688PScaledData* scaled_data) {
  102. if((fabsf(scaled_data->x) > 64.f) || (fabsf(scaled_data->y) > 64.f) ||
  103. (fabsf(scaled_data->z) > 64.f)) {
  104. return false;
  105. }
  106. uint16_t offset_x = (uint16_t)(-(int16_t)(scaled_data->x * 32.f) * 16) >> 4;
  107. uint16_t offset_y = (uint16_t)(-(int16_t)(scaled_data->y * 32.f) * 16) >> 4;
  108. uint16_t offset_z = (uint16_t)(-(int16_t)(scaled_data->z * 32.f) * 16) >> 4;
  109. uint8_t offset_regs[9];
  110. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4);
  111. icm42688p_read_mem(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs, 5);
  112. offset_regs[0] = offset_x & 0xFF;
  113. offset_regs[1] = (offset_x & 0xF00) >> 8;
  114. offset_regs[1] |= (offset_y & 0xF00) >> 4;
  115. offset_regs[2] = offset_y & 0xFF;
  116. offset_regs[3] = offset_z & 0xFF;
  117. offset_regs[4] &= 0xF0;
  118. offset_regs[4] |= (offset_z & 0x0F00) >> 8;
  119. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER0, offset_regs[0]);
  120. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER1, offset_regs[1]);
  121. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER2, offset_regs[2]);
  122. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER3, offset_regs[3]);
  123. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_OFFSET_USER4, offset_regs[4]);
  124. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0);
  125. return true;
  126. }
  127. void icm42688p_apply_scale(ICM42688PRawData* raw_data, float full_scale, ICM42688PScaledData* data) {
  128. data->x = ((float)(raw_data->x)) / 32768.f * full_scale;
  129. data->y = ((float)(raw_data->y)) / 32768.f * full_scale;
  130. data->z = ((float)(raw_data->z)) / 32768.f * full_scale;
  131. }
  132. void icm42688p_apply_scale_fifo(
  133. ICM42688P* icm42688p,
  134. ICM42688PFifoPacket* fifo_data,
  135. ICM42688PScaledData* accel_data,
  136. ICM42688PScaledData* gyro_data) {
  137. float full_scale = icm42688p->accel_scale;
  138. accel_data->x = ((float)(fifo_data->a_x)) / 32768.f * full_scale;
  139. accel_data->y = ((float)(fifo_data->a_y)) / 32768.f * full_scale;
  140. accel_data->z = ((float)(fifo_data->a_z)) / 32768.f * full_scale;
  141. full_scale = icm42688p->gyro_scale;
  142. gyro_data->x = ((float)(fifo_data->g_x)) / 32768.f * full_scale;
  143. gyro_data->y = ((float)(fifo_data->g_y)) / 32768.f * full_scale;
  144. gyro_data->z = ((float)(fifo_data->g_z)) / 32768.f * full_scale;
  145. }
  146. float icm42688p_read_temp(ICM42688P* icm42688p) {
  147. uint8_t reg_val[2];
  148. icm42688p_read_mem(icm42688p->spi_bus, ICM42688_TEMP_DATA1, reg_val, 2);
  149. int16_t temp_int = (reg_val[0] << 8) | reg_val[1];
  150. return ((float)temp_int / 132.48f) + 25.f;
  151. }
  152. void icm42688_fifo_enable(
  153. ICM42688P* icm42688p,
  154. ICM42688PIrqCallback irq_callback,
  155. void* irq_context) {
  156. // FIFO mode: stream
  157. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, (1 << 6));
  158. // Little-endian data, FIFO count in records
  159. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, (1 << 7) | (1 << 6));
  160. // FIFO partial read, FIFO packet: gyro + accel TODO: 20bit
  161. icm42688p_write_reg(
  162. icm42688p->spi_bus, ICM42688_FIFO_CONFIG1, (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0));
  163. // FIFO irq watermark
  164. uint16_t fifo_watermark = 1;
  165. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG2, fifo_watermark & 0xFF);
  166. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG3, fifo_watermark >> 8);
  167. // IRQ1: push-pull, active high
  168. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG, (1 << 1) | (1 << 0));
  169. // Clear IRQ on status read
  170. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG0, 0);
  171. // IRQ pulse duration
  172. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_CONFIG1, (1 << 6) | (1 << 5));
  173. uint8_t reg_data = 0;
  174. icm42688p_read_reg(icm42688p->spi_bus, ICM42688_INT_STATUS, &reg_data);
  175. furi_hal_gpio_init(icm42688p->irq_pin, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh);
  176. furi_hal_gpio_remove_int_callback(icm42688p->irq_pin);
  177. furi_hal_gpio_add_int_callback(icm42688p->irq_pin, irq_callback, irq_context);
  178. // IRQ1 source: FIFO threshold
  179. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, (1 << 2));
  180. }
  181. void icm42688_fifo_disable(ICM42688P* icm42688p) {
  182. furi_hal_gpio_remove_int_callback(icm42688p->irq_pin);
  183. furi_hal_gpio_init(icm42688p->irq_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  184. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0);
  185. // FIFO mode: bypass
  186. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_FIFO_CONFIG, 0);
  187. }
  188. uint16_t icm42688_fifo_get_count(ICM42688P* icm42688p) {
  189. uint16_t reg_val = 0;
  190. icm42688p_read_mem(icm42688p->spi_bus, ICM42688_FIFO_COUNTH, (uint8_t*)&reg_val, 2);
  191. return reg_val;
  192. }
  193. bool icm42688_fifo_read(ICM42688P* icm42688p, ICM42688PFifoPacket* data) {
  194. icm42688p_read_mem(
  195. icm42688p->spi_bus, ICM42688_FIFO_DATA, (uint8_t*)data, sizeof(ICM42688PFifoPacket));
  196. return (data->header) & (1 << 7);
  197. }
  198. ICM42688P* icm42688p_alloc(const FuriHalSpiBusHandle* spi_bus, const GpioPin* irq_pin) {
  199. ICM42688P* icm42688p = malloc(sizeof(ICM42688P));
  200. icm42688p->spi_bus = spi_bus;
  201. icm42688p->irq_pin = irq_pin;
  202. return icm42688p;
  203. }
  204. void icm42688p_free(ICM42688P* icm42688p) {
  205. free(icm42688p);
  206. }
  207. bool icm42688p_init(ICM42688P* icm42688p) {
  208. furi_hal_spi_bus_handle_init(icm42688p->spi_bus);
  209. // Software reset
  210. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0
  211. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset
  212. furi_delay_ms(1);
  213. uint8_t reg_value = 0;
  214. bool read_ok = icm42688p_read_reg(icm42688p->spi_bus, ICM42688_WHO_AM_I, &reg_value);
  215. if(!read_ok) {
  216. FURI_LOG_E(TAG, "Chip ID read failed");
  217. return false;
  218. } else if(reg_value != ICM42688_WHOAMI) {
  219. FURI_LOG_E(
  220. TAG, "Sensor returned wrong ID 0x%02X, expected 0x%02X", reg_value, ICM42688_WHOAMI);
  221. return false;
  222. }
  223. // Disable all interrupts
  224. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE0, 0);
  225. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE1, 0);
  226. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE3, 0);
  227. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE4, 0);
  228. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 4);
  229. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE6, 0);
  230. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INT_SOURCE7, 0);
  231. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0);
  232. // Data format: little endian
  233. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_INTF_CONFIG0, 0);
  234. // Enable all sensors
  235. icm42688p_write_reg(
  236. icm42688p->spi_bus,
  237. ICM42688_PWR_MGMT0,
  238. ICM42688_PWR_TEMP_ON | ICM42688_PWR_GYRO_MODE_LN | ICM42688_PWR_ACCEL_MODE_LN);
  239. furi_delay_ms(45);
  240. icm42688p_accel_config(icm42688p, AccelFullScale16G, DataRate1kHz);
  241. icm42688p_gyro_config(icm42688p, GyroFullScale2000DPS, DataRate1kHz);
  242. return true;
  243. }
  244. bool icm42688p_deinit(ICM42688P* icm42688p) {
  245. // Software reset
  246. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_REG_BANK_SEL, 0); // Set reg bank to 0
  247. icm42688p_write_reg(icm42688p->spi_bus, ICM42688_DEVICE_CONFIG, 0x01); // SPI Mode 0, SW reset
  248. furi_hal_spi_bus_handle_deinit(icm42688p->spi_bus);
  249. return true;
  250. }