/*----------------------------------------------------------------------------*/ static void tmrSetOverflow(void *object, uint32_t overflow) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; const uint32_t resolution = getMaxValue(timer); const uint32_t state = reg->TCR & TCR_CEN; /* Stop the timer before reconfiguration */ reg->TCR = 0; assert((overflow - 1) <= resolution); overflow = (overflow - 1) & resolution; /* Setup the match register */ reg->MR[timer->event] = overflow; if (overflow != resolution) { /* Reset the timer after reaching match register value */ reg->MCR |= MCR_RESET(timer->event); } else reg->MCR &= ~MCR_RESET(timer->event); reg->TCR = state; }
/*----------------------------------------------------------------------------*/ static void unitUpdateResolution(struct GpTimerPwmUnit *unit, uint8_t channel) { LPC_TIMER_Type * const reg = unit->base.reg; /* Put the timer into a reset state to clear prescaler and counter */ reg->TCR |= TCR_CRES; /* Disable previous match channel */ reg->MCR &= ~MCR_RESET(unit->current); /* Initialize new match channel and enable it */ unit->current = channel; reg->MR[unit->current] = unit->resolution - 1; reg->MCR |= MCR_RESET(unit->current); /* Clear reset bit and enable counting */ reg->TCR &= ~TCR_CRES; }
/*----------------------------------------------------------------------------*/ 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); }
/*----------------------------------------------------------------------------*/ static enum Result tmrInit(void *object, const void *configBase) { const struct GpTimerConfig * const config = configBase; assert(config); const struct GpTimerBaseConfig baseConfig = { .channel = config->channel }; struct GpTimer * const timer = object; enum Result res; assert(config->event < GPTIMER_EVENT_END); /* Call base class constructor */ if ((res = GpTimerBase->init(timer, &baseConfig)) != E_OK) return res; timer->base.handler = interruptHandler; timer->callback = 0; timer->event = (config->event ? config->event : GPTIMER_MATCH0) - 1; /* Initialize peripheral block */ LPC_TIMER_Type * const reg = timer->base.reg; reg->TCR = TCR_CRES; reg->IR = reg->IR; /* Clear pending interrupts */ reg->CCR = 0; reg->CTCR = 0; timer->frequency = config->frequency; gpTimerSetFrequency(&timer->base, timer->frequency); /* Configure prescaler and default match value */ reg->MR[timer->event] = getMaxValue(timer); /* Reset the timer after reaching the match register value */ reg->MCR = MCR_RESET(timer->event); /* Enable external match to generate signals to other peripherals */ reg->EMR = EMR_CONTROL(timer->event, CONTROL_TOGGLE); #ifdef CONFIG_PLATFORM_NXP_GPTIMER_PM if ((res = pmRegister(powerStateHandler, timer)) != E_OK) return res; #endif /* Clear timer reset flag, but do not enable */ reg->TCR = 0; irqSetPriority(timer->base.irq, config->priority); irqEnable(timer->base.irq); return E_OK; } /*----------------------------------------------------------------------------*/ #ifndef CONFIG_PLATFORM_NXP_GPTIMER_NO_DEINIT static void tmrDeinit(void *object) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; irqDisable(timer->base.irq); reg->TCR = 0; #ifdef CONFIG_PLATFORM_NXP_GPTIMER_PM pmUnregister(timer); #endif GpTimerBase->deinit(timer); } #endif /*----------------------------------------------------------------------------*/ static void tmrEnable(void *object) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; /* Clear pending interrupt flags and direct memory access requests */ reg->IR = IR_MATCH_INTERRUPT(timer->event); /* Clear match value to avoid undefined output level */ reg->EMR &= ~EMR_EXTERNAL_MATCH(timer->event); /* Start the timer */ reg->TCR = TCR_CEN; } /*----------------------------------------------------------------------------*/ static void tmrDisable(void *object) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; reg->TCR = 0; } /*----------------------------------------------------------------------------*/ static void tmrSetCallback(void *object, void (*callback)(void *), void *argument) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; timer->callbackArgument = argument; timer->callback = callback; if (callback) { reg->IR = reg->IR; reg->MCR |= MCR_INTERRUPT(timer->event); } else reg->MCR &= ~MCR_INTERRUPT(timer->event); } /*----------------------------------------------------------------------------*/ static uint32_t tmrGetFrequency(const void *object) { const struct GpTimer * const timer = object; const LPC_TIMER_Type * const reg = timer->base.reg; const uint32_t baseClock = gpTimerGetClock(&timer->base); return baseClock / (reg->PR + 1); } /*----------------------------------------------------------------------------*/ static void tmrSetFrequency(void *object, uint32_t frequency) { struct GpTimer * const timer = object; timer->frequency = frequency; gpTimerSetFrequency(&timer->base, timer->frequency); } /*----------------------------------------------------------------------------*/ static uint32_t tmrGetOverflow(const void *object) { const struct GpTimer * const timer = object; const LPC_TIMER_Type * const reg = timer->base.reg; return (reg->MR[timer->event] + 1) & getMaxValue(timer); } /*----------------------------------------------------------------------------*/ static void tmrSetOverflow(void *object, uint32_t overflow) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; assert(overflow <= getMaxValue(timer)); reg->MR[timer->event] = overflow - 1; } /*----------------------------------------------------------------------------*/ static uint32_t tmrGetValue(const void *object) { const struct GpTimer * const timer = object; const LPC_TIMER_Type * const reg = timer->base.reg; return reg->TC; } /*----------------------------------------------------------------------------*/ static void tmrSetValue(void *object, uint32_t value) { struct GpTimer * const timer = object; LPC_TIMER_Type * const reg = timer->base.reg; assert(value <= reg->MR[timer->event]); reg->PC = 0; reg->TC = value; }