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

SDK 12 PWM Driver + Softdevice 132 + NRF52832

Hi Devzone.

I've taken the ble_app_hrs example, and then added the example code from the PWM Driver example.

The PWM module is mostly functional, however I have a fatal error that is prohibiting operation for more than a minute. This only occurs when I use a PWM handler, which leads me to believe I have a sync issue.

The overall architecture is -

  • begin using PWM0 with sequence A
  • when sequence A finishes, the PWM handler sets a flag for the task function to switch to sequence B
  • when sequence B finishes, the PWM handler sets a flag for the task function to switch to sequence A
  • I am using this approach because I am switching physical pins. Sequence A is on pin A, sequence B is on pin B.

I'm using simple playback with NRF_DRV_PWM_FLAG_STOP. I have added FreeRTOS Semaphores and task functions for synchronization. This is using nRF SDK12 with a nrf52832.

I am seeing a few failure modes, using both ARMGCC and Keil compilers.

  • The NRF_DRV_PWM_EVT_FINISHED event occurs but the NRF_DRV_PWM_EVT_STOPPED does not. This halts my PWM sequences.
  • The handler does not execute, leaving my task function without any flags to act upon.
  • The task function itself stops running

Here is my handler function (it has support for all 3 PWM modules, each with their own semaphore)

if( xSemaphoreTakeFromISR( *pwm_sem_ptr, NULL ) == pdTRUE )
{
    switch(event_type)
    {
        case NRF_DRV_PWM_EVT_FINISHED: // using stopped vs finished
            printf("\t\tFINISHED %d\r\n", *pwm_state_ptr);
            break;
        case NRF_DRV_PWM_EVT_END_SEQ0: // not using complex playback
            printf("\t\tEND_SEQ0\r\n");
            break;
        case NRF_DRV_PWM_EVT_END_SEQ1: // not using complex playback
            printf("\t\tEND_SEQ1\r\n");
            break;
        case NRF_DRV_PWM_EVT_STOPPED:
            nrf_drv_pwm_uninit(pwm_instance_ptr);
            m_change |= CHANGE_PWM(pwm_instance_num);
            printf("\t\tSTOPPED %d\r\n", *pwm_state_ptr);
            break;
    }
    xSemaphoreGiveFromISR( *pwm_sem_ptr, NULL );
}    

Here is my task function for PWM0 (the rest are disabled while I figure this out)

vTaskDelay(50 / portTICK_PERIOD_MS);

if( xSemaphoreTake( xSemaphore0, 10 ) == pdTRUE )
{
    if (m_change & 0x01)
    {
        m_change &= ~0x01;
        switch(m_state_pwm0)
        {
            case PWM_IDLE:
                break;
            case PWM_SEQA:
                pwm_seqb(PWM0);
                break;
            case PWM_SEQB:
                pwm_seqa(PWM0);
                break;
        }
    }
    xSemaphoreGive( xSemaphore0 );
}

This is how the pwm sequences are initiated:

config.output_pins[0] = BSP_LED_2 | NRF_DRV_PWM_PIN_INVERTED;
err_code = nrf_drv_pwm_init(&m_pwm0, &config, handler0);
APP_ERROR_CHECK(err_code);
m_used |= USED_PWM(PWM0);
m_state_pwm0 = PWM_SEQA;
nrf_drv_pwm_simple_playback(&m_pwm0, &pwm_seq_a, 1,
                             NRF_DRV_PWM_FLAG_STOP );

I see some older versions of the SDK have a USE_WITH_SOFTDEVICE boolean, however I do not see that anywhere in SDK 12. Is there something obvious I missed in the docs/examples?

Related