mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-28 07:47:35 +03:00
f218c41d83
* TODO FL-3497: impossible to fix with current memory allocator * TODO FL-3497: removed, requires radically different settings approach * TODO FL-3514: removed, yes we should * TODO FL-3498: implemented, guarding view port access with mutex. * TODO FL-3515: removed, questionable but ok * TODO FL-3510: removed, comment added * TODO FL-3500: refactored, store pin numbers in GpioPinRecord, fix gpio app crash caused by incorrect gpio_pins traversal. * Format Sources * TODO FL-3505: removed, mutex alone is not going to fix issue with WPAN architecture * TODO FL-3506: removed, change ownership by copy is good * TODO FL-3519: documentation and link to source added * Lib: remove unneded total sum from comment in bq27220 --------- Co-authored-by: hedger <hedger@users.noreply.github.com>
272 lines
9.3 KiB
C
272 lines
9.3 KiB
C
|
|
#include "bq27220.h"
|
|
#include "bq27220_reg.h"
|
|
#include "bq27220_data_memory.h"
|
|
|
|
_Static_assert(sizeof(BQ27220DMGaugingConfig) == 2, "Incorrect structure size");
|
|
|
|
#include <furi.h>
|
|
#include <stdbool.h>
|
|
|
|
#define TAG "Gauge"
|
|
|
|
static uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) {
|
|
uint16_t buf = 0;
|
|
|
|
furi_hal_i2c_read_mem(
|
|
handle, BQ27220_ADDRESS, address, (uint8_t*)&buf, 2, BQ27220_I2C_TIMEOUT);
|
|
|
|
return buf;
|
|
}
|
|
|
|
static bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) {
|
|
bool ret = furi_hal_i2c_write_mem(
|
|
handle, BQ27220_ADDRESS, CommandControl, (uint8_t*)&control, 2, BQ27220_I2C_TIMEOUT);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) {
|
|
uint8_t ret = 0;
|
|
for(uint16_t i = 0; i < len; i++) {
|
|
ret += data[i];
|
|
}
|
|
return 0xFF - ret;
|
|
}
|
|
|
|
static bool bq27220_parameter_check(
|
|
FuriHalI2cBusHandle* handle,
|
|
uint16_t address,
|
|
uint32_t value,
|
|
size_t size,
|
|
bool update) {
|
|
furi_assert(size == 1 || size == 2 || size == 4);
|
|
bool ret = false;
|
|
uint8_t buffer[6] = {0};
|
|
uint8_t old_data[4] = {0};
|
|
|
|
do {
|
|
buffer[0] = address & 0xFF;
|
|
buffer[1] = (address >> 8) & 0xFF;
|
|
|
|
for(size_t i = 0; i < size; i++) {
|
|
buffer[1 + size - i] = (value >> (i * 8)) & 0xFF;
|
|
}
|
|
|
|
if(update) {
|
|
// Datasheet contains incorrect procedure for memory update, more info:
|
|
// https://e2e.ti.com/support/power-management-group/power-management/f/power-management-forum/719878/bq27220-technical-reference-manual-sluubd4-is-missing-extended-data-commands-chapter
|
|
|
|
// 2. Write the address AND the parameter data to 0x3E+ (auto increment)
|
|
if(!furi_hal_i2c_write_mem(
|
|
handle,
|
|
BQ27220_ADDRESS,
|
|
CommandSelectSubclass,
|
|
buffer,
|
|
size + 2,
|
|
BQ27220_I2C_TIMEOUT)) {
|
|
FURI_LOG_I(TAG, "DM write failed");
|
|
break;
|
|
}
|
|
|
|
furi_delay_us(10000);
|
|
|
|
// 3. Calculate the check sum: 0xFF - (sum of address and data) OR 0xFF
|
|
uint8_t checksum = bq27220_get_checksum(buffer, size + 2);
|
|
// 4. Write the check sum to 0x60 and the total length of (address + parameter data + check sum + length) to 0x61
|
|
buffer[0] = checksum;
|
|
// 2 bytes address, `size` bytes data, 1 byte check sum, 1 byte length
|
|
buffer[1] = 2 + size + 1 + 1;
|
|
if(!furi_hal_i2c_write_mem(
|
|
handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT)) {
|
|
FURI_LOG_I(TAG, "CRC write failed");
|
|
break;
|
|
}
|
|
|
|
furi_delay_us(10000);
|
|
ret = true;
|
|
} else {
|
|
if(!furi_hal_i2c_write_mem(
|
|
handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 2, BQ27220_I2C_TIMEOUT)) {
|
|
FURI_LOG_I(TAG, "DM SelectSubclass for read failed");
|
|
break;
|
|
}
|
|
|
|
if(!furi_hal_i2c_rx(handle, BQ27220_ADDRESS, old_data, size, BQ27220_I2C_TIMEOUT)) {
|
|
FURI_LOG_I(TAG, "DM read failed");
|
|
break;
|
|
}
|
|
|
|
if(*(uint32_t*)&(old_data[0]) != *(uint32_t*)&(buffer[2])) {
|
|
FURI_LOG_W( //-V641
|
|
TAG,
|
|
"Data at 0x%04x(%zu): 0x%08lx!=0x%08lx",
|
|
address,
|
|
size,
|
|
*(uint32_t*)&(old_data[0]),
|
|
*(uint32_t*)&(buffer[2]));
|
|
} else {
|
|
ret = true;
|
|
}
|
|
}
|
|
} while(0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool bq27220_data_memory_check(
|
|
FuriHalI2cBusHandle* handle,
|
|
const BQ27220DMData* data_memory,
|
|
bool update) {
|
|
if(update) {
|
|
if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) {
|
|
FURI_LOG_E(TAG, "ENTER_CFG_UPDATE command failed");
|
|
return false;
|
|
};
|
|
|
|
// Wait for enter CFG update mode
|
|
uint32_t timeout = 100;
|
|
OperationStatus status = {0};
|
|
while((status.CFGUPDATE != true) && (timeout-- > 0)) {
|
|
bq27220_get_operation_status(handle, &status);
|
|
}
|
|
|
|
if(timeout == 0) {
|
|
FURI_LOG_E(TAG, "CFGUPDATE mode failed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Process data memory records
|
|
bool result = true;
|
|
while(data_memory->type != BQ27220DMTypeEnd) {
|
|
if(data_memory->type == BQ27220DMTypeWait) {
|
|
furi_delay_us(data_memory->value.u32);
|
|
} else if(data_memory->type == BQ27220DMTypeU8) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.u8, 1, update);
|
|
} else if(data_memory->type == BQ27220DMTypeU16) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.u16, 2, update);
|
|
} else if(data_memory->type == BQ27220DMTypeU32) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.u32, 4, update);
|
|
} else if(data_memory->type == BQ27220DMTypeI8) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.i8, 1, update);
|
|
} else if(data_memory->type == BQ27220DMTypeI16) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.i16, 2, update);
|
|
} else if(data_memory->type == BQ27220DMTypeI32) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.i32, 4, update);
|
|
} else if(data_memory->type == BQ27220DMTypeF32) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, data_memory->value.u32, 4, update);
|
|
} else if(data_memory->type == BQ27220DMTypePtr8) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, *(uint8_t*)data_memory->value.u32, 1, update);
|
|
} else if(data_memory->type == BQ27220DMTypePtr16) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, *(uint16_t*)data_memory->value.u32, 2, update);
|
|
} else if(data_memory->type == BQ27220DMTypePtr32) {
|
|
result &= bq27220_parameter_check(
|
|
handle, data_memory->address, *(uint32_t*)data_memory->value.u32, 4, update);
|
|
} else {
|
|
furi_crash("Invalid DM Type");
|
|
}
|
|
data_memory++;
|
|
}
|
|
|
|
// Finalize configuration update
|
|
if(update) {
|
|
bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT);
|
|
furi_delay_us(10000);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool bq27220_init(FuriHalI2cBusHandle* handle) {
|
|
// Request device number(chip PN)
|
|
if(!bq27220_control(handle, Control_DEVICE_NUMBER)) {
|
|
FURI_LOG_E(TAG, "Device is not present");
|
|
return false;
|
|
};
|
|
// Check control response
|
|
uint16_t data = 0;
|
|
data = bq27220_read_word(handle, CommandControl);
|
|
if(data != 0xFF00) {
|
|
FURI_LOG_E(TAG, "Invalid control response: %x", data);
|
|
return false;
|
|
};
|
|
|
|
data = bq27220_read_word(handle, CommandMACData);
|
|
FURI_LOG_I(TAG, "Device Number %04x", data);
|
|
|
|
return data == 0x0220;
|
|
}
|
|
|
|
bool bq27220_apply_data_memory(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) {
|
|
FURI_LOG_I(TAG, "Verifying data memory");
|
|
if(!bq27220_data_memory_check(handle, data_memory, false)) {
|
|
FURI_LOG_I(TAG, "Updating data memory");
|
|
bq27220_data_memory_check(handle, data_memory, true);
|
|
}
|
|
FURI_LOG_I(TAG, "Data memory verification complete");
|
|
|
|
return true;
|
|
}
|
|
|
|
uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandVoltage);
|
|
}
|
|
|
|
int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandCurrent);
|
|
}
|
|
|
|
bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) {
|
|
uint16_t data = bq27220_read_word(handle, CommandBatteryStatus);
|
|
if(data == BQ27220_ERROR) {
|
|
return false;
|
|
} else {
|
|
*(uint16_t*)battery_status = data;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) {
|
|
uint16_t data = bq27220_read_word(handle, CommandOperationStatus);
|
|
if(data == BQ27220_ERROR) {
|
|
return false;
|
|
} else {
|
|
*(uint16_t*)operation_status = data;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandTemperature);
|
|
}
|
|
|
|
uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandFullChargeCapacity);
|
|
}
|
|
|
|
uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandDesignCapacity);
|
|
}
|
|
|
|
uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandRemainingCapacity);
|
|
}
|
|
|
|
uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandStateOfCharge);
|
|
}
|
|
|
|
uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle) {
|
|
return bq27220_read_word(handle, CommandStateOfHealth);
|
|
}
|