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;
}