static void doubleEdgeDeinit(void *object) { struct GpPwmDoubleEdge * const pwm = object; LPC_PWM_Type * const reg = pwm->unit->base.reg; reg->PCR &= ~PCR_OUTPUT_ENABLED(pwm->channel); unitReleaseChannel(pwm->unit, pwm->channel); unitReleaseChannel(pwm->unit, pwm->channel - 1); }
/*----------------------------------------------------------------------------*/ static enum result unitInit(void *object, const void *configBase) { const struct GpTimerPwmUnitConfig * const config = configBase; const struct GpTimerBaseConfig baseConfig = { .channel = config->channel }; struct GpTimerPwmUnit * const unit = object; enum result res; const uint32_t clockFrequency = gpTimerGetClock(object); const uint32_t timerFrequency = config->frequency * config->resolution; if (!timerFrequency || timerFrequency > clockFrequency) return E_VALUE; /* Call base class constructor */ if ((res = GpTimerBase->init(object, &baseConfig)) != E_OK) return res; unit->matches = 0; unit->resolution = config->resolution; /* Should be invoked after object initialization completion */ unit->current = gpTimerAllocateChannel(unit->matches); LPC_TIMER_Type * const reg = unit->base.reg; reg->TCR = 0; reg->IR = reg->IR; /* Clear pending interrupts */ reg->PC = reg->TC = 0; reg->CTCR = 0; reg->CCR = 0; reg->EMR = 0; reg->PWMC = 0; /* Register is available only on specific parts */ /* Configure timings */ reg->PR = clockFrequency / timerFrequency - 1; reg->MR[unit->current] = unit->resolution - 1; reg->MCR = MCR_RESET(unit->current); /* Enable timer */ reg->TCR = TCR_CEN; return E_OK; } /*----------------------------------------------------------------------------*/ static void unitDeinit(void *object) { struct GpTimerPwmUnit * const unit = object; LPC_TIMER_Type * const reg = unit->base.reg; reg->TCR &= ~TCR_CEN; GpTimerBase->deinit(unit); } /*----------------------------------------------------------------------------*/ static enum result channelInit(void *object, const void *configBase) { const struct GpTimerPwmConfig * const config = configBase; struct GpTimerPwm * const pwm = object; struct GpTimerPwmUnit * const unit = config->parent; enum result res; /* Initialize output pin */ pwm->channel = gpTimerConfigMatchPin(unit->base.channel, config->pin); /* Allocate channel */ if ((res = unitAllocateChannel(unit, pwm->channel)) != E_OK) return res; pwm->unit = unit; LPC_TIMER_Type * const reg = pwm->unit->base.reg; /* Calculate pointer to match register for fast access */ pwm->value = reg->MR + pwm->channel; /* Set initial duration */ channelSetDuration(pwm, 0); return E_OK; } /*----------------------------------------------------------------------------*/ static void channelDeinit(void *object) { struct GpTimerPwm * const pwm = object; LPC_TIMER_Type * const reg = pwm->unit->base.reg; reg->PWMC &= ~PWMC_ENABLE(pwm->channel); unitReleaseChannel(pwm->unit, pwm->channel); } /*----------------------------------------------------------------------------*/ static uint32_t channelGetResolution(const void *object) { return ((const struct GpTimerPwm *)object)->unit->resolution; } /*----------------------------------------------------------------------------*/ static void channelSetDuration(void *object, uint32_t duration) { struct GpTimerPwm * const pwm = object; const uint32_t resolution = pwm->unit->resolution; if (duration) { if (duration > resolution) duration = resolution; /* The output is inverted */ duration = resolution - duration; } else { /* * If match register is set to a value greater or equal to resolution, * then the output stays low during all cycle. */ duration = resolution + 1; } /* * If a match register is set to zero, than output pin goes high * and will stay in this state continuously. */ *pwm->value = duration; } /*----------------------------------------------------------------------------*/ static void channelSetEdges(void *object, uint32_t leading __attribute__((unused)), uint32_t trailing) { /* Leading edge time is constant in the single edge mode */ assert(leading == 0); channelSetDuration(object, trailing); }
/*----------------------------------------------------------------------------*/ static enum Result unitInit(void *object, const void *configBase) { const struct GpPwmUnitConfig * const config = configBase; assert(config); assert(config->resolution >= 2); const struct GpPwmUnitBaseConfig baseConfig = { .channel = config->channel }; struct GpPwmUnit * const unit = object; enum Result res; /* Call base class constructor */ if ((res = GpPwmUnitBase->init(unit, &baseConfig)) != E_OK) return res; LPC_PWM_Type * const reg = unit->base.reg; reg->TCR = TCR_CRES; reg->IR = reg->IR; /* Clear pending interrupts */ reg->CTCR = 0; reg->CCR = 0; reg->PCR = 0; /* Configure timings */ unit->frequency = config->frequency; unit->resolution = config->resolution; unitSetFrequency(unit, unit->frequency * unit->resolution); unit->matches = 0; reg->MR0 = unit->resolution; reg->MCR = MCR_RESET(0); #ifdef CONFIG_PLATFORM_NXP_GPTIMER_PM if ((res = pmRegister(powerStateHandler, unit)) != E_OK) return res; #endif /* Switch to the PWM mode and enable the timer */ reg->TCR = TCR_CEN | TCR_PWM_ENABLE; return E_OK; } /*----------------------------------------------------------------------------*/ #ifndef CONFIG_PLATFORM_NXP_GPPWM_NO_DEINIT static void unitDeinit(void *object) { struct GpPwmUnit * const unit = object; LPC_PWM_Type * const reg = unit->base.reg; reg->TCR = 0; #ifdef CONFIG_PLATFORM_NXP_GPTIMER_PM pmUnregister(unit); #endif GpPwmUnitBase->deinit(unit); } #endif /*----------------------------------------------------------------------------*/ static enum Result singleEdgeInit(void *object, const void *configBase) { const struct GpPwmConfig * const config = configBase; assert(config); struct GpPwm * const pwm = object; struct GpPwmUnit * const unit = config->parent; /* Initialize output pin */ pwm->channel = configMatchPin(unit->base.channel, config->pin); /* Allocate channel */ if (unitAllocateChannel(unit, pwm->channel)) { LPC_PWM_Type * const reg = unit->base.reg; /* Select single edge mode */ reg->PCR &= ~PCR_DOUBLE_EDGE(pwm->channel); pwm->unit = unit; pwm->latch = LER_ENABLE(pwm->channel); /* Calculate pointer to the match register */ pwm->value = calcMatchChannel(reg, pwm->channel); return E_OK; } else return E_BUSY; } /*----------------------------------------------------------------------------*/ #ifndef CONFIG_PLATFORM_NXP_GPPWM_NO_DEINIT static void singleEdgeDeinit(void *object) { struct GpPwm * const pwm = object; LPC_PWM_Type * const reg = pwm->unit->base.reg; reg->PCR &= ~PCR_OUTPUT_ENABLED(pwm->channel); unitReleaseChannel(pwm->unit, pwm->channel); } #endif /*----------------------------------------------------------------------------*/ static void singleEdgeEnable(void *object) { struct GpPwm * const pwm = object; LPC_PWM_Type * const reg = pwm->unit->base.reg; reg->PCR |= PCR_OUTPUT_ENABLED(pwm->channel); } /*----------------------------------------------------------------------------*/ static void singleEdgeDisable(void *object) { struct GpPwm * const pwm = object; LPC_PWM_Type * const reg = pwm->unit->base.reg; reg->PCR &= ~PCR_OUTPUT_ENABLED(pwm->channel); } /*----------------------------------------------------------------------------*/ static uint32_t singleEdgeGetResolution(const void *object) { return ((const struct GpPwm *)object)->unit->resolution; } /*----------------------------------------------------------------------------*/ static void singleEdgeSetDuration(void *object, uint32_t duration) { struct GpPwm * const pwm = object; LPC_PWM_Type * const reg = pwm->unit->base.reg; /* * If match register is set to a value greater than resolution, * than output stays high during all cycle. */ *pwm->value = duration; reg->LER |= pwm->latch; } /*----------------------------------------------------------------------------*/ static void singleEdgeSetEdges(void *object, uint32_t leading __attribute__((unused)), uint32_t trailing) { assert(leading == 0); /* Leading edge time must be zero */ singleEdgeSetDuration(object, trailing); }