This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

GPIOTE IN Event not triggering PPI task

I am trying to time a capacitor discharge through different resistance sensors for greater resolution than an ADC. I did this using the GPIO functions initially but the SD will sometimes interfere and cause the readings to be off. I have decided to try this via PPI and I have it just about working. The only issue I am having is the GPIOTE IN event does not seem to be triggering the timer capture task. I can use the IN Event callback to manually capture the value which does work but I really want to do it via PPI to avoid interference from the SD. I have included an annotated example; hopefully I am just missing something really simple:

#define TEMP_COMP_LINE	        11
#define TEMP_THERM_LINE	        12
#define TEMP_SENSE_LINE	        13

// Use a counter so we can manually increment it and start the PPI chain
nrf_drv_timer_t start_counter = NRF_DRV_TIMER_INSTANCE(2);
// Timer for capacitor discharge
nrf_drv_timer_t discharge_timer = NRF_DRV_TIMER_INSTANCE(3);

// PPI Channels
nrf_ppi_channel_t ppi_1, ppi_2;

uint32_t line_discharge_time = 0;

void timer_event_handler(nrf_timer_event_t event_type, void * p_context){}

void sense_dummy_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
  
  // Uncommenting this will allow timer capture on IN event!
  //line_discharge_time = nrf_drv_timer_capture(&discharge_timer, NRF_TIMER_CC_CHANNEL0);
  __NOP();
}

uint32_t do_thermistor_ppi(uint32_t line) {
  
  // Disable unused lines
  uint32_t other_line = TEMP_THERM_LINE;
  if (line == TEMP_THERM_LINE) {
    other_line = TEMP_COMP_LINE;
  }
  
  // GPIOTEs
  nrf_drv_gpiote_out_config_t line_cfg = GPIOTE_CONFIG_OUT_TASK_LOW;
  nrf_drv_gpiote_out_init(line, &line_cfg);
  nrf_drv_gpiote_out_clear(line);

  nrf_drv_gpiote_in_config_t sense_cfg = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
  
  // Init Manual Start Counter
  nrf_drv_timer_config_t sc_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
  sc_cfg.mode = NRF_TIMER_MODE_COUNTER;
  sc_cfg.bit_width = NRF_TIMER_BIT_WIDTH_8;
  nrf_drv_timer_init(&start_counter, &sc_cfg, timer_event_handler);
  
  // Cause counter to trigger compare[0] on count of 1
  nrf_drv_timer_compare(&start_counter, NRF_TIMER_CC_CHANNEL0, 1, true);
  
  // Init Discharge timer
  nrf_drv_timer_config_t dt_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
  dt_cfg.mode = NRF_TIMER_MODE_TIMER;
  dt_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
  dt_cfg.frequency = NRF_TIMER_FREQ_8MHz;
  nrf_drv_timer_init(&discharge_timer, &dt_cfg, timer_event_handler);
  
  // Configure a PPI channel to start the discharge timer for static compare
  // line when manual count is incremented to 1.
  nrf_drv_ppi_channel_alloc(&ppi_1);
  nrf_drv_ppi_channel_assign(ppi_1, 
                             nrf_drv_timer_event_address_get(&start_counter, NRF_TIMER_EVENT_COMPARE0),
                             nrf_drv_timer_task_address_get(&discharge_timer, NRF_TIMER_TASK_START));
  
  // Set the capacitor to discharge at the same moment the timer starts for count 1
  nrf_drv_ppi_channel_fork_assign(ppi_1, nrf_drv_gpiote_out_task_addr_get(line));
  
  // Configure a PPI channel to stop the discharge timer whenever a sense line goes low
  nrf_drv_ppi_channel_alloc(&ppi_2);
  nrf_drv_ppi_channel_assign(ppi_2, 
                             nrf_drv_gpiote_in_event_addr_get(TEMP_SENSE_LINE), 
                             nrf_drv_timer_task_address_get(&discharge_timer, NRF_TIMER_TASK_CAPTURE0));
  
  nrf_drv_ppi_channel_enable(ppi_1);
  nrf_drv_ppi_channel_enable(ppi_2);
  
  // Make sure both timers are enabled and cleared
  nrf_drv_timer_clear(&start_counter);
  nrf_drv_timer_enable(&start_counter);
  nrf_drv_timer_clear(&discharge_timer);
  // This will start the timer... whoops!
  //nrf_drv_timer_enable(&discharge_timer);
  
  // Other line not used. set to input
  nrf_gpio_cfg_input(other_line, NRF_GPIO_PIN_NOPULL);
  
  // Charge the capacitor via line
  nrf_drv_gpiote_out_set(line);
  nrf_delay_ms(600);

  // Initialize and enable the out task (high to low)
  nrf_drv_gpiote_out_task_enable(line);
  
  // Initialize and enable then sense line (high to low)
  sense_cfg.pull = NRF_GPIO_PIN_NOPULL;
  nrf_drv_gpiote_in_init(TEMP_SENSE_LINE, &sense_cfg, sense_dummy_handler);
  nrf_drv_gpiote_in_event_enable(TEMP_SENSE_LINE, true);
  
  // Start the PPI tasks
  nrf_drv_timer_increment(&start_counter);
  nrf_delay_ms(600);
  
  // PROBLEM! ->
  // Register only valid if we capture from Event IN callback!
  line_discharge_time = discharge_timer.p_reg->CC[0];
  uint32_t count = nrf_drv_timer_capture(&start_counter, NRF_TIMER_CC_CHANNEL0);

  // TODO: Cleanup
  
  return line_discharge_time;
}

uint32_t system_thermistor_resistance_ppi() {
  
  uint32_t compare_line = do_thermistor_ppi(TEMP_COMP_LINE);
  //uint32_t thermistor_line = do_thermistor_ppi(TEMP_THERM_LINE);
  return 0;
}

Using nRF5_SDK_12.2.0_f012efa with s132_nrf52_3.0.0_softdevice

Any ideas?

Thanks in advance,

  • FormerMember
    +1 FormerMember

    I would think the problem is that the GPIOTE event address is assigned to the PPI channel before the GPIOTE IN event is intialized. I would think you should do something like this:

    // 1) Initialize the sense line (gpiote):
    
    // Initialize and enable then sense line (high to low)
      sense_cfg.pull = NRF_GPIO_PIN_NOPULL;
      nrf_drv_gpiote_in_init(TEMP_SENSE_LINE, &sense_cfg, sense_dummy_handler);
    
    
    // 2) Assign the initialized GPIOTE event to a PPI channel.
    
    // Configure a PPI channel to stop the discharge timer whenever a sense line goes low
      nrf_drv_ppi_channel_alloc(&ppi_2);
      nrf_drv_ppi_channel_assign(ppi_2, 
                                 nrf_drv_gpiote_in_event_addr_get(TEMP_SENSE_LINE), 
                                 nrf_drv_timer_task_address_get(&discharge_timer, NRF_TIMER_TASK_CAPTURE0));
      
      ....

     

    Also, remember to check and handle all error codes! 

Related