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
  • Hello,

    If the two LEDs are always the opposite of one another, I would try to make the two use the same timer for both gpios. 

    E.g. see if you can set the timer to run for 20ms, but set one compare at 10ms and one at 20ms. Remember to only NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK at the 20ms compare.

    Then you have to set the ppi channels to turn one LED on and one off at 10ms, and vice versa at 20ms. In theory you should be able to use toggle with opposite starting conditions, but it is safer to set and clear the pins.

    Let me know if this was very unclear. I don't have a project that is set up the same as you, so if you get stuck, please try to upload the project folder (zip it and upload it here). 

    Best regards,

    Edvin

Reply
  • Hello,

    If the two LEDs are always the opposite of one another, I would try to make the two use the same timer for both gpios. 

    E.g. see if you can set the timer to run for 20ms, but set one compare at 10ms and one at 20ms. Remember to only NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK at the 20ms compare.

    Then you have to set the ppi channels to turn one LED on and one off at 10ms, and vice versa at 20ms. In theory you should be able to use toggle with opposite starting conditions, but it is safer to set and clear the pins.

    Let me know if this was very unclear. I don't have a project that is set up the same as you, so if you get stuck, please try to upload the project folder (zip it and upload it here). 

    Best regards,

    Edvin

Children
  • Hai Edvin,

    Thanks for the reply.

    Definitely, I will try your approach and let you know in case of any difficulties within 2 days. Unfortunately, I don't have the board with me today.

    Best regards,

    Adarsh 

  • No worries. After I reply to a ticket like this one, it will enter a waiting state and disappear from my queue. When you write something, it will pop back up. Just let me know if you have any issues when you have had time to do some testing.

    Best regards,

    Edvin

  • Hai Edvin,

    If I have understood you right, the below-given code would do the trick right? But I have trouble setting up and clearing the pins. So I have left that line empty. Is my approach correct? If so how can u add up the missing line? 

    static void led_blinking_setup()
    {
        uint32_t compare_evt_addr;
        uint32_t gpiote_task_addr;
        uint32_t compare_evt_addr1;
        uint32_t gpiote_task_addr1;
       
        nrf_ppi_channel_t ppi_channel;
        ret_code_t err_code;
    
       // Here comes the LED SET and CLEAR function
    
        uint32_t ticks_RED = nrfx_timer_ms_to_ticks(&timer, 20);
    
        nrf_drv_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, (ticks_RED/2), false);
        nrf_drv_timer_extended_compare(&timer, NRF_TIMER_CC_CHANNEL1, 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);
    
        compare_evt_addr1 = nrf_drv_timer_event_address_get(&timer, NRF_TIMER_EVENT_COMPARE1);
        gpiote_task_addr1 = 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_assign(ppi_channel, compare_evt_addr1, gpiote_task_addr1);
        APP_ERROR_CHECK(err_code);
        
        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);
    }
    

    regards,

    Adarsh

  • This is obviously not your entire application, so it is a bit difficult to say what's missing. But you should either use two different ppi channels for gpiote_task_addr and gpiote_task_addr1, or you should use the fork functionality, as we discussed earlier. 

    Other than that, it depends on what you have done outside this snippet. How did you set up your tasks on your gpiote_task_add and gpitoe_task_addr1?

    And did you remember to actually start the timer?

    Best regards,

    Edvin

  • Hai Edvin,

    Sorry for the missleadings.

    As Daniel mensioned I was able to work my application with single ppi channel using fork functionality. The file is  gpiote- Daniel.zip here.

    But as you mentioned, there is a risk of one LED to remain in the false state at some time during the application when driven by higher priority interrupt.

    So as you suggested, I initialized two PPI channel. Then I set the timer to run at 20ms and set one compare at 10ms and the other at 20ms. Now I am having difficulties in assigning the tasks to set and clear the pins. Here I am uploading the whole file. Kindly have a look.

    gpiote- Edvin.zip 

    Thank you

    Regards,

    Adarsh

Related