Last active
December 4, 2024 13:54
-
-
Save ryansturmer/6bad7f45b11de132c71b7782385b0e63 to your computer and use it in GitHub Desktop.
Embedded C Code Review Example 2 - Reading a Battery Fuel Gauge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <stdbool.h> | |
#define COULOMB_COUNT_POLL_INTERVAL 5000 // 5 seconds | |
#define COULOMB_REMOVAL_INTERVAL 2000 // 2 seconds | |
#define FUEL_GAUGE_ADDRESS 0x55 | |
#define FUEL_GAUGE_POWER_OK 0x01 | |
#define SYSTEM_TICK_MS 1 | |
// HAL Functions, assume these are defined elsewhere | |
bool I2C_WriteRegister(uint8_t address, uint8_t reg, uint16_t data); | |
bool I2C_ReadRegister(uint8_t address, uint8_t reg, uint16_t *data); | |
bool I2C_IsTransactionComplete(); | |
uint32_t GetSystemTick(void); | |
// Fuel gauge registers | |
#define FUEL_GAUGE_REG_POWER 0x00 | |
#define FUEL_GAUGE_REG_GAIN 0x01 | |
#define FUEL_GAUGE_REG_COULOMB_COUNT 0x02 | |
#define FUEL_GAUGE_REG_REMOVE_COULOMB 0x03 | |
// State machine states | |
typedef enum { | |
STATE_INIT, | |
STATE_IDLE, | |
STATE_REQUEST_POWER_STATUS, | |
STATE_WAIT_FOR_POWER_STATUS, | |
STATE_WAIT_FOR_GAIN_SET, | |
STATE_REQUEST_COULOMB_COUNT, | |
STATE_WAIT_FOR_COULOMB_COUNT, | |
STATE_REQUEST_REMOVE_COULOMBS, | |
STATE_WAIT_FOR_REMOVE_COULOMBS | |
} BatteryState; | |
static BatteryState current_state = STATE_INIT; | |
static uint32_t last_poll_time = 0; | |
static uint32_t last_coulomb_removal_time = 0; | |
static bool coulomb_removal_requested = false; | |
static uint16_t requested_coulomb_removal = 0; | |
static bool power_ok = false; | |
static uint16_t coulomb_count = 0; | |
// Public API, should be declared in header, but here for review | |
void StateMachine_Run(void); | |
void RequestCoulombRemoval(uint16_t amount); | |
uint16_t GetCoulombCount(void); | |
void StateMachine_Run(void) { | |
uint32_t current_time = GetSystemTick(); | |
static uint16_t response_data = 0; // Response buffer for I2C | |
switch (current_state) { | |
case STATE_INIT: | |
current_state = STATE_REQUEST_POWER_STATUS; | |
break; | |
case STATE_REQUEST_POWER_STATUS: | |
// Check to see if we've just powered on for the first time | |
// This should only happen if the battery goes completely dead | |
I2C_ReadRegister(FUEL_GAUGE_ADDRESS, FUEL_GAUGE_REG_POWER, &response_data); | |
current_state = STATE_WAIT_FOR_POWER_STATUS; | |
break; | |
case STATE_WAIT_FOR_POWER_STATUS: | |
if (I2C_TransactionComplete()) { | |
power_ok = (response_data & FUEL_GAUGE_POWER_OK) != 0; | |
if (!power_ok) { | |
// Fresh powerup. Need to set gain on fuel gauge. | |
I2C_WriteRegister(FUEL_GAUGE_ADDRESS, FUEL_GAUGE_REG_GAIN); | |
current_state = STATE_REQUEST_COULOMB_COUNT; | |
} | |
} | |
break; | |
case STATE_WAIT_FOR_GAIN_SET: | |
if (I2C_TransactionComplete()) { | |
current_state = STATE_IDLE; | |
} | |
case STATE_IDLE: | |
if (current_time - last_poll_time >= COULOMB_COUNT_POLL_INTERVAL) { | |
current_state = STATE_REQUEST_COULOMB_COUNT; | |
} | |
if (coulomb_removal_requested) { | |
current_state = STATE_REQUEST_REMOVE_COULOMBS; | |
} | |
break; | |
case STATE_REQUEST_COULOMB_COUNT: | |
I2C_ReadRegister(FUEL_GAUGE_ADDRESS, FUEL_GAUGE_REG_COULOMB_COUNT, &response_data); | |
current_state = STATE_WAIT_FOR_COULOMB_COUNT; | |
break; | |
case STATE_WAIT_FOR_COULOMB_COUNT: | |
while (!I2C_TransactionComplete()); | |
coulomb_count = response_data; | |
last_poll_time = current_time; | |
current_state = STATE_IDLE; | |
break; | |
case STATE_REQUEST_REMOVE_COULOMBS: | |
I2C_WriteRegister(FUEL_GAUGE_ADDRESS, FUEL_GAUGE_REG_REMOVE_COULOMB, requested_coulomb_removal); | |
current_state = STATE_WAIT_FOR_REMOVE_COULOMBS; | |
break; | |
case STATE_WAIT_FOR_REMOVE_COULOMBS: | |
if (I2C_TransactionComplete()) { | |
coulomb_removal_requested = false; | |
last_coulomb_removal_time = current_time; | |
current_state = STATE_IDLE; | |
} | |
break; | |
default: | |
current_state = STATE_INIT; | |
break; | |
} | |
} | |
void RequestCoulombRemoval(uint16_t amount) { | |
requested_coulomb_removal = amount; | |
coulomb_removal_requested = true; | |
} | |
uint16_t GetCoulombCount(void) { | |
return coulomb_count; | |
} | |
int main(void) { | |
while (1) { | |
StateMachine_Run(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment