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?