OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4365|回复: 3

我想要利用STM32H750的内部捕获比较中断实现1us级别的延时

[复制链接]

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2018-9-8
在线时间
8 小时
发表于 2022-7-21 09:48:27 | 显示全部楼层 |阅读模式
22金钱
本帖最后由 Zidone 于 2022-7-21 10:41 编辑

1.首先说明一下,我的目的是实现微秒级别的延时。我使用的单片机是STM32H750,主频目前跑在480MHz,使用的TIM2挂载在APB1总线上,APB1总线目前时钟频率是120MHz。
APB21 prescaler != 1, 所以APB1上的TIM2CLK = APB1 x 2 = 240MHz。我把定时器的分频Prescaler设置为240,这样就得到1MHz(1us)的定时器时钟。
另外,定时器2是32位的,那么最大的溢出值Period位2的32次方,即0xFFFF FFFF。


##############################分割线#################################################
# 以下内容位于bsp_timer.c


/* 保存 TIM定时中断到后执行的回调函数指针 */
static void (*TIM2_CallBack1)(void);
static void (*TIM2_CallBack2)(void);
static void (*TIM2_CallBack3)(void);

static void (*TIM2_CallBack4)(void);

/*下面这个函数初始化定时器2的配置,配置定时器时钟位1Mhz,配置定时器中断,启动定时器*/
void InitTIM2forTimer(void){
    TIM_HandleTypeDef  TIM_HandleStruct = {0};
        
        __HAL_RCC_TIM2_CLK_ENABLE();                /* 使能TIM2时钟 */
        
        TIM_HandleStruct.Instance = TIM2;
        TIM_HandleStruct.Init.Prescaler         = 240;//240MHz / 240 = 1MHz(TIM2CLK)
        TIM_HandleStruct.Init.Period            = 0xFFFFFFFF;
        TIM_HandleStruct.Init.ClockDivision     = 0;
        TIM_HandleStruct.Init.CounterMode       = TIM_COUNTERMODE_UP;
        TIM_HandleStruct.Init.RepetitionCounter = 0;
    TIM_HandleStruct.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
        
        if (HAL_TIM_Base_Init(&TIM_HandleStruct) != HAL_OK){
                Error_Handler();
        }


        /* 配置定时器中断,给CC捕获比较中断使用 */


        HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(TIM2_IRQn);        
   
    /* 启动定时器 */
        HAL_TIM_Base_Start(&TIM_HandleStruct);
}
/*这个函数用于启动定时器2用于定时,usChannel用于选择定时器的通道(1-4),usTimeOut用于定时多少us,void (*pCallBack)(void)是回调函数*/
void StartTIM2forTimer(uint8_t usChannel, uint32_t usTimeOut, void (*pCallBack) (void))
{
    /* H750速度较快,无需补偿延迟,实测精度正负1us */
        
        switch (usChannel){
                case 1:
                        TIM2_CallBack1 = pCallBack;
                        TIM2->CCR1 = TIM2->CNT + usTimeOut; /* 设置捕获比较计数器CC1 */
                        TIM2->SR = (uint32_t)~TIM_IT_CC1;   /* 清除CC1中断标志 */
                        TIM2->DIER |= TIM_IT_CC1;                        /* 使能CC1中断 */
                        break;
                case 2:
                        TIM2_CallBack2 = pCallBack;
                        TIM2->CCR2 = TIM2->CNT + usTimeOut; /* 设置捕获比较计数器CC2 */
                        TIM2->SR = (uint32_t)~TIM_IT_CC2;   /* 清除CC2中断标志 */
                        TIM2->DIER |= TIM_IT_CC2;                        /* 使能CC2中断 */
                        break;
                case 3:
                        TIM2_CallBack3 = pCallBack;
                        TIM2->CCR3 = TIM2->CNT + usTimeOut; /* 设置捕获比较计数器CC3 */
                        TIM2->SR = (uint32_t)~TIM_IT_CC3;   /* 清除CC3中断标志 */
                        TIM2->DIER |= TIM_IT_CC3;                        /* 使能CC3中断 */
                        break;
                case 4:
                        TIM2_CallBack4 = pCallBack;
                        TIM2->CCR4 = TIM2->CNT + usTimeOut; /* 设置捕获比较计数器CC4 */
                        TIM2->SR = (uint32_t)~TIM_IT_CC4;   /* 清除CC4中断标志 */
                        TIM2->DIER |= TIM_IT_CC4;                        /* 使能CC4中断 */
                        break;
                default:
                        Error_Handler();/*总共只可选通道1-4,选其它的就错误了*/
        }

}
/*这是定时器2的中断服务函数,在指定定时器通道的cnt到达cnt+usTimeout触发该中断,触发后执行回调函数,关闭定时器中断*/
void TIM2_IRQHandler(void)
{
        uint32_t itstatus = 0x0, itenable = 0x0;
        
   
          itstatus = TIM2->SR & TIM_IT_CC1;
        itenable = TIM2->DIER & TIM_IT_CC1;
   
        if ((itstatus != (uint32_t)RESET) && (itenable != (uint32_t)RESET))
        {
                TIM2->SR = (uint32_t)~TIM_IT_CC1;
                TIM2->DIER &= (uint32_t)~TIM_IT_CC1;                /* 禁能CC1中断 */        


        /* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
        TIM2_CallBack1();
    }


        itstatus = TIM2->SR & TIM_IT_CC2;
        itenable = TIM2->DIER & TIM_IT_CC2;
        if ((itstatus != (uint32_t)RESET) && (itenable != (uint32_t)RESET))
        {
                TIM2->SR = (uint32_t)~TIM_IT_CC2;
                TIM2->DIER &= (uint32_t)~TIM_IT_CC2;                /* 禁能CC2中断 */        


        /* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
        TIM2_CallBack2();
    }


        itstatus = TIM2->SR & TIM_IT_CC3;
        itenable = TIM2->DIER & TIM_IT_CC3;
        if ((itstatus != (uint32_t)RESET) && (itenable != (uint32_t)RESET))
        {
                TIM2->SR = (uint32_t)~TIM_IT_CC3;
                TIM2->DIER &= (uint32_t)~TIM_IT_CC3;                /* 禁能CC2中断 */        


        /* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
        TIM2_CallBack3();
    }


        itstatus = TIM2->SR & TIM_IT_CC4;
        itenable = TIM2->DIER & TIM_IT_CC4;
        if ((itstatus != (uint32_t)RESET) && (itenable != (uint32_t)RESET))
        {
                TIM2->SR = (uint32_t)~TIM_IT_CC4;
                TIM2->DIER &= (uint32_t)~TIM_IT_CC4;                /* 禁能CC4中断 */        


        /* 先关闭中断,再执行回调函数。因为回调函数可能需要重启定时器 */
        TIM2_CallBack4();
    }        

}


##############################分割线#################################################
# 以下内容位于main.c


bool flag = true;
/*TIM2通道1的回调函数*/

static void TIM2_CallBack1(void);


       InitTIM2forTimer();//首先调用定时器2初始化
        while (1){
                while(flag){
                        StartTIM2forTimer(1 ,100 0000, TIM2_CallBack1);//定时一秒
                        flag = false;

                }
         }
static void TIM2_CallBack1(void){
        LED1_Toggle();
        flag = true;

}


##############################分割线#################################################
但是从实验结果来看,好像确实能进入到上面这个回调函数,但是这个定时好像极其不准确,LED闪烁的超级快。之前做定时器中断的实验以及定时器用作PWM输出的实验时间都是非常准确的。
麻烦论坛各位大佬和原子哥能否帮忙看一下,提出一些建议,实在找不到问题所在了。

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

643

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2432
金钱
2432
注册时间
2019-12-28
在线时间
527 小时
发表于 2022-7-21 14:31:36 | 显示全部楼层
回复

使用道具 举报

12

主题

3344

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8463
金钱
8463
注册时间
2020-5-11
在线时间
3904 小时
发表于 2022-7-21 16:09:50 | 显示全部楼层
没看出问题,要不你先当它是16位的,设置较小的延时看看,先用示波器看这个时间是否稳定,是否把时钟算错了。
还有就是,32位的常量可能要在后面加个L,你先这样试试:StartTIM2forTimer(1 ,100 0000L,
专治疑难杂症
回复

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2018-9-8
在线时间
8 小时
 楼主| 发表于 2022-7-21 16:25:12 | 显示全部楼层
LcwSwust 发表于 2022-7-21 16:09
没看出问题,要不你先当它是16位的,设置较小的延时看看,先用示波器看这个时间是否稳定,是否把时钟算错了 ...

好的,谢谢你,我先试试看
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-25 12:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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