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

Custom waveform generation in two pins

Hello,

Considering the waveforms in the following chart, which will be the best approach to keep the power consumption as low as possible?

Specifically, which peripheral is suggested (i.e. pwm, timer, ppi)?

The two (2) waveform are identical, except for the delay between (0 to 2ms).

Thanks

Parents
  • Hi,

    the most power-saving approach is an RTC peripheral that would control two GPIOTE channels via PPI.

  • If the waveform is more or less static and don't require higher accuracy than 30.51µs, the RTC + GPIOTE is the best choice. 

    If the waveform is constantly changing and you have strict timing requirements the PWM peripheral will be best.

    Can you share more information on the nature of your signals? 

  • As you suggested, I'm using a Toggle task with single GPIOTE channel for each PIN. Sorry about the diagram, it's doesn't match your suggestion.

    I also set the initial conditions of the GPIOTE with function nrf_drv_gpiote_out_init, specifically as show below

        nrf_drv_gpiote_out_config_t config1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
        nrf_drv_gpiote_out_config_t config2 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
    
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN1, &config1);
        APP_ERROR_CHECK(err_code);
        
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN2, &config2);
        APP_ERROR_CHECK(err_code);

    In this case, how can I set the first pulse (in PIN1) with the correct width?

    Thanks for clarifying the approach suggested to count the cycles. It's clear.

    Currently, the COMPARE[0], COMPARE[1] and COMPARE[2] are toggling the PIN1 and PIN2 as expected, except for the following:

    • The initial pulse width in PIN1 is not correct
    • The COMPARE[3] is not toggling the PIN1
    • The COMPARE[3] is not clearing the RTC. If I change the task from COMPARE[3] to COMPARE[2] event, the RTC is clear.

    Below the waveform acquired

    And source code (modified version of rtc_pca10056)

    #include "nrf.h"
    #include "nrf_gpio.h"
    #include "nrf_drv_rtc.h"
    #include "nrf_drv_clock.h"
    #include "boards.h"
    #include "app_error.h"
    #include "nrf_drv_ppi.h"
    #include "nrf_drv_gpiote.h"
    #include <stdint.h>
    #include <stdbool.h>
    
    #define GPIO_OUTPUT_PIN1 NRF_GPIO_PIN_MAP(0,27)
    #define GPIO_OUTPUT_PIN2 NRF_GPIO_PIN_MAP(0,26)
    
    const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(0);
    
    void rtc_dummy_handler(nrfx_rtc_int_type_t int_type)
    {
        if (int_type == NRF_DRV_RTC_INT_COMPARE0)
        {
        	;
        }
        else if (int_type == NRF_DRV_RTC_INT_TICK)
        {
        	;
        }
    }
    
    /** @brief Function configuring gpio for pin toggling.
     */
    static void leds_config(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
    }
    
    /** @brief Function starting the internal LFCLK XTAL oscillator.
     */
    static void lfclk_config(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    /** @brief Function initialization and configuration of RTC driver instance.
     */
    static void rtc_config(void)
    {
        uint32_t err_code;
    
        //Initialize RTC instance
        nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
        config.prescaler = 0;
        err_code = nrf_drv_rtc_init(&rtc, &config, rtc_dummy_handler);
        APP_ERROR_CHECK(err_code);
    }
    
    static void waveform_setup()
    {
        uint32_t compare_evt_addr0;
        uint32_t compare_evt_addr1;
        uint32_t compare_evt_addr2;
        uint32_t compare_evt_addr3;
        uint32_t clear_task_addr3;
        uint32_t gpiote_task_addr0;
        uint32_t gpiote_task_addr1;
        uint32_t gpiote_task_addr2;
        uint32_t gpiote_task_addr3;
        uint32_t gpiote_task_addr3a;
        nrf_ppi_channel_t ppi_channel0;
        nrf_ppi_channel_t ppi_channel1;
        nrf_ppi_channel_t ppi_channel2;
        nrf_ppi_channel_t ppi_channel3;
        nrf_ppi_channel_t ppi_channel4;
        ret_code_t err_code;
    
        nrf_drv_gpiote_out_config_t config1 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
        nrf_drv_gpiote_out_config_t config2 = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
    
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN1, &config1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN2, &config2);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrfx_rtc_cc_set(&rtc, 0, 3, false);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_rtc_cc_set(&rtc, 1, 33, false);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_rtc_cc_set(&rtc, 2, 36, false);
        APP_ERROR_CHECK(err_code);
        err_code = nrfx_rtc_cc_set(&rtc, 3, 69, false);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel0);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel2);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel3);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel4);
        APP_ERROR_CHECK(err_code);
    
        compare_evt_addr0 = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_0);
        compare_evt_addr1 = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_1);
        compare_evt_addr2 = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_2);
        compare_evt_addr3 = nrf_drv_rtc_event_address_get(&rtc, NRF_RTC_EVENT_COMPARE_3);
    
        gpiote_task_addr0 = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN1); // PIN1: edge UP 
        gpiote_task_addr1 = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN2); // PIN2: edge UP 
        gpiote_task_addr2 = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN2); // PIN2: edge DOWN
        gpiote_task_addr3 = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN1); // PIN1: edge DOWN 
        clear_task_addr3 = nrf_drv_rtc_task_address_get(&rtc, NRF_RTC_TASK_CLEAR); // RTC: clear counter
    
        err_code = nrf_drv_ppi_channel_assign(ppi_channel0, compare_evt_addr0, gpiote_task_addr0);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_assign(ppi_channel1, compare_evt_addr1, gpiote_task_addr1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_assign(ppi_channel2, compare_evt_addr2, gpiote_task_addr2);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_assign(ppi_channel3, compare_evt_addr3, gpiote_task_addr3);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_assign(ppi_channel4, compare_evt_addr3, clear_task_addr3);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_enable(ppi_channel0);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel1);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel2);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel3);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel4);
        APP_ERROR_CHECK(err_code);
    
        nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN1);
        nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN2);
    
        nrf_gpio_pin_clear(GPIO_OUTPUT_PIN1);
        nrf_gpio_pin_clear(GPIO_OUTPUT_PIN2);
    }
    
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
    
        lfclk_config();
    
        rtc_config();
    
        waveform_setup();
    
        nrf_drv_rtc_enable(&rtc);
    
        while (true)
        {
            __SEV();
            __WFE();
            __WFE();
        }
    }
    

    I'm using nRF5_SDK_17.0.0_9d13099 and a nRF52840 DK for this task.

  • Hu Lao said:
    In this case, how can I set the first pulse (in PIN1) with the correct width?

    True, I think your approach is correct, but I think you still need the initial condition of the GPIOTE pin in order for the toggle to work as intended. 

    Hu Lao said:

    • The COMPARE[3] is not toggling the PIN1
    • The COMPARE[3] is not clearing the RTC. If I change the task from COMPARE[3] to

    You have to use RTC1 or RTC2. See:

    Registers

    Table 4. Instances
    Base address Peripheral Instance Description Configuration
    0x4000B000 RTC RTC0

    Real-time counter 0

    CC[0..2] implemented, CC[3] not implemented

     
    0x40011000 RTC RTC1

    Real-time counter 1

    CC[0..3] implemented

     
    0x40024000 RTC RTC2

    Real-time counter 2

    CC[0..3] implemented

     
  • True, I think your approach is correct, but I think you still need the initial condition of the GPIOTE pin in order for the toggle to work as intended.

    Please, may you clarify which part is still needed? I though I already put the initial conditions with the function nrf_drv_gpiote_out_init and the macro GPIOTE_CONFIG_OUT_TASK_TOGGLE.

    About the RTC0 and COMPARE, very good catch!! After changing to RC2, the waveform is as expected, except the initial pulse, which I still need to set in order to use the solution. Is very important the output pins comply with the width defined. It will be used in external switch for actuators.

    Please, could you give a suggestion in order to set the initial pulse width (PIN1)?

    Thanks again for your support in this issue!

  • Hi , I worked on the period counting using the timer and compare function. The implementation worked as suggested. Thanks again for that!

    I only have the issue about the initial pulse width, which is around 1ms, when I need 0.1ms. Do you think, there is a way to reduce that pulse width? In case not, the PWM will be the only solution?

    The application could have an error in the pulse width of ~30us, but in this case the error is around ~1000us, which is too much.

    Thanks

    PS: I deleted the previous reply because was a false positive (I didn't fix the initial pulse width)

  • You can configure RTC to clear with the end of second channel pulse and set initial level to 0 for both channels, so your sequence will be aligned to the end of period (there will be some delay before the first pulse though)

Reply Children
  • Hi , thanks again for your feedback. Your suggestion is great. In this application the position of the first pulse could be delayed at the beginning. 

    I just implemented and is working as expected. Below the waveform for future reference. 

    Regarding the other two segments (OFF left and OFF right) do you have a suggestion, again trying to keep the power as low as possible? 

    Thanks

  • Hi again, just a small note to mentioned that the phases OFF (pre and post) were implemented using the app_timer. Perhaps, using the RTC with a comparator (+interruption for setup ON phase) will be a better approach for power consumption but I still need to check overall resources.

    In any case, the phase ON was very power sensitive.

    Thanks all for your feedback!

Related