NRFX PWM Driver with Zephyr Threads combination

Hi there,

Currently we are developing our own system to drive WS2812B LED's using the NRFX pwm library. And the coloring and signaling works fine when it is in a bare metal mode, however once the I wish to loop over the nrfx_pwm_simple_playback(); function and update the sequence values in time, either within the main function and a separate thread, it fails with p_cb->state = NRFX_DRV_STATE_UNINITIALIZED, or an error code, indicating the initialisation is broken even though i have already initialised it in teh beginning. I have tried initialising the PWM on each loop however my assert(status == NRFX_SUCCESS) ends up failing. 

I have used the code from the nrfx_pwm_common_example, I believe it is an issue to do with the nrfx driver mechanism or how I am integrating it into zephyr, but I cannot seem to find the issue, looking for some guidance please.

/*
 * Copyright (c) 2022 - 2023, Nordic Semiconductor ASA
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * 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 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 the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
 */


#include "hal/nrf_pwm.h"
#include <nrfx_example.h>
#include <nrfx_pwm.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include "drivers/nrfx_errors.h"
#include <nrfx_glue.h>
#define NRFX_LOG_MODULE                 EXAMPLE
#define NRFX_EXAMPLE_CONFIG_LOG_ENABLED 1
#define NRFX_EXAMPLE_CONFIG_LOG_LEVEL   3
#include <nrfx_log.h>

/**
 * @defgroup nrfx_pwm_common_example Common mode PWM example
 * @{
 * @ingroup nrfx_pwm_examples
 *
 * @brief Example showing basic functionality of nrfx_pwm driver for sequence loaded in common mode.
 *
 * @details Application initializes nrfx_pmw driver. It plays a simple sequence on LEDs ("breath"
 *          effect) and replays this sequence @ref NUM_OF_LOOPS times. The @ref pwm_handler() is
 *          executed with relevant log message after every loop.
 */

/** @brief Symbol specifying PWM instance to be used. */
#define STACKSIZE 1024
#define THREAD0_PRIORITY 7
#define THREAD1_PRIORITY 7
#define PWM_INST_IDX 0

/**
 * @brief Symbol specifying number of times that each duty cycle is to be repeated (after being
 *        played once) and is strictly correlated with the "breath" effect speed.
 */
#define VALUE_REPEATS 0UL

/** @brief Symbol specifying number of loops (breaths) to be performed. */
#define NUM_OF_LOOPS 1UL

/**
 * @brief Symbol specifying number of playbacks to be performed. In this example couple of
 *        playbacks might be considered as one loop.
 */
#define PLAYBACK_COUNT 1UL

/** @brief Array with duty cycle values. */

// 6 is high , 13 is low -> logic is inverted for this PWM
nrfx_pwm_config_t config1 = {    
    .output_pins = {LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN},
    .pin_inverted = {0, 0, 0, 0},
    .irq_priority = 7,
    .base_clock = NRF_PWM_CLK_16MHz,
    .count_mode = NRF_PWM_MODE_UP,
    .top_value = 20,
    .load_mode = NRF_PWM_LOAD_COMMON,
    .step_mode = NRF_PWM_STEP_AUTO,
    .skip_gpio_cfg = 0
};

K_MUTEX_DEFINE(key);
nrf_pwm_values_common_t pwm_val []=
{
    0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 13, 6, 13, 6, 13, 6, 6, 13, 13, 13, 13, 6, 13
};
nrf_pwm_values_common_t reset_seq [55] = {0};
nrf_pwm_values_common_t pwm_array_val [192] = {0};

void ledarraybuild(nrf_pwm_values_common_t *array,int lednumber,nrf_pwm_values_common_t *coloursignal){
    int base;
    base = 24*lednumber;
    for(int i = 0; i < 24; i++){
        array[base + i] = coloursignal[i];
    }
    return;
}


void colour_sequence(nrf_pwm_values_common_t *val, uint8_t red, uint8_t green, uint8_t blue){

    for(int i = 7; i >= 0; i--){
        if((red&(0x80>>i))){
            val[i + 8] = 6;
        } else {
            val[i + 8] = 14;
        }
    
    
        if((blue&(0x80>>i))){
            val[i + 16] = 6;
        } else {
            val[i + 16] = 14;
        }
        //val[i + 15] |= 1<<15;
        if((green&(0x80>>i))){
            val[i] = 6;
        } else {
            val[i] = 14;
        }
    }
    return;
}

void thread0(void)
{
    nrfx_pwm_t pwm_instance1 = NRFX_PWM_INSTANCE(PWM_INST_IDX);
        nrf_pwm_sequence_t seq_set1 =
    {
        .values = {pwm_array_val},
        .length = NRFX_ARRAY_SIZE(pwm_array_val),
        .repeats = VALUE_REPEATS,
        .end_delay = 0
    };
	while (1) {
            k_mutex_lock(&key, K_FOREVER);
            colour_sequence(pwm_val, 0x00, 0x0F, 0x00);
            ledarraybuild(pwm_array_val, 2, pwm_val);
            colour_sequence(pwm_val, 0x00, 0x0F, 0x00);
            ledarraybuild(pwm_array_val, 4, pwm_val);
            //LOG_HEXDUMP_INF(pwm_array_val, sizeof(pwm_array_val),"Thread0 Data");
            k_mutex_unlock(&key);

		    
            
	}
}

void thread1(void)
{   
    nrfx_pwm_t pwm_instance2 = NRFX_PWM_INSTANCE(PWM_INST_IDX);
        nrf_pwm_sequence_t seq_set2 =
    {
        .values = {pwm_array_val},
        .length = NRFX_ARRAY_SIZE(pwm_array_val),
        .repeats = VALUE_REPEATS,
        .end_delay = 0
    };
	while (1) {
            k_mutex_lock(&key, K_FOREVER);
            colour_sequence(pwm_val, 0x00, 0x00, 0x0F);
            ledarraybuild(pwm_array_val, 3, pwm_val);
            k_mutex_unlock(&key);

	}
}





/**
 * @brief Function for handling PWM driver events.
 *
 * @param[in] event_type PWM event.
 * @param[in] p_context  General purpose parameter set during initialization of
 *                       the timer. This parameter can be used to pass
 *                       additional information to the handler function.
 */
static void pwm_handler(nrfx_pwm_evt_type_t event_type, void * p_context)
{
    nrfx_pwm_t * inst = p_context;
    static uint32_t curr_loop = 1;
   
    NRFX_LOG_INFO("Loops: %u / %lu", curr_loop, NUM_OF_LOOPS);

    if (curr_loop == NUM_OF_LOOPS)
    {
        NRFX_LOG_INFO("PWM finished");
        nrfx_pwm_uninit(inst);
    }
    curr_loop++;
}

/**
 * @brief Function for application main entry.
 *
 * @return Nothing.
 */

   K_THREAD_DEFINE(thread0_id, STACKSIZE, thread0, NULL, NULL, NULL,
		THREAD0_PRIORITY, 0, 0);
    K_THREAD_DEFINE(thread1_id, STACKSIZE, thread1, NULL, NULL, NULL,
		THREAD1_PRIORITY, 0, 0);
int main(void)
{
    nrfx_err_t status;
    (void) status;


#if defined(__ZEPHYR__)
    IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_PWM_INST_GET(PWM_INST_IDX)), IRQ_PRIO_LOWEST,
                NRFX_PWM_INST_HANDLER_GET(PWM_INST_IDX), 0, 0);
#endif

    //NRFX_EXAMPLE_LOG_INIT();

    //NRFX_LOG_INFO("Starting nrfx_pwm example for sequence loaded in common mode.");
    NRFX_EXAMPLE_LOG_PROCESS();
    //colour_sequence(pwm_val, 0x00, 0x00, 0xFF);
    nrfx_pwm_t pwm_instance = NRFX_PWM_INSTANCE(PWM_INST_IDX);
    //nrfx_pwm_config_t config = NRFX_PWM_DEFAULT_CONFIG(LED1_PIN, LED2_PIN, LED3_PIN, LED4_PIN);
    status = nrfx_pwm_init(&pwm_instance, &config1, pwm_handler, &pwm_instance);
    NRFX_ASSERT(status == NRFX_SUCCESS);
    

    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 0, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 1, pwm_val);
    colour_sequence(pwm_val, 0x00, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 2, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 3, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 4, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 5, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 6, pwm_val);
    colour_sequence(pwm_val, 0x0F, 0x00, 0x00);
    ledarraybuild(pwm_array_val, 7, pwm_val);
    nrf_pwm_sequence_t seq_set =
    {
        .values = {pwm_array_val},
        .length = NRFX_ARRAY_SIZE(pwm_array_val),
        .repeats = VALUE_REPEATS,
        .end_delay = 0
    };


    while (1){

        k_mutex_lock(&key, K_FOREVER);

        nrfx_pwm_sequence_update(&pwm_instance,0, &seq_set);
        nrfx_pwm_simple_playback(&pwm_instance, &seq_set, PLAYBACK_COUNT, NRFX_PWM_FLAG_LOOP);
        k_mutex_unlock(&key);
        k_msleep(2000);
        k_mutex_lock(&key, K_FOREVER);
        for(int i = 0; i <8; i++){
        nrfx_pwm_simple_playback(&pwm_instance, &seq_set, PLAYBACK_COUNT, NRFX_PWM_FLAG_LOOP);

       }

       k_mutex_unlock(&key);
    k_msleep(2000);
        }


    }



/** @} */

Parents Reply
  • All Good, I found a solution where i link PPI from the SEQ0END event to trigger the STOP task, so that the sequence can be reset before it is run again. Thank you for your help!

    void configurePPI(void){
        uint8_t pwm_stop_ppi_channel;
        nrfx_err_t err = nrfx_gppi_channel_alloc(&pwm_stop_ppi_channel);
        if (err != NRFX_SUCCESS) {
            LOG_ERR("nrfx_gppi_channel_alloc error: %08x", err);
            return;
        }
        nrfx_gppi_channel_endpoints_setup(pwm_stop_ppi_channel,nrfx_pwm_event_address_get(&pwm_instance1, NRF_PWM_EVENT_SEQEND0),
                                      nrf_pwm_task_address_get(pwm_instance1.p_reg,  NRF_PWM_TASK_STOP));
    
        nrfx_gppi_channels_enable(BIT(pwm_stop_ppi_channel));
    }

Children
No Data
Related