NXP-S32 PWMIC输入捕获测量频率与周期
·
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]);
}
}
更多推荐

所有评论(0)