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);
}
}
/** @} */