1. PWM输入捕获

输入捕获是指输入引脚有电平变化时候,当前CNT的值将被锁存到寄存器

记录电平变化时的定时器计数值可实现对pwm的周期与占空比的测量。

2. 结构框图

channel输入,如果有滤波加上滤波(去除信号毛刺),当检测到高低电平变化,当前CNT值记录下来,CHIE是中断使能标志,会进入中断服务函数,CHF是有电平变化的触发flag,需要读取CnSC寄存器清除。

3.开发实践

NXP S32K1的FlexTimer_ic1配置如下

本代码逻辑是先设置定时器捕获初始检测边缘为上升沿,在中断callback中记录每个电平的边沿CNT,通过切换检测上升沿/下降沿来检测不同边沿。

如下图所示

代码实现

main()
{
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);

    /* Initialize FTM instances, PWM and Input capture
     *  -   See ftm component for more details
     */
    FTM_DRV_Init(INST_FLEXTIMER_IC1, &flexTimer_ic1_InitConfig, &ftm1StateStruct);
    LPUART_DRV_Init(INST_LPUART1, &lpuart1_State, &lpuart1_InitConfig0);
    FTM_DRV_InitInputCapture(INST_FLEXTIMER_IC1, &flexTimer_ic1_InputCaptureConfig);
    INT_SYS_InstallHandler(FTM0_Ovf_Reload_IRQn, &FTM0_Timer_OverflowISR, (isr_t*) 0);
	INT_SYS_EnableIRQ(FTM0_Ch2_Ch3_IRQn);
	INT_SYS_EnableIRQ(FTM0_Ovf_Reload_IRQn);

	frequency = FTM_DRV_GetFrequency(INST_FLEXTIMER_IC1);
}

volatile uint32_t  rise_prev = 0, rise_curr = 0, fall_curr = 0;
volatile uint32_t g_ftm_ovf = 0;          // 统计整周期溢出(rise->rise)
volatile uint32_t ovf_high   = 0;          // 统计高电平段溢出(rise->fall)
volatile uint32_t period_cnt = 0, high_cnt = 0;
volatile uint32_t pwm_freq_hz = 0;
volatile float    duty_pct = 0.0f;

static volatile uint8_t got_first_rise = 0;
static volatile uint8_t waiting_fall   = 0;   // 0=等上升沿;1=等下降沿

void FTM0_Timer_OverflowISR()
{

	FTM_DRV_ClearStatusFlags(INST_FLEXTIMER_IC1, (uint32_t)FTM_TIME_OVER_FLOW_FLAG);
	g_ftm_ovf++;                     // 整个周期都要计
}
static  uint32_t read_ts32_ch2(void)
{
    uint32_t ovf_before = g_ftm_ovf;
    (void)FTM0->CONTROLS[2].CnSC;        // 清 CHF 步骤1
    uint32_t cap32 = FTM0->CONTROLS[2].CnV;  // 清 CHF 步骤2 & 取时戳
    uint16_t cap16 = (uint16_t)(cap32 & 0xFFFFu); // 仅低16位有效
    uint32_t ovf_after  = g_ftm_ovf;

    // 若捕获后又溢出了,且 cap16 很小,认为该捕获发生在溢出之前,需要补偿
    if ((ovf_after != ovf_before) && (cap16 < 0x8000u)) {
        ovf_before++;
    }
    return (ovf_before << 16) | cap16;
}

#if 1//func ok but sometimes error 使用边沿切换的方式,可能有抖动
void FTM0_CH2_Input_Detect(void)
{
    uint32_t cap_ts= read_ts32_ch2();//获取的通道计数器的值
		if(!waiting_fall)
		{
			 /* ---------- 上升沿 ---------- */
			rise_prev = rise_curr;
			rise_curr = cap_ts;

			/* 先武装下一边沿:下降沿 */
			waiting_fall = 1;
			FTM0->CONTROLS[2].CnSC =
				(FTM0->CONTROLS[2].CnSC & ~(FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK)) |
				(FTM_CnSC_ELSB_MASK);

			if (got_first_rise)
			{
				period_cnt = (uint32_t)(rise_curr - rise_prev);

				if (period_cnt == 0u)
				{
					got_first_rise = 0;
				}  /* 保护 */
				else
				{
					pwm_freq_hz = frequency / period_cnt;//Ccnt-Acnt=period
				}
			}
			else
			{
				got_first_rise = 1;
			}

		}
		else
		{
			 /* ---------- 下降沿 ---------- */
			fall_curr = cap_ts;

			/* 切回上升沿 */
			waiting_fall = 0;
			FTM0->CONTROLS[2].CnSC =
				(FTM0->CONTROLS[2].CnSC & ~(FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK)) |
				(FTM_CnSC_ELSA_MASK);

			if (got_first_rise && (fall_curr >= rise_curr))
			{
				high_cnt = (uint32_t)(fall_curr - rise_curr);
				if ((period_cnt!=0) && (pwm_freq_hz !=0) )
				{
					duty_pct = ((float)high_cnt * 100.0f) / (float)period_cnt;//Bcnt-Acnt=duty

					new_sample = 1;
				}

			}

		}
	

}

4.S32ds pwmic局限性说明

S32DS中有直接测量频率/周期的配置,但是问题是无法同时测量频率和周期。查看源码发现,测量结果只有一个变量state->measurementResults[channel],所以无法同时得到频率和周期。

原理是通过一个定时器通道对(pari),一个检测上升沿,一个检测下降沿,再通过相减得到duty。或则两个通道同时检测上升沿/下降沿,再通过相减得到频率。

  • first_event_time:第一个沿(比如上升沿)被捕获时,FTM 计数器 CNT 的值

  • second_event_time:第二个沿(比如下降沿)被捕获时 CNT 的值

static void FTM_DRV_InputCaptureHandler(uint32_t instance,
                                        uint8_t channelPair)
{
    DEV_ASSERT(instance < FTM_INSTANCE_COUNT);
    DEV_ASSERT(channelPair < (FEATURE_FTM_CHANNEL_COUNT >> 1U));

    ftm_state_t * state = ftmStatePtr[instance];
    FTM_Type * ftmBase = g_ftmBase[instance];
    uint8_t channel = (uint8_t)(channelPair << 1U);

    /* Verify the mode for current pair of channels */
    if (FTM_DRV_GetDualEdgeCaptureBit(ftmBase, channelPair))
    {
        /* Dual edge input capture case */
        uint16_t first_event_time = FTM_DRV_GetChnCountVal(ftmBase, channel);
        uint16_t second_event_time = FTM_DRV_GetChnCountVal(ftmBase, (uint8_t)(channel + 1U));
        if (second_event_time < first_event_time)
        {
            /* Measurement when overflow occurred */
            state->measurementResults[channel] = (uint16_t)(second_event_time + (FTM_DRV_GetMod(ftmBase) - first_event_time));
        }
        else
        {
            /* Measurement when overflow doesn't occurred */
            state->measurementResults[channel] = (uint16_t)(second_event_time - first_event_time);
        }

        /* Clear flags for channels n and n+1 */
        FTM_DRV_ClearChnEventFlag(ftmBase, channel);
        FTM_DRV_ClearChnEventFlag(ftmBase, (uint8_t)(channel + 1U));
    }
    else
    {
        /* To get the channel interrupt source the both channels flag must be checked */
        if (false == FTM_DRV_HasChnEventOccurred(ftmBase, channel))
        {
            channel++;
        }

        /* Get the time stamp of the event */
        state->measurementResults[channel] = FTM_DRV_GetChnCountVal(ftmBase, channel);
        /* Clear the flag for C(n+1) channel */
        FTM_DRV_ClearChnEventFlag(ftmBase, channel);
    }

    /* If the callback is defined to use it */
    if (((state->channelsCallbacks[channel]) != NULL) && (state->enableNotification[channel] == true))
    {
        state->channelsCallbacks[channel](IC_EVENT_MEASUREMENT_COMPLETE, state->channelsCallbacksParams[channel]);
    }
}

更多推荐