status_t TPM_SetupPwm()

in hw/mcu/nxp/src/ext/nxp-kinetis-sdk/drivers/fsl_tpm.c [206:417]


status_t TPM_SetupPwm(TPM_Type *base,
                      const tpm_chnl_pwm_signal_param_t *chnlParams,
                      uint8_t numOfChnls,
                      tpm_pwm_mode_t mode,
                      uint32_t pwmFreq_Hz,
                      uint32_t srcClock_Hz)
{
    assert(NULL != chnlParams);
    assert(0U != pwmFreq_Hz);
    assert(0U != numOfChnls);
    assert(0U != srcClock_Hz);
#if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
    if (mode == kTPM_CombinedPwm)
    {
        assert(0 != FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base));
    }
#endif

    uint32_t mod      = 0;
    uint32_t u32flag  = 1;
    uint32_t tpmClock = (srcClock_Hz / (u32flag << (base->SC & TPM_SC_PS_MASK)));
    uint16_t cnv;
    uint8_t i;

#if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
    /* The TPM's QDCTRL register required to be effective */
    if (0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
    {
        /* Clear quadrature Decoder mode because in quadrature Decoder mode PWM doesn't operate*/
        base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
    }
#endif

    switch (mode)
    {
        case kTPM_EdgeAlignedPwm:
#if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
        case kTPM_CombinedPwm:
#endif
            base->SC &= ~TPM_SC_CPWMS_MASK;
            mod = (tpmClock / pwmFreq_Hz) - 1u;
            break;
        case kTPM_CenterAlignedPwm:
            base->SC |= TPM_SC_CPWMS_MASK;
            mod = tpmClock / (pwmFreq_Hz * 2u);
            break;
        default:
            /* All the cease have been listed above, the default case should not be reached. */
            assert(false);
            break;
    }

    /* Return an error in case we overflow the registers, probably would require changing
     * clock source to get the desired frequency */
    if (mod > 65535U)
    {
        return kStatus_Fail;
    }
    /* Set the PWM period */
    base->MOD = mod;

    /* Setup each TPM channel */
    for (i = 0; i < numOfChnls; i++)
    {
        /* Return error if requested dutycycle is greater than the max allowed */
        if (chnlParams->dutyCyclePercent > 100U)
        {
            return kStatus_Fail;
        }
#if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
        if (mode == kTPM_CombinedPwm)
        {
            uint16_t cnvFirstEdge;

            /* This check is added for combined mode as the channel number should be the pair number */
            if ((int8_t)(chnlParams->chnlNumber) >= (FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2))
            {
                return kStatus_Fail;
            }

            /* Return error if requested value is greater than the max allowed */
            if (chnlParams->firstEdgeDelayPercent > 100U)
            {
                return kStatus_Fail;
            }
            /* Configure delay of the first edge */
            if (chnlParams->firstEdgeDelayPercent == 0U)
            {
                /* No delay for the first edge */
                cnvFirstEdge = 0;
            }
            else
            {
                cnvFirstEdge = (uint16_t)((mod * chnlParams->firstEdgeDelayPercent) / 100U);
            }
            /* Configure dutycycle */
            if (chnlParams->dutyCyclePercent == 0U)
            {
                /* Signal stays low */
                cnv          = 0;
                cnvFirstEdge = 0;
            }
            else
            {
                cnv = (uint16_t)((mod * chnlParams->dutyCyclePercent) / 100U);
                /* For 100% duty cycle */
                if (cnv >= mod)
                {
                    cnv = (uint16_t)(mod + 1u);
                }
            }

            /* Set the combine bit for the channel pair */
            base->COMBINE |=
                (u32flag << (TPM_COMBINE_COMBINE0_SHIFT + (TPM_COMBINE_SHIFT * (uint32_t)chnlParams->chnlNumber)));

            /* When switching mode, disable channel n first */
            base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &=
                ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);

            /* Wait till mode change to disable channel is acknowledged */
            while (0U != (base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }

            /* Set the requested PWM mode for channel n, PWM output requires mode select to be set to 2 */
            base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC |=
                (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));

            /* Wait till mode change is acknowledged */
            while (0U == (base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }
            /* Set the channel pair values */
            base->CONTROLS[(uint16_t)chnlParams->chnlNumber * 2U].CnV = cnvFirstEdge;

            /* When switching mode, disable channel n + 1 first */
            base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &=
                ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);

            /* Wait till mode change to disable channel is acknowledged */
            while (0U != (base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }

            /* Set the requested PWM mode for channel n + 1, PWM output requires mode select to be set to 2 */
            base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC |=
                (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));

            /* Wait till mode change is acknowledged */
            while (0U == (base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }
            /* Set the channel pair values */
            base->CONTROLS[((uint16_t)chnlParams->chnlNumber * 2u) + 1u].CnV = (uint32_t)cnvFirstEdge + (uint32_t)cnv;
        }
        else
        {
#endif
            if (chnlParams->dutyCyclePercent == 0U)
            {
                /* Signal stays low */
                cnv = 0;
            }
            else
            {
                cnv = (uint16_t)((mod * chnlParams->dutyCyclePercent) / 100U);
                /* For 100% duty cycle */
                if (cnv >= mod)
                {
                    cnv = (uint16_t)(mod + 1U);
                }
            }
            /* Fix ERROR050050  When TPM is configured in EPWM mode as PS = 0, the compare event is missed on
            the first reload/overflow after writing 1 to the CnV register and causes an incorrect duty output.*/
#if (defined(FSL_FEATURE_TPM_HAS_ERRATA_050050) && FSL_FEATURE_TPM_HAS_ERRATA_050050)
            assert(
                !(mode == kTPM_EdgeAlignedPwm && cnv == 1U && (base->SC & TPM_SC_PS_MASK) == kTPM_Prescale_Divide_1));
#endif
            /* When switching mode, disable channel first */
            base->CONTROLS[chnlParams->chnlNumber].CnSC &=
                ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);

            /* Wait till mode change to disable channel is acknowledged */
            while (0U != (base->CONTROLS[chnlParams->chnlNumber].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }

            /* Set the requested PWM mode, PWM output requires mode select to be set to 2 */
            base->CONTROLS[chnlParams->chnlNumber].CnSC |=
                (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));

            /* Wait till mode change is acknowledged */
            while (0U == (base->CONTROLS[chnlParams->chnlNumber].CnSC &
                          (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
            {
            }
            base->CONTROLS[chnlParams->chnlNumber].CnV = cnv;
#if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
        }
#endif

        chnlParams++;
    }

    return kStatus_Success;
}