Files
MSE-SoftwEng/pico-sensor/McuLib/src/McuLC709203F.c
2025-05-06 13:07:01 +00:00

455 lines
17 KiB
C

/*
* Copyright (c) 2019, Erich Styger, Sarah Gander
* All rights reserved.
*
* Driver for GPIO pins
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "McuLib.h"
#if McuLib_CONFIG_CPU_IS_STM
#include "libIIC.h"
#endif
#include "McuLC709203Fconfig.h"
#include "McuLC709203F.h"
#include "McuGenericI2C.h"
#include "McuUtility.h"
#include "McuWait.h"
#define LC709203F_I2C_SLAVE_ADDR 0x0B
#define LC709203F_REG_SET_BEFORE_RSOC 0x04 /* Before RSCOC */
#define LC709203F_REG_SET_THERMB 0x06 /* Thermistor B */
#define LC709203F_REG_INIT_RSOC 0x07 /* Initial RSOC */
#define LC709203F_REG_CELL_TEMP 0x08 /* Cell temperature */
#define LC709203F_REG_VOLTAGE 0x09 /* Cell voltage */
#define LC709203F_REG_CURRENT_DIRECTION 0x0A /* Current direction */
#define LC709203F_REG_ADJ_APPLI 0x0B /* APA, Adjustment Pack Application, sets Parasitic impedance */
#define LC709203F_REG_APT 0x0C /* APT, Adjustment Pack Thermistor, adjust temperature measurement delay timing */
#define LC709203F_REG_RSOC 0x0D /* RSOC, value based 0-100 scale */
#define LC709203F_REG_ITE 0x0F /* ITE, Indicator to Empty */
#define LC709203F_REG_IC_VER 0x11 /* IC version */
#define LC709203F_REG_CHG_PARAM 0x12 /* change of parameter */
#define LC709203F_REG_RSOC_ALM 0x13 /* Alarm low RSOC */
#define LC709203F_REG_LOW_CELL_VOL_ALM 0x14 /* Alarm Low Cell Voltage */
#define lC709203F_REG_PW_MODE 0x15 /* IC Power Mode */
#define LC709203F_REG_EN_NTC 0x16 /* Status Bit, enable Thermistor mode */
#define LC709203F_REG_NUM_PARAMS 0x1A /* Number of the parameter, display batter profile code */
#define MCULC_TEMP_ZERO_CELSIUS 0x0AAC /* From the data sheet command table: 0x0AAC is 0 degree Celsius */
static uint8_t i2cReadCmdData(uint8_t i2cAddr, uint8_t cmd, uint8_t *data, size_t length) {
return McuGenericI2C_ReadAddress(i2cAddr, &cmd, sizeof(cmd), data, length);
}
static uint8_t i2cWriteCmdData(uint8_t i2cAddr, uint8_t cmd, uint8_t *data, size_t length) {
return McuGenericI2C_WriteAddress(i2cAddr, &cmd, sizeof(cmd), data, length);
}
static uint8_t calcCrc8Atm(uint8_t a, uint8_t b) {
/* the sensor uses a CRC-8-ATM,
* Polynomial: x8 + x2 + x + 1
* Corresponds to: 100000111
*/
#define POLY_8 0x8380
uint8_t i = 0;
uint16_t val = 0;
val = (uint16_t)(a^b);
val <<= 8;
for(i=0; i<8; i++) {
if (val&0x8000 ){
val ^= POLY_8;
}
val <<= 1;
}
return (uint8_t)(val >> 8);
}
static uint8_t calcCRC_WriteAccess16(uint8_t i2cSlaveAddr, uint8_t cmd, uint8_t low, uint8_t high) {
uint8_t crc;
crc = calcCrc8Atm(0x00, i2cSlaveAddr<<1); /* I2C slave address */
crc = calcCrc8Atm(crc, cmd); /* command */
crc = calcCrc8Atm(crc, low); /* data byte */
return calcCrc8Atm(crc, high); /* data byte */
}
static uint8_t calcCRC_ReadAccess16(uint8_t i2cSlaveAddr, uint8_t cmd, uint8_t low, uint8_t high) {
uint8_t crc;
crc = calcCrc8Atm(0x00, i2cSlaveAddr<<1); /* I2C slave address */
crc = calcCrc8Atm(crc, cmd); /* command */
crc = calcCrc8Atm(crc, (i2cSlaveAddr<<1)|1); /* I2C address with R bit set */
crc = calcCrc8Atm(crc, low); /* data byte */
return calcCrc8Atm(crc, high); /* data byte */
}
static uint8_t CheckCrc(uint8_t i2cSlaveAddr, uint8_t cmd, uint8_t low, uint8_t high, uint8_t crc) {
uint8_t val;
val = calcCRC_ReadAccess16(i2cSlaveAddr, cmd, low, high);
if (val != crc) {
return ERR_FAILED;
}
return ERR_OK;
}
static uint8_t CheckI2CErr(uint8_t res) {
if (res==ERR_OK) {
return ERR_OK; /* ok */
}
#if MCULC_CONFIG_BLOCK_ON_I2C_ERROR
for(;;) { /* I2C Error? wait for the watchdog to kick in... */
McuWait_WaitOSms(50);
}
#endif
return ERR_FAILED;
}
static uint8_t WriteCmdWordChecked(uint8_t i2cSlaveAddr, uint8_t cmd, uint8_t low, uint8_t high) {
uint8_t data[3];
data[0] = low;
data[1] = high;
data[2] = calcCRC_WriteAccess16(i2cSlaveAddr, cmd, data[0], data[1]);
return CheckI2CErr(i2cWriteCmdData(i2cSlaveAddr, cmd, data, sizeof(data)));
}
void McuLC_Wakeup(void) {
/* Generates wake up signal on SDA, according to the data sheet.
* This has to happen before any other I2C communication on the bus:
* Pull down SDA for 0.6us, then high again,
* wait for 400 us
* */
#if McuLib_CONFIG_CPU_IS_STM
/* set SDA pin of I2C to GPIO Output mode. */
HAL_I2C_MspDeInit(&hi2c3);
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_RESET);
McuWait_Waitus(1); /* SDA min 0.6us low */
HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET);
McuWait_Waitus(400); /* wait 400us */
/* set SDA pin of I2C to GPIO Ouput mode. */
HAL_I2C_MspInit(&hi2c3);
#elif McuLib_CONFIG_SDK_VERSION_USED==McuLib_CONFIG_SDK_PROCESSOR_EXPERT
PORT_PDD_SetPinMuxControl(PORTB_BASE_PTR, 3, PORT_PDD_MUX_CONTROL_ALT1); /* MUX SDA/PTB3 as GPIO */
PORT_PDD_SetPinOpenDrain(PORTB_BASE_PTR, 3, PORT_PDD_OPEN_DRAIN_ENABLE);
GPIO_PDD_SetPortOutputDirectionMask(PTB_BASE_PTR, (1<<3)); /* SDA/PTB3 as output */
GPIO_PDD_ClearPortDataOutputMask(PTB_BASE_PTR, 1<<3); /* SDA/PB3 low */
McuWait_Waitus(1); /* SDA min 0.6us low */
GPIO_PDD_SetPortDataOutputMask(PTB_BASE_PTR, 1<<3); /* SDA/PB3 high */
McuWait_Waitus(400); /* wait 400us */
/* mux back to normal I2C mode with interrupts enabled */
PORTB_PCR3 = (uint32_t)((PORTB_PCR3 & (uint32_t)~(uint32_t)(
PORT_PCR_ISF_MASK |
PORT_PCR_MUX(0x05)
)) | (uint32_t)(
PORT_PCR_MUX(0x02)
));
PORT_PDD_SetPinOpenDrain(PORTB_BASE_PTR, 0x03u, PORT_PDD_OPEN_DRAIN_ENABLE); /* Set SDA pin as open drain */
#else
/* NYI for NXP SDK yet */
#endif
}
static uint8_t ReadCmdWordChecked(uint8_t i2cSlaveAddr, uint8_t cmd, uint16_t *val) {
uint8_t data[3];
uint8_t res;
res = CheckI2CErr(i2cReadCmdData(i2cSlaveAddr, cmd, data, sizeof(data)));
if (res!=ERR_OK) {
return res;
}
res = CheckI2CErr(CheckCrc(i2cSlaveAddr, cmd, data[0], data[1], data[2]));
if (res!=ERR_OK) {
return res;
}
*val = (data[1]<<8) | data[0];
return ERR_OK;
}
/* returns cell voltage in mV */
uint8_t McuLC_GetVoltage(uint16_t *pVoltage) {
return ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_VOLTAGE, pVoltage);
}
/* returns cell temperature (10 = 1 degree C) */
uint8_t McuLC_GetTemperature(int16_t *pTemperature) {
/* cell temperature is in 0.1C units, from 0x09E4 (-20C) up to 0x0D04 (60C) */
uint8_t res;
res = ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_CELL_TEMP, (uint16_t*)pTemperature);
if (res!=ERR_OK) {
return res;
}
*pTemperature -= MCULC_TEMP_ZERO_CELSIUS; /* From the data sheet command table: 0x0AAC is 0 degree Celsius */
return ERR_OK;
}
uint8_t McuLC_SetTemperature(int16_t temperature) {
/* cell temperature is in 0.1C units, from 0x09E4 (-20C) up to 0x0D04 (60C) */
temperature = ((uint16_t)temperature)+MCULC_TEMP_ZERO_CELSIUS; /* From the data sheet command table: 0x0AAC is 0 degree Celsius */
return WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_CELL_TEMP, ((uint16_t)temperature)&0xff, (((uint16_t)temperature)>>8)&0xff);
}
/* returns battery Relative State of Charge in percent */
uint8_t McuLC_GetRSOC(uint16_t *pRsoc) {
return ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_RSOC, pRsoc);
}
/* Indicator to empty, returns battery charge in thousandth */
uint8_t McuLC_GetITE(uint16_t *pIte) {
return ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_ITE, pIte);
}
/* Indicator to empty, returns battery charge in thousandth */
uint8_t McuLC_GetICversion(uint16_t *pVersion) {
return ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_IC_VER, pVersion);
}
#if MCULC709203F_CONFIG_PARSE_COMMAND_ENABLED
static const unsigned char *McuLC_CurrentDirectionToString(McuLC_CurrentDirection dir) {
switch(dir) {
case McuLC_CURRENT_DIR_AUTO: return (const unsigned char*)"auto";
case McuLC_CURRENT_DIR_CHARGING: return (const unsigned char*)"charging";
case McuLC_CURRENT_DIR_DISCHARING: return (const unsigned char*)"discharging";
case McuLC_CURRENT_DIR_ERROR:
default: return (const unsigned char*)"ERROR";
}
}
#endif
uint8_t McuLC_GetCurrentDirection(McuLC_CurrentDirection *pDir) {
uint16_t val;
uint8_t res;
res = ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_CURRENT_DIRECTION, &val);
if (res!=ERR_OK) {
return res;
}
switch(val) {
case 0: *pDir = McuLC_CURRENT_DIR_AUTO; break;
case 1: return McuLC_CURRENT_DIR_CHARGING; break;
case 0xffff: return McuLC_CURRENT_DIR_DISCHARING; break;
default: return McuLC_CURRENT_DIR_ERROR; break;
}
return ERR_OK;
}
uint8_t McuLC_SetCurrentDirection(McuLC_CurrentDirection direction) {
uint8_t low, high;
switch(direction) {
case McuLC_CURRENT_DIR_AUTO: low = 0x00; high = 0x00; break;
case McuLC_CURRENT_DIR_CHARGING: low = 0x01; high = 0x00; break;
case McuLC_CURRENT_DIR_DISCHARING: low = 0xff; high = 0xff; break;
default:
CheckI2CErr(ERR_FAILED);
return ERR_FAILED;
}
return WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_CURRENT_DIRECTION, low, high);
}
#if MCULC709203F_CONFIG_PARSE_COMMAND_ENABLED
static uint8_t PrintStatus(McuShell_ConstStdIOType *io) {
uint8_t buf[32], res;
McuShell_SendStatusStr((unsigned char*)"LC", (const unsigned char*)"\r\n", io->stdOut);
{
uint16_t version;
res = McuLC_GetICversion(&version); /* battery charge in thousandth */
if (res==ERR_OK) {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x");
McuUtility_strcatNum16Hex(buf, sizeof(buf), version);
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)"\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((unsigned char*)" IC version", buf, io->stdOut);
}
{
uint16_t rsoc;
res = McuLC_GetRSOC(&rsoc); /* battery charge in percent */
if (res==ERR_OK) {
McuUtility_Num16uToStr(buf, sizeof(buf), rsoc);
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)"\% (0..100)\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((unsigned char*)" RSOC", buf, io->stdOut);
}
{
uint16_t ite;
res = McuLC_GetITE(&ite); /* battery charge in thousandth */
if (res==ERR_OK) {
McuUtility_Num16uToStr(buf, sizeof(buf), ite);
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" (0..1000)\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((unsigned char*)" ITE", buf, io->stdOut);
}
{
int16_t temperature;
bool isI2Cmode;
res = McuLC_GetTemperature(&temperature); /* cell temperature in 1/10-degree C */
if (res==ERR_OK) {
McuUtility_Num16sToStr(buf, sizeof(buf), temperature/10);
McuUtility_chcat(buf, sizeof(buf), '.');
if (temperature<0) { /* make it positive */
temperature = -temperature;
}
McuUtility_strcatNum16s(buf, sizeof(buf), temperature%10);
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" C");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR");
}
if (McuLC_GetTemperatureMeasurementMode(&isI2Cmode)==ERR_OK) {
if (isI2Cmode) {
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" (I2C mode)");
} else {
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" (Thermistor mode)");
}
} else {
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" (ERROR!)");
}
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" Temperature", buf, io->stdOut);
}
{
uint16_t mVolt;
res = McuLC_GetVoltage(&mVolt); /* cell voltage in milli-volts */
if (res==ERR_OK) {
McuUtility_Num16uToStr(buf, sizeof(buf), mVolt);
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)" mV\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((unsigned char*)" Voltage", buf, io->stdOut);
}
{
McuLC_CurrentDirection direction;
res = McuLC_GetCurrentDirection(&direction);
if (res==ERR_OK) {
McuUtility_strcpy(buf, sizeof(buf), McuLC_CurrentDirectionToString(direction));
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)"\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"ERROR\r\n");
}
McuShell_SendStatusStr((unsigned char*)" Current dir", buf, io->stdOut);
}
return ERR_OK;
}
#endif /* MCULC709203F_CONFIG_PARSE_COMMAND_ENABLED */
#if MCULC709203F_CONFIG_PARSE_COMMAND_ENABLED
uint8_t McuLC_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
uint8_t res = ERR_OK;
const uint8_t *p;
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "LC help")==0) {
McuShell_SendHelpStr((unsigned char*)"LC", (const unsigned char*)"Group of LC709203F commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (const unsigned char*)"Print help or status information\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" settemp <temp>", (const unsigned char*)"In I2C temperature mode, set the temperature in deci-Celsius\r\n", io->stdOut);
*handled = TRUE;
return ERR_OK;
} else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "LC status")==0)) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, (char*)"LC settemp ", sizeof("LC settemp ")-1)==0) {
int16_t val;
*handled = TRUE;
p = cmd+sizeof("LC settemp ")-1;
if (McuUtility_ScanDecimal16sNumber(&p, &val)!=ERR_OK) {
return ERR_FAILED;
}
return McuLC_SetTemperature(val);
}
return res;
}
#endif /* MCULC709203F_CONFIG_PARSE_COMMAND_ENABLED */
uint8_t McuLC_GetTemperatureMeasurementMode(bool *isI2Cmode) {
uint16_t val=0;
uint8_t res;
res = ReadCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_EN_NTC, &val);
if (res!=ERR_OK) {
return res;
}
*isI2Cmode = val==0; /* 0: i2c, 1: thermistor mode */
return ERR_OK;
}
uint8_t McuLC_SetTemperatureMeasurementMode(bool i2cMode) {
return WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_EN_NTC, i2cMode?0x00:0x01, 0x0);
}
uint8_t McuLC_SetPowerMode(bool sleepMode) {
return WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, lC709203F_REG_PW_MODE, sleepMode?0x02:0x01, 0x00);
}
uint8_t McuLC_Init(void) {
/* initializes LC709203F for Renata ICP543759PMT battery */
uint8_t res;
/* operational mode (1: operational, 2: sleep), 0x0001 = operational mode */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, lC709203F_REG_PW_MODE, 0x01, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
/* APA, set parasitic impedance (table 7 in data sheet): 0x000C, value for 280mA */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_ADJ_APPLI, 0x0C, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
/* Battery Profile (table 8 in data sheet): 0x0001 = Type 1 */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_CHG_PARAM, 0x01, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
/* write 0xAA55 to initialize RSOC */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_INIT_RSOC, 0x55, 0xAA);
if (res!=ERR_OK) { return ERR_FAILED; }
/* Set battery measurement mode: 0x0001: thermistor mode. 0x0000: I2C mode */
#if MCULC709203F_CONFIG_USE_THERMISTOR
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_EN_NTC, 0x01, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
#else
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_EN_NTC, 0x00, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
#endif
/* set the B-constant of the thermistor to be measured */
#if MCULC709203F_CONFIG_USE_THERMISTOR
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_SET_THERMB, 0x6B, 0x0D); /* Thermistor B-Constant: 0x0D6B = B=3435 */
if (res!=ERR_OK) { return ERR_FAILED; }
#endif
/* Low voltage alarm setting: 0x0C1C = 3100mV = 3.1V, low byte first */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_LOW_CELL_VOL_ALM, 0x1C, 0x0C);
if (res!=ERR_OK) { return ERR_FAILED; }
/* 0x0000 = alarm disable, low byte first */
res = WriteCmdWordChecked(LC709203F_I2C_SLAVE_ADDR, LC709203F_REG_RSOC_ALM, 0x00, 0x00);
if (res!=ERR_OK) { return ERR_FAILED; }
return ERR_OK;
}
void McuLC_Deinit(void) {
/* nothing to do */
}