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