This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Configuring GPIOE output task on NCS 1.9.1

Hello,

I've an issue migrating an NRF52840 project from NCS 1.7.0 to 1.9.1. I use PPI/GPIOTE to set a capacitor sensor charging pin and start a timer simultaneously, then clear the charging pin and stop the timer on comparator crossing. My old code looked like this:

static const struct wp_cfg wp_cfg = {
    .charge_pin = GPIO_DT_SPEC_GET(DT_PATH(water_presence), c_charge_gpios),
    .charge_pin_gpio = DT_GPIO_PIN(DT_PATH(water_presence), c_charge_gpios),
    //..
}

int prepare(const struct device *dev) {
    struct wp_data *data = dev->data;
    const struct wp_cfg *cfg = dev->config;
    int err;

    nrfx_gpiote_out_config_t gpiote_start_charge = NRFX_GPIOTE_CONFIG_OUT_TASK_HIGH;
    err == nrfx_gpiote_out_init(cfg->charge_pin_gpio, &gpiote_start_charge);
    data->is_gpiote_charge_pin_allocated = err == NRF_SUCCESS;
    //..
    
    nrfx_gpiote_out_task_enable(cfg->charge_pin_gpio);
    
    //..
    
    //Start measuremet (triggered by EGU):
    //Capture CC1
    nrfx_ppi_channel_assign(
        data->ppi_on_start.ch,
        (uint32_t)&NRF_EGU0->EVENTS_TRIGGERED[0],
        nrfx_timer_capture_task_address_get(data->timer, 1));
    //Set charge pin
    nrfx_ppi_channel_fork_assign(
        data->ppi_on_start.ch,
        nrfx_gpiote_out_task_addr_get(cfg->charge_pin_gpio));

    //End measurement (triggered by LPCOMP):
    //Capture CC1
    nrfx_ppi_channel_assign(
        data->ppi_on_hit.ch,
        (uint32_t)&NRF_LPCOMP->EVENTS_UP,
        nrfx_timer_capture_task_address_get(data->timer, 0));
    //Clear charge pin
    nrfx_ppi_channel_fork_assign(
        data->ppi_on_hit.ch,
        nrfx_gpiote_clr_task_addr_get(cfg->charge_pin_gpio));
}


void shutdown(const struct device *dev) {
    if(data->is_gpiote_charge_pin_allocated)
        nrfx_gpiote_out_uninit(cfg->charge_pin_gpio);
    
    //Ensure charge pin is low even if LPCOMP didn't trigger.
    data->is_gpiote_charge_pin_allocated = false;
    gpio_pin_configure_dt(&cfg->charge_pin, GPIO_OUTPUT_LOW | GPIO_ACTIVE_HIGH);
}

This worked for 1.7.0, but on 1.9.1 nrfx_gpiote_out_init() returns NRFX_BUSY. According to the documentation, it is obsoleted and nrfx_gpiote_output_configure should be used instead().

Here is my attempt with the new API:


static int init(const struct device *dev) {
    struct wp_data *data = dev->data;
    const struct wp_cfg *cfg = dev->config;
    int err;
    
    if(!nrfx_gpiote_is_init()) {
        err = nrfx_gpiote_init(0);
        if(err != NRFX_SUCCESS) {
            LOG_ERR("Cannot init gpiote: %i", err);
            return err;
        }
    }

    //Also tried gpio_pin_configure_dt(&cfg->charge_pin, GPIO_DISCONNECTED);
    gpio_pin_configure_dt(&cfg->charge_pin, GPIO_OUTPUT|GPIO_OUTPUT_LOW);
    gpio_pin_configure_dt(&cfg->measure_pin, GPIO_INPUT);

    err = nrfx_gpiote_channel_get(cfg->charge_pin_gpio, &data->gpiote_charge_pin_channel);

    if(err != NRFX_SUCCESS) {
        err = nrfx_gpiote_channel_alloc(&data->gpiote_charge_pin_channel);
    }
    
    if(err != NRFX_SUCCESS) {
        LOG_ERR("Can't allocate GPIOTE channel: %x", err);
        return err;
    }
        
    nrfx_gpiote_output_config_t charge_pin_config = {
        .drive = NRF_GPIO_PIN_S0S1,
        .input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
        .pull = NRF_GPIO_PIN_NOPULL,
    };
    nrfx_gpiote_task_config_t charge_task_config = {
        .task_ch = data->gpiote_charge_pin_channel,
        .polarity = NRF_GPIOTE_POLARITY_NONE,
        .init_val = NRF_GPIOTE_INITIAL_VALUE_LOW,
    };

    err = nrfx_gpiote_output_configure(cfg->charge_pin_gpio, &charge_pin_config, &charge_task_config);
    if(err != NRFX_SUCCESS) {
        LOG_ERR("Can't setup GPIOTE task: %x", err);
        return err;
    }
    nrfx_gpiote_out_task_enable(cfg->charge_pin_gpio);

    return 0;
}


int prepare(const struct device *dev) {
    struct wp_data *data = dev->data;
    const struct wp_cfg *cfg = dev->config;
    //...
    
    //Start measuremet (triggered by EGU):
    //Capture CC1
    nrfx_ppi_channel_assign(
        data->ppi_on_start.ch,
        (uint32_t)&NRF_EGU0->EVENTS_TRIGGERED[0],
        nrfx_timer_capture_task_address_get(data->timer, 1));
    //Set charge pin
    nrfx_ppi_channel_fork_assign(
        data->ppi_on_start.ch,
        nrfx_gpiote_set_task_addr_get(cfg->charge_pin_gpio));

    //End measurement (triggered by LPCOMP):
    //Capture CC1
    nrfx_ppi_channel_assign(
        data->ppi_on_hit.ch,
        (uint32_t)&NRF_LPCOMP->EVENTS_UP,
        nrfx_timer_capture_task_address_get(data->timer, 0));
    //Clear charge pin
    nrfx_ppi_channel_fork_assign(
        data->ppi_on_hit.ch,
        nrfx_gpiote_clr_task_addr_get(cfg->charge_pin_gpio));
    
    //...
}


void shutdown(const struct device *dev) {
    struct wp_data *data = dev->data;
    const struct wp_cfg *cfg = dev->config;
    //...
    nrfx_gpiote_clr_task_trigger(cfg->charge_pin_gpio);
    //...
}

It looks like GPIOTE never takes control of the pin (I checked it by changing gpio_pin_configure_dt(&cfg->charge_pin, GPIO_OUTPUT|GPIO_OUTPUT_LOW) to GPIO_OUTPUT_HIGH, then the pin was stuck high). What is the idiomatic way to use GPIO under Zephyr?

Parents Reply Children
No Data
Related