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

Configuring nRF52840 for counting input frequency of upto 5MHz.

Hello,

I want to configure my nRF52840 development kit for counting input pulses on one of the GPIOs (Pin 13) in this case. I am using SEGGER Embedded Studio as IDE.

Here I am using PPI, GPIOTE and TIMER for this configuration. I have configured GPIOTE as an input event. As I want to count the rising edge on that particular GPIO, I have configured that GPIO to sense Low to High signal. According to this event, gpiote event handler is called and the static counter is incremented. 

As I searched in nordic forum, PPI is capable for operating in 8MHz. But when I apply 2MHz clock as input to the above mentioned GPIO, I don't get exact count in the watch window. As per the count, nRF kit is only able to count pulses at about 120kHz frequency.

If I apply clock below 120kHz it is counting perfectly. And when I increase the frequency, for example 1MHz or something, I get maximum count of 120kHz.

Is there anything missing here? Is my configuration correct? Is there a specific GPIO which has to be used for this purpose?

I want to capture the input frequency of about 5MHz. Is it possible with the nRF52840 dev kit?

Below is the configuration for PPI channel as well as timer0 settings.

static void gpiote_event_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
++m_gpiote_counter;
}

static void ppi_init(void)
{
uint32_t err_code = NRF_SUCCESS;
nrf_drv_gpiote_in_config_t config = NRFX_GPIOTE_RAW_CONFIG_IN_SENSE_LOTOHI(true);
config.pull = NRF_GPIO_PIN_NOPULL;
uint32_t gpiote_event_addr;
uint32_t PIN_NUMBER = 13;

err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);

err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
nrfx_gpiote_in_uninit(PIN_NUMBER);

err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
APP_ERROR_CHECK(err_code);

err_code = nrf_drv_gpiote_in_init(PIN_NUMBER, &config, gpiote_event_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_gpiote_in_event_enable(PIN_NUMBER, true);

gpiote_event_addr = nrf_drv_gpiote_in_event_addr_get(PIN_NUMBER);


err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1,
gpiote_event_addr,
nrf_drv_timer_task_address_get(&m_timer0,
NRF_TIMER_TASK_COUNT));
APP_ERROR_CHECK(err_code);


// Enable configured PPI channels
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);
APP_ERROR_CHECK(err_code);

}

static void timer0_init(void)
{
// Check TIMER0 configuration for details.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.frequency = NRF_TIMER_FREQ_16MHz;
timer_cfg.mode = NRF_TIMER_MODE_COUNTER;
ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
APP_ERROR_CHECK(err_code);

nrf_drv_timer_extended_compare(&m_timer0,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_us_to_ticks(&m_timer0,
1),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
}

Thanks.

  • You mention that your input frequency is about 5 MHz (about 12 CPU cycles). An interrupt requires 12 cycles for stacking + 12 cycles for unstacking + some time for your handling code. You have no CPU resources for your task...  but 52840 has a good task-event concept that allows to handle such cases directly by hardware. Could you give more details about required logic when you detect a pulse?

  • The requirement goes like this. We want to capture parallel data from camera sensor.

    The data from the camera sensor would be in sync with pixel clock. And we have to sample the 8 bit data on one of the nRF IO port's at every rising edge of the pixel clock. For our requirement, the pixel clock comes around 3-5MHz. This is the rate at which data would be coming to the kit.

    So in short, at every rising edge of the pulse, we want to get data from IO port and store it to an array or something.

    Is this possible by using task-event concept?

  • Unfortunately, there's no peripheral in nrf52 that is able to read parallel data and save it to RAM, you can only read GPIO register. This makes things more interesting )  If you're lucky and CPU power will suffice, you can write a pure software routine in assembly that will do this task. All interrupts should be disabled for the time of reading a stream; if you need to hold a BLE connection, request a timeslot (see Timeslot API). Pass CLK signal through a flip-flop to divide its frequency by two - thus you'll have to handle one CLK transition instead of rise+fall. 

    If data bus is connected to P1.0-P1.7, CLK to P1.8, the rough assembly routine will look like this:

    AcquireData
    	ldr r1, P1_IN
    	ldr r2, DestinationArray
    	ldr r3, #(ByteCount/2)
    wait1
    	ldrb r0, [r1]
    	tst r0, #(1 << 8)   ; waiting for CLK transition 0-1
    	beq wait1
    	strb r0, [r2],#1
    	subs r3, #1
    wait2
    	ldrb r0, [r1]
    	tst r0, #(1 << 8)   ; waiting for CLK transition 1-0
    	bne wait2
    	strb r0, [r2],#1
    	cbnz r3, wait1
        bx lr

Related