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

Toggle 2 LEDs back and forth with PPI, GPIOTE and timer

Hello,

I am using NRF52840 DK to toggle led 3 and 4 back and forth (when LED 3 is ON, LED 4 should be OFF and so on). I have to toggle them in a small time interval of 10ms. I am using SDK 17.02. In order for the power consumption I am using GPIOTE with PPI and timer. Right now I am using a single timer to drive both the LEDs. 

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


static void led_blinking_setup()
{
    uint32_t compare_evt_addr_RED;
    uint32_t gpiote_task_addr_RED;
    uint32_t compare_evt_addr_IR;
    uint32_t gpiote_task_addr_IR;

    ret_code_t err_code;

    nrf_ppi_channel_t ppi_channel_RED;
    nrf_ppi_channel_t ppi_channel_IR;

    uint32_t ticks_RED = nrfx_timer_ms_to_ticks(&timer_gpio, 10); //same ticks for both IR and RED
    NRF_LOG_INFO("GPIO STARTED");


 nrf_drv_gpiote_out_clear(GPIO_OUTPUT_PIN_NUMBER1);

    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
    
    nrf_drv_gpiote_out_config_t config1=
    {
    .action  = NRF_GPIOTE_POLARITY_TOGGLE,                                               \
        .init_state =  NRF_GPIOTE_INITIAL_VALUE_HIGH , \
        .task_pin   = true 

   };
   
    err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER1, &config1);
    



    nrf_drv_timer_extended_compare(&timer_gpio, NRF_TIMER_CC_CHANNEL0, ticks_RED, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);




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

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




    compare_evt_addr_RED = nrf_drv_timer_event_address_get(&timer_gpio, NRF_TIMER_EVENT_COMPARE0);
    gpiote_task_addr_RED = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER);


    compare_evt_addr_IR = nrf_drv_timer_event_address_get(&timer_gpio, NRF_TIMER_EVENT_COMPARE0);
    gpiote_task_addr_IR = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER1);


    err_code = nrf_drv_ppi_channel_assign(ppi_channel_RED, compare_evt_addr_RED, gpiote_task_addr_RED);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel_IR, compare_evt_addr_IR, gpiote_task_addr_IR);
    APP_ERROR_CHECK(err_code);



    err_code = nrf_drv_ppi_channel_enable(ppi_channel_RED);
    APP_ERROR_CHECK(err_code);
      err_code = nrf_drv_ppi_channel_enable(ppi_channel_IR);
    APP_ERROR_CHECK(err_code);


    nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER);
   nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER1);
}

The code works perfectly fine. But I believe there is a better way approach rather than this method. By using this method, one of the LED remains ON even after the required time of the activity ends. I need the signal to be precise and accurate and should not course any problem in the timing. They have to be synchronized perfectly. Is there any other better approach to this problem?

Would appreciate your help. Thank you. 

Parents
  • Why not use a single PPI?  In addition to its "task endpoint", you can add a "fork endpoint" too, so in this way from one input a PPI can drive two outputs.

    Setup your two GPIOTEs to toggle, but with opposite initial states.  Keep your timer setup to cycle every 10ms with the shortcut (like your have already done). When the one PPI is triggered, it will toggle both of your LEDs simultaneously (and having started out in opposite states, they'll always be in opposite states)

Reply
  • Why not use a single PPI?  In addition to its "task endpoint", you can add a "fork endpoint" too, so in this way from one input a PPI can drive two outputs.

    Setup your two GPIOTEs to toggle, but with opposite initial states.  Keep your timer setup to cycle every 10ms with the shortcut (like your have already done). When the one PPI is triggered, it will toggle both of your LEDs simultaneously (and having started out in opposite states, they'll always be in opposite states)

Children
  • Hi Daniel,

    Thanks for your suggestion,

    As per your suggestion, the code would look something like this right?

    static void led_blinking_setup()
    {
        uint32_t compare_evt_addr;
        uint32_t gpiote_task_addr;
        uint32_t gpiote_task_addr2;
        nrf_ppi_channel_t ppi_channel;
        ret_code_t err_code;
    
        nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
        nrf_drv_gpiote_out_config_t config2 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
    
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &config);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER2, &config2);
        APP_ERROR_CHECK(err_code);
    
        uint32_t ticks_RED = nrfx_timer_ms_to_ticks(&timer, 10);
    
        nrf_drv_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL0, ticks_RED, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
        APP_ERROR_CHECK(err_code);
    
        compare_evt_addr = nrf_drv_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE0);
        gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER);
    
        gpiote_task_addr2 = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER2);
    
        err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel, gpiote_task_addr2);  
        
        err_code = nrf_drv_ppi_channel_enable(ppi_channel);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER);
        nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER2);
    }
    

    But as Edvin suggested it might be safer to set and clear the pins. With that being said, I will test both the approach.  Thank you.

    Best Regards,

    Adarsh

  • Yes, just like that (BTW looks like you forgot to check error return code after line 31)

  • Oh yeah. Was just a test code. Will include in the main code after testing Edvin's approach as well. Thank you for pointing out the error. Cheers!!

  • That looks good! 

    Thanks for pointing out the forking,

    I have given the toggle vs set/clr some thought. When you are using PPI I think you will be just fine using toggle, because the execution of the tasks are not dependent on the CPU. Particularly when you use the Fork functionality, these are based on the same event signal. 

    If you had a toggle functionality that was done in an event handler in your application, which would require the CPU to execute it is more "scary", because this execution may be interrupted e.g. between the two toggle pin operations, by a higher priority interrupt. In worst case, the event will not be resumed until it occurs again, which may leave one pin in the wrong state. 

    Best regards,

    Edvin

Related