Configure EasyDMA + GPIOTE output + Timer

Hi, I hope you had a nice weekend.

In my project, I basically need to output some data over GPIO from a memory buffer (highs and lows).  Currently I'm doing this with a timer and ISR. The problem is that the timing must be accurate between each transmission (exactly 420us) but with other tasks in the app (running BLE with NCS1.9), there is almost always a drift from this timing. 

I'm thinking of using EasyDMA + GPIOTE to match this timing requirement. My idea is to configure the system such that:

- Configure PPI to connect timer event to GPIOTE task

- Each time a timer event occurs (with exactly 420us intervals), move a bit (high/low value) from DMA buffer (I think it is an ArrayList) to GPIO output.

From the discussions in this forum, I feel that it can be done in this way but I couldn't see any example of connecting DMA buffer and GPIOTE output. 

It would be highly appreciated if one could point me to an example of this.

Many thanks,

Ozan

Parents
  • Hi,

    - Each time a timer event occurs (with exactly 420us intervals), move a bit (high/low value) from DMA buffer (I think it is an ArrayList) to GPIO output.

    You can either set the pin high, set the pin low, or toggle it with GPIOTE. The task that is connected through the PPI can't be decided based on the memory buffer. In other words, a specific event needs to be connected to a specific task before run time. 

    Maybe you could use SPIM with PPI?

    regards

    Jared 

  • Thanks for the reply, Jared! 

    Unfortunately, I have to go with GPIOTE.

    Now, I have such a configuration:

    - I have timer1, generating compare event every 420us.

    - I have a PPI configured to connect timer1-compare to GPIOTE-toggle task.

    So I have a regular pattern HIGH and LOW, alternating every 420us.

    My question is now, can I update timer1-CC register while it is running (while still generating compare events) to, let's say, 840us, so that I can have different HIGH/LOW patterns instead of toggling every 420us? 

    When timer1 is not active ( nrfx_timer_pause(&m_timer1) ) , I can do that. But when it is active, updating CC makes it unstable.

    What I have:

    420us   420us  420us  420us  420us  420us  

    HIGH    LOW   HIGH   LOW   HIGH   LOW 

    pause here then set CC to 840us, then resume:

    420us   420us  420us  420us  420us  420us  

    HIGH    HIGH   LOW   LOW   HIGH   HIGH

    What I want to have:

    420us   420us  420us  420us  420us  420us  

    HIGH   HIGH    LOW   LOW    HIGH   LOW     (and any random combination of HIGHs/LOWs based on compare value without pausing timer1)

    Is this possible?

    Thanks again for the help.

    Ozan

  • Hi,

    This was a very good explanation of what you want. Slight smile

    ozanoner said:
    I can do that. But when it is active, updating CC makes it unstable.

    But I'm wondering if you could elaborate on what happens when you load the CC when it's active? Is it only unstable after the subsequent events or the rest of the time after updating the CC. Have you verified that the CC register is loaded properly by reading it out with a debugger? 

    Are you using the nrfx_timer_compare() to load the new CC? What are you passing as the last parameter? Note that the function call will clear any pending interrupts if it's set to true.

    regards

    Jared

  • Thanks for the reply, Jared. After a bit more digging, the following code piece worked for me. I am sharing for others if somewhere needed.

            nrfx_timer_clear(&m_timer1);
    		nrfx_timer_clear(&m_timer2);
    		nrfx_timer_resume(&m_timer1);
    		nrfx_timer_resume(&m_timer2);
    		k_sleep(K_MSEC(10));
    		nrfx_timer_pause(&m_timer2);
    		nrfx_timer_pause(&m_timer1);

    In the timer2 event handler, I am updating the CC register of timer1, and it works Slight smile

    static bool change[] = {true, false, false, true, false, false};
    static volatile int idx = 0;
    static volatile int cnt = 0;
    static volatile bool skip = false;
    
    void timer2_event_handler(nrf_timer_event_t event_type, void *p_context)
    {
    	switch (event_type)
    	{
    	case NRF_TIMER_EVENT_COMPARE0:
    	{
    		if (skip)
    		{
    			skip = false;
    			break;
    		}
    		if (change[idx++])
    		{
    			skip = true;
    			m_timer1.p_reg->CC[NRF_TIMER_CC_CHANNEL0] = INTERVAL_US * 2;
    		}
    		else
    		{
    			m_timer1.p_reg->CC[NRF_TIMER_CC_CHANNEL0] = INTERVAL_US;
    		}
    		idx %= 6;
    		++cnt;
    	}
    	break;
    
    	default:
    		break;
    	}
    }

    If you have any better idea to do the same thing in another way, I would gladly test it and share the result here.

    Many thanks,

    Ozan

Reply
  • Thanks for the reply, Jared. After a bit more digging, the following code piece worked for me. I am sharing for others if somewhere needed.

            nrfx_timer_clear(&m_timer1);
    		nrfx_timer_clear(&m_timer2);
    		nrfx_timer_resume(&m_timer1);
    		nrfx_timer_resume(&m_timer2);
    		k_sleep(K_MSEC(10));
    		nrfx_timer_pause(&m_timer2);
    		nrfx_timer_pause(&m_timer1);

    In the timer2 event handler, I am updating the CC register of timer1, and it works Slight smile

    static bool change[] = {true, false, false, true, false, false};
    static volatile int idx = 0;
    static volatile int cnt = 0;
    static volatile bool skip = false;
    
    void timer2_event_handler(nrf_timer_event_t event_type, void *p_context)
    {
    	switch (event_type)
    	{
    	case NRF_TIMER_EVENT_COMPARE0:
    	{
    		if (skip)
    		{
    			skip = false;
    			break;
    		}
    		if (change[idx++])
    		{
    			skip = true;
    			m_timer1.p_reg->CC[NRF_TIMER_CC_CHANNEL0] = INTERVAL_US * 2;
    		}
    		else
    		{
    			m_timer1.p_reg->CC[NRF_TIMER_CC_CHANNEL0] = INTERVAL_US;
    		}
    		idx %= 6;
    		++cnt;
    	}
    	break;
    
    	default:
    		break;
    	}
    }

    If you have any better idea to do the same thing in another way, I would gladly test it and share the result here.

    Many thanks,

    Ozan

Children
No Data
Related