Modifying the nPM1300 one button example

Hi.

I want to modify the "nPM1300 One Button" example.

I need the Ship mode to be activated after pressing and holding the SHPHLD button for longer than 3 seconds. The Ship mode should activate before I release the SHPHLD button.

I plan to implement this function using the nRF52 timer, but for some reason the function regulator_parent_ship_mode() is not executed inside the timer function (timer0_handler).

#include <zephyr/drivers/mfd/npm1300.h>
#include <zephyr/drivers/regulator.h>

static const struct device *pmic = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_pmic));
static const struct device *regulators = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_regulators));

static int press_t;

static void timer0_handler(struct k_timer *dummy)
{
    /*Interrupt Context - System Timer ISR */
	press_t = k_uptime_get() - press_t;
	LOG_INF("timer0_handler function started. Press_time = %d", press_t);
	if (vbus_connected) {
		LOG_INF("Ship mode entry not possible with USB connected\n");
		} else {
			LOG_INF("SHIP MODE");
			regulator_parent_ship_mode(regulators);
		}
}

K_TIMER_DEFINE(timer0, timer0_handler, NULL);

static void event_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	if (pins & BIT(NPM1300_EVENT_SHIPHOLD_PRESS)) {
		LOG_INF("SHPHLD press");
		press_t = k_uptime_get();
		/* start timer */
		k_timer_start(&timer0, K_MSEC(3000), K_NO_WAIT);
	}

	if (pins & BIT(NPM1300_EVENT_SHIPHOLD_RELEASE)) {
		LOG_INF("SHPHLD release");
		/* stop timer */
		k_timer_stop(&timer0);
	}

	if (pins & BIT(NPM1300_EVENT_VBUS_DETECTED)) {
		LOG_INF("Vbus connected\n");
		vbus_connected = true;
	}

	if (pins & BIT(NPM1300_EVENT_VBUS_REMOVED)) {
		LOG_INF("Vbus removed\n");
		vbus_connected = false;
	}
}

bool configure_events(void) ...

int main(void)
{
	LOG_INF("PMIC device ok");

	while (1) {
		led_on(leds, 2U);
		k_msleep(1000);
		led_off(leds, 2U);
		k_msleep(1000);
		fuel_gauge_update(charger);
	}
}

The result of the program is on the screenshot -->

As you can see, the program executes the code inside the function timer0_handler, but for some reason it does not switch to the Ship mode and continues to execute the code in the main function.

Please tell me why this is happening and where I am making a mistake?

Thank you.

  • Hi!

    1) Do you see the same with the default, unmodified sample?

    2) Can you check the return code of regulator_parent_ship_mode() ?

  • Hi Sigurd!

    1. In the default example (when the function regulator_parent_ship_mode() is inside event_callback()) everything works fine. 

    2. The return code is -5. But I don't know what it means.

    static void timer0_handler(struct k_timer *dummy)
    {
    	int ret;
        /*Interrupt Context - System Timer ISR */
    	press_t = k_uptime_get() - press_t;
    	LOG_INF("timer0_handler function started. Press_time = %d", press_t);
    	if (vbus_connected) {
    		LOG_INF("Ship mode entry not possible with USB connected\n");
    		} else {
    			LOG_INF("SHIP MODE");
    			ret = regulator_parent_ship_mode(regulators);
    			LOG_INF("ret = %d", ret);
    		}
    }

    Screen -->

    I'm hoping for your help.
    Thank you.

  • I don't think you can do this inside an ISR as it requires TWI comms.
    You will have to set a flag and poll it in the background task, or setup a worker thread.

  • Hi Andy.

    I don’t know what the reason is, but I also can’t enter ship mode from the work queue.

    #define WORKQ_THREAD_STACK_SIZE   1024
    #define WORKQ_PRIORITY             5
    
    K_THREAD_STACK_DEFINE(ship_mode_stack_area, WORKQ_THREAD_STACK_SIZE);
    
    struct k_work_q ship_mode_work_q = {0};
    
    static void ship_mode_work_cb (struct k_work *work){
    	/* Enter to Ship mode*/
    	LOG_INF("Enter to SHIP MODE");
    	regulator_parent_ship_mode(regulators);
    }
    
    K_WORK_DEFINE(ship_mode_work, ship_mode_work_cb);
    
    static void timer0_handler(struct k_timer *dummy)
    {
    	int ret;
        /*Interrupt Context - System Timer ISR */
    	press_t = k_uptime_get() - press_t;
    	LOG_INF("timer0_handler function started. Press_time = %d", press_t);
    	if (vbus_connected) {
    		LOG_INF("Ship mode entry not possible with USB connected\n");
    		} else {
    			k_work_submit(&ship_mode_work);
    		}
    }
    
    K_TIMER_DEFINE(timer0, timer0_handler, NULL);
    
    static void event_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {
    	if (pins & BIT(NPM1300_EVENT_SHIPHOLD_PRESS)) {
    		LOG_INF("SHPHLD press");
    		press_t = k_uptime_get();
    		/* start timer */
    		k_timer_start(&timer0, K_MSEC(3000), K_NO_WAIT);
    	}
    
    	if (pins & BIT(NPM1300_EVENT_SHIPHOLD_RELEASE)) {
    		LOG_INF("SHPHLD release");
    		/* stop timer */
    		k_timer_stop(&timer0);
    	}
    
    	if (pins & BIT(NPM1300_EVENT_VBUS_DETECTED)) {
    		LOG_INF("Vbus connected\n");
    		vbus_connected = true;
    	}
    
    	if (pins & BIT(NPM1300_EVENT_VBUS_REMOVED)) {
    		LOG_INF("Vbus removed\n");
    		vbus_connected = false;
    	}
    }
    
    bool configure_events(void)...
    
    int main(void)
    {
        k_work_queue_init(&ship_mode_work_q);
        
        k_work_queue_start(&ship_mode_work_q, ship_mode_stack_area,
                        K_THREAD_STACK_SIZEOF(ship_mode_stack_area), WORKQ_PRIORITY, NULL);
    
    	LOG_INF("PMIC device ok");
    
    while (1) {
    		led_on(leds, 2U);
    		k_msleep(1000);
    		led_off(leds, 2U);
    		k_msleep(1000);
    		fuel_gauge_update(charger);
    	}
    }

    Here's the output on the terminal -->

    Perhaps the program enters ship mode, but immediately exits it, because the SHPHLD Press event is triggered (although I did not release the SHPHLD button).

    Logic level screen -->

    I don't know if it's even possible to implement a function to enter ship mode and still keep the SHPHLD button pressed.

  • Hi.

    You are correct that this is unlikely to work correctly, as as soon as ship mode is entered the SHPHLD button will be enabled to wake up the device.
    You can change the debounce time for waking up again, but it can't be disabled.

    Is there a particular reason you would like to go to ship before the button is released?

    Andy

Related