Nanopower nPZero G1S power-saving IC
The nPZero G1S (NPZG1SQ32) from Nanopower Semiconductor is a power-saving IC for sensor systems. While the host MCU is powered, the nPZero is an I2C slave of the host (Standby state). When the host sends the Idle command, the nPZero switches the host off and polls up to four I2C or SPI peripherals by itself, powering them, sending them initialization commands from its 128-byte SRAM, reading their values and comparing them with thresholds. When a wake-up condition happens, it switches the host on again. The wake-up conditions are:
a peripheral value inside the trigger area, or an interrupt of a peripheral,
a VBAT or
ADC_INvalue outside the ADC thresholds,a NAK of an I2C peripheral (if enabled),
the global time-out.
So the host boots at each wake-up. The usual program reads the wake-up reason, handles it, sends the configuration and enters Idle state again.
The driver (drivers/power/supply/npzero.c) is a character driver controlled
with ioctl() commands. The register map, the values of the fields and the
commands are in include/nuttx/power/npzero.h.
Configuration
CONFIG_NPZERO: enables the driver. It needsCONFIG_I2C.CONFIG_NPZERO_I2C_FREQUENCY: I2C frequency, 100 kHz by default. The nPZero supports the I2C standard mode only.
Board interface
The board registers the driver with:
int npzero_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
uint8_t addr);
addr is the 7-bit I2C address, NPZERO_I2C_ADDR (0x3d). The function
reads the ID register and returns -ENODEV if it is not NPZERO_ID
(0x60). Since the host is switched off in Idle state, it must be called at each
boot, for instance from board_late_initialize():
#include <nuttx/power/npzero.h>
i2c = stm32_i2cbus_initialize(1);
ret = npzero_register("/dev/npzero0", i2c, NPZERO_I2C_ADDR);
Errata
The driver applies the workarounds of the errata NPZ-G1S-ERRATA-V3.0:
#2, sequential SRAM writes can corrupt the SRAM:
NPZEROIOC_CONFIGUREwrites the whole SRAM one byte at a time, in the order of the workaround.NPZEROIOC_WRITEREGrefuses the SRAM addresses (-EACCES).#11, incorrect IREF trim values (unexpected reset in Idle state):
npzero_register()corrects the trim values when no wake-up flag is set in STA1 and STA2, that is after a reset of the nPZero.
The other errata are for the application:
#3: an interrupt pulse shorter than 2.5 us wakes the nPZero up without setting its
FPbit in STA2.#4: after a NAK (
FNAKPbit in STA2) or an interrupt time-out, the VALP registers of a peripheral may keep the value of another peripheral.#5: only the soft reset bit (
NPZERO_STA1_RSTSRC_SOFT) of the reset source is reliable.#6, #7: for an 8-bit value, two bytes are read from the peripheral.
#9: with the
ADC_INwake-up enabled,ADC_INmust stay between 600 mV and VBAT.
The datasheet also forbids the peripheral power switch mode 0 (switch with output voltage rise detection), which can lock up the polling, so the driver refuses it.
IOCTL commands
Command |
Argument |
Description |
|---|---|---|
|
|
Read the ID register (0x60) |
|
|
Read STA1 and STA2 |
|
|
Write the configuration and the SRAM |
|
|
Last value read from a peripheral |
|
|
Last ADC values |
|
None |
Enter Idle state |
|
None |
Soft reset |
|
|
Read a register |
|
|
Write a register (not the SRAM) |
Status
struct npzero_status_s holds the raw sta1 and sta2 registers.
Use the NPZERO_STA1_* bits (FTOUT global time-out, FA1 VBAT
trigger, FA2 ADC_IN trigger, RSTSRC reset source) and the
NPZERO_STA2_FP(n) (trigger) and NPZERO_STA2_FNAKP(n) (NAK) bits of
peripheral n.
NPZEROIOC_GETVALUE returns the 16-bit value of the peripheral
periph (1 to 4) in the type set by its dtype. NPZEROIOC_GETADC
returns the ADC codes, see the table ADC codes of the datasheet. The codes
of the VBAT channel are the ones of the ADC_IN channel minus 0x20.
Configuration structure
NPZEROIOC_CONFIGURE takes a struct npzero_config_s. It checks the
whole configuration first (-EINVAL, or -E2BIG when the commands do not
fit in the SRAM), then writes it. Global fields:
Field |
Description |
|---|---|
|
Host switch ( |
|
Bit |
|
Gate boost of the power switches (lower Rds(on)) |
|
Wake-up sources: |
|
Wake up when all the sources trigger, instead of any |
|
System clock: |
|
System clock divider, |
|
ADC clock, |
|
Sample |
|
High strength of the I/O pins |
|
I2C pull-ups: |
|
SPI outputs in high impedance when not used |
|
|
|
Global time-out, in system clock periods (not 0) |
|
Pull-up of |
|
ADC thresholds of VBAT ( |
|
( |
|
Peripherals 1 to 4, |
Fields of struct npzero_periph_s:
Field |
Description |
|---|---|
|
|
|
Polling mode: |
|
|
|
|
|
SPI peripheral, otherwise I2C |
|
SPI mode, 0 to 3 |
|
|
|
Trigger between the thresholds, instead of above or equal to
|
|
Multi-byte I2C transfers for sequential addresses |
|
Wake up on an I2C NAK |
|
Swap the bytes of the value (big endian peripheral) |
|
7-bit I2C address of the peripheral |
|
I2C register of the value (low byte) |
|
I2C retries on NAK, 0 to 3 |
|
Polling period, in system clock periods (2 or more) |
|
Thresholds, in the type of |
|
|
|
Wait time count, in units of 256 or 4096 periods of the 400 kHz oscillator (640 us or 10.24 ms) |
|
Wait before the initialization: |
|
Wait after the initialization, before the read or for the
interrupt: |
|
Initialization commands. I2C: |
|
and value ( |
|
SPI only: bytes shifted out to read the value |
|
The commands are stored in the SRAM in the order of the peripherals, the read bytes of an SPI peripheral after its initialization bytes.
Example
Configuration of the AS6212 temperature sensor of the DevKit on peripheral 4, which wakes the host up above 25 C or below 10 C:
static const uint8_t g_init[] =
{
0x01, 0x82, 0x02, 0xa0
};
static const struct npzero_periph_s g_temp =
{
.pwmod = NPZERO_PWMOD_PERIODIC,
.tmod = NPZERO_TMOD_READ,
.pswmod = NPZERO_PSWMOD_LOGIC,
.dtype = NPZERO_DTYPE_INT16,
.seqrw = true,
.swap = true,
.addr = 0x49,
.rreg = 0x00,
.period = 300, /* 30 s with the 10 Hz clock */
.throver = 25 * 128,
.thrunder = 10 * 128,
.twtp = 49,
.tinit = NPZERO_WAIT_256,
.twait = NPZERO_WAIT_256,
.ninit = sizeof(g_init) / 2,
.init = g_init,
};
static const struct npzero_config_s g_config =
{
.hostmode = NPZERO_HOSTMODE_LOGIC,
.pswint = 1 << 3,
.wakeup = NPZERO_SYSCFG1_WUP_P(4),
.sclkdiv = NPZERO_SCLKDIV_1,
.i2cpull = NPZERO_I2CPULL_AUTO,
.timeout = 3000, /* 300 s */
.periph =
{
NULL, NULL, NULL, &g_temp
},
};
fd = open("/dev/npzero0", O_RDWR);
ioctl(fd, NPZEROIOC_GETSTATUS, (unsigned long)&status);
if (status.sta2 & NPZERO_STA2_FP(4))
{
value.periph = 4;
ioctl(fd, NPZEROIOC_GETVALUE, (unsigned long)&value);
}
ioctl(fd, NPZEROIOC_CONFIGURE, (unsigned long)&g_config);
ioctl(fd, NPZEROIOC_IDLE, 0); /* The host is switched off */
The complete example is apps/examples/npzero, see
npzero nPZero G1S power-saving example, and the board is
Nanopower nPZero G1S DevKit (STM32).
Differences with the Nanopower driver API
The driver follows the datasheet NPZ-G1S-DS-V1.0 where the Nanopower driver API does not:
The host switch mode “power switch” is 0, the value 1 is invalid.
The reset source is 4 bits (bit 3 is the soft reset) and the odd bits of STA2 are the NAK flags of the peripherals, not time-out flags.
The ADC clocks derived from the crystal need
NPZERO_SCLK_XO.gateboostis written, and no ADC channel configuration is needed.The SRAM is written once, in 128 single writes, instead of 128 writes for each byte.
tinitandtwaitare named after theTINITandTWTfields of TCFGP.pre_wait_timeof the Nanopower API setsTWT.