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

nRF52840 How to play 2 PWM sequences in one channel with different duty cycle in Common Mode?

 

Hello,


I started to work in a recen project with previous code developed and I want to use the current code to play 2 PWM pulses but each pulse with different duty cycle.  For example, the 1st pulse at 100% 1000ms ON / 1000ms OFF and the 2nd pattern sequence at 25% duty cycle with the same 1000ms / 1000ms OFF.

Current code was able to play a pulse at one duty cycle and now I extended their usability to 2 pattern sequence, but as far as I try to modify the duty cycle independently, I ended with two pulses with the same duty cycle... 

For this I wanted to implement the nrfx_pwm_complex_playback() function.

But after testing the behavior of the code, it seems that the dutycle is set by a line of code when I implement the 2nd sequence definition. specially in 

// Write pattern to memory
p_mem2 = p_instance->p_mem;
nrfx_pwm_complex_playback(p_instance->p_drv_instance, &pwm_sequence, &pwm_sequence2, pattern_rep,
                                        NRFX_PWM_FLAG_STOP );
/**@brief Motor instance data
   @details Contains pointer to PWM pattern array, pointer to nrfx_pwm instance data, 
   energy of current pattern being played, to be taken into account when the repetition is over
   and bool indicating if instance is enabled, if patterns are accepted
   */
typedef struct
{
    uint16_t           *p_mem;
    nrfx_pwm_t const   *p_drv_instance;
    uint32_t           last_pat_energy;
    bool               is_pat_playing;
    bool               motor_is_enabled;
} motor_data_t;

/**@brief Plays the passed motor pattern
  
   @details If the instance is enabled, decodes the passed pattern and executes it. 
   Overwrites any motor pattern that was being played, if any. Calls the handler notifying
   that the motor module is ON.

  @param[in] p_pattern New pattern to be played.
  @param[in] p_instance Struct where information related to the instance is stored.

 */
void motor_play(const motor_pat_t *p_pattern, motor_data_t *p_instance)
{
    motor_evt_t evt_data = {0};
    nrf_pwm_sequence_t pwm_sequence = {0};
    uint16_t pwm_value   = 0;
    uint32_t duration_on  = 0;
    uint32_t duration_off = 0;
    uint32_t pattern_rep  = 0;
    uint32_t i = 0;
    uint16_t *p_mem = 0; 
    uint32_t int_scaler = 0;
    
    
     // Decode pattern 
    int_scaler   = motor_int_scaler[p_pattern->intensity];
    if (int_scaler > 25)
    {
        int_scaler -= 25;
    }
    pwm_value    = MOTOR_TOP_VALUE * int_scaler / 100;
    duration_on  = 200UL*MOTOR_FREQ/1000UL;
    duration_off = 50UL*MOTOR_FREQ/1000UL;
    pattern_rep  = motor_pat_rep[p_pattern->repetitions];

    // Write pattern to memory
    p_mem = p_instance->p_mem;

    // Turn on at 100% for 20ms
    for(i=0;i<(20UL*MOTOR_FREQ/1000UL);i++)
    {
        p_mem[MOTOR_CH] = 0;        // 0 -> 100% duty cycle
        p_mem += MOTOR_N_CH;
    }
    duration_on = duration_on - (20UL*MOTOR_FREQ/1000UL);
    for(i=0;i<duration_on;i++)
    {
        p_mem[MOTOR_CH] = MOTOR_TOP_VALUE-pwm_value;
        p_mem += MOTOR_N_CH;
    }
    for(i=0;i<duration_off;i++)
    {
        p_mem[MOTOR_CH] = MOTOR_TOP_VALUE-0;
        p_mem += MOTOR_N_CH;
    }
    pwm_sequence.values.p_raw = p_instance->p_mem;
    pwm_sequence.repeats = 0;
    pwm_sequence.end_delay = 0;
    pwm_sequence.length = (20UL*MOTOR_FREQ/1000UL + duration_on + duration_off) * MOTOR_N_CH;   // Add turn on + active + inactive times to determine length

    // Execute pattern
    nrf_pwm_sequence_t pwm_sequence2 = {0};
    uint16_t pwm_value2   = 0;
    uint32_t duration_on2  = 0;
    uint32_t duration_off2 = 0;
    uint32_t pattern_rep2  = 0;
    uint32_t i2 = 0;
    uint16_t *p_mem2 = 0; 
    uint32_t int_scaler2 = 0;

    // Decode pattern 
    int_scaler2   = motor_int_scaler[p_pattern->intensity];
    pwm_value2    = MOTOR_TOP_VALUE * int_scaler2 / 100;
    duration_on2  = 200UL*MOTOR_FREQ/1000UL;
    duration_off2 = 50UL*MOTOR_FREQ/1000UL;
    pattern_rep2  = motor_pat_rep[p_pattern->repetitions];

    // Write pattern to memory
    // if the intensity duty cycle was 50% and modified to 25%, this will set it back to 50% for all the patterns
    p_mem2 = p_instance->p_mem;  // ---> duty cycle is modified here with the previous pattern command

    // Turn on at 100% for 20ms
    for(i=0;i<(20UL*MOTOR_FREQ/1000UL);i++)
    {
        p_mem2[MOTOR_CH] = 0;        // 0 -> 100% duty cycle
        p_mem2 += MOTOR_N_CH;
    }
    duration_on2 = duration_on2 - (20UL*MOTOR_FREQ/1000UL);
    for(i=0;i<duration_on2;i++)
    {
        p_mem2[MOTOR_CH] = MOTOR_TOP_VALUE-pwm_value2;
        p_mem2 += MOTOR_N_CH;
    }
    for(i=0;i<duration_off2;i++)
    {
        p_mem2[MOTOR_CH] = MOTOR_TOP_VALUE-0;
        p_mem2 += MOTOR_N_CH;
    }
    pwm_sequence2.values.p_raw = p_instance->p_mem;
    pwm_sequence2.repeats = 0;
    pwm_sequence2.end_delay = 0;
    pwm_sequence2.length = (20UL*MOTOR_FREQ/1000UL + duration_on2 + duration_off2) * MOTOR_N_CH;   // Add turn on + active + inactive times to determine length

    // play two patterns here..
    nrfx_pwm_complex_playback(p_instance->p_drv_instance, &pwm_sequence, &pwm_sequence2, 2*pattern_rep,
                            NRFX_PWM_FLAG_STOP );
}
if I removed that line of code, setup in sequence 1 is not ignored anymore. but both pulses will use the same duty cycle.
So my question is: How to set up two pulses with different duty cycle with the nrf_pwm_complex_playback() with pwm_sequence.values.p_raw ?  how to avoid this situation and do it properly?

Parents
  • Hi,

    For example, the 1st pulse at 100% 1000ms ON / 1000ms OFF and the 2nd pattern sequence at 25% duty cycle with the same 1000ms / 1000ms OFF.

    I can create small example that does this. What frequency should this 2nd pattern use?

  • Hi,

    Thanks for your reply.

    Here's the configuration for the PWM instance. From what I can understand 


    .base_clock   = (nrf_pwm_clk_t)NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK

    which in the sdk_config.h is set to 1MHz and the .top_value = 
    NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE is equal to 1000.

    nrfx_pwm_handler_t pwm_handler = NULL;
    const nrfx_pwm_config_t pwm_motor_cfg = {
    .output_pins  = { p_config->gpio_motor,
                          NRFX_PWM_PIN_NOT_USED,
                          NRFX_PWM_PIN_NOT_USED,
                          NRFX_PWM_PIN_NOT_USED,},
        .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
        .base_clock   = (nrf_pwm_clk_t)NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK,
        .count_mode   = (nrf_pwm_mode_t)NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE,
        .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,
        .load_mode    = (nrf_pwm_dec_load_t)NRF_PWM_LOAD_COMMON,
        .step_mode    = (nrf_pwm_dec_step_t)NRFX_PWM_DEFAULT_CONFIG_STEP_MODE,
    };
    
    // initialize the pwm module 
    nrfx_pwm_init(p_instance->p_drv_instance, &pwm_motor_cfg, pwm_handler);



    I also want to understand how the values represent when is in the pwm_sequence.values.p_raw. 

    I checked in the API that p_raw is a pointer providing raw access to the values, but I would like to understand how can I set values to it without knowing what exactly means each bit. 

  • Hi,

     

    For example, the 1st pulse at 100% 1000ms ON / 1000ms OFF and the 2nd pattern sequence at 25% duty cycle with the same 1000ms / 1000ms OFF.

     Here is some example code for that, based on pwm_driver example in nRF5SDK.

    /**
     * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    /** @file
     * @defgroup pwm_example_main main.c
     * @{
     * @ingroup pwm_example
     *
     * @brief PWM Example Application main file.
     *
     * This file contains the source code for a sample application using PWM.
     */
    
    #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 "app_timer.h"
    #include "nrf_drv_clock.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
    
    #define PWM_PIN 3
    #define TOP_VALUE 1000
    
    #define LOW 0x8000
    #define HIGH 0
    
    static void pwm_init(void)
    {
        nrf_drv_pwm_config_t config =
        {
            // These are the common configuration options we use for all PWM
            // instances.
            .irq_priority = APP_IRQ_PRIORITY_LOWEST,
            .count_mode   = NRF_PWM_MODE_UP,
            .step_mode    = NRF_PWM_STEP_AUTO,
        };
    
        config.base_clock = NRF_PWM_CLK_1MHz;
        config.top_value  = TOP_VALUE;
        config.load_mode  = NRF_PWM_LOAD_COMMON;
    
        config.output_pins[0] = PWM_PIN;
        config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED;
        config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED;
        config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED;
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config, NULL));
    }
    
    static void start_pwm_demo()
    {
        // Sequence 0:   1000ms high level, 1000ms low level
        static nrf_pwm_values_common_t seq0_values[] =
        {
           HIGH , LOW
        };
    
        nrf_pwm_sequence_t const pwm0_seq0 =
        {
            .values.p_common = seq0_values,    
            .length          = NRF_PWM_VALUES_LENGTH(seq0_values),
            .repeats         = 1000,
            .end_delay       = 0
        };
    
    
    
        // Sequence 1 : 25% duty cycle for 1000ms high, then low for 1000ms.
       static nrf_pwm_values_common_t seq1_values[] =
       {
        TOP_VALUE-(TOP_VALUE/4) | HIGH, LOW
       };
    
        nrf_pwm_sequence_t const pwm0_seq1 =
        {
            .values.p_common = seq1_values,
            .length          = NRF_PWM_VALUES_LENGTH(seq1_values),
            .repeats         = 1000,
            .end_delay       = 1000-1
        };
    
        (void)nrf_drv_pwm_complex_playback(&m_pwm0, &pwm0_seq0, &pwm0_seq1, 1,
                                           NRF_DRV_PWM_FLAG_LOOP);
    
    
    }
    
    
    
    static void bsp_evt_handler(bsp_event_t evt)
    {
    
        switch (evt)
        {
            // Button 1 on DK - start pwm demo
            case BSP_EVENT_KEY_0:
                 //start_pwm_demo();
                 break;
                
            default:
                return;
        }
    
    }
    
    
    
    void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
    {
        bsp_board_leds_on();
        app_error_save_and_stop(id, pc, info);
    }
    
    static void init_bsp()
    {
        APP_ERROR_CHECK(nrf_drv_clock_init());
        nrf_drv_clock_lfclk_request(NULL);
    
        APP_ERROR_CHECK(app_timer_init());
        APP_ERROR_CHECK(bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler));
        APP_ERROR_CHECK(bsp_buttons_enable());
    }
    
    
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    
        NRF_LOG_INFO("PWM example started.");
        init_bsp();
    
        bool accurate_HFCLK = true;
    
        if(accurate_HFCLK)
        {
          // Start accurate HFCLK (XOSC) (will consume more current)
          NRF_CLOCK->TASKS_HFCLKSTART = 1;
          while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
          NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        }
    
        pwm_init();
    
        start_pwm_demo();
    
        for (;;)
        {
            // Wait for an event.
            __WFE();
    
            // Clear the event register.
            __SEV();
            __WFE();
    
            NRF_LOG_FLUSH();
        }
    }
    
    
    /** @} */

    Output:

Reply
  • Hi,

     

    For example, the 1st pulse at 100% 1000ms ON / 1000ms OFF and the 2nd pattern sequence at 25% duty cycle with the same 1000ms / 1000ms OFF.

     Here is some example code for that, based on pwm_driver example in nRF5SDK.

    /**
     * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
     * 
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     * 
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     * 
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     * 
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     * 
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     * 
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     * 
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * 
     */
    /** @file
     * @defgroup pwm_example_main main.c
     * @{
     * @ingroup pwm_example
     *
     * @brief PWM Example Application main file.
     *
     * This file contains the source code for a sample application using PWM.
     */
    
    #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 "app_timer.h"
    #include "nrf_drv_clock.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    static nrf_drv_pwm_t m_pwm0 = NRF_DRV_PWM_INSTANCE(0);
    
    #define PWM_PIN 3
    #define TOP_VALUE 1000
    
    #define LOW 0x8000
    #define HIGH 0
    
    static void pwm_init(void)
    {
        nrf_drv_pwm_config_t config =
        {
            // These are the common configuration options we use for all PWM
            // instances.
            .irq_priority = APP_IRQ_PRIORITY_LOWEST,
            .count_mode   = NRF_PWM_MODE_UP,
            .step_mode    = NRF_PWM_STEP_AUTO,
        };
    
        config.base_clock = NRF_PWM_CLK_1MHz;
        config.top_value  = TOP_VALUE;
        config.load_mode  = NRF_PWM_LOAD_COMMON;
    
        config.output_pins[0] = PWM_PIN;
        config.output_pins[1] = NRF_DRV_PWM_PIN_NOT_USED;
        config.output_pins[2] = NRF_DRV_PWM_PIN_NOT_USED;
        config.output_pins[3] = NRF_DRV_PWM_PIN_NOT_USED;
        APP_ERROR_CHECK(nrf_drv_pwm_init(&m_pwm0, &config, NULL));
    }
    
    static void start_pwm_demo()
    {
        // Sequence 0:   1000ms high level, 1000ms low level
        static nrf_pwm_values_common_t seq0_values[] =
        {
           HIGH , LOW
        };
    
        nrf_pwm_sequence_t const pwm0_seq0 =
        {
            .values.p_common = seq0_values,    
            .length          = NRF_PWM_VALUES_LENGTH(seq0_values),
            .repeats         = 1000,
            .end_delay       = 0
        };
    
    
    
        // Sequence 1 : 25% duty cycle for 1000ms high, then low for 1000ms.
       static nrf_pwm_values_common_t seq1_values[] =
       {
        TOP_VALUE-(TOP_VALUE/4) | HIGH, LOW
       };
    
        nrf_pwm_sequence_t const pwm0_seq1 =
        {
            .values.p_common = seq1_values,
            .length          = NRF_PWM_VALUES_LENGTH(seq1_values),
            .repeats         = 1000,
            .end_delay       = 1000-1
        };
    
        (void)nrf_drv_pwm_complex_playback(&m_pwm0, &pwm0_seq0, &pwm0_seq1, 1,
                                           NRF_DRV_PWM_FLAG_LOOP);
    
    
    }
    
    
    
    static void bsp_evt_handler(bsp_event_t evt)
    {
    
        switch (evt)
        {
            // Button 1 on DK - start pwm demo
            case BSP_EVENT_KEY_0:
                 //start_pwm_demo();
                 break;
                
            default:
                return;
        }
    
    }
    
    
    
    void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
    {
        bsp_board_leds_on();
        app_error_save_and_stop(id, pc, info);
    }
    
    static void init_bsp()
    {
        APP_ERROR_CHECK(nrf_drv_clock_init());
        nrf_drv_clock_lfclk_request(NULL);
    
        APP_ERROR_CHECK(app_timer_init());
        APP_ERROR_CHECK(bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler));
        APP_ERROR_CHECK(bsp_buttons_enable());
    }
    
    
    int main(void)
    {
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    
        NRF_LOG_INFO("PWM example started.");
        init_bsp();
    
        bool accurate_HFCLK = true;
    
        if(accurate_HFCLK)
        {
          // Start accurate HFCLK (XOSC) (will consume more current)
          NRF_CLOCK->TASKS_HFCLKSTART = 1;
          while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
          NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
        }
    
        pwm_init();
    
        start_pwm_demo();
    
        for (;;)
        {
            // Wait for an event.
            __WFE();
    
            // Clear the event register.
            __SEV();
            __WFE();
    
            NRF_LOG_FLUSH();
        }
    }
    
    
    /** @} */

    Output:

Children
No Data
Related