我想知道如何测量大于 16 或 32 位定时器分辨率的脉冲持续时间。更具体地说,我想记录多个定时器通道上捕获的输入脉冲的绝对时间戳。然后可以使用这些时间戳来计算中间持续时间。我的感觉是,这是一个常见的计时器应用程序,但我只能想到一个相当复杂的解决方案。
下面我将使用
STM32F302CB MCU 和 HAL 驱动程序描述我的解决方案。因为它需要 HAL 驱动程序修改和竞争条件检测,所以我很好奇是否有更简单的解决方案。
绝对时间戳计算
由于定时器周期对于我的应用程序来说太小,具有所需的频率,我的想法是结合“输入捕获”和“定时器更新”事件。当为特定通道调用 HAL_
tiM_IC_CaptureCallback() 时,绝对时间戳为
- (TIMx_Overflows * (htim.Init.Period + 1)) + HAL_TIM_ReadCapturedValue(htim, channel)
,其中 TIMx_Overflows 是为定时器 TIMx 调用 HAL_TIM_PeriodElapsedCallback 的次数。
两种竞争条件
问题是,当调用 HAL_TIM_IC_CaptureCallback() 时,我预计会有两个竞争条件:
- 定时器更新事件可能在输入捕获事件之后发生,但在输入捕获事件之前处理。在这种情况下,我们需要在计算中减去一个溢出周期。
- 挂起的计时器更新事件可能恰好在捕获发生之前发生。在这种情况下,我们需要在计算中添加一个溢出周期。
竞争条件 1
据我所知,STM32F302CB 无法按时间顺序处理输入捕获和定时器更新事件。但是我们可以定义中断优先级。为了防止出现第一个竞争条件,我们可以将 TIMx 捕获比较中断设置为比 TIMx 更新中断更高(次)的优先级。但是大多数定时器只有一个全局的 TIMx 全局中断。此外,在使用 HAL 驱动程序时,我们可以从 stm32f3xx_it.c 观察到所有三个可能的中断都被转发到 stm32f3xx_hal_tim.c 中的 HAL_TIM_IRQHandler(),因此得出结论 HAL 忽略特定定时器内任何已配置的 IRQ(子)优先级。
HAL_TIM_IRQHandler() 首先处理所有捕获比较通道,然后继续处理定时器更新和其他事件。因为该函数在清除挂起中断标志后不会直接返回,所以无法保证在定时器更新事件之前发生的输入捕获事件也将在定时器更新之前得到处理。
以下示例显示了定时器更新事件如何在通道 1 上的输入捕获之后发生但在通道 1 上的输入捕获之前被处理:
- 通道 2 事件发生
- HAL_TIM_IRQHandler() 开始
- HAL_TIM_IC_CaptureCallback() 为通道 2 启动
- 通道 1 事件发生
- 定时器更新事件发生
- 通道 2 的 HAL_TIM_IC_CaptureCallback() 完成
- HAL_TIM_PeriodElapsedCallback() 开始
- HAL_TIM_PeriodElapsedCallback() 完成
- HAL_TIM_IRQHandler() 完成
- HAL_TIM_IRQHandler() 开始
- HAL_TIM_IC_CaptureCallback() 为通道 1 启动
- 通道 1 的 HAL_TIM_IC_CaptureCallback 完成
- HAL_TIM_IRQHandler() 完成
通过修改 HAL_TIM_IRQHandler(),我们可以通过在每个事件后添加“return”或“else if”来确保在定时器更新之前处理输入捕获。因此竞争条件 1 不会再发生。
比赛条件 2
第二个竞争条件无法避免,但可以通过检查 HAL_TIM_IC_CaptureCallback() 中的 TIM_FLAG_UPDATE 来检测。如果存在挂起的定时器更新标志并且捕获的值小于定时器周期的一半,我们可以假设定时器更新发生在输入捕获之前。
定时器事件回调
- volatile uint32_t TIM2_overflow_count = 0;
- volatile uint64_t TIM2_CH1_absolute_timestamp = 0;
- volatile uint64_t TIM2_CH2_absolute_timestamp = 0;
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if (htim->Instance == TIM2) {
- // Increment overflow counter
- TIM2_overflow_count++;
- }
- }
- void HAL_TIM_IC_CaptureCallback (TIM_HandleTypeDef *htim)
- {
- if (htim->Instance == TIM2) {
- HAL_TIM_ActiveChannel channel = htim->Channel;
- switch (channel) {
- case HAL_TIM_ACTIVE_CHANNEL_1:
- TIM2_CH1_absolute_timestamp = AbsoluteTimestamp(htim, TIM2_overflow_count, TIM_CHANNEL_1);
- break;
- case HAL_TIM_ACTIVE_CHANNEL_2:
- TIM2_CH2_absolute_timestamp = AbsoluteTimestamp(htim, TIM2_overflow_count, TIM_CHANNEL_2);
- break;
- case HAL_TIM_ACTIVE_CHANNEL_3:
- case HAL_TIM_ACTIVE_CHANNEL_4:
- case HAL_TIM_ACTIVE_CHANNEL_CLEARED:
- break;
- }
- }
- }
- uint64_t AbsoluteTimestamp(TIM_HandleTypeDef *htim, uint32_t overflow_count, uint32_t channel)
- {
- // Retrieve captured value
- uint32_t captured_value = HAL_TIM_ReadCapturedValue(htim, channel);
- // Check pending overflow flag
- bool pending_update = __HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET;
- // Calculate overflow duration
- uint64_t timer_period = (uint64_t) htim->Init.Period + 1;
- uint64_t overflow_duration;
- if (pending_update && captured_value < timer_period / 2) {
- overflow_duration = timer_period * (overflow_count + 1) ;
- } else {
- overflow_duration = timer_period * overflow_count;
- }
- return overflow_duration + captured_value;
- }
修改 HAL_TIM_IRQHandler()
嵌套 if 语句使用 && 和中间添加的“else”关键字组合。
- void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
- {
- /* Capture compare 1 event */
- if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
- &&(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET))
- {
- [...]
- }
- /* Capture compare 2 event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) != RESET))
- {
- [...]
- }
- /* Capture compare 3 event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) != RESET))
- {
- [...]
- }
- /* Capture compare 4 event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) != RESET))
- {
- [...]
- }
- /* TIM Update event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET))
- {
- [...]
- }
- /* TIM Break input event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) != RESET))
- {
- [...]
- }
- /* TIM Trigger detection event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) != RESET))
- {
- [...]
- }
- /* TIM commutation event */
- else if ((__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET)
- && (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) != RESET))
- {
- [...]
- }
- }
如果您知道更简单的解决方案,请告诉我。
0