AP_Compass_RM3100.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * This file is free software: you can redistribute it and/or modify it
  3. * under the terms of the GNU General Public License as published by the
  4. * Free Software Foundation, either version 3 of the License, or
  5. * (at your option) any later version.
  6. *
  7. * This file is distributed in the hope that it will be useful, but
  8. * WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. * See the GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License along
  13. * with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. /*
  16. Driver by Thomas Schumacher, Jan 2019
  17. Structure based on LIS3MDL driver
  18. */
  19. #include "AP_Compass_RM3100.h"
  20. #include <AP_HAL/AP_HAL.h>
  21. #include <utility>
  22. #include <AP_Math/AP_Math.h>
  23. #include <stdio.h>
  24. #define RM3100_POLL_REG 0x00
  25. #define RM3100_CMM_REG 0x01
  26. #define RM3100_CCX1_REG 0x04
  27. #define RM3100_CCX0_REG 0x05
  28. #define RM3100_CCY1_REG 0x06
  29. #define RM3100_CCY0_REG 0x07
  30. #define RM3100_CCZ1_REG 0x08
  31. #define RM3100_CCZ0_REG 0x09
  32. #define RM3100_TMRC_REG 0x0B
  33. #define RM3100_MX2_REG 0x24
  34. #define RM3100_MX1_REG 0x25
  35. #define RM3100_MX0_REG 0x26
  36. #define RM3100_MY2_REG 0x27
  37. #define RM3100_MY1_REG 0x28
  38. #define RM3100_MY0_REG 0x29
  39. #define RM3100_MZ2_REG 0x2A
  40. #define RM3100_MZ1_REG 0x2B
  41. #define RM3100_MZ0_REG 0x2C
  42. #define RM3100_BIST_REG 0x33
  43. #define RM3100_STATUS_REG 0x34
  44. #define RM3100_HSHAKE_REG 0x34
  45. #define RM3100_REVID_REG 0x36
  46. #define CCP0 0xC8 // Cycle Count values
  47. #define CCP1 0x00
  48. #define CCP0_DEFAULT 0xC8 // Default Cycle Count values (used as a whoami check)
  49. #define CCP1_DEFAULT 0x00
  50. #define GAIN_CC50 20.0f // LSB/uT
  51. #define GAIN_CC100 38.0f
  52. #define GAIN_CC200 75.0f
  53. #define UTESLA_TO_MGAUSS 10.0f // uT to mGauss conversion
  54. #define TMRC 0x94 // Update rate 150Hz
  55. #define CMM 0x71 // read 3 axes and set data ready if 3 axes are ready
  56. extern const AP_HAL::HAL &hal;
  57. AP_Compass_Backend *AP_Compass_RM3100::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev,
  58. bool force_external,
  59. enum Rotation rotation)
  60. {
  61. if (!dev) {
  62. return nullptr;
  63. }
  64. AP_Compass_RM3100 *sensor = new AP_Compass_RM3100(std::move(dev), force_external, rotation);
  65. if (!sensor || !sensor->init()) {
  66. delete sensor;
  67. return nullptr;
  68. }
  69. return sensor;
  70. }
  71. AP_Compass_RM3100::AP_Compass_RM3100(AP_HAL::OwnPtr<AP_HAL::Device> _dev,
  72. bool _force_external,
  73. enum Rotation _rotation)
  74. : dev(std::move(_dev))
  75. , force_external(_force_external)
  76. , rotation(_rotation)
  77. {
  78. }
  79. bool AP_Compass_RM3100::init()
  80. {
  81. if (!dev->get_semaphore()->take(HAL_SEMAPHORE_BLOCK_FOREVER)) {
  82. return false;
  83. }
  84. if (dev->bus_type() == AP_HAL::Device::BUS_TYPE_SPI) {
  85. // read has high bit set for SPI
  86. dev->set_read_flag(0x80);
  87. }
  88. // high retries for init
  89. dev->set_retries(10);
  90. // use default cycle count values as a whoami test
  91. uint8_t ccx0;
  92. uint8_t ccx1;
  93. uint8_t ccy0;
  94. uint8_t ccy1;
  95. uint8_t ccz0;
  96. uint8_t ccz1;
  97. if (!dev->read_registers(RM3100_CCX1_REG, &ccx1, 1) ||
  98. !dev->read_registers(RM3100_CCX0_REG, &ccx0, 1) ||
  99. !dev->read_registers(RM3100_CCY1_REG, &ccy1, 1) ||
  100. !dev->read_registers(RM3100_CCY0_REG, &ccy0, 1) ||
  101. !dev->read_registers(RM3100_CCZ1_REG, &ccz1, 1) ||
  102. !dev->read_registers(RM3100_CCZ0_REG, &ccz0, 1) ||
  103. ccx1 != CCP1_DEFAULT || ccx0 != CCP0_DEFAULT ||
  104. ccy1 != CCP1_DEFAULT || ccy0 != CCP0_DEFAULT ||
  105. ccz1 != CCP1_DEFAULT || ccz0 != CCP0_DEFAULT) {
  106. // couldn't read one of the cycle count registers or didn't recognize the default cycle count values
  107. goto fail;
  108. }
  109. dev->setup_checked_registers(8);
  110. dev->write_register(RM3100_TMRC_REG, TMRC, true); // cycle count z
  111. dev->write_register(RM3100_CMM_REG, CMM, false); // CMM configuration
  112. dev->write_register(RM3100_CCX1_REG, CCP1, true); // cycle count x
  113. dev->write_register(RM3100_CCX0_REG, CCP0, true); // cycle count x
  114. dev->write_register(RM3100_CCY1_REG, CCP1, true); // cycle count y
  115. dev->write_register(RM3100_CCY0_REG, CCP0, true); // cycle count y
  116. dev->write_register(RM3100_CCZ1_REG, CCP1, true); // cycle count z
  117. dev->write_register(RM3100_CCZ0_REG, CCP0, true); // cycle count z
  118. _scaler = (1 / GAIN_CC200) * UTESLA_TO_MGAUSS; // has to be changed if using a different cycle count
  119. // lower retries for run
  120. dev->set_retries(3);
  121. dev->get_semaphore()->give();
  122. /* register the compass instance in the frontend */
  123. compass_instance = register_compass();
  124. printf("Found a RM3100 at address 0x%x as compass %u\n", dev->get_bus_address(), compass_instance);
  125. set_rotation(compass_instance, rotation);
  126. if (force_external) {
  127. set_external(compass_instance, true);
  128. }
  129. dev->set_device_type(DEVTYPE_RM3100);
  130. set_dev_id(compass_instance, dev->get_bus_id());
  131. // call timer() at 80Hz
  132. dev->register_periodic_callback(1000000U/80U,
  133. FUNCTOR_BIND_MEMBER(&AP_Compass_RM3100::timer, void));
  134. return true;
  135. fail:
  136. dev->get_semaphore()->give();
  137. return false;
  138. }
  139. void AP_Compass_RM3100::timer()
  140. {
  141. struct PACKED {
  142. uint8_t magx_2;
  143. uint8_t magx_1;
  144. uint8_t magx_0;
  145. uint8_t magy_2;
  146. uint8_t magy_1;
  147. uint8_t magy_0;
  148. uint8_t magz_2;
  149. uint8_t magz_1;
  150. uint8_t magz_0;
  151. } data;
  152. Vector3f field;
  153. int32_t magx = 0;
  154. int32_t magy = 0;
  155. int32_t magz = 0;
  156. // check data ready on 3 axis
  157. uint8_t status;
  158. if (!dev->read_registers(RM3100_STATUS_REG, (uint8_t *)&status, 1)) {
  159. goto check_registers;
  160. }
  161. if (!(status & 0x80)) {
  162. // data not available yet
  163. goto check_registers;
  164. }
  165. if (!dev->read_registers(RM3100_MX2_REG, (uint8_t *)&data, sizeof(data))) {
  166. goto check_registers;
  167. }
  168. // the 24 bits of data for each axis are in 2s complement representation
  169. // each byte is shifted to its position in a 24-bit unsigned integer and from 8 more bits to be left-aligned in a 32-bit integer
  170. magx = ((uint32_t)data.magx_2 << 24) | ((uint32_t)data.magx_1 << 16) | ((uint32_t)data.magx_0 << 8);
  171. magy = ((uint32_t)data.magy_2 << 24) | ((uint32_t)data.magy_1 << 16) | ((uint32_t)data.magy_0 << 8);
  172. magz = ((uint32_t)data.magz_2 << 24) | ((uint32_t)data.magz_1 << 16) | ((uint32_t)data.magz_0 << 8);
  173. // right-shift signed integer back to get correct measurement value
  174. magx >>= 8;
  175. magy >>= 8;
  176. magz >>= 8;
  177. // apply scaler and store in field vector
  178. field(magx * _scaler, magy * _scaler, magz * _scaler);
  179. accumulate_sample(field, compass_instance);
  180. check_registers:
  181. dev->check_next_register();
  182. }
  183. void AP_Compass_RM3100::read()
  184. {
  185. drain_accumulated_samples(compass_instance);
  186. }