Statistics
| Branch: | Tag: | Revision:

amiro-os / components / power / bq27500.cpp @ f8cf404d

History | View | Annotate | Download (13.763 KB)

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

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

    
7
#include <cstring>
8

    
9
using namespace chibios_rt;
10
using namespace amiro;
11
using namespace BQ27500;
12

    
13
extern Global global;
14

    
15
Driver::Driver(I2CDriver &i2c_driver, const GPIO_TypeDef &batgd_pingrp, const uint8_t batgd_pin, const GPIO_TypeDef &batlow_pingrp, const uint8_t batlow_pin) :
16
  BaseSensor<BQ27500::InitData,BQ27500::CalibData>(), i2c_driver(&i2c_driver), tx_params({I2C_ADDR, NULL, 0, NULL, 0}),
17
  batgd_pingrp(&batgd_pingrp), batgd_pin(batgd_pin), batlow_pingrp(&batlow_pingrp), batlow_pin(batlow_pin)
18
{}
19

    
20
Driver::~Driver()
21
{}
22

    
23
chibios_rt::EvtSource*
24
Driver::getEventSource()
25
{
26
  return &this->eventSource;
27
}
28

    
29
msg_t
30
Driver::init(InitData* initialization_data)
31
{
32
  chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): TODO\n", __FILE__, __LINE__);
33
  return NOT_IMPLEMENTED;
34
}
35

    
36
msg_t
37
Driver::update()
38
{
39
  msg_t res = SUCCESS;
40
  res |= this->stdCommand(STD_CMD_TimeToEmpty, this->status.minutes_to_empty);
41
  res |= this->stdCommand(STD_CMD_TimeToFull, this->status.minutes_to_full);
42
  res |= this->stdCommand(STD_CMD_AveragePower, this->status.average_power_mW);
43
  uint16_t tmp;
44
  res |= this->stdCommand(STD_CMD_StateOfCharge, tmp);
45
  this->status.state_of_charge = tmp;
46
  return (res == SUCCESS)? SUCCESS : ERROR;
47
}
48

    
49
msg_t
50
Driver::wakeup()
51
{
52
  const msg_t res = this->subCommand(SUB_CMD_CLEAR_HIBERNATE);
53
  return (res? ERROR : SUCCESS);
54
}
55

    
56
msg_t
57
Driver::hibernate()
58
{
59
  const msg_t res = this->subCommand(SUB_CMD_SET_HIBERNATE);
60
  return (res? ERROR : SUCCESS);
61
}
62

    
63
#ifndef AMIRO_NCALIBRATION
64
msg_t
65
Driver::calibration(CalibData* calibration_data)
66
{
67
  DataFlashBlock block;
68
  this->readDataFlashBlock(block, CONFIGURATION_Safety);
69

    
70
  chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d):\n", __FILE__, __LINE__);
71
  for(uint8_t i = 0; i < 32; ++i) {
72
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%02X ", block.content.data[i]);
73
  }
74
  chprintf((BaseSequentialStream*) &global.sercanmux1, "\n");
75
  chprintf((BaseSequentialStream*) &global.sercanmux1, "%02X\n", block.content.checksum);
76

    
77
  chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): TODO\n", __FILE__, __LINE__);
78
  return NOT_IMPLEMENTED;
79
}
80
#endif
81

    
82
#ifndef AMIRO_NSELFTEST
83
msg_t
84
Driver::selftest()
85
{
86
  uint16_t val = 0;
87
  Version version;
88

    
89
  // read hardware version
90
  version.value = 0;
91
  if (this->subCommand(SUB_CMD_HW_VERSION, &version.value))
92
  {
93
    return ST_FAIL_READ_HW_VERSION;
94
  }
95
  chprintf((BaseSequentialStream*) &global.sercanmux1, "hardware version: %X%X-%X%X (0x%04X)\n", version.content.major_high, version.content.major_low, version.content.minor_high, version.content.minor_low, version.value);
96

    
97
  // read firmware version
98
  version.value = 0;
99
  if (this->subCommand(SUB_CMD_FW_VERSION, &version.value))
100
  {
101
    return ST_FAIL_READ_FW_VERSION;
102
  }
103
  chprintf((BaseSequentialStream*) &global.sercanmux1, "firmware version: %X%X-%X%X (0x%04X)\n", version.content.major_high, version.content.major_low, version.content.minor_high, version.content.minor_low, version.value);
104

    
105
  // read device name
106
  uint8_t name_length = 0;
107
  if (this->extCommand(EXT_CMD_DNAMELEN, EXT_CMD_READ, &name_length))
108
  {
109
    return ST_FAIL_READ_DEVICENAMELENGTH;
110
  }
111
  char name_buffer[9]; // maximum name length is 8
112
  if (this->extCommand(EXT_CMD_DNAME, EXT_CMD_READ, (uint8_t*)name_buffer, name_length))
113
  {
114
    return ST_FAIL_READ_DEVICENAME;
115
  }
116
  name_buffer[name_length] = '\0';
117
  chprintf((BaseSequentialStream*) &global.sercanmux1, "device name: %s (%u characters)\n", name_buffer, name_length);
118

    
119
  // read the current flags
120
  Flags flags;
121
  if (this->stdCommand(STD_CMD_FLAGS, flags.value))
122
  {
123
    return ST_FAIL_READ_FLAGS;
124
  }
125
  chprintf((BaseSequentialStream*) &global.sercanmux1, "flags: 0x%04X\n", flags.value);
126
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  OTC     : %u\n", flags.content.otc);
127
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  OTD     : %u\n", flags.content.otd);
128
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  CHG_INH : %u\n", flags.content.chg_inh);
129
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  XCHG    : %u\n", flags.content.xchg);
130
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  FC      : %u\n", flags.content.fc);
131
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  CHG     : %u\n", flags.content.chg);
132
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  OCV_GD  : %u\n", flags.content.ocv_gd);
133
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  WAIT_ID : %u\n", flags.content.wait_id);
134
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  BAT_DET : %u\n", flags.content.bat_det);
135
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  SOC1    : %u\n", flags.content.soc1);
136
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  SOCF    : %u\n", flags.content.socf);
137
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  DSG     : %u\n", flags.content.dsg);
138

    
139
  // read the current controller status
140
  ControlStatus ctrl_status;
141
  if (this->subCommand(SUB_CMD_CONTROL_STATUS, &ctrl_status.value))
142
  {
143
    return ST_FAIL_READ_STATUS;
144
  }
145
  chprintf((BaseSequentialStream*) &global.sercanmux1, "control status: 0x%04X\n", ctrl_status.value);
146
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  FAS       : %u\n", ctrl_status.content.fas);
147
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  SS        : %u\n", ctrl_status.content.ss);
148
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  CSV       : %u\n", ctrl_status.content.csv);
149
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  CSA       : %u\n", ctrl_status.content.cca);
150
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  BCA       : %u\n", ctrl_status.content.bca);
151
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  HIBERNATE : %u\n", ctrl_status.content.hibernate);
152
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  SNOOZE    : %u\n", ctrl_status.content.snooze);
153
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  SLEEP     : %u\n", ctrl_status.content.sleep);
154
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  LDMD      : %u\n", ctrl_status.content.ldmd);
155
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  RUP_DIS   : %u\n", ctrl_status.content.rup_dis);
156
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  VOK       : %u\n", ctrl_status.content.vok);
157
  chprintf((BaseSequentialStream*) &global.sercanmux1, "  QEN       : %u\n", ctrl_status.content.qen);
158

    
159
  // if no battery was detected, abort
160
  if (!flags.content.bat_det)
161
  {
162
    return ST_ABORT_NO_BAT;
163
  }
164

    
165
  // read the BATGD_N pin
166
  chprintf((BaseSequentialStream*) &global.sercanmux1, "battery good: %s\n", (this->isBatteryGood()? "yes" : "no"));
167

    
168
  // read temperature
169
  if (this->stdCommand(STD_CMD_TEMP, val))
170
  {
171
    return ST_FAIL_READ_TEMP;
172
  }
173
  chprintf((BaseSequentialStream*) &global.sercanmux1, "temperature: %fK (%fC)\n", float(val)/10.0f, float(val)/10.0f - 273.15f);
174

    
175
  // read the full available capacity
176
  if (this->stdCommand(STD_CMD_FAC, val))
177
  {
178
    return ST_FAIL_READ_FAC;
179
  }
180
  chprintf((BaseSequentialStream*) &global.sercanmux1, "full available capacity: %umAh\n", val);
181

    
182
  // read the full charge capacity
183
  if (this->stdCommand(STD_CMD_FCC, val))
184
  {
185
    return ST_FAIL_READ_FCC;
186
  }
187
  chprintf((BaseSequentialStream*) &global.sercanmux1, "full charge capacity: %umAh\n", val);
188

    
189
  // read the remaining capacity
190
  if (this->stdCommand(STD_CMD_RM, val))
191
  {
192
    return ST_FAIL_READ_RM;
193
  }
194
  chprintf((BaseSequentialStream*) &global.sercanmux1, "remaining capacity capacity: %umAh\n", val);
195

    
196
  // read the state of charge
197
  if (this->stdCommand(STD_CMD_SOC, val))
198
  {
199
    return ST_FAIL_READ_SOC;
200
  }
201
  chprintf((BaseSequentialStream*) &global.sercanmux1, "state of charge: %3u%%\n", val);
202

    
203
  // read voltage
204
  if (this->stdCommand(STD_CMD_VOLT, val))
205
  {
206
    return ST_FAIL_READ_VOLT;
207
  }
208
  chprintf((BaseSequentialStream*) &global.sercanmux1, "voltage: %umV\n", val);
209

    
210
  // read average current
211
  if (this->stdCommand(STD_CMD_AI, val))
212
  {
213
    return ST_FAIL_READ_AI;
214
  }
215
  chprintf((BaseSequentialStream*) &global.sercanmux1, "average current: %dmA\n", *reinterpret_cast<int8_t*>(&val));
216

    
217
  // read average power
218
  if (this->stdCommand(STD_CMD_AP, val))
219
  {
220
    return ST_FAIL_READ_AP;
221
  }
222
  chprintf((BaseSequentialStream*) &global.sercanmux1, "average power: %dmW\n", *reinterpret_cast<int8_t*>(&val));
223

    
224
  // read the BATLOW pin
225
  chprintf((BaseSequentialStream*) &global.sercanmux1, "battery low: %s\n", (this->isBatteryLow()? "yes" : "no"));
226

    
227
  // read the time to empty
228
  if (this->stdCommand(STD_CMD_TTE, val))
229
  {
230
    return ST_FAIL_READ_TTE;
231
  }
232
  chprintf((BaseSequentialStream*) &global.sercanmux1, "time to empty: ");
233
  if (uint16_t(~val)) {
234
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%u minutes", val);
235
  } else {
236
    chprintf((BaseSequentialStream*) &global.sercanmux1, "(not discharging)");
237
  }
238
  chprintf((BaseSequentialStream*) &global.sercanmux1, "\n");
239

    
240
  // read the time to full
241
  if (this->stdCommand(STD_CMD_TTF, val))
242
  {
243
    return ST_FAIL_READ_TTF;
244
  }
245
  chprintf((BaseSequentialStream*) &global.sercanmux1, "time to full: ");
246
  if (uint16_t(~val)) {
247
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%u minutes", val);
248
  } else {
249
    chprintf((BaseSequentialStream*) &global.sercanmux1, "(not charging)");
250
  }
251
  chprintf((BaseSequentialStream*) &global.sercanmux1, "\n");
252

    
253
  return ST_OK;
254
}
255
#endif
256

    
257
bool
258
Driver::isBatteryGood() const
259
{
260
  return (palReadPad(this->batgd_pingrp, this->batgd_pin) == PAL_LOW);
261
}
262

    
263
bool
264
Driver::isBatteryLow() const
265
{
266
  return (palReadPad(this->batlow_pingrp, this->batlow_pin) == PAL_HIGH);
267
}
268

    
269
const Driver::UpdateData&
270
Driver::getStatus() const
271
{
272
  return this->status;
273
}
274

    
275
msg_t
276
Driver::main(void)
277
{
278
  while (!this->shouldTerminate())
279
  {
280
    this->update();
281
    this->eventSource.broadcastFlags(0);
282

    
283
    this->waitAnyEventTimeout(ALL_EVENTS, MS2ST(1000));
284
  }
285

    
286
  return RDY_OK;
287
}
288

    
289
msg_t
290
Driver::stdCommand(const StandardCommand cmd, uint16_t &dst)
291
{
292
  uint8_t buffer[2];
293
  this->tx_params.txbuf = reinterpret_cast<const uint8_t*>(&cmd);
294
  this->tx_params.txbytes = 1;
295
  this->tx_params.rxbuf = buffer;
296
  this->tx_params.rxbytes = 2;
297

    
298
  this->i2c_driver->acquireBus();
299
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
300
  this->i2c_driver->releaseBus();
301

    
302
  if (!res) {
303
    dst = uint16_t((buffer[1] << 8) | buffer[0]);
304
#ifndef NDEBUG
305
  } else {
306
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: i2c transmit failed (%d)\n", __FILE__ , __LINE__ , res);
307
#endif
308
  }
309

    
310
  return res;
311
}
312

    
313
msg_t
314
Driver::readName()
315
{
316
  uint8_t buffer[9];
317
  uint8_t reg = 0x62u;
318
  this->tx_params.txbuf = &reg;
319
  this->tx_params.txbytes = 1;
320
  this->tx_params.rxbuf = &buffer[0];
321
  this->tx_params.rxbytes = 8;
322

    
323
  this->i2c_driver->acquireBus();
324
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
325
  this->i2c_driver->releaseBus();
326

    
327
  buffer[buffer[0] + 1] = '\0';
328

    
329
  chprintf((BaseSequentialStream*) &global.sercanmux1, "name: %u - %s\n", buffer[0], (char*)&buffer[1]);
330

    
331
  return res;
332
}
333

    
334
msg_t
335
Driver::subCommand(const ControlSubcommand cmd, uint16_t *dst)
336
{
337
  uint8_t buffer[3] = {STD_CMD_CNTL, uint8_t(cmd & 0x00FFu), uint8_t((cmd & 0xFF00u) >> 8)};
338
  this->tx_params.txbuf = buffer;
339
  this->tx_params.txbytes = 3;
340
  this->tx_params.rxbytes = 0;
341

    
342
  this->i2c_driver->acquireBus();
343
  msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
344
  this->i2c_driver->releaseBus();
345
  if (dst) {
346
    this->tx_params.txbytes = 1;
347

    
348
    this->tx_params.rxbuf = &buffer[1];
349
    this->tx_params.rxbytes = 2;
350

    
351
    BaseThread::sleep(US2ST(2));
352
    this->i2c_driver->acquireBus();
353
    res |= this->i2c_driver->masterTransmit(&this->tx_params);
354
    this->i2c_driver->releaseBus();
355
  }
356

    
357
#ifndef NDEBUG
358
  if (res) {
359
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: i2c transmit failed (%d)\n", __FILE__ , __LINE__ , res);
360
  }
361
#endif
362

    
363
  if (dst && !res) {
364
    *dst = uint16_t((buffer[2] << 8) | buffer[1]);
365
  }
366

    
367
  return res;
368
}
369

    
370
msg_t
371
Driver::extCommand(const ExtendedCommand cmd, const ExtendedCommandAccess rw, uint8_t* buf, const uint8_t length, const uint8_t offset)
372
{
373
  if (!buf) {
374
#ifndef NDEBUG
375
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: received NULL-pointer as buffer\n", __FILE__ , __LINE__);
376
#endif
377
    return ~RDY_OK;
378
  }
379
  if (rw != EXT_CMD_WRITE && rw != EXT_CMD_READ) {
380
#ifndef NDEBUG
381
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: invalid access mode selected\n", __FILE__ , __LINE__);
382
#endif
383
    return ~RDY_OK;
384
  }
385
  if (length > 33) {
386
#ifndef NDEBUG
387
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: length exceeds maximum of 33 bytes\n", __FILE__ , __LINE__);
388
#endif
389
    return ~RDY_OK;
390
  }
391

    
392
  uint8_t in_buffer[34];
393
  in_buffer[0] = cmd + offset;
394
  if (rw == EXT_CMD_WRITE) {
395
    strncpy((char*)&in_buffer[1], (char*)buf, length);
396
  }
397
  this->tx_params.txbuf = in_buffer;
398
  this->tx_params.txbytes = 1 + ((rw == EXT_CMD_WRITE)? length : 0);
399
  this->tx_params.rxbuf = (rw == EXT_CMD_READ)? buf : NULL;
400
  this->tx_params.rxbytes = (rw == EXT_CMD_READ)? length : 0;
401

    
402
  this->i2c_driver->acquireBus();
403
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
404
  this->i2c_driver->releaseBus();
405

    
406
#ifndef NDEBUG
407
  if (res) {
408
    chprintf((BaseSequentialStream*) &global.sercanmux1, "%s(%d): ERROR: i2c transmit failed (%d)\n", __FILE__ , __LINE__ , res);
409
  }
410
#endif
411

    
412
  return res;
413
}
414

    
415
msg_t
416
Driver::readDataFlashBlock(DataFlashBlock &block, const DataFlashSubClassID sub_id, const uint8_t sub_block)
417
{
418
  block.raw[0] = sub_id;
419
  block.raw[1] = sub_block;
420
  msg_t res = this->extCommand(EXT_CMD_DFCLS, EXT_CMD_WRITE, &block.raw[0]);
421
  res |= this->extCommand(EXT_CMD_DFBLK, EXT_CMD_WRITE, &block.raw[1]);
422

    
423
  BaseThread::sleep(US2ST(1)); // Without this delay the whole block is shifted and the first byte is lost. TODO: investigate
424
  res |= this->extCommand(EXT_CMD_DFD, EXT_CMD_READ, block.raw, 33);
425

    
426
  return res;
427
}