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; }
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); } } }
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; }
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; }