/** * @brief Enables a PWM channel. * @pre The PWM unit must have been activated using @p pwmStart(). * @post The channel is active using the specified configuration. * @note The function has effect at the next cycle start. * @note Channel notification is not enabled. * * @param[in] pwmp pointer to a @p PWMDriver object * @param[in] channel PWM channel identifier (0...channels-1) * @param[in] width PWM pulse width as clock pulses number * * @notapi */ void pwm_lld_enable_channel(PWMDriver *pwmp, pwmchannel_t channel, pwmcnt_t width) { #if NRF5_PWM_USE_GPIOTE_PPI const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel]; const uint8_t gpiote_channel = cfg_channel->gpiote_channel; const uint8_t *ppi_channel = cfg_channel->ppi_channel; uint32_t outinit; switch(cfg_channel->mode & PWM_OUTPUT_MASK) { case PWM_OUTPUT_ACTIVE_LOW : outinit = GPIOTE_CONFIG_OUTINIT_Low; break; case PWM_OUTPUT_ACTIVE_HIGH: outinit = GPIOTE_CONFIG_OUTINIT_High; break; case PWM_OUTPUT_DISABLED : /* fall-through */ default : goto no_output_config; } /* Deal with corner case: 0% and 100% */ if ((width <= 0) || (width >= pwmp->period)) { /* Disable GPIOTE/PPI task */ NRF_GPIOTE->CONFIG[gpiote_channel] = GPIOTE_CONFIG_MODE_Disabled; NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); /* Set Line */ palWriteLine(cfg_channel->ioline, ((width <= 0) ^ ((cfg_channel->mode & PWM_OUTPUT_MASK) == PWM_OUTPUT_ACTIVE_HIGH))); /* Really doing PWM */ } else { const uint32_t gpio_pin = PAL_PAD(cfg_channel->ioline); const uint32_t polarity = GPIOTE_CONFIG_POLARITY_Toggle; /* Program tasks (one for duty cycle, one for periode) */ NRF_PPI->CH[ppi_channel[0]].EEP = (uint32_t)&pwmp->timer->EVENTS_COMPARE[channel]; NRF_PPI->CH[ppi_channel[0]].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; NRF_PPI->CH[ppi_channel[1]].EEP = (uint32_t)&pwmp->timer->EVENTS_COMPARE[pwmp->channels]; NRF_PPI->CH[ppi_channel[1]].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; NRF_PPI->CHENSET = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); /* Something Old, something New */ const uint32_t old_width = pwmp->timer->CC[channel]; const uint32_t new_width = width; /* Check GPIOTE state */ const bool gpiote = (NRF_GPIOTE->CONFIG[gpiote_channel] & GPIOTE_CONFIG_MODE_Msk) != GPIOTE_CONFIG_MODE_Disabled; /* GPIOTE is currently running */ if (gpiote) { uint32_t current; while (true) { pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; current = pwmp->timer->CC[PWM_GPIOTE_PPI_CC]; if (pwm_within_safe_margins(pwmp, current, old_width) && pwm_within_safe_margins(pwmp, current, new_width)) break; } if (((old_width <= current) && (current < new_width)) || ((new_width <= current) && (current < old_width))) { NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; } /* GPIOTE need to be restarted */ } else { /* Create GPIO Task */ NRF_GPIOTE->CONFIG[gpiote_channel] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) | ((gpio_pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PSEL_Msk )| ((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk)| ((outinit << GPIOTE_CONFIG_OUTINIT_Pos ) & GPIOTE_CONFIG_OUTINIT_Msk ); pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; if (pwmp->timer->CC[PWM_GPIOTE_PPI_CC] > width) NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; } } no_output_config: #endif pwmp->timer->CC[channel] = width; }
static void sp100_spi_start(int n) { spi_cfg.ssport = PAL_PORT(sp100_ss[n]); spi_cfg.sspad = PAL_PAD(sp100_ss[n]); spiStart(sp100_spid, &spi_cfg); }