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

nRF52832 PWM sequence endlessly by easyDMA

Hi, I'm developping endless PWM sequence.

That is ... Only one output pin and the signal from it is repeated with the same period of pulse but with two different duty setting contiguous and repeats back again.

3 cycles of Duty-A followed by 259 cycles of Duty-B and these total 262 cycles repeates forever. I would like to run PWM by itself to do that without any ISR support by CPU.

The scenario I suppose now is ... I need to set shortcuts that makes repeating behavior. At first, the same duty-A is applied for contiguous 3 PWM cycles. So, I can employ REFRESH=2 or ENDDELAY=2 setting to have following 2 cycle with the same duty that the first one does. Then, I need another duty-B. So, I need to employ the second sequence with REFRESH=258 or ENDDELAY=258 respectively as the first sequence. Then finally, for repeating those ones automatically, I need to set shortcut that invokes alternative sequence when the current sequence is done. Maybe a shortcut setting LOOPSDONE_SEQSTART0 would work fine initiated by TASKS_SEQSTART[0]. Regarding LOOP.CNT, a minimum non-ZERO value "1" would be enough.

Here, I have several questions in the scenario above: Q1: Which is better/reasonable employing REFRESH or ENDDELAY to get required numbers of repeating PWM with unchanged duty ? Q2: About DECODER.LOAD and SEQ[].CNT setting... I need only one output, so I need only one comarator. I do not want to allocate too much SRAM holding comparison value(s). Which .LOAD parameter is the proper one ? For only one comparator update, how much value should be set for .CNT ? Is that in-byte or in-number-of-halfwords ?

Or, am I completely misunderstanding and nRF52's easyDMA-ed PWM ?

Any comments or suggestions from NRF52 PWM experts ?

Regards, Shoichi Kojima

  • Hi,

    Have you considered using the PWM driver in the SDK?

    Here is some code that will give you 3 cycles of DUTY A, and 259 cycles of DUTY B, and will loop forever. This is with EasyDMA, and with no CPU usage.

    Code snippet:

    #include <stdio.h>
    #include <string.h>
    #include "nrf_drv_pwm.h"
    #include "app_util_platform.h"
    #include "app_error.h"
    #include "boards.h"
    #include "bsp.h"
    #include "nrf_drv_clock.h"
    #include "nrf_delay.h"
    
    
    #define OUTPUT_PIN 4
    
    #define DUTY_A 50
    #define DUTY_B 80
    
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
    
    
    static nrf_pwm_values_common_t /*const*/ seq0_values[] =
    {
             DUTY_A,DUTY_A,DUTY_A
    };
    
    nrf_pwm_sequence_t const seq0 =
    {
        .values.p_common = seq0_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq0_values),
        .repeats         = 0,
        .end_delay       = 0
    };
    
    
    static nrf_pwm_values_common_t /*const*/ seq1_values[] =
    {
             DUTY_B,DUTY_B,DUTY_B,DUTY_B,DUTY_B,DUTY_B,DUTY_B
    };
    
    nrf_pwm_sequence_t const seq1 =
    {
        .values.p_common = seq1_values,
        .length          = NRF_PWM_VALUES_LENGTH(seq1_values),
        .repeats         = 36,
        .end_delay       = 0
    };
    
    
    static void pwm_init(void)
    {
        nrf_drv_pwm_config_t const config0 =
        {
            .output_pins =
            {
                OUTPUT_PIN, // channel 0
                NRF_DRV_PWM_PIN_NOT_USED,             // channel 1
                NRF_DRV_PWM_PIN_NOT_USED,             // channel 2
                NRF_DRV_PWM_PIN_NOT_USED,             // channel 3
            },
            .irq_priority = APP_IRQ_PRIORITY_LOWEST,
            .base_clock   = NRF_PWM_CLK_1MHz,
            .count_mode   = NRF_PWM_MODE_UP,
            .top_value    = 100,
            .load_mode    = NRF_PWM_LOAD_COMMON,
            .step_mode    = NRF_PWM_STEP_AUTO
        };
        // Init PWM without error handler
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config0, NULL));
        
    }
    
    
    int main(void)
    {
    
        // Start clock for accurate frequencies
        NRF_CLOCK->TASKS_HFCLKSTART = 1; 
        // Wait for clock to start   
        while(NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);   
        
        pwm_init();
    
       APP_ERROR_CHECK(nrf_drv_pwm_complex_playback(&m_pwm0, &seq0, &seq1, 1,
                                           NRF_DRV_PWM_FLAG_LOOP ));
    
        while(true)
        {
        __WFE();
        __SEV();
        __WFE();
    
        }
    }
    
    
    /** @} */
    
  • Dear sigurdon,

    Thank you very much for showing me a good code sippet.

    Have you considered using the PWM driver in the SDK? No. not yet. Because I did NOT learn enough about the basic philothopy of Nordic's SDK coding structure and style to write MY OWN code along that.

    Here is some code that will give you 3 cycles of DUTY A, and 259 cycles of DUTY B, and will loop >forever. This is with EasyDMA, and with no CPU usage.

    This is very similar to MY experimental code just done last night!. I fould LOAD_COMMON would be the reasonable choice. But, I'm not sure this is the reasonable and universally-appliable solution or not... Because, this code includes very puzzly magic number combination for Duty-B cycles. I'd also found that (7)*(36+1) can make up 259 and employing REFRESH.

    How do you think ?

    P.S. Regarding three cycles for Duty-A, I did employing REFRESH=(3-1), CNT=1 to save RAM space.

  • The reason why I used SEQ[1].CNT = 7, and SEQ[1].REFRESH = 36 in order to make it 259 cycles, is that the REFRESH value is ignored when CNT =1 for SEQ[1]. So you cannot have SEQ[1].CNT =1 and SEQ[1].REFRESH = 258 for SEQ[1]. This is explained in the PWM Product Specification, quote:

    SEQ[n].REFRESH and SEQ[n].ENDDELAY are ignored at the end of a complex sequence, indicated by a LOOPSDONE event. The reason for this is that the last value loaded from RAM is maintained until further action from software (restarting a new sequence, or stopping PWM generation).

    So you need SEQ[1].CNT > 1. The solution with SEQ[1].CNT = 7, and SEQ[1].REFRESH = 36 is fully reasonable and appliable in order to get 269 cycles.

Related