Harmandeep Dubb
Harmandeep Dubb

Reputation: 149

Outputting PWM on GPIO using nrf library Seeed Studio XIAO

I am trying to set a PWM signal on a GPIO pin on the Seeed Stuido XIAO BLE board.

To my understanding:

I am getting a NRFX_ERROR_INVALID_STATE when I initialize the PWM driver. Does anyone know what the direct cause of this error is? To my understanding it is due to when the PWM driver is in a state and an invalid operation is used on it. However at this point of the code I am not applying an operation that is not used in the default config code that is available in the library.

UPDATE: I have figured out from the nrfx documentation that the error return is because the driver has been initialized before hand, but how is this possible? I have not run any code prior to this to initialize? From the documentation I don't think the NRFX_PWM_INSTANCE macro initializes anything but returns a pointer to parameters of a driver.

The below is my code for the main script:

int main(void)
{
    k_sleep(K_SECONDS(3));

    int err; 
    int blink_status = 0;

    err = dk_leds_init();
    if (err) {
        printk("LEDs init failed (err %d)\n", err);
    }
    nrfx_pwm_t pwm0 = NRFX_PWM_INSTANCE(0);

    nrfx_pwm_config_t pwm0_config = {
        .output_pins = {
            2,
            0xFF,
            0xFF, 
            0xFF,
        },

        .pin_inverted = {
            false, 
            false, 
            false, 
            false,
        },
        .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
        .base_clock = NRF_PWM_CLK_16MHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = PWM_TOP_VALUE,
        .load_mode = NRF_PWM_LOAD_COMMON,
        .step_mode = NRF_PWM_STEP_AUTO, //need to set the repeat value to one so this moves every time through the memory. 
        .skip_gpio_cfg = false,
        .skip_psel_cfg = false
    };

    err = nrfx_pwm_init(&pwm0,&pwm0_config,NULL,NULL);
    if (err != NRFX_SUCCESS){
        printk("Error Occured when initializing the pwm: %d\n", err);
    }

    nrf_pwm_values_common_t* pulse_array = (nrf_pwm_values_common_t*)malloc(PWM_ARRAY_SIZE * sizeof(nrf_pwm_values_common_t));
    if (pulse_array == NULL) {
        printk("Error occured when intializing the pulse array in memory\n");
    }

    for (int i = 0; i < PWM_ARRAY_SIZE-1; i++){
        pulse_array[i] = PWM_0;
    }

    pulse_array[PWM_ARRAY_SIZE-1] = 0;
    
    nrf_pwm_sequence_t pw0_seq_config = {
        .values = pulse_array, //this is suppose to be a 16 bit value but lets see how a 8 bit value impacts
        .length = PWM_ARRAY_SIZE,
        .repeats = 0, //only play the duty cycle in each cell once. 
        .end_delay = RESET_CODE_DURATION,
    };

    nrfx_pwm_simple_playback(&pwm0, &pw0_seq_config, 1, NRFX_PWM_FLAG_LOOP);

    for (;;) {
        dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
        k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    }

    return 0; 
}

The below is my configuration file code:

CONFIG_PWM=y
CONFIG_NRFX_PWM0=y
CONFIG_NRFX_PWM1=y
CONFIG_PWM_NRFX=y
CONFIG_COMMON_LIBC_MALLOC=y
CONFIG_DK_LIBRARY=y

Can anyone provide insight as to what the INVALID STATE Error could be referring to?

thank you in advance

Upvotes: 0

Views: 160

Answers (1)

Harmandeep Dubb
Harmandeep Dubb

Reputation: 149

I have determined the main issue with my implementation was that I should have uninitiated the pwm driver before initiating the PWM driver.

I believe there was probably some residual register information that showed the pwm being active but when I used the driver to initialize the PWM channel it failed.

in order to do this you can use the following piece of code from the NRFX library:

//ensuring that a prior initialization is not present. 
    nrfx_pwm_uninit(&pwm0);

Furthermore, I ran into issue with the configuration files, due to some config modifiers not being compatible with others. The below is my final config to get the PWM code running:

CONFIG_COMMON_LIBC_MALLOC=y
CONFIG_DK_LIBRARY=y
CONFIG_PWM=y
CONFIG_PWM_NRFX=y

The below is my final code to get the PWM generation correctly:

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <nrfx_pwm.h>
#include <zephyr/kernel.h>
#include <zephyr/toolchain.h>
#include <nrfx.h>
#include <stdlib.h>

#include <dk_buttons_and_leds.h>

#define PWM_TOP_VALUE (20)
#define PWM_NUM_LED (1)
#define PWM_RESET_PERIODS (70)
#define PWM_PULSE_ARRAY_SIZE (PWM_NUM_LED*4*8)
#define PWM_ARRAY_SIZE PWM_PULSE_ARRAY_SIZE+PWM_RESET_PERIODS //need the last value for the reset
#define PWM_0 (PWM_TOP_VALUE - 5) //clock ticks at 16 MHz
#define PWM_1 (PWM_TOP_VALUE - 10) //clock ticks at 16 MHz
#define RESET_CODE_DURATION (10) //number of periods to hold low 

#define RUN_LED_BLINK_INTERVAL (500)
#define RUN_STATUS_LED DK_LED1

int main(void)
{
    k_sleep(K_SECONDS(2));

    int err; 
    int blink_status = 0;

    err = dk_leds_init();
    if (err) {
        printk("LEDs init failed (err %d)\n", err);
    }
    nrfx_pwm_t pwm0 = NRFX_PWM_INSTANCE(0);

    nrfx_pwm_config_t pwm0_config = {
        .output_pins = {
            2,
            NRF_PWM_PIN_NOT_CONNECTED,
            NRF_PWM_PIN_NOT_CONNECTED, 
            NRF_PWM_PIN_NOT_CONNECTED,
        },

        .pin_inverted = {
            false, 
            false, 
            false, 
            false,
        },
        .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
        .base_clock = NRF_PWM_CLK_16MHz,
        .count_mode = NRF_PWM_MODE_UP,
        .top_value = PWM_TOP_VALUE,
        .load_mode = NRF_PWM_LOAD_COMMON,
        .step_mode = NRF_PWM_STEP_AUTO, //need to set the repeat value to one so this moves every time through the memory. 
        .skip_gpio_cfg = false,
        .skip_psel_cfg = false
    };

    //ensuring that a prior initialization is not present. 
    nrfx_pwm_uninit(&pwm0);

    err = nrfx_pwm_init(&pwm0,&pwm0_config,NULL,NULL);
    if (err != NRFX_SUCCESS){
        printk("Error Occured when initializing the pwm: %d\n", err);
    }

    nrf_pwm_values_common_t* pulse_array = (nrf_pwm_values_common_t*)malloc(PWM_ARRAY_SIZE * sizeof(nrf_pwm_values_common_t));
    if (pulse_array == NULL) {
        printk("Error occured when intializing the pulse array in memory\n");
    }

    for (int i = 0; i < PWM_ARRAY_SIZE; i++){
        if(i < PWM_PULSE_ARRAY_SIZE) {
            pulse_array[i] = PWM_0;
        } else {
            pulse_array[i] = PWM_TOP_VALUE;
        }
    }
    
    nrf_pwm_sequence_t pw0_seq_config = {
        .values = pulse_array, //this is suppose to be a 16 bit value but lets see how a 8 bit value impacts
        .length = PWM_ARRAY_SIZE,
        .repeats = 0, //only play the duty cycle in each cell once. 
        .end_delay = RESET_CODE_DURATION,
    };

    nrfx_pwm_simple_playback(&pwm0, &pw0_seq_config, 1, NRFX_PWM_FLAG_LOOP);

    for (;;) {
        dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
        k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    }

    return 0; 
}

I hope this helps someone else save alot of time.

Thanks

Upvotes: 2

Related Questions