ADD: BWM status includes AW32001ECSR registerd, Charging set to 256mAh and keeps BWM safety guards for temperature

This commit is contained in:
Niel Nielsen
2026-08-23 20:31:01 +02:00
parent 9a083e6873
commit 190ed8a990
4 changed files with 130 additions and 1 deletions
+84
View File
@@ -196,6 +196,27 @@ static void print_pm5_battery_status(void) {
(sysstat & 0x02) ? ", power good" : ", power fail",
(sysstat & 0x01) ? ", thermal-reg" : "");
}
// Configured charge profile (read-only). Decode tables from AW32001E datasheet V1.4:
// IIN_LIM (REG00[3:0]): 0000=50mA, else 80mA + 30mA*(code-1) [1111=500mA]
// VIN_DPM (REG00[7:4]): 3880mV + 80mV*code [1000=4.52V default]
// ICHG (REG02[5:0]): 8mA * (code+1) [63=512mA]
uint8_t reg00 = 0, reg01 = 0, reg02 = 0;
if (I2C_BufferReadRaw(&reg00, 1, 0x00, BWM_CHG_ADDR) > 0) {
uint8_t iin = reg00 & 0x0F;
uint8_t vdpm = (reg00 >> 4) & 0x0F;
uint16_t iin_ma = (iin == 0) ? 50 : (80 + 30 * (iin - 1));
uint16_t vdpm_mv = 3880 + 80 * vdpm;
Dbprintf(" Input limit......... %u mA, VIN_DPM %u.%02u V",
iin_ma, vdpm_mv / 1000, (vdpm_mv % 1000) / 10);
}
if (I2C_BufferReadRaw(&reg01, 1, 0x01, BWM_CHG_ADDR) > 0) {
Dbprintf(" Charge enable....... %s", (reg01 & (1u << 3)) ? _YELLOW_("disabled") : _GREEN_("enabled"));
}
if (I2C_BufferReadRaw(&reg02, 1, 0x02, BWM_CHG_ADDR) > 0) {
uint16_t ichg_ma = 8 * ((reg02 & 0x3F) + 1);
Dbprintf(" Charge current...... %u mA", ichg_ma);
}
}
// --- fuel gauge (BQ27427) ---
@@ -259,6 +280,24 @@ static bool bq_read_design_cap(uint16_t *cap) {
return true;
}
// Enable or disable battery charging by clearing/setting CEB (REG01[3]:
// 0 = charge enabled, 1 = charge disabled). Read-modify-write to preserve the
// other REG01 fields. NOTE: REG01 is watchdog-affected on the AW32001E - this
// reverts to its default on watchdog expiry (~160 s) unless the watchdog is
// serviced (REG02[6]=1) or disabled (REG05[6:5]=00), so treat it as a one-shot.
static bool bwm_charger_set_charge(bool enable) {
uint8_t reg01 = 0;
if (I2C_BufferReadRaw(&reg01, 1, 0x01, BWM_CHG_ADDR) <= 0) {
return false;
}
if (enable) {
reg01 &= ~(1u << 3); // CEB = 0 -> charge enabled
} else {
reg01 |= (1u << 3); // CEB = 1 -> charge disabled
}
return I2C_BufferWrite(&reg01, 1, 0x01, BWM_CHG_ADDR);
}
// Program Design Capacity (and matching Design Energy). Idempotent: returns true
// without a config-update cycle if the value is already correct.
static bool bwm_gauge_provision_capacity(uint16_t cap_mah) {
@@ -311,6 +350,41 @@ static bool bwm_gauge_provision_capacity(uint16_t cap_mah) {
while (((flags & 0x0010) != 0) && (++tries < 40));
return ((flags & 0x0010) == 0);
}
// Strong override of the weak UnitTestMain() in start.c. Vector() calls UnitTestMain()
// after ConfigSystemClocks() and before AppMain(); the weak default is empty, so a
// strong definition here runs at boot and then returns into AppMain() normally.
//
// This enables battery charging on the BWM by replicating the charger register writes
// from at32_unit_test.c:test_bat_charger_only_settings() (upstream RRG values), WITHOUT
// pulling in that file's unrelated UART-debug / RGB test routines. AW32001E @ 0x93:
// REG01[3] CEB -> 0 : charge enabled
// REG02 ICHG = 0x1F : 256 mA charge current
// REG05 = 0x1A : safety timer disabled (matches upstream; note the charger
// watchdog is thus relied upon off - see REG05 handling)
// REG03 = 0xE1 : 3 A discharge current
// REG0B = 0x6B : 11 mA pre-charge current
void UnitTestMain(void); // strong override of the weak stub in start.c
void UnitTestMain(void) {
StartTicks();
I2C_init(true);
uint8_t v = 0;
if (I2C_BufferReadRaw(&v, 1, 0x01, BWM_CHG_ADDR) > 0) {
if (v & (1u << 3)) { v &= ~(1u << 3); I2C_BufferWrite(&v, 1, 0x01, BWM_CHG_ADDR); }
}
// REG02: charge current = 256 mA
v = 0x1F;
I2C_BufferWrite(&v, 1, 0x02, BWM_CHG_ADDR);
// REG03: discharge current = 3 A
v = 0xE1;
I2C_BufferWrite(&v, 1, 0x03, BWM_CHG_ADDR);
// REG05: disable safety timer (upstream behaviour)
// v = 0x1A;
// I2C_BufferWrite(&v, 1, 0x05, BWM_CHG_ADDR);
// REG0B: pre-charge current = 11 mA
v = 0x6B;
I2C_BufferWrite(&v, 1, 0x0B, BWM_CHG_ADDR);
}
#endif // WITH_BWM_STATUS
#ifdef WITH_LCD
@@ -3891,6 +3965,16 @@ static void PacketReceived(PacketCommandNG *packet) {
reply_ng(CMD_PM5_BWM_SET_CAP, ok ? PM3_SUCCESS : PM3_EFAILED, (uint8_t *)&cap, sizeof(cap));
break;
}
case CMD_PM5_BWM_CHARGE_EN: {
// Enable/disable battery charging (clear/set AW32001E CEB, REG01[3]).
// Payload: 1 byte, non-zero = enable (default), zero = disable.
// One-shot: reverts on the charger watchdog timeout (~160 s).
bool enable = (packet->length >= 1) ? (packet->data.asBytes[0] != 0) : true;
I2C_init(true);
bool ok = bwm_charger_set_charge(enable);
reply_ng(CMD_PM5_BWM_CHARGE_EN, ok ? PM3_SUCCESS : PM3_EFAILED, NULL, 0);
break;
}
#endif
#endif
default: {
+42
View File
@@ -1500,6 +1500,47 @@ static int CmdTearoff(const char *Cmd) {
return handle_tearoff(&params, !silent);
}
static int CmdBwmCharge(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hw bwmcharge",
"Enable or disable BWM battery charging by clearing/setting the\n"
"AW32001E charge-enable bit (CEB, REG01[3]). PM5 only.\n"
_RED_("One-shot:") " the charger watchdog reverts this after ~160 s unless\n"
"serviced, so charging may stop on its own. Use to nudge a top-up.",
"hw bwmcharge --> enable charging\n"
"hw bwmcharge --off --> disable charging");
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "off", "disable charging (default is enable)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool disable = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
uint8_t payload = disable ? 0 : 1;
PrintAndLogEx(INFO, "%s BWM battery charging...", disable ? "Disabling" : "Enabling");
clearCommandBuffer();
SendCommandNG(CMD_PM5_BWM_CHARGE_EN, &payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_PM5_BWM_CHARGE_EN, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "command timeout (is this a PM5 with a BWM fitted?)");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(FAILED, "charger did not respond (check BWM present)");
return resp.status;
}
PrintAndLogEx(SUCCESS, "Charging %s. Verify with " _YELLOW_("hw status") ".",
disable ? "disabled" : "enabled");
if (disable == false) {
PrintAndLogEx(HINT, "Reverts on the charger watchdog (~160 s) if not serviced.");
}
return PM3_SUCCESS;
}
static int CmdBwmSetCap(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hw bwmsetcap",
@@ -2009,6 +2050,7 @@ static command_t CommandTable[] = {
{"standalone", CmdStandalone, IfPm3Present, "Start installed standalone mode on device"},
{"tia", CmdTia, IfPm3Present, "Trigger a Timing Interval Acquisition to re-adjust the RealTimeCounter divider"},
{"bwmsetcap", CmdBwmSetCap, IfPm5, "Set BWM fuel-gauge design capacity (PM5, run once after battery change)"},
{"bwmcharge", CmdBwmCharge, IfPm5, "Enable/disable BWM battery charging (PM5, one-shot)"},
{"tune", CmdTune, IfPm3Lf, "Measure tuning of device antenna"},
{"decay", CmdDecay, IfPm3Present, "Measure HF antenna decay after field-off"},
{NULL, NULL, NULL, NULL}
+1 -1
View File
@@ -178,7 +178,7 @@ else ifeq ($(PLATFORM),PM5)
FPGA_BITSTREAMS = fpga_pm3_hf.bit # TODO DXL 虽然可以不把打包比特流,但是还是得把FPGA的版本信息给生成,让EXE依赖。
SKIP_FPGA_EMBED = true # important!!! disable the fpga bit files pack to arm!
SKIP_COMPRESSION = true # Skip data section compress. The new mcu has enough flash space.
PLATFORM_DEFS = -DWITH_FLASH -DPM5 -DCHIP_AT32F435_37 -DWITH_BWM_STATUS # TODO 暂时不要编译i2c -DWITH_SMARTCARD
PLATFORM_DEFS = -DWITH_FLASH -DPM5 -DCHIP_AT32F435_37 -DWITH_BWM_STATUS -DWITH_BWM_LOWBATT_BEEP # TODO 暂时不要编译i2c -DWITH_SMARTCARD
PLTNAME = Proxmark5
PLATFORM_FPGA = GW1NR-LV2MG49GC6/i5
else
+3
View File
@@ -641,6 +641,9 @@ typedef struct {
#define CMD_PM5_RGB_SET 0x0178
// PM5, provision BWM fuel-gauge (BQ27427) Design Capacity. Used by `hw bwmsetcap`.
#define CMD_PM5_BWM_SET_CAP 0x0179
#define CMD_PM5_BWM_SET_CAP 0x0179
// PM5, enable/disable BWM battery charging (AW32001E CEB). Used by `hw bwmcharge`.
#define CMD_PM5_BWM_CHARGE_EN 0x017A
// For low-frequency tags
#define CMD_LF_TI_READ 0x0202
#define CMD_LF_TI_WRITE 0x0203