Tlv493d.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /**
  2. * Tlv493d.cpp - Library for Arduino to control the TLV493D-A1B6 3D magnetic sensor.
  3. *
  4. * The 3D magnetic sensor TLV493D-A1B6 offers accurate three dimensional sensing with extremely low power consumption
  5. * in a small 6-pin package. With an opportunity to detect the magnetic field in x, y, and z-direction the sensor is
  6. * ideally suited for the measurement of 3D movements, linear movements and rotation movements.
  7. *
  8. * Have a look at the application note/reference manual for more information.
  9. *
  10. * Copyright (c) 2018 Infineon Technologies AG
  11. *
  12. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
  13. * following conditions are met:
  14. *
  15. * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
  16. * disclaimer.
  17. *
  18. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  19. * disclaimer in the documentation and/or other materials provided with the distribution.
  20. *
  21. * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote
  22. * products derived from this software without specific prior written permission.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  25. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  28. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  29. * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. #include "Tlv493d.h"
  33. #include "./util/RegMask.h"
  34. #include "./util/BusInterface2.h"
  35. #include <math.h>
  36. Tlv493d::Tlv493d(void)
  37. {
  38. mXdata = 0;
  39. mYdata = 0;
  40. mZdata = 0;
  41. mTempdata = 0;
  42. mExpectedFrameCount = 0x00;
  43. }
  44. Tlv493d::~Tlv493d(void)
  45. {
  46. end();
  47. }
  48. void Tlv493d::begin(void)
  49. {
  50. begin(Wire, TLV493D_ADDRESS1, true);
  51. }
  52. void Tlv493d::begin(TwoWire &bus)
  53. {
  54. begin(bus, TLV493D_ADDRESS1, true);
  55. }
  56. void Tlv493d::begin(TwoWire &bus, Tlv493d_Address_t slaveAddress, bool reset)
  57. {
  58. /**
  59. * Workaround for kit2go vdd enable
  60. */
  61. #ifdef TLV493D_A1B6_KIT2GO
  62. pinMode(LED2, OUTPUT);
  63. digitalWrite(LED2, HIGH);
  64. delay(50);
  65. #endif
  66. initInterface(&mInterface, &bus, slaveAddress);
  67. delay(TLV493D_STARTUPDELAY);
  68. mInterface.bus->begin();
  69. if(reset == true)
  70. {
  71. resetSensor(mInterface.adress);
  72. }
  73. // get all register data from sensor
  74. tlv493d::readOut(&mInterface);
  75. // copy factory settings to write registers
  76. setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1));
  77. setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1));
  78. setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1));
  79. // enable parity detection
  80. setRegBits(tlv493d::W_PARITY_EN, 1);
  81. // config sensor to lowpower mode
  82. // also contains parity calculation and writeout to sensor
  83. setAccessMode(TLV493D_DEFAULTMODE);
  84. }
  85. void Tlv493d::end(void)
  86. {
  87. disableInterrupt();
  88. setAccessMode(POWERDOWNMODE);
  89. }
  90. bool Tlv493d::setAccessMode(AccessMode_e mode)
  91. {
  92. bool ret = BUS_ERROR;
  93. const tlv493d::AccessMode_t *modeConfig = &(tlv493d::accModes[mode]);
  94. setRegBits(tlv493d::W_FAST, modeConfig->fast);
  95. setRegBits(tlv493d::W_LOWPOWER, modeConfig->lp);
  96. setRegBits(tlv493d::W_LP_PERIOD, modeConfig->lpPeriod);
  97. calcParity();
  98. ret = tlv493d::writeOut(&mInterface);
  99. if ( ret != BUS_ERROR )
  100. {
  101. mMode = mode;
  102. }
  103. return ret;
  104. }
  105. void Tlv493d::enableInterrupt(void)
  106. {
  107. setRegBits(tlv493d::W_INT, 1);
  108. calcParity();
  109. tlv493d::writeOut(&mInterface);
  110. }
  111. void Tlv493d::disableInterrupt(void)
  112. {
  113. setRegBits(tlv493d::W_INT, 0);
  114. calcParity();
  115. tlv493d::writeOut(&mInterface);
  116. }
  117. void Tlv493d::enableTemp(void)
  118. {
  119. setRegBits(tlv493d::W_TEMP_NEN, 0);
  120. calcParity();
  121. tlv493d::writeOut(&mInterface);
  122. }
  123. void Tlv493d::disableTemp(void)
  124. {
  125. setRegBits(tlv493d::W_TEMP_NEN, 1);
  126. calcParity();
  127. tlv493d::writeOut(&mInterface);
  128. }
  129. uint16_t Tlv493d::getMeasurementDelay(void)
  130. {
  131. return tlv493d::accModes[mMode].measurementTime;
  132. }
  133. Tlv493d_Error_t Tlv493d::updateData(void)
  134. {
  135. Tlv493d_Error_t ret = TLV493D_NO_ERROR;
  136. // in POWERDOWNMODE, sensor has to be switched on for one measurement
  137. uint8_t powerdown = 0;
  138. if(mMode == POWERDOWNMODE)
  139. {
  140. if (setAccessMode(MASTERCONTROLLEDMODE) != BUS_OK)
  141. {
  142. ret = TLV493D_BUS_ERROR;
  143. }
  144. delay(getMeasurementDelay());
  145. powerdown = 1;
  146. }
  147. if(ret == TLV493D_NO_ERROR)
  148. {
  149. #ifdef TLV493D_ACCELERATE_READOUT
  150. // just read the most important results in FASTMODE, if this behaviour is desired
  151. if(mMode == FASTMODE)
  152. {
  153. if (readOut(&mInterface, TLV493D_FAST_READOUT) != BUS_OK)
  154. {
  155. ret = TLV493D_BUS_ERROR;
  156. }
  157. }
  158. else
  159. {
  160. if (readOut(&mInterface, TLV493D_MEASUREMENT_READOUT) != BUS_OK)
  161. {
  162. ret = TLV493D_BUS_ERROR;
  163. }
  164. }
  165. #else
  166. if (readOut(&mInterface, TLV493D_MEASUREMENT_READOUT) != BUS_OK)
  167. {
  168. ret = TLV493D_BUS_ERROR;
  169. }
  170. #endif
  171. if (ret == TLV493D_NO_ERROR)
  172. {
  173. // construct results from registers
  174. mXdata = concatResults(getRegBits(tlv493d::R_BX1), getRegBits(tlv493d::R_BX2), true);
  175. mYdata = concatResults(getRegBits(tlv493d::R_BY1), getRegBits(tlv493d::R_BY2), true);
  176. mZdata = concatResults(getRegBits(tlv493d::R_BZ1), getRegBits(tlv493d::R_BZ2), true);
  177. mTempdata = concatResults(getRegBits(tlv493d::R_TEMP1), getRegBits(tlv493d::R_TEMP2), false);
  178. // switch sensor back to POWERDOWNMODE, if it was in POWERDOWNMODE before
  179. if(powerdown)
  180. {
  181. if (setAccessMode(POWERDOWNMODE) != BUS_OK)
  182. {
  183. ret = TLV493D_BUS_ERROR;
  184. }
  185. }
  186. if (ret == TLV493D_NO_ERROR)
  187. {
  188. // if the return value is 0, all results are from the same frame
  189. // otherwise some results may be outdated
  190. if(getRegBits(tlv493d::R_CHANNEL) != 0)
  191. {
  192. ret = TLV493D_FRAME_ERROR;
  193. }
  194. // Todo: removed due to a lot of frame errors
  195. // // if received frame count does not match expected one (frame count from 0 to 3)
  196. // else if( getRegBits(tlv493d::R_FRAMECOUNTER) != (mExpectedFrameCount % 4) )
  197. // {
  198. // ret = TLV493D_FRAME_ERROR;
  199. // }
  200. }
  201. }
  202. }
  203. mExpectedFrameCount = getRegBits(tlv493d::R_FRAMECOUNTER) + 1;
  204. return ret;
  205. }
  206. // SBEZEK
  207. uint8_t Tlv493d::getExpectedFrameCount(void) {
  208. return mExpectedFrameCount;
  209. }
  210. float Tlv493d::getX(void)
  211. {
  212. return static_cast<float>(mXdata) * TLV493D_B_MULT;
  213. }
  214. float Tlv493d::getY(void)
  215. {
  216. return static_cast<float>(mYdata) * TLV493D_B_MULT;
  217. }
  218. float Tlv493d::getZ(void)
  219. {
  220. return static_cast<float>(mZdata) * TLV493D_B_MULT;
  221. }
  222. float Tlv493d::getTemp(void)
  223. {
  224. return static_cast<float>(mTempdata-TLV493D_TEMP_OFFSET) * TLV493D_TEMP_MULT;
  225. }
  226. float Tlv493d::getAmount(void)
  227. {
  228. // sqrt(x^2 + y^2 + z^2)
  229. return TLV493D_B_MULT * sqrt(pow(static_cast<float>(mXdata), 2) + pow(static_cast<float>(mYdata), 2) + pow(static_cast<float>(mZdata), 2));
  230. }
  231. float Tlv493d::getAzimuth(void)
  232. {
  233. // arctan(y/x)
  234. return atan2(static_cast<float>(mYdata), static_cast<float>(mXdata));
  235. }
  236. float Tlv493d::getPolar(void)
  237. {
  238. // arctan(z/(sqrt(x^2+y^2)))
  239. return atan2(static_cast<float>(mZdata), sqrt(pow(static_cast<float>(mXdata), 2) + pow(static_cast<float>(mYdata), 2)));
  240. }
  241. /* internal function called by begin()
  242. * The sensor has a special reset sequence which allows to change its i2c address by setting SDA to high or low during a reset.
  243. * As some i2c peripherals may not cope with this, the simplest way is to use for this very few bytes bitbanging on the SCL/SDA lines.
  244. * Furthermore, as the uC may be stopped during a i2c transmission, a special recovery sequence allows to bring the bus back to
  245. * an operating state.
  246. */
  247. void Tlv493d::resetSensor(uint8_t adr) // Recovery & Reset - this can be handled by any uC as it uses bitbanging
  248. {
  249. mInterface.bus->beginTransmission(0x00);
  250. if (adr == TLV493D_ADDRESS1) {
  251. // if the sensor shall be initialized with i2c address 0x1F
  252. mInterface.bus->write(0xFF);
  253. } else {
  254. // if the sensor shall be initialized with address 0x5E
  255. mInterface.bus->write((uint8_t)0x00);
  256. }
  257. mInterface.bus->endTransmission(true);
  258. }
  259. void Tlv493d::setRegBits(uint8_t regMaskIndex, uint8_t data)
  260. {
  261. if(regMaskIndex < TLV493D_NUM_OF_REGMASKS)
  262. {
  263. tlv493d::setToRegs(&(tlv493d::regMasks[regMaskIndex]), mInterface.regWriteData, data);
  264. }
  265. }
  266. uint8_t Tlv493d::getRegBits(uint8_t regMaskIndex)
  267. {
  268. if(regMaskIndex < TLV493D_NUM_OF_REGMASKS)
  269. {
  270. const tlv493d::RegMask_t *mask = &(tlv493d::regMasks[regMaskIndex]);
  271. if(mask->rw == REGMASK_READ)
  272. {
  273. return tlv493d::getFromRegs(mask, mInterface.regReadData);
  274. }
  275. else
  276. {
  277. return tlv493d::getFromRegs(mask, mInterface.regWriteData);
  278. }
  279. }
  280. return 0;
  281. }
  282. void Tlv493d::calcParity(void)
  283. {
  284. uint8_t i;
  285. uint8_t y = 0x00;
  286. // set parity bit to 1
  287. // algorithm will calculate an even parity and replace this bit,
  288. // so parity becomes odd
  289. setRegBits(tlv493d::W_PARITY, 1);
  290. // combine array to one byte first
  291. for(i = 0; i < TLV493D_BUSIF_WRITESIZE; i++)
  292. {
  293. y ^= mInterface.regWriteData[i];
  294. }
  295. // combine all bits of this byte
  296. y = y ^ (y >> 1);
  297. y = y ^ (y >> 2);
  298. y = y ^ (y >> 4);
  299. // parity is in the LSB of y
  300. setRegBits(tlv493d::W_PARITY, y&0x01);
  301. }
  302. int16_t Tlv493d::concatResults(uint8_t upperByte, uint8_t lowerByte, bool upperFull)
  303. {
  304. int16_t value=0x0000; //16-bit signed integer for 12-bit values of sensor
  305. if(upperFull)
  306. {
  307. value=upperByte<<8;
  308. value|=(lowerByte&0x0F)<<4;
  309. }
  310. else
  311. {
  312. value=(upperByte&0x0F)<<12;
  313. value|=lowerByte<<4;
  314. }
  315. value>>=4; //shift left so that value is a signed 12 bit integer
  316. return value;
  317. }