In our project we have functions for outputting a single frequency via PWM for an indefinite amount of time until we call a function to turn it off. See attached code.
void speaker_output_freq(uint16_t freq) {
ret_code_t err_code;
const uint16_t counter_top = 1000000 / freq;
static nrf_pwm_values_individual_t seq_values; // This array cannot be allocated on stack (hence "static") and it must be in RAM
seq_values.channel_0 = counter_top / 2; // always use 50% duty cycle for max volume
nrfx_pwm_config_t const config0 = {
.output_pins = {
SPEAKER_PWM_PIN, // channel 0
NRFX_PWM_PIN_NOT_USED, // channel 1
NRFX_PWM_PIN_NOT_USED, // channel 2
NRFX_PWM_PIN_NOT_USED // channel 3
},
.irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,
.base_clock = NRF_PWM_CLK_1MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = counter_top,
.load_mode = NRF_PWM_LOAD_INDIVIDUAL,
.step_mode = NRF_PWM_STEP_AUTO
};
nrf_pwm_sequence_t const seq = {
.values.p_individual = &seq_values,
.length = NRF_PWM_VALUES_LENGTH(seq_values),
.repeats = 0,
.end_delay = 0
};
if (is_pwm_running == true) {
speaker_output_off();
}
err_code = nrfx_pwm_init(&pwm_instance, &config0, NULL);
SFG_ERROR_CHECK(err_code, M_SPEAKER_OUTPUT);
nrfx_pwm_simple_playback(&pwm_instance, &seq, 1, NRFX_PWM_FLAG_LOOP);
is_pwm_running = true;
}
void speaker_output_off(void) {
if (is_pwm_running == true) { // stop/uninit calls will cause reset if pwm is not initialized when called
nrfx_pwm_stop(&pwm_instance, true); // waits until duty cycle is complete
nrfx_pwm_uninit(&pwm_instance);
is_pwm_running = false;
}
// Pin must be driven low so speaker FET doesn't get any signal. Must reconfigure as output each time since PWM module reconfigures pin
nrf_gpio_cfg_output(SPEAKER_PWM_PIN);
nrf_gpio_pin_clear(SPEAKER_PWM_PIN);
}
As you can see, we have a flag in place (is_pwm_running) to track if the PWM instance is running, and turn it off before attempting to start the next tone if so. Our SFG_ERROR_CHECK function serves to store errors in flash so we can retrieve and analyze them later.
What I am seeing is that occasionally nrfx_pwm_init() generates an error, NRFX_ERROR_INVALID_STATE, meaning the pwm instance was already initialized when we called nrfx_pwm_init(). Our logic ensures nrfx_pwm_uninit() was called prior the nrfx_pwm_init() call, so I am unsure how this error is being generated. I am unable to reproduce the error myself, but our error tracking indicates that it is indeed happening, albeit rarely.
So my questions are:
1. Am I misusing the PWM driver in some way I do not realize? Is my un-initialization procedure set up correctly?
2. If my driver usage is correct, are there any reasons why nrfx_pwm_uninit() would be failing? Is nrfx_pwm_uninit() non-blocking and I am simply calling nrfx_pwm_init too quickly for the driver to fully uninitialize?