INA3221.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /*
  2. Arduino library for INA3221 current and voltage sensor.
  3. MIT License
  4. Copyright (c) 2020 Beast Devices, Andrejs Bondarevs
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in all
  12. copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. SOFTWARE.
  20. */
  21. #include "INA3221.h"
  22. // Configuration register
  23. typedef struct {
  24. uint16_t mode_shunt_en:1;
  25. uint16_t mode_bus_en:1;
  26. uint16_t mode_continious_en:1;
  27. uint16_t shunt_conv_time:3;
  28. uint16_t bus_conv_time:3;
  29. uint16_t avg_mode:3;
  30. uint16_t ch3_en:1;
  31. uint16_t ch2_en:1;
  32. uint16_t ch1_en:1;
  33. uint16_t reset:1;
  34. } conf_reg_t;
  35. // Mask/Enable register
  36. typedef struct {
  37. uint16_t conv_ready:1;
  38. uint16_t timing_ctrl_alert:1;
  39. uint16_t pwr_valid_alert:1;
  40. uint16_t warn_alert_ch3:1;
  41. uint16_t warn_alert_ch2:1;
  42. uint16_t warn_alert_ch1:1;
  43. uint16_t shunt_sum_alert:1;
  44. uint16_t crit_alert_ch3:1;
  45. uint16_t crit_alert_ch2:1;
  46. uint16_t crit_alert_ch1:1;
  47. uint16_t crit_alert_latch_en:1;
  48. uint16_t warn_alert_latch_en:1;
  49. uint16_t shunt_sum_en_ch3:1;
  50. uint16_t shunt_sum_en_ch2:1;
  51. uint16_t shunt_sum_en_ch1:1;
  52. uint16_t reserved:1;
  53. } masken_reg_t;
  54. // I2C buffers
  55. static uint8_t rx_data[4];
  56. static uint8_t tx_data[4];
  57. // Value of Mask/Enable register.
  58. static masken_reg_t _masken_reg;
  59. // Reads 16 bytes from a register.
  60. static void _read(ina3221_t * ina, const ina3221_reg_t reg, uint16_t *val);
  61. // Writes 16 bytes to a register.
  62. static void _write(ina3221_t * ina, const ina3221_reg_t reg, uint16_t *val);
  63. static void _read(ina3221_t * ina, ina3221_reg_t reg, uint16_t *val) {
  64. msg_t status = MSG_OK;
  65. sysinterval_t tmo = TIME_MS2I(4);
  66. /* read in burst mode */
  67. tx_data[0] = (uint8_t)reg;
  68. i2cAcquireBus(ina->i2cd);
  69. status = i2cMasterTransmitTimeout(ina->i2cd, ina->i2c_addr, tx_data, 1, rx_data, 2, tmo);
  70. i2cReleaseBus(ina->i2cd);
  71. if (status != MSG_OK) {
  72. *val = 0xFFFF;
  73. }
  74. // data in rx_data[]
  75. *val = rx_data[0] << 8;
  76. *val |= rx_data[1];
  77. }
  78. static void _write(ina3221_t * ina, ina3221_reg_t reg, uint16_t *val) {
  79. msg_t status = MSG_OK;
  80. sysinterval_t tmo = TIME_MS2I(4);
  81. tx_data[0] = (uint8_t)reg;
  82. tx_data[1] = (uint8_t)(*val >> 8);
  83. tx_data[2] = (uint8_t)(*val & 0xff);
  84. /* sending */
  85. i2cAcquireBus(ina->i2cd);
  86. status = i2cMasterTransmitTimeout(ina->i2cd, ina->i2c_addr, tx_data, 3, NULL, 0, tmo);
  87. i2cReleaseBus(ina->i2cd);
  88. if (status != MSG_OK) {
  89. *val = 0xFFFE;
  90. }
  91. }
  92. uint16_t INA3221_getReg(ina3221_t * ina, ina3221_reg_t reg) {
  93. uint16_t val = 0;
  94. _read(ina, reg, &val);
  95. return val;
  96. }
  97. void INA3221_reset(ina3221_t * ina) {
  98. conf_reg_t conf_reg;
  99. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  100. conf_reg.reset = 1;
  101. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  102. }
  103. void INA3221_setModePowerDown(ina3221_t * ina) {
  104. conf_reg_t conf_reg;
  105. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  106. conf_reg.mode_bus_en = 0;
  107. conf_reg.mode_continious_en =0 ;
  108. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  109. }
  110. void INA3221_setModeContinious(ina3221_t * ina) {
  111. conf_reg_t conf_reg;
  112. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  113. conf_reg.mode_continious_en =1;
  114. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  115. }
  116. void INA3221_setModeTriggered(ina3221_t * ina) {
  117. conf_reg_t conf_reg;
  118. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  119. conf_reg.mode_continious_en = 0;
  120. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  121. }
  122. void INA3221_setShuntMeasEnable(ina3221_t * ina) {
  123. conf_reg_t conf_reg;
  124. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  125. conf_reg.mode_shunt_en = 1;
  126. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  127. }
  128. void INA3221_setShuntMeasDisable(ina3221_t * ina) {
  129. conf_reg_t conf_reg;
  130. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  131. conf_reg.mode_shunt_en = 0;
  132. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  133. }
  134. void INA3221_setBusMeasEnable(ina3221_t * ina) {
  135. conf_reg_t conf_reg;
  136. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  137. conf_reg.mode_bus_en = 1;
  138. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  139. }
  140. void INA3221_setBusMeasDisable(ina3221_t * ina) {
  141. conf_reg_t conf_reg;
  142. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  143. conf_reg.mode_bus_en = 0;
  144. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  145. }
  146. void INA3221_setAveragingMode(ina3221_t * ina, ina3221_avg_mode_t mode) {
  147. conf_reg_t conf_reg;
  148. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  149. conf_reg.avg_mode = mode;
  150. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  151. }
  152. void INA3221_setBusConversionTime(ina3221_t * ina, ina3221_conv_time_t convTime) {
  153. conf_reg_t conf_reg;
  154. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  155. conf_reg.bus_conv_time = convTime;
  156. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  157. }
  158. void INA3221_setShuntConversionTime(ina3221_t * ina, ina3221_conv_time_t convTime) {
  159. conf_reg_t conf_reg;
  160. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  161. conf_reg.shunt_conv_time = convTime;
  162. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  163. }
  164. void INA3221_setPwrValidUpLimit(ina3221_t * ina, int16_t voltagemV) {
  165. _write(ina, INA3221_REG_PWR_VALID_HI_LIM, (uint16_t*)&voltagemV);
  166. }
  167. void INA3221_setPwrValidLowLimit(ina3221_t * ina, int16_t voltagemV) {
  168. _write(ina, INA3221_REG_PWR_VALID_LO_LIM, (uint16_t*)&voltagemV);
  169. }
  170. void INA3221_setShuntSumAlertLimit(ina3221_t * ina, int32_t voltageuV) {
  171. int16_t val = voltageuV / 20;
  172. _write(ina, INA3221_REG_SHUNTV_SUM_LIM, (uint16_t*)&val);
  173. }
  174. void INA3221_setCurrentSumAlertLimit(ina3221_t * ina, int32_t currentmA) {
  175. int16_t shuntuV = currentmA * (int32_t)ina->shuntRes[INA3221_CH1];
  176. INA3221_setShuntSumAlertLimit(ina, shuntuV);
  177. }
  178. void INA3221_setWarnAlertLatchEnable(ina3221_t * ina) {
  179. masken_reg_t masken_reg;
  180. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  181. masken_reg.warn_alert_latch_en = 1;
  182. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  183. _masken_reg = masken_reg;
  184. }
  185. void INA3221_setWarnAlertLatchDisable(ina3221_t * ina) {
  186. masken_reg_t masken_reg;
  187. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  188. masken_reg.warn_alert_latch_en = 1;
  189. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  190. _masken_reg = masken_reg;
  191. }
  192. void INA3221_setCritAlertLatchEnable(ina3221_t * ina) {
  193. masken_reg_t masken_reg;
  194. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  195. masken_reg.crit_alert_latch_en = 1;
  196. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  197. _masken_reg = masken_reg;
  198. }
  199. void INA3221_setCritAlertLatchDisable(ina3221_t * ina) {
  200. masken_reg_t masken_reg;
  201. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  202. masken_reg.crit_alert_latch_en = 1;
  203. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  204. _masken_reg = masken_reg;
  205. }
  206. void INA3221_readFlags(ina3221_t * ina) {
  207. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&_masken_reg);
  208. }
  209. bool INA3221_getTimingCtrlAlertFlag(void) {
  210. return _masken_reg.timing_ctrl_alert;
  211. }
  212. bool INA3221_getPwrValidAlertFlag(void) {
  213. return _masken_reg.pwr_valid_alert;
  214. }
  215. bool INA3221_getCurrentSumAlertFlag(void) {
  216. return _masken_reg.shunt_sum_alert;
  217. }
  218. bool INA3221_getConversionReadyFlag(void) {
  219. return _masken_reg.conv_ready;
  220. }
  221. uint16_t INA3221_getManufID(ina3221_t * ina) {
  222. uint16_t id = 0;
  223. _read(ina, INA3221_REG_MANUF_ID, &id);
  224. return id;
  225. }
  226. uint16_t INA3221_getDieID(ina3221_t * ina) {
  227. uint16_t id = 0;
  228. _read(ina, INA3221_REG_DIE_ID, &id);
  229. return id;
  230. }
  231. void INA3221_setChannelEnable(ina3221_t * ina, ina3221_ch_t channel) {
  232. conf_reg_t conf_reg;
  233. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  234. switch(channel){
  235. case INA3221_CH1:
  236. conf_reg.ch1_en = 1;
  237. break;
  238. case INA3221_CH2:
  239. conf_reg.ch2_en = 1;
  240. break;
  241. case INA3221_CH3:
  242. conf_reg.ch3_en = 1;
  243. break;
  244. default:
  245. break;
  246. }
  247. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  248. }
  249. void INA3221_setChannelDisable(ina3221_t * ina, ina3221_ch_t channel) {
  250. conf_reg_t conf_reg;
  251. _read(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  252. switch(channel){
  253. case INA3221_CH1:
  254. conf_reg.ch1_en = 0;
  255. break;
  256. case INA3221_CH2:
  257. conf_reg.ch2_en = 0;
  258. break;
  259. case INA3221_CH3:
  260. conf_reg.ch3_en = 0;
  261. break;
  262. default:
  263. break;
  264. }
  265. _write(ina, INA3221_REG_CONF, (uint16_t*)&conf_reg);
  266. }
  267. void INA3221_setWarnAlertShuntLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t voltageuV) {
  268. ina3221_reg_t reg;
  269. int16_t val = 0;
  270. switch(channel){
  271. case INA3221_CH1:
  272. reg = INA3221_REG_CH1_WARNING_ALERT_LIM;
  273. break;
  274. case INA3221_CH2:
  275. reg = INA3221_REG_CH2_WARNING_ALERT_LIM;
  276. break;
  277. case INA3221_CH3:
  278. reg = INA3221_REG_CH3_WARNING_ALERT_LIM;
  279. break;
  280. default:
  281. break;
  282. }
  283. val = voltageuV / 5;
  284. _write(ina, reg, (uint16_t*)&val);
  285. }
  286. void INA3221_setCritAlertShuntLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t voltageuV) {
  287. ina3221_reg_t reg;
  288. int16_t val = 0;
  289. switch(channel){
  290. case INA3221_CH1:
  291. reg = INA3221_REG_CH1_CRIT_ALERT_LIM;
  292. break;
  293. case INA3221_CH2:
  294. reg = INA3221_REG_CH2_CRIT_ALERT_LIM;
  295. break;
  296. case INA3221_CH3:
  297. reg = INA3221_REG_CH3_CRIT_ALERT_LIM;
  298. break;
  299. default:
  300. break;
  301. }
  302. val = voltageuV / 5;
  303. _write(ina, reg, (uint16_t*)&val);
  304. }
  305. void INA3221_setWarnAlertCurrentLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t currentmA) {
  306. int32_t shuntuV = 0;
  307. shuntuV = currentmA * (int32_t)ina->shuntRes[channel];
  308. INA3221_setWarnAlertShuntLimit(ina, channel, shuntuV);
  309. }
  310. void INA3221_setCritAlertCurrentLimit(ina3221_t * ina, ina3221_ch_t channel, int32_t currentmA) {
  311. int32_t shuntuV = 0;
  312. shuntuV = currentmA * (int32_t)ina->shuntRes[channel];
  313. INA3221_setCritAlertShuntLimit(ina, channel, shuntuV);
  314. }
  315. void INA3221_setCurrentSumEnable(ina3221_t * ina, ina3221_ch_t channel) {
  316. masken_reg_t masken_reg;
  317. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  318. switch(channel){
  319. case INA3221_CH1:
  320. masken_reg.shunt_sum_en_ch1 = 1;
  321. break;
  322. case INA3221_CH2:
  323. masken_reg.shunt_sum_en_ch2 = 1;
  324. break;
  325. case INA3221_CH3:
  326. masken_reg.shunt_sum_en_ch3 = 1;
  327. break;
  328. default:
  329. break;
  330. }
  331. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  332. _masken_reg = masken_reg;
  333. }
  334. void INA3221_setCurrentSumDisable(ina3221_t * ina, ina3221_ch_t channel) {
  335. masken_reg_t masken_reg;
  336. _read(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  337. switch(channel){
  338. case INA3221_CH1:
  339. masken_reg.shunt_sum_en_ch1 = 0;
  340. break;
  341. case INA3221_CH2:
  342. masken_reg.shunt_sum_en_ch2 = 0;
  343. break;
  344. case INA3221_CH3:
  345. masken_reg.shunt_sum_en_ch3 = 0;
  346. break;
  347. default:
  348. break;
  349. }
  350. _write(ina, INA3221_REG_MASK_ENABLE, (uint16_t*)&masken_reg);
  351. _masken_reg = masken_reg;
  352. }
  353. int32_t INA3221_getShuntVoltage(ina3221_t * ina, ina3221_ch_t channel) {
  354. int32_t res;
  355. ina3221_reg_t reg;
  356. uint16_t val_raw = 0;
  357. switch(channel){
  358. case INA3221_CH1:
  359. reg = INA3221_REG_CH1_SHUNTV;
  360. break;
  361. case INA3221_CH2:
  362. reg = INA3221_REG_CH2_SHUNTV;
  363. break;
  364. case INA3221_CH3:
  365. reg = INA3221_REG_CH3_SHUNTV;
  366. break;
  367. default:
  368. break;
  369. }
  370. _read(ina, reg, &val_raw);
  371. res = (int16_t)val_raw;
  372. res *= 5; // 1 LSB = 5uV
  373. return res;
  374. }
  375. bool INA3221_getWarnAlertFlag(ina3221_t * ina, ina3221_ch_t channel) {
  376. (void)ina;
  377. bool ret;
  378. switch(channel) {
  379. case INA3221_CH1:
  380. return _masken_reg.warn_alert_ch1;
  381. break;
  382. case INA3221_CH2:
  383. return _masken_reg.warn_alert_ch2;
  384. break;
  385. case INA3221_CH3:
  386. return _masken_reg.warn_alert_ch3;
  387. break;
  388. default:
  389. break;
  390. }
  391. return ret;
  392. }
  393. bool INA3221_getCritAlertFlag(ina3221_t * ina, ina3221_ch_t channel) {
  394. (void)ina;
  395. bool ret;
  396. switch(channel) {
  397. case INA3221_CH1:
  398. ret = _masken_reg.crit_alert_ch1;
  399. break;
  400. case INA3221_CH2:
  401. ret = _masken_reg.crit_alert_ch2;
  402. break;
  403. case INA3221_CH3:
  404. ret = _masken_reg.crit_alert_ch3;
  405. break;
  406. default:
  407. break;
  408. }
  409. return ret;
  410. }
  411. int32_t INA3221_estimateOffsetVoltage(ina3221_t * ina, ina3221_ch_t channel, uint32_t busV) {
  412. uint32_t bias_in = 10; // Input bias current at IN– in uA
  413. uint32_t r_in = 670; // Input resistance at IN– in KOhm
  414. int32_t adc_step = 40; // smallest shunt ADC step in uV
  415. uint32_t shunt_res = ina->shuntRes[channel];
  416. uint32_t filter_res = ina->filterRes[channel];
  417. int32_t offset = 0;
  418. int32_t reminder;
  419. offset = (shunt_res + (filter_res * 1000) + 500) / 1000; // resistance in Ohm
  420. offset *= (((busV * 1000) + (r_in/2)) / r_in) + bias_in; // cuurent in uA
  421. offset -= bias_in * filter_res; // offset in uV
  422. // Round the offset to the closest shunt ADC value
  423. reminder = offset % adc_step;
  424. if (reminder < adc_step/2) {
  425. offset -= reminder;
  426. } else {
  427. offset += adc_step - reminder;
  428. }
  429. return offset;
  430. }
  431. int32_t INA3221_getCurrent(ina3221_t * ina, ina3221_ch_t channel) {
  432. int32_t shunt_uV = 0;
  433. int32_t current_A = 0;
  434. shunt_uV = INA3221_getShuntVoltage(ina, channel);
  435. current_A = shunt_uV / (int32_t)ina->shuntRes[channel];
  436. return current_A;
  437. }
  438. int32_t INA3221_getCurrentCompensated(ina3221_t * ina, ina3221_ch_t channel) {
  439. int32_t shunt_uV = 0;
  440. int32_t bus_V = 0;
  441. uint32_t current_A = 0;
  442. int32_t offset_uV = 0;
  443. shunt_uV = INA3221_getShuntVoltage(ina, channel);
  444. bus_V = INA3221_getVoltage(ina, channel);
  445. offset_uV = INA3221_estimateOffsetVoltage(ina, channel, bus_V);
  446. current_A = (shunt_uV - offset_uV) / (int32_t)ina->shuntRes[channel];
  447. return current_A;
  448. }
  449. uint32_t INA3221_getVoltage(ina3221_t * ina, ina3221_ch_t channel) {
  450. uint32_t voltage_V = 0;
  451. ina3221_reg_t reg;
  452. uint16_t val_raw = 0;
  453. switch(channel) {
  454. case INA3221_CH1:
  455. reg = INA3221_REG_CH1_BUSV;
  456. break;
  457. case INA3221_CH2:
  458. reg = INA3221_REG_CH2_BUSV;
  459. break;
  460. case INA3221_CH3:
  461. reg = INA3221_REG_CH3_BUSV;
  462. break;
  463. default:
  464. break;
  465. }
  466. _read(ina, reg, &val_raw);
  467. voltage_V = val_raw;
  468. return voltage_V;
  469. }