硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4459|回复: 20
收起左侧

[STM32CubeF4] HAL中的定时器中断处理函数,存在重复进入的隐患。

  [复制链接]

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
发表于 2019-8-10 17:50:27 | 显示全部楼层 |阅读模式
HAL库启动定时器运行和中断是HAL_TIM_Base_Start_IT(); 但是,执行一次中断后就会停止。需要在中断函数中重新调用一次才可继续执行。然而,第二次打开的时候就直接进入中断了,定时器没有完整计数!
分析结果如下:
定时器的中断处理函数是:HAL_TIM_IRQHandler(&htim4);//以定时器4作为例子。
这个函数里面的update中断处理部分如下://update的中断处理最常用,其他的冗长代码省略。
/* TIM Update event */
  if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
  {
    if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); //这个句子很诡异!查询得知这个是禁止update event中断!并不是容易理解的“清除中断标志”
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
      htim->PeriodElapsedCallback(htim);
#else
      HAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
    }
  }

全程的处理函数,没有清除中断标志!!需要人为在HAL_TIM_PeriodElapsedCallback()中加上__HAL_TIM_CLEAR_FLAG(htim,TIM_FLAG_UPDATE);


总结: __HAL_TIM_CLEAR_FLAG()和__HAL_TIM_CLEAR_IT()是完全不同的意义,及其容易混淆!

评分

参与人数 1金币 +50 收起 理由
eric2013 + 50 赞一个!

查看全部评分

回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-10 17:54:00 | 显示全部楼层
同时,我还用到了串口接收中断,就没有发生重复进入的问题。 因为,串口在读取缓冲区数据后,自动清零了中断标志位。无需人为干预。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-10 17:56:37 | 显示全部楼层
原来是这样。。。

因为HAL的定时器中断处理繁琐,我都是下面这样
  1. TIM定时中断服务程序范例,必须清中断标志
  2. void TIM6_DAC_IRQHandler(void)
  3. {
  4.         if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
  5.         {
  6.                 TIM6->SR = ~ TIM_FLAG_UPDATE;
  7.                 //添加用户代码
  8.         }
  9. }
复制代码


回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-10 18:10:43 | 显示全部楼层
eric2013 发表于 2019-8-10 17:56
原来是这样。。。

因为HAL的定时器中断处理繁琐,我都是下面这样

我以前都是用标准库,参考了你的modbus例程和bsp,使用都是很正常的。。
现在有个小电路板使用Modbus,就尝试了cubemx,使用了hal库。
为了长久的考虑,尽量按照hal的要求来,都是放自己的代码在user code部分,不更改它们的思路和方法,所以遇到了这些奇葩的问题。。。


hal真的是非常繁琐,有太多的erro处理。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-10 18:13:44 | 显示全部楼层
这两个是一样的

#define __HAL_TIM_CLEAR_FLAG(__HANDLE__, __FLAG__)           ((__HANDLE__)->Instance->SR = ~(__FLAG__))
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__)      ((__HANDLE__)->Instance->SR = ~(__INTERRUPT__))

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-10 18:24:55 | 显示全部楼层
进一步确认了下


看了下标准库的:

*            @arg TIM_FLAG_Update: TIM update Flag
  *            @arg TIM_FLAG_CC1: TIM Capture Compare 1 Flag
  *            @arg TIM_FLAG_CC2: TIM Capture Compare 2 Flag
  *            @arg TIM_FLAG_CC3: TIM Capture Compare 3 Flag
  *            @arg TIM_FLAG_CC4: TIM Capture Compare 4 Flag
  *            @arg TIM_FLAG_COM: TIM Commutation Flag
  *            @arg TIM_FLAG_Trigger: TIM Trigger Flag
  *            @arg TIM_FLAG_Break: TIM Break Flag
  *            @arg TIM_FLAG_CC1OF: TIM Capture Compare 1 over capture Flag
  *            @arg TIM_FLAG_CC2OF: TIM Capture Compare 2 over capture Flag
  *            @arg TIM_FLAG_CC3OF: TIM Capture Compare 3 over capture Flag
  *            @arg TIM_FLAG_CC4OF: TIM Capture Compare 4 over capture Flag

void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{  
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));

  /* Clear the flags */
  TIMx->SR = (uint16_t)~TIM_FLAG;
}


  *            @arg TIM_IT_Update: TIM1 update Interrupt source
  *            @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
  *            @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
  *            @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
  *            @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
  *            @arg TIM_IT_COM: TIM Commutation Interrupt source
  *            @arg TIM_IT_Trigger: TIM Trigger Interrupt source
  *            @arg TIM_IT_Break: TIM Break Interrupt source


void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
{
  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx));

  /* Clear the IT pending Bit */
  TIMx->SR = (uint16_t)~TIM_IT;
}


两个操作的都是SR寄存器,而且TIM_FLAG_Update 和 TIM_IT_Update是一样的

#define TIM_IT_Update                      ((uint16_t)0x0001)
#define TIM_IT_CC1                         ((uint16_t)0x0002)
#define TIM_IT_CC2                         ((uint16_t)0x0004)
#define TIM_IT_CC3                         ((uint16_t)0x0008)
#define TIM_IT_CC4                         ((uint16_t)0x0010)
#define TIM_IT_COM                         ((uint16_t)0x0020)
#define TIM_IT_Trigger                     ((uint16_t)0x0040)
#define TIM_IT_Break                       ((uint16_t)0x0080)

#define TIM_FLAG_Update                    ((uint16_t)0x0001)
#define TIM_FLAG_CC1                       ((uint16_t)0x0002)
#define TIM_FLAG_CC2                       ((uint16_t)0x0004)
#define TIM_FLAG_CC3                       ((uint16_t)0x0008)
#define TIM_FLAG_CC4                       ((uint16_t)0x0010)
#define TIM_FLAG_COM                       ((uint16_t)0x0020)
#define TIM_FLAG_Trigger                   ((uint16_t)0x0040)
#define TIM_FLAG_Break                     ((uint16_t)0x0080)



HAL库里面就比较厉害了,完全两个不同的定义,初次看还以为他操作的DIER寄存器

实际TIM_DIER_UIE和TIM_SR_UIF是等效的。因为程序里面都是操作的SR寄存器。


#define TIM_IT_UPDATE                   TIM_DIER_UIE                         /*!< Update interrupt            */
#define TIM_IT_CC1                         TIM_DIER_CC1IE                       /*!< Capture/Compare 1 interrupt */
#define TIM_IT_CC2                         TIM_DIER_CC2IE                       /*!< Capture/Compare 2 interrupt */
#define TIM_IT_CC3                         TIM_DIER_CC3IE                       /*!< Capture/Compare 3 interrupt */
#define TIM_IT_CC4                         TIM_DIER_CC4IE                       /*!< Capture/Compare 4 interrupt */
#define TIM_IT_COM                         TIM_DIER_COMIE                       /*!< Commutation interrupt       */
#define TIM_IT_TRIGGER                   TIM_DIER_TIE                       /*!< Trigger interrupt           */
#define TIM_IT_BREAK                       TIM_DIER_BIE                      /*!< Break interrupt             */

#define TIM_FLAG_UPDATE                 TIM_SR_UIF                           /*!< Update interrupt flag         */
#define TIM_FLAG_CC1                       TIM_SR_CC1IF                         /*!< Capture/Compare 1 interrupt flag */
#define TIM_FLAG_CC2                       TIM_SR_CC2IF                         /*!< Capture/Compare 2 interrupt flag */
#define TIM_FLAG_CC3                       TIM_SR_CC3IF                         /*!< Capture/Compare 3 interrupt flag */
#define TIM_FLAG_CC4                       TIM_SR_CC4IF                         /*!< Capture/Compare 4 interrupt flag */
#define TIM_FLAG_COM                      TIM_SR_COMIF                         /*!< Commutation interrupt flag    */
#define TIM_FLAG_TRIGGER               TIM_SR_TIF                           /*!< Trigger interrupt flag        */
#define TIM_FLAG_BREAK                  TIM_SR_BIF                           /*!< Break interrupt flag          */





回复

使用道具 举报

3

主题

65

回帖

74

积分

初级会员

积分
74
发表于 2019-8-11 09:27:31 | 显示全部楼层
本帖最后由 westzg 于 2019-8-11 09:32 编辑

没有遇到你说的那种情况,定时器中断是可以 正常进入的。
以tim4为例,代码如下:


回复

使用道具 举报

3

主题

65

回帖

74

积分

初级会员

积分
74
发表于 2019-8-11 09:31:10 | 显示全部楼层
定时器中断正常的,
以stm32f429, tim4为例,代码如下
tim4 配置----------------------------------------

TIM_HandleTypeDef htim4;

/* TIM4 init function */
void MX_TIM4_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 84-1;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 1000;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
  sSlaveConfig.InputTrigger = TIM_TS_ITR0;
  if (HAL_TIM_SlaveConfigSynchronization(&htim4, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspInit 0 */

  /* USER CODE END TIM4_MspInit 0 */
    /* TIM4 clock enable */
    __HAL_RCC_TIM4_CLK_ENABLE();

    /* TIM4 interrupt Init */
    HAL_NVIC_SetPriority(TIM4_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(TIM4_IRQn);
  /* USER CODE BEGIN TIM4_MspInit 1 */

  /* USER CODE END TIM4_MspInit 1 */
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM4)
  {
  /* USER CODE BEGIN TIM4_MspDeInit 0 */

  /* USER CODE END TIM4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM4_CLK_DISABLE();

    /* TIM4 interrupt Deinit */
    HAL_NVIC_DisableIRQ(TIM4_IRQn);
  /* USER CODE BEGIN TIM4_MspDeInit 1 */

  /* USER CODE END TIM4_MspDeInit 1 */
  }
}
如何使用 -------------------------------------------
extern TIM_HandleTypeDef htim4;




void user_tim_init(void)
{

    HAL_TIM_Base_Start_IT(&htim4);

}


void user_tim4_callback(void)
{
        USER_LED4_TOGGLE();

}
中断调用函数 ---------------------------------
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */
  else if (htim->Instance == TIM4) {
      user_tim4_callback();
   
  }
  /* USER CODE END Callback 1 */
}

回复

使用道具 举报

0

主题

59

回帖

59

积分

初级会员

积分
59
发表于 2019-8-11 11:28:27 | 显示全部楼层
我觉得hal库中断处理是败笔,本来不同定时器都有中断向量,HAL库先从中断向量进入一个总的回调函数然后再分支到不同定时器处理函数,不符合中断处理简洁的原则;大家不知我说的对否?
回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-12 08:23:14 | 显示全部楼层
本帖最后由 caicaptain2 于 2019-8-12 08:38 编辑

  还是你看的细致啊。。。
这明显是hal的编写人员非常的马虎,定义为TIM_DIER_UIE(DIER寄存器),结果去操作SR寄存器,简直是坑人嘛!

那我这个帖子是错误的,请删除以免误导。

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-12 09:38:59 | 显示全部楼层
caicaptain2 发表于 2019-8-12 08:23
还是你看的细致啊。。。
这明显是hal的编写人员非常的马虎,定义为TIM_DIER_UIE(DIER寄存器) ...

没事,非常好的讨论帖子
回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-16 16:46:36 | 显示全部楼层
本帖最后由 caicaptain2 于 2019-8-16 16:47 编辑

今日有空,又把这个问题翻出来研究了一下子。 csdn中也有多个帖子说到,使能定时器中断后,即使定时器没有运行,依然会立刻进入中断。
问题的真正原因是:
定时器在初始化的时候,会产生中断置位的。hal库在初始化定时器的时候,会导致定时器的reset中断置位。那么当开启任何定时器中断时,就会直接进入中断函数。
Snipaste_2019-08-16_16-13-11.png

所以,在开启定时器中断的时候,提前清理一下标志位。就像这样子:
  1. __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);//第一步,必须有这个,不然重复进入中断。芯片特性。
  2. __HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE);//打开tim4的update中断。tim4的总中断在初始化时已经打开。
复制代码
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-16 16:54:15 | 显示全部楼层
caicaptain2 发表于 2019-8-16 16:46
今日有空,又把这个问题翻出来研究了一下子。 csdn中也有多个帖子说到,使能定时器中断后,即使定时器没有 ...

清除标志没用的,这个问题在早年的标准库就有,有几个寄存器的位要专门设置下才行。
回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-16 16:59:54 | 显示全部楼层
eric2013 发表于 2019-8-16 16:54
清除标志没用的,这个问题在早年的标准库就有,有几个寄存器的位要专门设置下才行。

哪里能找到相关说明?

我这个问题,确实是通过请这个标志,解决了这个问题。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2019-8-16 17:11:08 | 显示全部楼层
caicaptain2 发表于 2019-8-16 16:59
哪里能找到相关说明?

我这个问题,确实是通过请这个标志,解决了这个问题。

F407定时器更新中断问题(TIM_IT_Update中断)
http://www.armbbs.cn/forum.php?m ... 4187&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
 楼主| 发表于 2019-8-19 16:14:48 | 显示全部楼层
eric2013 发表于 2019-8-16 17:11
F407定时器更新中断问题(TIM_IT_Update中断)
http://www.armbbs.cn/forum.php?mod=viewthread&tid=418 ...

这个我也看到了。主要是我想找到内部的真原因。
从手册看,确实是初始化,reset定时器,会产生update的中断标志的。
回复

使用道具 举报

30

主题

139

回帖

234

积分

高级会员

积分
234
发表于 2023-2-22 15:48:44 | 显示全部楼层
caicaptain2 发表于 2019-8-19 16:14
这个我也看到了。主要是我想找到内部的真原因。
从手册看,确实是初始化,reset定时器,会产生update的 ...



帖子最终讨论的结果是两个函数清除中断标志是一样的效果吗?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2023-2-23 09:49:21 | 显示全部楼层
Biby 发表于 2023-2-22 15:48
帖子最终讨论的结果是两个函数清除中断标志是一样的效果吗?

是一个东西。就是6楼贴的。
回复

使用道具 举报

30

主题

139

回帖

234

积分

高级会员

积分
234
发表于 2023-2-23 10:00:03 | 显示全部楼层
eric2013 发表于 2023-2-23 09:49
是一个东西。就是6楼贴的。

嗯,又了解了一个知识点
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2023-7-10 17:04:09 | 显示全部楼层
eric2013 发表于 2019-8-10 17:56
原来是这样。。。

因为HAL的定时器中断处理繁琐,我都是下面这样

根据帖子的内容,我去写了定时器中断函数,一部分模仿标准库的方式来写的,但是我发现我在标准库中写的申请内存并发送的任务在HAL库中实现不了
回复

使用道具 举报

1

主题

8

回帖

11

积分

新手上路

积分
11
发表于 2023-7-14 08:57:00 | 显示全部楼层
学到了,记录一下!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-2 19:20 , Processed in 0.267987 second(s), 32 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表