Statistics
| Branch: | Tag: | Revision:

amiro-os / components / power / ina219.cpp @ 552936c8

History | View | Annotate | Download (10.641 KB)

1
#include <amiro/power/ina219.hpp>
2

    
3
#include <ch.hpp>
4
#include <chprintf.h>
5

    
6
using namespace chibios_rt;
7
using namespace amiro;
8
using namespace INA219;
9

    
10
Driver::Driver(I2CDriver &i2c_driver, const uint8_t i2c_address) :
11
  BaseSensor<InitData,CalibData>(), i2c_driver(&i2c_driver), tx_params({i2c_address, NULL, 0, NULL, 0}), current_lsb_uA(0)
12
{
13
  this->config.content.brng = Configuration::BRNG_DEFAULT;
14
  this->config.content.pg = Configuration::PGA_DEFAULT;
15
  this->config.content.badc = Configuration::ADC_DEFAULT;
16
  this->config.content.sadc = Configuration::ADC_DEFAULT;
17
  this->config.content.mode = Configuration::MODE_DEFAULT;
18

    
19
  return;
20
}
21

    
22
Driver::~Driver()
23
{}
24

    
25
msg_t
26
Driver::init(InitData* initialization_data)
27
{
28
  if(!initialization_data)
29
  {
30
    return ERROR;
31
  }
32

    
33
  // write configuration
34
  if (this->writeRegister(REG_CONFIGURATION, initialization_data->configuration.value & MASK_CONFIGURATION)) {
35
    return ERROR;
36
  }
37
  this->config.value = initialization_data->configuration.value & MASK_CONFIGURATION;
38

    
39
  if (this->writeRegister(REG_CALIBRATION, initialization_data->calibration & MASK_CALIBRATION)) {
40
    return ERROR;
41
  }
42

    
43
  this->current_lsb_uA = initialization_data->current_lsb_uA;
44

    
45
  return this->update();
46
}
47

    
48
msg_t
49
Driver::update()
50
{
51
  this->status.power = 0;
52
  msg_t res = this->readRegister(REG_BUS_VOLTAGE, this->status.bus_voltage.value);
53
  res |= this->readRegister(REG_POWER, this->status.power);
54

    
55
  // if the power register was not updated yet, try again
56
  while (!this->status.bus_voltage.content.conversion_ready || this->status.power == 0 || res != 0)
57
  {
58
    BaseThread::sleep(MS2ST(10));
59
    res |= this->readRegister(REG_BUS_VOLTAGE, this->status.bus_voltage.value);
60
    res |= this->readRegister(REG_POWER, this->status.power);
61
  }
62

    
63
  return res ? ERROR : SUCCESS;
64
}
65

    
66
msg_t
67
Driver::wakeup()
68
{
69
  if (this->writeRegister(REG_CONFIGURATION, this->config.value)) {
70
    return ERROR;
71
  } else {
72
    return this->update();
73
  }
74
}
75

    
76
msg_t
77
Driver::hibernate()
78
{
79
  Configuration::Register tmp_config = this->config;
80
  tmp_config.content.mode = Configuration::MODE_PowerDown;
81
  if (this->writeRegister(REG_CONFIGURATION, tmp_config.value)) {
82
    return ERROR;
83
  } else {
84
    return SUCCESS;
85
  }
86

    
87
}
88

    
89
#ifndef AMIRO_NCALIBRATION
90
msg_t
91
Driver::calibration(CalibData* calibration_data)
92
{
93
  if (!calibration_data) {
94
    return ERROR;
95
  }
96

    
97
  uint16_t current_lsb_uA = calibration_data->input.current_lsb_uA;
98
  if (current_lsb_uA < calibration_data->input.max_expected_current_A / 0.032767f) {
99
    current_lsb_uA = calibration_data->input.max_expected_current_A / 0.032767f;
100
  } else if (current_lsb_uA > calibration_data->input.max_expected_current_A / 0.004096f) {
101
    current_lsb_uA = calibration_data->input.max_expected_current_A / 0.004096f;
102
  }
103

    
104
  const uint16_t calibration_value = uint16_t(40960 / (current_lsb_uA * calibration_data->input.shunt_resistance_O));
105

    
106
  float V_shunt_max;
107
  switch (calibration_data->input.configuration.content.pg)
108
  {
109
    case Configuration::PGA_40mV:
110
      V_shunt_max = 0.04f;
111
      break;
112
    case Configuration::PGA_80mV:
113
      V_shunt_max = 0.08f;
114
      break;
115
    case Configuration::PGA_160mV:
116
      V_shunt_max = 0.16f;
117
      break;
118
    case Configuration::PGA_320mV:
119
      V_shunt_max = 0.32f;
120
      break;
121
  }
122

    
123
  const float max_current_before_overflow = ( (current_lsb_uA * 0.032767f >= V_shunt_max) / (calibration_data->input.shunt_resistance_O) )?
124
                                            V_shunt_max / calibration_data->input.shunt_resistance_O :
125
                                            current_lsb_uA * 0.032767f;
126

    
127
  const float max_shuntvoltage_before_overflow = ( (max_current_before_overflow * calibration_data->input.shunt_resistance_O) >= V_shunt_max )?
128
                                                 V_shunt_max :
129
                                                 max_current_before_overflow * calibration_data->input.shunt_resistance_O;
130

    
131
  calibration_data->output.max_current_before_overflow_A = max_current_before_overflow;
132
  calibration_data->output.max_shuntvoltage_before_overflow_V = max_shuntvoltage_before_overflow;
133
  calibration_data->output.current_lsb_uA = current_lsb_uA;
134
  calibration_data->output.calibration_value = calibration_value;
135

    
136
  return SUCCESS;
137
}
138
#endif
139

    
140
#ifndef AMIRO_NSELFTEST
141
msg_t
142
Driver::selftest()
143
{
144
  struct RegisterContent {
145
    Configuration::Register configuration;
146
    uint16_t shunt_voltage = 0;
147
    BusVoltage bus_voltage;
148
    uint16_t power = 0;
149
    uint16_t current = 0;
150
    uint16_t calibration = 0;
151
  };
152

    
153
  // backup the current status
154
  RegisterContent backup;
155
  msg_t res = this->readRegister(REG_CONFIGURATION, backup.configuration.value);
156
  res |= this->readRegister(REG_SHUNT_VOLTAGE, backup.shunt_voltage);
157
  res |= this->readRegister(REG_BUS_VOLTAGE, backup.bus_voltage.value);
158
  res |= this->readRegister(REG_POWER, backup.power);
159
  res |= this->readRegister(REG_CURRENT, backup.current);
160
  res |= this->readRegister(REG_CALIBRATION, backup.calibration);
161
  if (res ||
162
      !(backup.configuration.value ||
163
        backup.shunt_voltage ||
164
        backup.bus_voltage.value ||
165
        backup.power ||
166
        backup.current ||
167
        backup.calibration))
168
  {
169
    return ST_FAIL_BACKUP;
170
  }
171

    
172
  // reset the INA219
173
  if (this->reset())
174
  {
175
    return ST_FAIL_RESET;
176
  }
177

    
178
  // read the configuration
179
  Configuration::Register config = this->readConfiguration();
180

    
181
  // check for the default configuration
182
  Configuration::Register config_default;
183
  config_default.content.brng = Configuration::BRNG_DEFAULT;
184
  config_default.content.pg = Configuration::PGA_DEFAULT;
185
  config_default.content.badc = Configuration::ADC_DEFAULT;
186
  config_default.content.sadc = Configuration::ADC_DEFAULT;
187
  config_default.content.mode = Configuration::MODE_DEFAULT;
188
  if (config.value != config_default.value)
189
  {
190
    return ST_FAIL_IS_DEFAULT;
191
  }
192

    
193
  // revert to the previous configuration
194
  if (this->writeRegister(REG_CONFIGURATION, backup.configuration.value))
195
  {
196
    return ST_FAIL_WRITE_CONFIG;
197
  }
198

    
199
  // revert to the previous calibration
200
  if (this->writeRegister(REG_CALIBRATION, backup.calibration))
201
  {
202
    return ST_FAIL_WRITE_CALIB;
203
  }
204

    
205
  // read the current configuration
206
  if (this->readConfiguration().value != backup.configuration.value)
207
  {
208
    return ST_FAIL_CHECK_CONFIG;
209
  }
210

    
211
  // read the current calibration
212
  if (this->readCalibration() != backup.calibration)
213
  {
214
    return ST_FAIL_CHECK_CALIB;
215
  }
216

    
217
  // read and print the current status
218
  INA219::BusVoltage bus_voltage = this->readBusVoltage();
219
  uint16_t power;
220
  this->readRegister(REG_POWER, power);
221
  // wait until the bus voltage and the power register are valid
222
  while (!bus_voltage.conversion_ready || power == 0)
223
  {
224
    BaseThread::sleep(MS2ST(10));
225
    bus_voltage = this->readBusVoltage();
226
    this->readRegister(REG_POWER, power);
227
  }
228
  chprintf((BaseSequentialStream*) &SD1, "shunt voltage : %fV\n", this->readShuntVoltage_uV() / 1000000.f);
229
  chprintf((BaseSequentialStream*) &SD1, "bus voltage   : %fV\n", bus_voltage.voltage_uV / 1000000.f);
230
  chprintf((BaseSequentialStream*) &SD1, "power         : %fW\n", this->readPower_uW() / 1000000.f);
231
  chprintf((BaseSequentialStream*) &SD1, "current       : %fA\n", this->readCurrent_uA() / 1000000.f);
232

    
233
  return ST_OK;
234
}
235
#endif
236

    
237
Configuration::Register
238
Driver::readConfiguration()
239
{
240
  Configuration::Register reg;
241
  this->readRegister(REG_CONFIGURATION, reg.value);
242
  return reg;
243
}
244

    
245
int32_t
246
Driver::readShuntVoltage_uV()
247
{
248
  uint16_t val = 0;
249
  this->readRegister(REG_SHUNT_VOLTAGE, val);
250
  /*
251
   * Depending on the configuration either one, two, three or four most significant bits are used for coding the sign (no two's complement!)
252
   * -> Masking bits in order to separate sign bits and value bits.
253
   * Furthermore, the least significant bit represents 10uV.
254
   * -> Multiplication by 10 or -10 respectively.
255
   */
256
  switch (this->config.content.pg)
257
  {
258
    case Configuration::PGA_320mV:
259
      return ( uint32_t(val & 0x7FFFu) * ((val & 0x8000u)? -10 : 10) );
260
    case Configuration::PGA_160mV:
261
      return ( uint32_t(val & 0x3FFFu) * ((val & 0xC000u)? -10 : 10) );
262
    case Configuration::PGA_80mV:
263
      return ( uint32_t(val & 0x1FFFu) * ((val & 0xE000u)? -10 : 10) );
264
    case Configuration::PGA_40mV:
265
      return ( uint32_t(val & 0x0FFFu) * ((val & 0xF000u)? -10 : 10) );
266
  }
267
}
268

    
269
INA219::BusVoltage
270
Driver::readBusVoltage()
271
{
272
  Driver::BusVoltage reg;
273
  this->readRegister(REG_BUS_VOLTAGE, reg.value);
274
  return this->busVoltageReg2uV(reg);
275
}
276

    
277
uint32_t
278
Driver::readPower_uW()
279
{
280
  uint16_t val = 0;
281
  this->readRegister(REG_POWER, val);
282
  return this->powerReg2uW(val);
283
}
284

    
285
int32_t
286
Driver::readCurrent_uA()
287
{
288
  uint16_t val = 0;
289
  this->readRegister(REG_CURRENT, val);
290

    
291
  /*
292
   * Reinterpret register value as a signed integer (two's complement).
293
   * Multiply with the value of the least significant bit.
294
   */
295
  return int32_t(*reinterpret_cast<int16_t*>(&val)) * this->current_lsb_uA;
296
}
297

    
298
uint16_t
299
Driver::readCalibration()
300
{
301
  uint16_t val = 0;
302
  this->readRegister(REG_CALIBRATION, val);
303
  return (val & MASK_CALIBRATION);
304
}
305

    
306
uint8_t
307
Driver::reset()
308
{
309
  const msg_t res = this->writeRegister(REG_CONFIGURATION, MASK_RESET);
310
  return (res? ERROR : SUCCESS);
311
}
312

    
313
msg_t
314
Driver::main(void)
315
{
316
  while (!this->shouldTerminate())
317
  {
318
    this->update();
319
    //this->eventSource.broadcastFlags(0);
320
    this->sleep(MS2ST(1000));
321
    //this->waitAnyEventTimeout(ALL_EVENTS, MS2ST(1000));
322
  }
323

    
324
  return RDY_OK;
325
}
326

    
327
msg_t
328
Driver::readRegister(const RegisterAddress reg, uint16_t& dst)
329
{
330
  uint8_t buffer[2];
331
  this->tx_params.txbuf = reinterpret_cast<const uint8_t*>(&reg);
332
  this->tx_params.txbytes = 1;
333
  this->tx_params.rxbuf = buffer;
334
  this->tx_params.rxbytes = 2;
335

    
336
  this->i2c_driver->acquireBus();
337
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
338
  this->i2c_driver->releaseBus();
339

    
340
  if (!res) {
341
    dst = (buffer[0] << 8) | buffer[1];
342
    if (reg == REG_CALIBRATION)
343
      dst &= MASK_CALIBRATION;
344
#ifndef NDEBUG
345
  } else {
346
    chprintf((BaseSequentialStream*) &SD1, "%s(%d): ERROR: i2c transmit failed (%d | 0x%08X)\n", __FILE__ , __LINE__ , res, this->i2c_driver->getErrors());
347
#endif
348
  }
349

    
350
  return res;
351
}
352

    
353
msg_t
354
Driver::writeRegister(const RegisterAddress reg, const uint16_t& val)
355
{
356
  const uint8_t buffer[3] = {reg,
357
                             static_cast<uint8_t>((val & 0xFF00u) >> 8),
358
                             static_cast<uint8_t>(val & 0x00FFu)};
359
  this->tx_params.txbuf = buffer;
360
  this->tx_params.txbytes = 3;
361
  this->tx_params.rxbytes = 0;
362

    
363
  this->i2c_driver->acquireBus();
364
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
365
  this->i2c_driver->releaseBus();
366

    
367
#ifndef NDEBUG
368
  if (res) {
369
    chprintf((BaseSequentialStream*) &SD1, "%s(%d): ERROR: i2c transmit failed (%d | 0x%08X)\n", __FILE__ , __LINE__ , res, this->i2c_driver->getErrors());
370
  }
371
#endif
372

    
373
  return res;
374
}
375