amiro-os / components / power / bq27500.cpp @ bc91a128
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 = ®
|
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 |
} |