nrfx_err_t nrfx_pwm_init(nrfx_pwm_t const * const p_instance,
                         nrfx_pwm_config_t const * p_config,
                         nrfx_pwm_handler_t        handler)
{
    NRFX_ASSERT(p_config);

    nrfx_err_t err_code;

    pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];

    if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
    {
        err_code = NRFX_ERROR_INVALID_STATE;
        NRFX_LOG_WARNING("Function: %s, error code: %s.",
                         __func__,
                         NRFX_LOG_ERROR_STRING_GET(err_code));
        return err_code;
    }

    p_cb->handler = handler;

    configure_pins(p_instance, p_config);

    nrf_pwm_enable(p_instance->p_registers);
    nrf_pwm_configure(p_instance->p_registers,
        p_config->base_clock, p_config->count_mode, p_config->top_value);
    nrf_pwm_decoder_set(p_instance->p_registers,
        p_config->load_mode, p_config->step_mode);

    nrf_pwm_shorts_set(p_instance->p_registers, 0);
    nrf_pwm_int_set(p_instance->p_registers, 0);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);

    // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by handling
    // interrupts generated on SEQEND0 and SEQEND1 events (this ensures that
    // the 64 MHz clock is ready when data for the next sequence to be played
    // is read). Therefore, the PWM interrupt must be enabled even if the event
    // handler is not used.
#if defined(USE_DMA_ISSUE_WORKAROUND)
    NRFX_IRQ_PRIORITY_SET(DMA_ISSUE_EGU_IRQn, p_config->irq_priority);
    NRFX_IRQ_ENABLE(DMA_ISSUE_EGU_IRQn);
#else
    if (p_cb->handler)
#endif
    {
        NRFX_IRQ_PRIORITY_SET(nrfx_get_irq_number(p_instance->p_registers),
            p_config->irq_priority);
        NRFX_IRQ_ENABLE(nrfx_get_irq_number(p_instance->p_registers));
    }

    p_cb->state = NRFX_DRV_STATE_INITIALIZED;

    err_code = NRFX_SUCCESS;
    NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
    return err_code;
}
Example #2
0
static void start_playback(nrf_drv_pwm_t const * const p_instance,
                           pwm_control_block_t * p_cb,
                           uint8_t               flags,
                           nrf_pwm_task_t        starting_task)
{
    p_cb->state = NRF_DRV_STATE_POWERED_ON;

    if (p_cb->handler)
    {
        // The notification about finished playback is by default enabled, but
        // this can be suppressed. The notification that the peripheral has been
        // stopped is always enable.
        uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
                            NRF_PWM_INT_STOPPED_MASK;

        if (flags & NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ0)
        {
            int_mask |= NRF_PWM_INT_SEQEND0_MASK;
        }
        if (flags & NRF_DRV_PWM_FLAG_SIGNAL_END_SEQ1)
        {
            int_mask |= NRF_PWM_INT_SEQEND1_MASK;
        }
        if (flags & NRF_DRV_PWM_FLAG_NO_EVT_FINISHED)
        {
            int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
        }

        nrf_pwm_int_set(p_instance->p_registers, int_mask);
    }

    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);

    nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
}
static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
{
    // The user handler is called for SEQEND0 and SEQEND1 events only when the
    // user asks for it (by setting proper flags when starting the playback).
    if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
        if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0) && p_cb->handler)
        {
            p_cb->handler(NRFX_PWM_EVT_END_SEQ0);
        }
    }
    if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
        if ((p_cb->flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1) && p_cb->handler)
        {
            p_cb->handler(NRFX_PWM_EVT_END_SEQ1);
        }
    }
    // For LOOPSDONE the handler is called by default, but the user can disable
    // this (via flags).
    if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
        if (!(p_cb->flags & NRFX_PWM_FLAG_NO_EVT_FINISHED) && p_cb->handler)
        {
            p_cb->handler(NRFX_PWM_EVT_FINISHED);
        }
    }

    // The STOPPED event is always propagated to the user handler.
    if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);

        p_cb->state = NRFX_DRV_STATE_INITIALIZED;
        if (p_cb->handler)
        {
            p_cb->handler(NRFX_PWM_EVT_STOPPED);
        }
    }
}
Example #4
0
ret_code_t nrf_drv_pwm_init(nrf_drv_pwm_t const * const p_instance,
                            nrf_drv_pwm_config_t const * p_config,
                            nrf_drv_pwm_handler_t        handler)
{
    ASSERT(p_config)
    pwm_control_block_t * p_cb  = &m_cb[p_instance->drv_inst_idx];

    if (p_cb->state != NRF_DRV_STATE_UNINITIALIZED)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    p_cb->handler = handler;

    configure_pins(p_instance, p_config);

    nrf_pwm_enable(p_instance->p_registers);
    nrf_pwm_configure(p_instance->p_registers,
        p_config->base_clock, p_config->count_mode, p_config->top_value);
    nrf_pwm_decoder_set(p_instance->p_registers,
        p_config->load_mode, p_config->step_mode);

    nrf_pwm_shorts_set(p_instance->p_registers, 0);
    nrf_pwm_int_set(p_instance->p_registers, 0);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_LOOPSDONE);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND0);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_SEQEND1);
    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);

    if (p_cb->handler)
    {
        nrf_drv_common_irq_enable(nrf_drv_get_IRQn(p_instance->p_registers),
            p_config->irq_priority);
    }

    p_cb->state = NRF_DRV_STATE_INITIALIZED;

    return NRF_SUCCESS;
}
Example #5
0
static void irq_handler(NRF_PWM_Type * p_pwm, pwm_control_block_t * p_cb)
{
    ASSERT(p_cb->handler);

    // The SEQEND0 and SEQEND1 events are only handled when the user asked for
    // it (by setting proper flags when starting the playback).
    if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_SEQEND0_MASK) &&
        nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND0))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND0);
        p_cb->handler(NRF_DRV_PWM_EVT_END_SEQ0);
    }
    if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_SEQEND1_MASK) &&
        nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_SEQEND1))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_SEQEND1);
        p_cb->handler(NRF_DRV_PWM_EVT_END_SEQ1);
    }

    // The LOOPSDONE event is handled by default, but this can be disabled.
    if (nrf_pwm_int_enable_check(p_pwm, NRF_PWM_INT_LOOPSDONE_MASK) &&
        nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_LOOPSDONE))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_LOOPSDONE);
        p_cb->handler(NRF_DRV_PWM_EVT_FINISHED);
    }

    if (nrf_pwm_event_check(p_pwm, NRF_PWM_EVENT_STOPPED))
    {
        nrf_pwm_event_clear(p_pwm, NRF_PWM_EVENT_STOPPED);

        p_cb->state = NRF_DRV_STATE_INITIALIZED;

        p_cb->handler(NRF_DRV_PWM_EVT_STOPPED);
    }
}
static uint32_t start_playback(nrfx_pwm_t const * const p_instance,
                               pwm_control_block_t * p_cb,
                               uint8_t               flags,
                               nrf_pwm_task_t        starting_task)
{
    p_cb->state = NRFX_DRV_STATE_POWERED_ON;
    p_cb->flags = flags;

    if (p_cb->handler)
    {
        // The notification about finished playback is by default enabled,
        // but this can be suppressed.
        // The notification that the peripheral has stopped is always enabled.
        uint32_t int_mask = NRF_PWM_INT_LOOPSDONE_MASK |
                            NRF_PWM_INT_STOPPED_MASK;

        // The workaround for nRF52 Anomaly 109 "protects" DMA transfers by
        // handling interrupts generated on SEQEND0 and SEQEND1 events (see
        // 'nrfx_pwm_init'), hence these events must be always enabled
        // to generate interrupts.
        // However, the user handler is called for them only when requested
        // (see 'irq_handler').
#if defined(USE_DMA_ISSUE_WORKAROUND)
        int_mask |= NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK;
#else
        if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ0)
        {
            int_mask |= NRF_PWM_INT_SEQEND0_MASK;
        }
        if (flags & NRFX_PWM_FLAG_SIGNAL_END_SEQ1)
        {
            int_mask |= NRF_PWM_INT_SEQEND1_MASK;
        }
#endif
        if (flags & NRFX_PWM_FLAG_NO_EVT_FINISHED)
        {
            int_mask &= ~NRF_PWM_INT_LOOPSDONE_MASK;
        }

        nrf_pwm_int_set(p_instance->p_registers, int_mask);
    }
#if defined(USE_DMA_ISSUE_WORKAROUND)
    else
    {
        nrf_pwm_int_set(p_instance->p_registers,
            NRF_PWM_INT_SEQEND0_MASK | NRF_PWM_INT_SEQEND1_MASK);
    }
#endif

    nrf_pwm_event_clear(p_instance->p_registers, NRF_PWM_EVENT_STOPPED);

    if (flags & NRFX_PWM_FLAG_START_VIA_TASK)
    {
        uint32_t starting_task_address =
            nrf_pwm_task_address_get(p_instance->p_registers, starting_task);

#if defined(USE_DMA_ISSUE_WORKAROUND)
        // To "protect" the initial DMA transfer it is required to start
        // the PWM by triggering the proper task from EGU interrupt handler,
        // it is not safe to do it directly via PPI.
        p_cb->starting_task_address = starting_task_address;
        nrf_egu_int_enable(DMA_ISSUE_EGU,
            nrf_egu_int_get(DMA_ISSUE_EGU, p_instance->drv_inst_idx));
        return (uint32_t)nrf_egu_task_trigger_address_get(DMA_ISSUE_EGU,
            p_instance->drv_inst_idx);
#else
        return starting_task_address;
#endif
    }

    nrf_pwm_task_trigger(p_instance->p_registers, starting_task);
    return 0;
}