GPIO, interrupt & timer latencies larger than expected.

I'm trying to implement communication protocol that involves clock and bidirectional I/O line. Clock signal is coming from an external device and expected to be in 1-5MHz range. I/O pin state is read or changed every N clock pulses.

So I expected to use Timer/Counter peripherial which uses external clock as a source, i.e. each external clock pulse increments the counter and on compare event it is reset to zero and some other actions are done, like sensing I/O input or sending something back on the same I/O pin.

minimal example:


After long investigations using oscilloscope, I've found out that timer skips many clock pulses and compare events are triggered less frequently than expected.

void timer1_init(void)
{ 
 nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
 timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
 timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_16;
 nrf_drv_timer_init(&clock_counter, &timer_cfg, empty_timer_handler);

 nrfx_timer_compare(&clock_counter, NRF_TIMER_CC_CHANNEL0, N, true);

}
void ppi_init(void)
{  
 nrf_drv_ppi_channel_alloc(&ppi_channel1);
 nrf_drv_ppi_channel_alloc(&ppi_channel2);
 
 nrfx_gpiote_in_config_t clock_pin_conf = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
 nrfx_gpiote_in_init(CLOCK_PIN, &clock_pin_conf, empty_gpiote_handler);
  
 nrf_drv_ppi_channel_assign(ppi_channel1,
                            nrfx_gpiote_in_event_addr_get(CLOCK_PIN),
                            nrf_drv_timer_task_address_get(&clock_counter,
                                                           NRF_TIMER_TASK_COUNT));


 //reset counter on COMPARE0 event
 nrf_drv_ppi_channel_assign(ppi_channel2,
                            nrf_drv_timer_event_address_get(&clock_counter,
                                                            NRF_TIMER_EVENT_COMPARE0),
                            nrf_drv_timer_task_address_get(&clock_counter,
                                                           NRF_TIMER_TASK_CLEAR));
  
 nrf_drv_ppi_channel_enable(ppi_channel1);
 nrf_drv_ppi_channel_enable(ppi_channel2);

 nrfx_gpiote_in_event_enable(CLOCK_PIN, true);
}
I've tried to toggle test GPIO pin state in GPIOTE event handler callback for debugging, and it appears it is triggered on every 5-10 input pulses instead of every rising edge as expected. It could mean GPIO events or interrupts have unexpectedly big latency.

I tried to avoid using any additional peripherials and poll the clock pin in a loop, and the best result I was able to achieve is that output debug pin is toggled on every 3 incoming pulses. That means NRF52 takes too long to read changed GPIO pin state, unexpectedly long for CPU running at 64MHz and peripherial clocks at 16 or 32 MHz

 int clockstate = 0;


 NRF_POWER->TASKS_CONSTLAT = 1;

 nrfx_clock_init(empty_clock_event_handler);
 nrfx_clock_hfclk_start();
 while(!nrfx_clock_hfclk_is_running());


nrf_gpio_cfg_output(19);
 __disable_irq();

  while(1)
 {
    int newclockstate;
    newclockstate = nrf_gpio_pin_read(CLOCK_PIN);

    if (clockstate != newclockstate)
    {
      nrf_gpio_pin_write(19, newclockstate);
      clockstate = newclockstate;
    }
 }

Channel1 -- input clock, Channel2 -- output debug pin

Even just changing the state of output debug pin results in only ~3.1MHz output

 int clockstate = 0;



  NRF_POWER->TASKS_CONSTLAT = 1;

  nrfx_clock_init(empty_clock_event_handler);
  nrfx_clock_hfclk_start();
  while(!nrfx_clock_hfclk_is_running());

  nrf_gpio_cfg_output(19);
  __disable_irq();
  while(1)
  {
    nrf_gpio_pin_write(19, (clockstate++) & 0x1);
  }

output signal

Is NRF52832 (or all NRF52) that bad and slow in peripherials, or am I doing something wrong?
Why there is no information about GPIO switching times in datasheet?

Is it possible to achieve my task to sense 5MHz clock using NRF52832 or NRF52810 at all?
I thought about using SPIS (SPI slave peripherial) as a possible workaround,

datasheet says it can handle up to 8Mbit input clock,

and probably MOSI/MISO pin roles can be changed on the fly and assigned to the same I/O pin for input and output,

as I need to change I/O pin state on every 200-300 clock, not on every clock pulse, so TX register can be fed with

all zeroes/all ones value.

Parents Reply Children
No Data
Related