Statistics
| Branch: | Tag: | Revision:

amiro-os / components / power / bq27500.cpp @ 58fe0e0b

History | View | Annotate | Download (12.898 KB)

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

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

    
6
#include <cstring>
7

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

    
12
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) :
13
  BaseSensor<BQ27500::InitData,BQ27500::CalibData>(), i2c_driver(&i2c_driver), tx_params({I2C_ADDR, NULL, 0, NULL, 0}),
14
  batgd_pingrp(&batgd_pingrp), batgd_pin(batgd_pin), batlow_pingrp(&batlow_pingrp), batlow_pin(batlow_pin)
15
{}
16

    
17
Driver::~Driver()
18
{}
19

    
20
chibios_rt::EvtSource*
21
Driver::getEventSource()
22
{
23
  return &this->eventSource;
24
}
25

    
26
msg_t
27
Driver::init(InitData* initialization_data)
28
{
29
  chprintf((BaseSequentialStream*) &SD1, "%s(%d): TODO\n", __FILE__, __LINE__);
30
  return NOT_IMPLEMENTED;
31
}
32

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

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

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

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

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

    
74
  chprintf((BaseSequentialStream*) &SD1, "%s(%d): TODO\n", __FILE__, __LINE__);
75
  return NOT_IMPLEMENTED;
76
}
77
#endif
78

    
79
#ifndef AMIRO_NSELFTEST
80
msg_t
81
Driver::selftest()
82
{
83
  uint16_t val = 0;
84
  Version version;
85

    
86
  // read hardware version
87
  version.value = 0;
88
  if (this->subCommand(SUB_CMD_HW_VERSION, &version.value))
89
  {
90
    return ST_FAIL_READ_HW_VERSION;
91
  }
92
  chprintf((BaseSequentialStream*) &SD1, "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);
93

    
94
  // read firmware version
95
  version.value = 0;
96
  if (this->subCommand(SUB_CMD_FW_VERSION, &version.value))
97
  {
98
    return ST_FAIL_READ_FW_VERSION;
99
  }
100
  chprintf((BaseSequentialStream*) &SD1, "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);
101

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

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

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

    
156
  // if no battery was detected, abort
157
  if (!flags.content.bat_det)
158
  {
159
    return ST_ABORT_NO_BAT;
160
  }
161

    
162
  // read the BATGD_N pin
163
  chprintf((BaseSequentialStream*) &SD1, "battery good: %s\n", (this->isBatteryGood()? "yes" : "no"));
164

    
165
  // read temperature
166
  if (this->stdCommand(STD_CMD_TEMP, val))
167
  {
168
    return ST_FAIL_READ_TEMP;
169
  }
170
  chprintf((BaseSequentialStream*) &SD1, "temperature: %fK (%fC)\n", float(val)/10.0f, float(val)/10.0f - 273.15f);
171

    
172
  // read the full available capacity
173
  if (this->stdCommand(STD_CMD_FAC, val))
174
  {
175
    return ST_FAIL_READ_FAC;
176
  }
177
  chprintf((BaseSequentialStream*) &SD1, "full available capacity: %umAh\n", val);
178

    
179
  // read the full charge capacity
180
  if (this->stdCommand(STD_CMD_FCC, val))
181
  {
182
    return ST_FAIL_READ_FCC;
183
  }
184
  chprintf((BaseSequentialStream*) &SD1, "full charge capacity: %umAh\n", val);
185

    
186
  // read the remaining capacity
187
  if (this->stdCommand(STD_CMD_RM, val))
188
  {
189
    return ST_FAIL_READ_RM;
190
  }
191
  chprintf((BaseSequentialStream*) &SD1, "remaining capacity capacity: %umAh\n", val);
192

    
193
  // read the state of charge
194
  if (this->stdCommand(STD_CMD_SOC, val))
195
  {
196
    return ST_FAIL_READ_SOC;
197
  }
198
  chprintf((BaseSequentialStream*) &SD1, "state of charge: %3u%%\n", val);
199

    
200
  // read voltage
201
  if (this->stdCommand(STD_CMD_VOLT, val))
202
  {
203
    return ST_FAIL_READ_VOLT;
204
  }
205
  chprintf((BaseSequentialStream*) &SD1, "voltage: %umV\n", val);
206

    
207
  // read average current
208
  if (this->stdCommand(STD_CMD_AI, val))
209
  {
210
    return ST_FAIL_READ_AI;
211
  }
212
  chprintf((BaseSequentialStream*) &SD1, "average current: %dmA\n", *reinterpret_cast<int8_t*>(&val));
213

    
214
  // read average power
215
  if (this->stdCommand(STD_CMD_AP, val))
216
  {
217
    return ST_FAIL_READ_AP;
218
  }
219
  chprintf((BaseSequentialStream*) &SD1, "average power: %dmW\n", *reinterpret_cast<int8_t*>(&val));
220

    
221
  // read the BATLOW pin
222
  chprintf((BaseSequentialStream*) &SD1, "battery low: %s\n", (this->isBatteryLow()? "yes" : "no"));
223

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

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

    
250
  return ST_OK;
251
}
252
#endif
253

    
254
bool
255
Driver::isBatteryGood() const
256
{
257
  return (palReadPad(this->batgd_pingrp, this->batgd_pin) == PAL_LOW);
258
}
259

    
260
bool
261
Driver::isBatteryLow() const
262
{
263
  return (palReadPad(this->batlow_pingrp, this->batlow_pin) == PAL_HIGH);
264
}
265

    
266
const Driver::UpdateData&
267
Driver::getStatus() const
268
{
269
  return this->status;
270
}
271

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

    
280
    this->waitAnyEventTimeout(ALL_EVENTS, MS2ST(1000));
281
  }
282

    
283
  return RDY_OK;
284
}
285

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

    
295
  this->i2c_driver->acquireBus();
296
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
297
  this->i2c_driver->releaseBus();
298

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

    
307
  return res;
308
}
309

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

    
320
  this->i2c_driver->acquireBus();
321
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
322
  this->i2c_driver->releaseBus();
323

    
324
  buffer[buffer[0] + 1] = '\0';
325

    
326
  chprintf((BaseSequentialStream*) &SD1, "name: %u - %s\n", buffer[0], (char*)&buffer[1]);
327

    
328
  return res;
329
}
330

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

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

    
345
    this->tx_params.rxbuf = &buffer[1];
346
    this->tx_params.rxbytes = 2;
347

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

    
354
#ifndef NDEBUG
355
  if (res) {
356
    chprintf((BaseSequentialStream*) &SD1, "%s(%d): ERROR: i2c transmit failed (%d)\n", __FILE__ , __LINE__ , res);
357
  }
358
#endif
359

    
360
  if (dst && !res) {
361
    *dst = uint16_t((buffer[2] << 8) | buffer[1]);
362
  }
363

    
364
  return res;
365
}
366

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

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

    
399
  this->i2c_driver->acquireBus();
400
  const msg_t res = this->i2c_driver->masterTransmit(&this->tx_params);
401
  this->i2c_driver->releaseBus();
402

    
403
#ifndef NDEBUG
404
  if (res) {
405
    chprintf((BaseSequentialStream*) &SD1, "%s(%d): ERROR: i2c transmit failed (%d)\n", __FILE__ , __LINE__ , res);
406
  }
407
#endif
408

    
409
  return res;
410
}
411

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

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

    
423
  return res;
424
}