diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7d184a3cf..97dbaad5f 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -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(®00, 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(®01, 1, 0x01, BWM_CHG_ADDR) > 0) { + Dbprintf(" Charge enable....... %s", (reg01 & (1u << 3)) ? _YELLOW_("disabled") : _GREEN_("enabled")); + } + if (I2C_BufferReadRaw(®02, 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(®01, 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(®01, 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: { diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 5796179f3..915b33e88 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -1500,6 +1500,47 @@ static int CmdTearoff(const char *Cmd) { return handle_tearoff(¶ms, !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} diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index 92c4ba2cf..af6293784 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -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 diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 0df3c1f68..bfe25945e 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -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