Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDSW, IBATT and NRF9160 examples #9

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open

Conversation

fredriknk
Copy link

Hi, thanks for the awesome work on this driver, im implementing it in a project im working on but i needed some additions since i needed LDSW and ibat current measurements. I only have a nrf9160 board to test it on so i added examples for the nrf9160 soc and a .vscode settings.json folder to easily switch the different targets

The ldsw is pretty straight forward implementation, i dont have time to make the drivers for the LDO, but i thought it might be good to at least get ldsw implementation?

For the IBAT measurements im using the zephyr NPM1300charger.c as my reference since the nordic documentation has basically no info whatsoever. Its kind of a weird measurement because it scales depending on your config of the max charge and discharge currents, so you need to read those registers in order to calculate the full scale values.

For note, im in the early process of learning rust so my code is probably not kosher, so please dont blindly accept my additions. but it does seem to work well on my board at least.

@fredriknk
Copy link
Author

fredriknk commented Mar 3, 2025

I didnt see the issue you made untill now, but i do get some reasonable values out for the current consumption on both charge and discharge. Regarding the registers you mention, the zephyr drivers use the BCHARGERMODE and bchargericharge register as a 4 bit register with theese 4 statuses

/// define IBAT_STAT_DISCHARGE      0x04U
/// define IBAT_STAT_CHARGE_TRICKLE 0x0CU
/// define IBAT_STAT_CHARGE_COOL    0x0DU
/// define IBAT_STAT_CHARGE_NORMAL  0x0FU

And a status for no battery connected

IbatStatChargeError = 8, //0x08 undocumented in driver but occurs if you remove the battery while charging

Copy link
Contributor

@badrbouslikhin badrbouslikhin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your contribution!
I've added a few comments, but nothing major.

Parts of the code are not formatted correctly (src/charger/mod.rs for eg), do you use rustfmt or rust-analyzer?
Can you also please make sure the docstrings follow the crate convention? Eg.

    /// Measure VBAT
    ///
    /// This function triggers a VBAT measurement and returns the result.
    ///
    /// # Returns
    ///
    /// * `Ok(f32)` - The measured VBAT voltage
    /// * `Err(NPM1300Error)` - An error occurred while reading the VBAT measurement result

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with VS Code, are these files mandatory?
Can you make them generic? I see a lot of MCU specific settings and this crate is MCU-agnostic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not familiar with VS Code, how does this .gitignore differ from the main .gitignore?

@@ -1708,7 +1708,7 @@ ADC:
description: ADC VBAT (or VBUS) burst measurement result upper 8-bits
ADCGP1RESULTLSBS:
type: register
description: ADC result LSB's (VBAT burst 0, 1, 2 and 3)
description: ADC result LSB's (VBAT burst 0, 1, 2 and 3) or 2 IBAT/ 3 VBUS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that VBAT2RESULTLSB contains IBAT LSB and VBAT3RESULTLSB VBUS LSB?

@@ -22,7 +22,8 @@ This crate provides both low-level register access and a high-level API for mana
| SYSREG — System regulator | ✅ | ✅ |
| CHARGER — Battery charger | ✅ | ✅ |
| BUCK — Buck regulators | ✅ | ✅ |
| LOADSW/LDO — Load switches/LDO regulators | ❌ | ❌ |
| LOADSW — Load switches | ✅ | ✅ |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like many LOADSW configuration registers are not supported LDSW1GPISEL, LDSWCONFIG, LDSW1LDOSEL, LDSW1VOUTSEL, etc.
If that's really the case, I suggest making it explicit in the README and create an issue to add support for those registers.

npm1300-rs = { path = "../../", features = ["defmt-03"] }

[patch.crates-io]
embassy-nrf = { git = "https://github.com/embassy-rs/embassy.git" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest pinning a specific embassy-nrf/embassy-executor/embassy-time revision to avoid a breaking changes (see the Cargo.toml of the nRF52840 example).

@@ -785,4 +809,163 @@ impl<I2c: embedded_hal_async::i2c::I2c, Delay: embedded_hal_async::delay::DelayN
})
.await
}

/// Configure auto ibat measurement enabled. It seems you dont
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed the case, according the datasheet, ADCIBATMEASEN: Enable Auto IBAT measurement after VBAT task

/// # Returns
///
/// a scaled signed integer in ma of the current charge or discharge current
pub async fn measure_ibat(&mut self) -> Result<f32, crate::NPM1300Error<I2c::Error>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is looks like this method doesn't triggers IBAT measurements.
How can we make sure we aren't reading a VBAT measurement?

return Ok(result);
}
/// Measure IBAT current and convert it to a scaled value
/// This is more at home in the ADC function, but i couldnt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you, all adc methods should be in the ADC module. I've pushed that refactor.

let discharge_current_limit = self.get_discharge_current_limit().await?;
let ibat_scaled = match ibat_status {
IbatStatuscodes::IbatStatDischarge => {
let max_discharge_current = match discharge_current_limit{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to implement this to the type struct directly, something like:

// Implement conversion to f32
impl From<DischargeCurrentLimit> for f32 {
    fn from(value: DischargeCurrentLimit) -> Self {
        match value {
            DischargeCurrentLimit::Low => 200.0,
            DischargeCurrentLimit::High => 1000.0,
        }
    }
}

.bchgisetdischargelsb();

match (msb, lsb) {
(42, 0) => Ok(DischargeCurrentLimit::Low), // 200mA case
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add:

// NOTE: the following magic numbers come from the product specification doc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants