PM-BAT battery charging issue

I’m trying to get the PM-BAT module to work alongside a 7V solar panel and B404X. The PM-BAT is powering the B-SoM, however it is not charging the battery. I’ve tried adjusting various settings while VIN is connected to a 5V power supply, such as SystemPowerFeature::PMIC_DETECTION and SystemPowerFeature::ENABLE_CHARGING however the battery is still not charging. Are there any configurations I need to apply? Thanks in advance.

Do you have the charge LED populated, or can you monitor CHG pin (PM-BAT pin 13)? If charging is turning on and off every few seconds, you need to reduce the charge current. If the charge current is higher than the panel can supply if will collapse the voltage, which will cause charging to stop, which causes the voltage to rise, and the process repeats forever without actually charging

I do have an LED on the CHG pin and it’s not turning on. Right now I am testing it with a basic 5V DC adapter going to VIN, and CHG pin is not going high. Both the battery and 5V power supply are powering the B-SoM—I can hot swap between them so there doesn’t appear to be an issue with the power delivery.

System.powerSource() returns VIN and System.batteryState() returns not charging. When I disconnect the 5V power source, System.powerSource() returns battery and System.batteryState() returns discharging.

Maybe it’s a chip defect? I’ve got another PM-BAT on order, hopefully I can contact support about the first module if the second one works.

Just to be sure, the battery is not fully charged, correct? The charge LED turns off and the charge status is not charging once the battery is fully charged.

Also are you using the 3-pin battery with pack thermistor, or the 2-pin battery? If the 3-pin, what values did you populate for RT1 (REGN to TS) and RT2 (TS to GND)?

The battery is not fully charged, it’s at ~3.7V. It also does not have a thermistor built into it, it’s a basic 2 pin connection. I’ve left the TS pin on PM-BAT unpopulated.

This is the circuit of the module. Does anything jump out as incorrect? Does EN or RST need to be pulled high? I cannot figure out a reason the battery isn’t charging or CHG isn’t going high.

That looks fine as long as you don't have large loads on 3V3 or 3V8 beyond the SoM itself. If you do, and they start immediately, you will need to split those off onto 3V3_AUX.

I have a very small 5v boost that’s connected to the 3v3 line for an external switch (a rain bucket), but I’ve severed the connection to the booster and CHG still isn’t going high when powered with a battery.

Are there any firmware parameters that would prevent he battery from charging, apart from ENABLE_CHARGING? Thanks

Here's firmware to log most of the settings to USB serial:


#include "Particle.h"

SYSTEM_MODE(SEMI_AUTOMATIC);

#ifndef SYSTEM_VERSION_v620
SYSTEM_THREAD(ENABLED); // System thread defaults to on in 6.2.0 and later and this line is not required
#endif

SerialLogHandler logHandler(LOG_LEVEL_TRACE);

std::chrono::milliseconds logPeriod = 30s;

void powerLog();

void setup() {
	// Wait for a USB serial connection for up to 10 seconds
	waitFor(Serial.isConnected, 10000);

	// This setting is for the M.2 breakout board and only needs to be run once because the setting is persistent
	SystemPowerConfiguration powerConfig = System.getPowerConfiguration();
	powerConfig.auxiliaryPowerControlPin(D23).interruptPin(A6);
	System.setPowerConfiguration(powerConfig);

	// Particle.connect()
}

void loop() {
	static unsigned long lastLog = 0;

	if (lastLog == 0 || millis() - lastLog >= logPeriod.count()) {
		lastLog = millis();

		powerLog();		
	}
}



void powerManagerLog() {
    // See system_power.h for these definitions
    // typedef enum {
    //     BATTERY_STATE_UNKNOWN = 0,
    //     BATTERY_STATE_NOT_CHARGING = 1,
    //     BATTERY_STATE_CHARGING = 2,
    //     BATTERY_STATE_CHARGED = 3,
    //     BATTERY_STATE_DISCHARGING = 4,
    //     BATTERY_STATE_FAULT = 5,
    //     BATTERY_STATE_DISCONNECTED = 6
    // } battery_state_t;

    // typedef enum {
    //     POWER_SOURCE_UNKNOWN = 0,
    //     POWER_SOURCE_VIN = 1,
    //     POWER_SOURCE_USB_HOST = 2,
    //     POWER_SOURCE_USB_ADAPTER = 3,
    //     POWER_SOURCE_USB_OTG = 4,
    //     POWER_SOURCE_BATTERY = 5
    // } power_source_t;

    int powerSource = System.powerSource();
    int batteryState = System.batteryState();
    float batterySoc = System.batteryCharge();
    
    constexpr char const* batteryStates[] = {
        "unknown", "not charging", "charging",
        "charged", "discharging", "fault", "disconnected"
    };
    constexpr char const* powerSources[] = {
        "unknown", "vin", "usb host", "usb adapter",
        "usb otg", "battery"
    };

    Log.info("Power source: %s", powerSources[std::max(0, powerSource)]);
    Log.info("Battery state: %s", batteryStates[std::max(0, batteryState)]);
    Log.info("Battery charge: %f", batterySoc);	
}

void powerConfigurationLog() {
	SystemPowerConfiguration c;

	c = System.getPowerConfiguration();

	Log.info("SystemPowerConfiguration powerSourceMinVoltage=%d, powerSourceMaxCurrent=%d",
		(int) c.powerSourceMinVoltage(), (int) c.powerSourceMaxCurrent(), (int)c.batteryChargeVoltage());

	Log.info("SystemPowerConfiguration powerSourceMinVoltage=%d, batteryChargeCurrent=%d",
		(int) c.batteryChargeVoltage(), (int) c.batteryChargeCurrent());

	Log.info("SystemPowerConfiguration auxiliaryPowerControlPin=%d, auxiliaryPowerControlActiveLevel=%d",
		(int) c.auxiliaryPowerControlPin(), (int) c.auxiliaryPowerControlActiveLevel());

	Log.info("SystemPowerConfiguration interruptPin=%d, socBitPrecision=%d",
		(int) c.interruptPin(), (int) c.socBitPrecision());

}

void pmicLog() {
	PMIC pmic(true);

	Log.info("PMIC version=%02x, fault=%02x", (int)pmic.getVersion(), (int)pmic.getFault());

	//-----------------------------------------------------------------------------
	//System Status Register
	//-----------------------------------------------------------------------------
	//NOTE: This is a read-only register

	/*
	REG08
	BIT
	--- VBUS status
	7: VBUS_STAT[1] | 00: Unknown (no input, or DPDM detection incomplete), 01: USB host
	6: VBUS_STAT[0] | 10: Adapter port, 11: OTG
	--- Charging status
	5: CHRG_STAT[1] | 00: Not Charging,  01: Pre-charge (<VBATLOWV)
	4: CHRG_STAT[0] | 10: Fast Charging, 11: Charge termination done
	3: DPM_STAT     0: Not DPM
					1: VINDPM or IINDPM
	2: PG_STAT      0: Power NO Good :(
					1: Power Good :)
	1: THERM_STAT   0: Normal
					1: In Thermal Regulation (HOT)
	0: VSYS_STAT    0: Not in VSYSMIN regulation (BAT > VSYSMIN)
					1: In VSYSMIN regulation (BAT < VSYSMIN)

	*/
	byte systemStatus = pmic.getSystemStatus();

    constexpr char const* vbusStates[] = {
		"unknown", "USB host", "adapter port", "OTG"
	};
	char const *vbusStatus = vbusStates[(systemStatus >> 6) & 0b11];

    constexpr char const* chargeStates[] = {
		"not charging", "pre charge", "fast charging", "charge done"
	};
	const char *chargeStatus = chargeStates[(systemStatus >> 4) & 0b11];
	
	char const *dpmStatus = ((systemStatus >> 3) & 0b1) ? "VINDPM or IINDPM" : "not DPM";

	const char *pgStatus = ((systemStatus >> 2) & 0b1) ? "power good" : "power not good";

	const char *thermStatus = ((systemStatus >> 1) & 0b1) ? "in thermal regulation (hot)" : "normal thermal regulation";

	const char *vsysStatus = ((systemStatus >> 0) & 0b1) ? "in VSYSMIN regulation" : "not in VSYSMIN regulation";


	Log.info("PMIC systemStatus=0x%02x, vbusStatus=%s, chargeStatus=%s", (int)systemStatus, vbusStatus, chargeStatus);
	Log.info("PMIC %s, %s, %s, %s", dpmStatus, pgStatus, thermStatus, vsysStatus);

	//-----------------------------------------------------------------------------
	// Input source control register
	//-----------------------------------------------------------------------------

	/*
	REG00
	BIT
	7 : 0:Enable Buck regulator 1:disable buck regulator (powered only from a LiPo)
	--- input volatge limit. this is used to determine if USB source is overloaded
	6 : VINDPM[3] 640mV | offset is 3.88V, Range is 3.88 to 5.08
	5 : VINDPM[2] 320mV | enabling bits 3 to 6 adds the volatges to 3.88 base value
	4 : VINDPM[1] 160mV | Default is 4.36 (0110)
	3 : VINDPM[0] 80mV  |
	--- input current limit
	2 : INLIM[2]  000: 100mA, 001: 150mA, 010: 500mA,   | Default: 100mA when OTG pin is LOW and
	1 : INLIM[1]  011: 900mA, 100: 1.2A,   101: 1.5A    | 500mA when OTG pin is HIGH
	0 : INLIM[0]  110: 2.0A,  111: 3.0A                 | when charging port detected, 1.5A
	*/

	int inputCurrentLimit = (int) pmic.getInputCurrentLimit();
	float inputVoltageLimit = (float) pmic.getInputVoltageLimit() / 1000.0;

	Log.info("PMIC input source register=0x%02x, inputCurrentLimit=%d mA, inputVoltageLimit=%.3f", (int)pmic.readInputSourceRegister(), inputCurrentLimit, inputVoltageLimit);

	//-----------------------------------------------------------------------------
	// Charge current control register
	//-----------------------------------------------------------------------------

	/*
	REG02
	BIT
	7: ICHG[5] 2048mA   | offset is 512mA
	6: ICHG[4] 1024mA   | Range: 512 to 4544mA (BQ24195)
	5: ICHG[3] 512mA    | Range: 512 to 2496mA (BQ24195L)
	4: ICHG[2] 256mA    | Default: 2048mA (011000) = 512mA+1024mA+512mA
	3: ICHG[1] 128mA    | enabling bits 2 to 7 adds the current to 512mA base value
	2: ICHG[0] 64mA     |
	1: Reserved (should always be 0)
	0: FORCE_20PCT (fill this descri
	*/

	byte chargeCurrentRaw = pmic.getChargeCurrent();

	int chargeCurrent = (((int) chargeCurrentRaw) & 0b11111100) >> 2; 
	chargeCurrent *= 64;
	chargeCurrent += 512;
	Log.info("PMIC charge current control register=0x%02x, chargeCurrent=%d mA", (int)chargeCurrentRaw, chargeCurrent);


	//-----------------------------------------------------------------------------
	//PreCharge/ Termination Current Control Register
	//-----------------------------------------------------------------------------

	/*
	REG03
	BIT
	--- precharge current limit
	7: IPRECHG[3]   1024mA  | offset is 128mA
	6: IPRECHG[2]   512mA   | Range: 128 mA to 2048 mA
	5: IPRECHG[1]   256mA   | Default: 256 mA (0001) = 128mA+128mA
	4: IPRECHG[0]   128mA   | enabling bits 4 to 7, adds the current to 128mA base value
	--- termination current limit
	3: ITERM[3]     1024mA  | offset is 128mA
	2: ITERM[2]     512mA   | Range: 128 mA to 2048 mA
	1: ITERM[1]     256mA   | Default: 256 mA (0001) = 128mA+128mA
	0: ITERM[0]     128mA   | enabling bits 0 to 3, adds the current to 128mA base value
	*/
	byte chargeTermRegister = pmic.readChargeTermRegister();
	Log.info("PMIC charge termination register=0x%02x", (int)chargeTermRegister);

	// byte preChargeCurrent = pmic.getPreChargeCurrent();
    // byte termChargeCurrent = pmic.getTermChargeCurrent();

	//-----------------------------------------------------------------------------
	//Charge Voltage Control Register
	//-----------------------------------------------------------------------------

	/*
	REG04
	BIT
	--- Charge Voltage Limit
	7: VREG[5]  512mV   | offset os 3.504V
	6: VREG[4]  256mV   | Range: 3.504V to 4.400V
	5: VREG[3]  128mV   | Default: 4.208V (101100) = 3.504V+512mV+128mV+64mV
	4: VREG[2]  64mV    | enabling bits 2 to 7, adds the voltage to 3.504V base value
	3: VREG[1]  32mV    |
	2: VREG[0]  16mV    |
	--- Battery Precharge to Fast Charge Threshold
	1: BATLOWV  0:2.8V, 1:3.0V Default:3.0V(1)
	--- Battery Recharge Threshold (below battery regulation voltage)
	0: VRECHG   0:100mV, 1:300mV Default:100mV(0)
	*/
	Log.info("PMIC charge voltage control register=0x%02x, chargeVoltage=%.03f", (int)pmic.getChargeVoltage(), ((float)pmic.getChargeVoltageValue()) / 1000.0);


	// uint16_t minimumSystemVoltage = pmic.getMinimumSystemVoltage();
    // byte chargingStat = pmic.getChargingStat();


}



void powerLog() {
	powerManagerLog();

	powerConfigurationLog();

	pmicLog();
}

Here's sample output:

  • M-SoM, Device OS 6.3.4
  • In M.2 Breakout Board
  • Powered by VIN (barrel connector) with battery
0000031727 [app] INFO: Power source: vin
0000031737 [app] INFO: Battery state: charging
0000031749 [app] INFO: Battery charge: 66.250000
0000031763 [app] INFO: SystemPowerConfiguration powerSourceMinVoltage=3880, powerSourceMaxCurrent=900
0000031788 [app] INFO: SystemPowerConfiguration powerSourceMinVoltage=4112, batteryChargeCurrent=896
0000031813 [app] INFO: SystemPowerConfiguration auxiliaryPowerControlPin=23, auxiliaryPowerControlActiveLevel=1
0000031841 [app] INFO: SystemPowerConfiguration interruptPin=29, socBitPrecision=18
0000031863 [app] INFO: PMIC version=23, fault=00
0000031876 [app] INFO: PMIC systemStatus=0x24, vbusStatus=unknown, chargeStatus=fast charging
0000031900 [app] INFO: PMIC not DPM, power good, normal thermal regulation, not in VSYSMIN regulation
0000031927 [app] INFO: PMIC input source register=0x03, inputCurrentLimit=900 mA, inputVoltageLimit=3.880
0000031954 [app] INFO: PMIC charge current control register=0x18, chargeCurrent=896 mA
0000031976 [app] INFO: PMIC charge termination register=0x8a
0000031992 [app] INFO: PMIC charge voltage control register=0x9a, chargeVoltage=4.112

There is now sample code for logging power settings to USB serial debug in the docs.

The updated version there also decodes fault modes, which may be handy.

Thanks again for the help. Here is the output I am receiving on serial with external VIN power and battery connected:

0000060925 [app] INFO: Power source: vin
0000060925 [app] INFO: Battery state: not charging
0000060926 [app] INFO: Battery charge: 45.898438
0000060928 [app] INFO: SystemPowerConfiguration powerSourceMinVoltage=4300, powerSourceMaxCurrent=900
0000060928 [app] INFO: SystemPowerConfiguration powerSourceMinVoltage=4210, batteryChargeCurrent=910
0000060929 [app] INFO: SystemPowerConfiguration auxiliaryPowerControlPin=29, auxiliaryPowerControlActiveLevel=1
0000060930 [app] INFO: SystemPowerConfiguration interruptPin=21, socBitPrecision=18
0000060932 [app] INFO: PMIC version=23, fault=00
0000060932 [app] INFO: PMIC systemStatus=0x04, vbusStatus=unknown, chargeStatus=not charging
0000060933 [app] INFO: PMIC not DPM, power good, normal thermal regulation, not in VSYSMIN regulation
0000060936 [app] INFO: PMIC input source register=0x2b, inputCurrentLimit=900 mA, inputVoltageLimit=4.280
0000060937 [app] INFO: PMIC charge current control register=0x18, chargeCurrent=896 mA
0000060938 [app] INFO: PMIC charge termination register=0x82
0000060940 [app] INFO: PMIC charge voltage control register=0xb2, chargeVoltage=4.208

What are the voltages on the TS and REGN pins?

A 10K NTC pack thermistor makes a voltage divider between REGN and ground. When the voltage is in the required range, charging is enabled. This is done using voltage comparators in the bq24195 in the analog domain; there is no digital processing for enabling charging based on temperature.

There are two resistors forming a voltage divider in the PM-BAT that simulates a pack in the correct voltage that work when you are not using a temperature sensor battery. Testing the TS pin voltage will determine if it's working correctly.

I’m getting ~1.7V across the TS and REGN pins when VIN is supplied

I know these modules are fairly new. Have there been any reports of issues with the PM-BAT?

I’ve connected it to a breadboard and while there is no load, it should be charging the battery. I’ve tried photographing it but this proves difficult. You can see a 5V external power supply connected via a barrel jack to VIN pins 11 and 12. A JST battery is connected to pin 20. A 500 ohm resistor and LED are tied to CHG pin 13. My multimeter shows 3.3V exiting pin 1 (and 3.8V on pin 5 & 6 but not in the photo). The CHG pin is not going high as the battery is not receiving a charge.

I don't think you can test the PM-BAT isolated that way. When the bq24195 powers up it defaults to an input current limit of 100 mA. This is overridden by DPDM (USB power negotiation), except this isn't used on PM-BAT. The MCU resets the input current limit early in boot, but since you don't have the Particle device connected, it won't ever be reset. I believe that because the input current limit is so low, charging won't enable.

bq24195 docs state that the device’s default mode requires no host management.
”In default mode, bq24195L, bq24195 can be used as an autonomous charger with no host or with host in sleep.”

When you say the MCU resets the input current limit, what is the BSoM doing to reset it? Is there a specific library that needs to be used just to get the PM-BAT to run and charge the battery? I thought you could use the I2C interface to configure it (and get battery charge info from the MAX17043), but it would operate normally without any configurations (without 3V3_AUX enabled).

The MCU just sends commands to the bq24195 over I2C to set the input current limit.

However, is your LED connected between CHG and GND? It needs to be connected to 3V3 because it's open-collector. This is from the M2 breakout board, which is one of the two product devices that use the PM-BAT. (The other is the Muon.)