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

Unable to use a DPPI group that disable itself

Hello,

I'm trying to port the radio synchronization  to a nRF5340. For this i need to use a DPPI group that clear a timer and disable itself for doing the task only 1 time. Unfortunately when i try to do it, the timer is not cleared.

I have try to isolate the problem with a simpler code that use a timer and DPPI group for making some shorter period. The code is not clearing the timer when using the DPPI group:

/* Includes ------------------------------------------------------------------*/

#include <nrfx_dppi.h>
#include <nrfx_gpiote.h>
#include <nrfx_timer.h>
#include <drivers/clock_control/nrf_clock_control.h>

/* Private define ------------------------------------------------------------*/

#define OUTPUT_PIN NRF_GPIO_PIN_MAP(1,7)

#define PERIOD       (40*16000)
#define SHORT_PERIOD (20*16000)
#define OFFSET       1
#define PULSE_WIDTH  16000

/* Private variables ---------------------------------------------------------*/

// Timer need 4 CC
// CC0: overflow
// CC1: short period
// CC2: for generating the raise of the output signal
// CC3: for generating the fall of the output signal
static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(0);

static nrf_dppi_channel_group_t group;

static uint32_t counter = 0;

/* Private functions declaration ---------------------------------------------*/

static void timer_init(void);
static void configure_one_time_clear(void);
static void timer_handler(nrf_timer_event_t event_type, void *p_context);
static void output_init(void);

/* Public functions ----------------------------------------------------------*/

void main(void)
{
    z_nrf_clock_bt_ctlr_hf_request();

    timer_init();
    output_init();

    while(1) {
        k_sleep(K_MSEC(1000));
    }
}

/* Private functions implementation ------------------------------------------*/

static void timer_init(void)
{
    nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrfx_timer_init(&timer, &timer_config, timer_handler);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, PERIOD, true);
    nrf_timer_shorts_enable(timer.p_reg, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL1, SHORT_PERIOD, false);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL2, OFFSET, false);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL3, OFFSET + PULSE_WIDTH, false);

    IRQ_CONNECT(TIMER0_IRQn, NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - 1,
        nrfx_timer_0_irq_handler, NULL, 0);

    nrfx_timer_enable(&timer);

    configure_one_time_clear();
}

static void configure_one_time_clear(void)
{
    uint8_t channel;

    nrfx_dppi_group_alloc(&group);
    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE1, channel);
    nrf_timer_subscribe_set(timer.p_reg, NRF_TIMER_TASK_CLEAR, channel);
    nrf_dppi_subscribe_set(NRF_DPPIC, nrf_dppi_group_disable_task_get(group), channel);
    nrfx_dppi_channel_include_in_group(channel, group);
}

static void timer_handler(nrf_timer_event_t event_type, void *p_context)
{
    if (event_type == NRF_TIMER_EVENT_COMPARE0) {
        counter++;
        if (counter == 2) {
            counter = 0;
            nrfx_dppi_group_enable(group);
        }
    }
}

static void output_init(void)
{
    if (!nrfx_gpiote_is_init()) {
        nrfx_gpiote_init(0);
    }

    nrfx_gpiote_out_config_t const out_config = {
            .init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
            .action     = NRF_GPIOTE_POLARITY_TOGGLE,
            .task_pin   = true,
    };

    nrfx_gpiote_out_init(OUTPUT_PIN, &out_config);
    nrfx_gpiote_out_task_enable(OUTPUT_PIN);

    uint8_t channel;
    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE2, channel);
    nrf_gpiote_subscribe_set(
            NRF_GPIOTE,
            nrfx_gpiote_set_task_get(OUTPUT_PIN),
            channel);
    nrfx_dppi_channel_enable(channel);

    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE3, channel);
    nrf_gpiote_subscribe_set(
            NRF_GPIOTE,
            nrfx_gpiote_clr_task_get(OUTPUT_PIN),
            channel);
    nrfx_dppi_channel_enable(channel);
}

The equivalent code that use PPI instead of DPPI work correctly on a nRF52840:

/* Includes ------------------------------------------------------------------*/

#include <nrfx_ppi.h>
#include <nrfx_gpiote.h>
#include <nrfx_timer.h>
#include <drivers/clock_control/nrf_clock_control.h>

/* Private define ------------------------------------------------------------*/

#define OUTPUT_PIN NRF_GPIO_PIN_MAP(1,7)

#define PERIOD       (40*16000)
#define SHORT_PERIOD (20*16000)
#define OFFSET       1
#define PULSE_WIDTH  16000

/* Private variables ---------------------------------------------------------*/

// Timer need 4 CC
// CC0: overflow
// CC1: short period
// CC2: for generating the raise of the output signal
// CC3: for generating the fall of the output signal
static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(0);

static nrf_ppi_channel_group_t group;

static uint32_t counter = 0;

/* Private functions declaration ---------------------------------------------*/

static void timer_init(void);
static void configure_one_time_clear(void);
static void timer_handler(nrf_timer_event_t event_type, void *p_context);
static void output_init(void);

/* Public functions ----------------------------------------------------------*/

void main(void)
{
    z_nrf_clock_bt_ctlr_hf_request();

    timer_init();
    output_init();

    while(1) {
        k_sleep(K_MSEC(1000));
    }
}

/* Private functions implementation ------------------------------------------*/

static void timer_init(void)
{
    nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrfx_timer_init(&timer, &timer_config, timer_handler);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, PERIOD, true);
    nrf_timer_shorts_enable(timer.p_reg, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL1, SHORT_PERIOD, false);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL2, OFFSET, false);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL3, OFFSET + PULSE_WIDTH, false);

    IRQ_CONNECT(TIMER0_IRQn, NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - 1,
        nrfx_timer_0_irq_handler, NULL, 0);

    nrfx_timer_enable(&timer);

    configure_one_time_clear();
}

static void configure_one_time_clear(void)
{
    nrf_ppi_channel_t channel;

    nrfx_ppi_group_alloc(&group);

    nrfx_ppi_channel_alloc(&channel);
    nrfx_ppi_channel_assign(channel,
            nrfx_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE1),
            nrfx_timer_task_address_get(&timer, NRF_TIMER_TASK_CLEAR));
    nrfx_ppi_channel_include_in_group(channel, group);

    nrfx_ppi_channel_alloc(&channel);
    nrfx_ppi_channel_assign(channel,
            nrfx_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE1),
            nrfx_ppi_task_addr_group_disable_get(group));
    nrfx_ppi_channel_include_in_group(channel, group);
}

static void timer_handler(nrf_timer_event_t event_type, void *p_context)
{
    if (event_type == NRF_TIMER_EVENT_COMPARE0) {
        counter++;
        if (counter == 2) {
            counter = 0;
            nrfx_ppi_group_enable(group);
        }
    }
}

static void output_init(void)
{
    if (!nrfx_gpiote_is_init()) {
        nrfx_gpiote_init(0);
    }

    nrfx_gpiote_out_config_t const out_config = {
            .init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
            .action     = NRF_GPIOTE_POLARITY_TOGGLE,
            .task_pin   = true,
    };

    nrfx_gpiote_out_init(OUTPUT_PIN, &out_config);
    nrfx_gpiote_out_task_enable(OUTPUT_PIN);

    nrf_ppi_channel_t channel;
    nrfx_ppi_channel_alloc(&channel);
    nrf_ppi_event_endpoint_setup(NRF_PPI, channel, nrf_timer_event_address_get(timer.p_reg, NRF_TIMER_EVENT_COMPARE2));
    nrf_ppi_task_endpoint_setup(NRF_PPI, channel, nrfx_gpiote_set_task_addr_get(OUTPUT_PIN));
    nrfx_ppi_channel_enable(channel);

    nrfx_ppi_channel_alloc(&channel);
    nrf_ppi_event_endpoint_setup(NRF_PPI, channel, nrf_timer_event_address_get(timer.p_reg, NRF_TIMER_EVENT_COMPARE3));
    nrf_ppi_task_endpoint_setup(NRF_PPI, channel, nrfx_gpiote_clr_task_addr_get(OUTPUT_PIN));
    nrfx_ppi_channel_enable(channel);
}

Is there something wrong in my DPPI code or is there a bug or limitation in the nRF5340 ?

Parents
  • Hi,

     

    My apologies for the long wait. Reproducing the issue was easy, but finding a fix for the problem took a bit of time.

    It seems that you have to include the dppi channel into the group before subscribing to the disable task:

        ...
        nrfx_dppi_channel_include_in_group(channel, group);
        nrf_dppi_subscribe_set(NRF_DPPIC, nrf_dppi_group_disable_task_get(group), channel);
        ...
        

     

    I slightly modified your test code, changing the base frequency and print "0" and "1" from the timer interrupt.

    Here's my code:

    /* Includes ------------------------------------------------------------------*/
    
    #include <nrfx_dppi.h>
    #include <nrfx_gpiote.h>
    #include <nrfx_timer.h>
    #include <drivers/clock_control/nrf_clock_control.h>
    
    /* Private define ------------------------------------------------------------*/
    
    #define OUTPUT_PIN NRF_GPIO_PIN_MAP(1,7)
    
    #define PERIOD       (40*16000)
    #define SHORT_PERIOD (20*16000)
    #define OFFSET       1
    #define PULSE_WIDTH  16000
    
    /* Private variables ---------------------------------------------------------*/
    
    // Timer need 4 CC
    // CC0: overflow
    // CC1: short period
    // CC2: for generating the raise of the output signal
    // CC3: for generating the fall of the output signal
    static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(0);
    
    static nrf_dppi_channel_group_t group;
    
    static uint32_t counter = 0;
    
    /* Private functions declaration ---------------------------------------------*/
    
    static void timer_init(void);
    static void configure_one_time_clear(void);
    static void timer_handler(nrf_timer_event_t event_type, void *p_context);
    static void output_init(void);
    
    /* Public functions ----------------------------------------------------------*/
    
    void main(void)
    {
        z_nrf_clock_bt_ctlr_hf_request();
    
        timer_init();
        output_init();
    
        while(1) {
            k_sleep(K_MSEC(1000));
        }
    }
    
    /* Private functions implementation ------------------------------------------*/
    
    static void timer_init(void)
    {
        nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
        timer_config.frequency = NRF_TIMER_FREQ_125kHz;
        timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
        nrfx_timer_init(&timer, &timer_config, timer_handler);
        nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, PERIOD, true);
        nrf_timer_shorts_enable(timer.p_reg, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK);
        nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL1, SHORT_PERIOD, true);
        nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL2, OFFSET, false);
        nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL3, OFFSET + PULSE_WIDTH, false);
    
        IRQ_CONNECT(TIMER0_IRQn, NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - 1,
            nrfx_timer_0_irq_handler, NULL, 0);
    
        nrfx_timer_enable(&timer);
    
        configure_one_time_clear();
    }
    
    static void configure_one_time_clear(void)
    {
        uint8_t channel;
    
        nrfx_dppi_group_alloc(&group);
        nrfx_dppi_channel_alloc(&channel);
        nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE1, channel);
        nrf_timer_subscribe_set(timer.p_reg, NRF_TIMER_TASK_CLEAR, channel);
        nrfx_dppi_channel_include_in_group(channel, group);
        nrf_dppi_subscribe_set(NRF_DPPIC, nrf_dppi_group_disable_task_get(group), channel);    
    }
    
    static void timer_handler(nrf_timer_event_t event_type, void *p_context)
    {
        if (event_type == NRF_TIMER_EVENT_COMPARE0) {
            printk("0");
            nrfx_dppi_group_enable(group);
        }
        if (event_type == NRF_TIMER_EVENT_COMPARE1) {
            printk("1");
        }
    }
    
    static void output_init(void)
    {
        if (!nrfx_gpiote_is_init()) {
            nrfx_gpiote_init(0);
        }
    
        nrfx_gpiote_out_config_t const out_config = {
                .init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
                .action     = NRF_GPIOTE_POLARITY_TOGGLE,
                .task_pin   = true,
        };
    
        nrfx_gpiote_out_init(OUTPUT_PIN, &out_config);
        nrfx_gpiote_out_task_enable(OUTPUT_PIN);
    
        uint8_t channel;
        nrfx_dppi_channel_alloc(&channel);
        nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE2, channel);
        nrf_gpiote_subscribe_set(
                NRF_GPIOTE,
                nrfx_gpiote_set_task_get(OUTPUT_PIN),
                channel);
        nrfx_dppi_channel_enable(channel);
    
        nrfx_dppi_channel_alloc(&channel);
        nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE3, channel);
        nrf_gpiote_subscribe_set(
                NRF_GPIOTE,
                nrfx_gpiote_clr_task_get(OUTPUT_PIN),
                channel);
        nrfx_dppi_channel_enable(channel);
    }

     

    Which should print in this order:

    10110110110....

     

    Could you see if you get the correct behavior when switching the order of the above two functions?

     

    Kind regards,

    Håkon

  • A small update.

    The strict ordering is a hardware restriction, as explained in the DPPIC part of the PS:

    https://infocenter.nordicsemi.com/topic/ps_nrf5340/dppi.html?cp=3_0_0_6_8_1#concept_crl_ql5_v1b

    In order to write to CHG[x], the corresponding CHG[x].EN and CHG[x].DIS subscribe registers must be disabled. 
    Writes to CHG[x] are ignored if any of the two subscribe registers are enabled.

     

    Kind regards,

    Håkon

Reply Children
Related