+ for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
+ {
+ period = CPU_FREQ / (BV(i) * freq);
+ LOG_INFO("period[%ld], prescale[%d]\n", period, i);
+ if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
+ {
+ //Clear previous channel prescaler, and set new
+ ctx->hw->base->CMR &= ~PWM_CPRE_MCK_MASK;
+ ctx->hw->base->CMR |= i;
+ //Set pwm period
+ ATOMIC(
+ ctx->hw->base->CPRD = period;
+ ctx->hw->base->CDTY = period;
+ );
+ break;
+ }
+ }
+
+ LOG_INFO("PWM ch[%d] period[%ld]\n", ctx->ch, period);
+ }
+
+ pwm_hwreg_t pwm_hw_getPeriod(Pwm *ctx)
+ {
+ return ctx->hw->base->CPRD;
+ }
+
+ /*
+ * Set pwm duty cycle.
+ *
+ * duty value 0 - (2^16 - 1)
+ */
+ void pwm_hw_setDuty(Pwm *ctx, pwm_hwreg_t hw_duty)
+ {
+ ASSERT(hw_duty <= ctx->hw->base->CPRD);
+
+ /*
+ * WARNING: is forbidden to write 0 or 1 to duty cycle value,
+ * and so for duty < 2 we must enable PIO and clear output!
+ */
+ if (hw_duty < 2)
+ {
+ hw_duty = 2;
+ PWM_PIO_PER = ctx->hw->pwm_pin;
+ }
+ else
+ PWM_PIO_PDR = ctx->hw->pwm_pin;
+
+ ctx->hw->base->CUPD = hw_duty;
+ LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", ctx->ch, hw_duty, ctx->hw->base->CPRD);
+ }
+
+ static PwmHardware pwm_channels[] =
+ {
+ {//PWM Channel 0
+ .pwm_pin = BV(PWM0),
+ .base = (volatile PwmChannelRegs *)&PWM_CMR0,
+ },
+ {//PWM Channel 1
+ .pwm_pin = BV(PWM1),
+ .base = (volatile PwmChannelRegs *)&PWM_CMR1,
+ },
+ {//PWM Channel 2
+ .pwm_pin = BV(PWM2),
+ .base = (volatile PwmChannelRegs *)&PWM_CMR2,
+ },
+ {//PWM Channel 3
+ .pwm_pin = BV(PWM3),
+ .base = (volatile PwmChannelRegs *)&PWM_CMR3,
+ },
+ };