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?

  • As a sanity check, I've disabled the PWM entirely, just to monitor the task function. Even the task function by itself does not survive much longer than a minute.

    It looks like I should probably hold off on the PWM until this works properly.

    The task function is currently logging and toggling a byte:

    void pwm_task_fxn( void *pvParameters )
    {
        for (;;)
            {
                vTaskDelay(100 / portTICK_PERIOD_MS);
                m_change ^= 0x01;
                printf("m_change: 0x%02x\r\n", m_change);
           }
    }
    

    I initialize the task like this

    BaseType_t xReturned = xTaskCreate(    /* Create the task, storing the handle. */
                    pwm_task_fxn,    /* Function that implements the task. */
                    "PWM",           /* Text name for the task. */
                    2*60,              /* Stack size in words, not bytes. */
                    ( void * ) 1,    /* Parameter passed into the task. */
                    tskIDLE_PRIORITY,/* Priority at which the task is created. */
                    &xHandle );      /* Used to pass out the created task's handle. */
    
    if( xReturned != pdPASS )
    {
        APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
    
Related