STM32时钟系统和TIMER配置(溢出中断/PWM)实例

目录:

1. STM32时钟系统

2. STM32的定时器典型配置之溢出中断

3. STM32的定时器典型配置之PWM输出

1. STM32时钟系统

(1)Clock tree

        可以在官方手册(Stm32x-series-Reference-manual)的clock tree中可以对相应MCU的时钟系统有个大致了解。

        也可以从中了解到STM32哪些外设挂在哪一条总线上,然后根据Cube中配置的时钟频率和各种分频就可以得到各总线的时钟频率,注意挂在这些总线上的外设还需要在配置时经过分频得到外设的时钟频率。

(2)STM32总线与时钟配置

        假如STM32时钟源为HSE高速外部晶振,HSE一般为8MHz,经过x分频和y倍频后得到系统时钟,即PLLCLK = (8/x)*y MHz。

        AHB总线上时钟由系统时钟PLLCLK经过z分频得到,HCLK = PLLCLK/z。

        APB2上的总线时钟PCLK2经过HCLK的m分频得到,PCLK2 = HCLK/m。

        APB1上的总线时钟PCLK1经过HCLK的n分频得到,PCLK1 = HCLK/n。

外设时钟举例: 挂在高速总线APB2上的ADC时钟经过APB2总线时钟2分频得到,即ADCCLK = PCLK2/2 。

外设时钟举例:挂在低速总线APB1上的TIM2时钟经过APB1总线时钟2分频得到,即TIM2CLK = PCLK1/2 。

2. STM32的定时器典型配置之溢出中断

(1)定时器分类

        STM32的物理定时器解决了MCU单任务运行使用CPU计数对性能的影响,定时器根据功能从多到少分为高级定时器、通用定时器、基本定时器。低功耗定时器、看门狗定时器、某些MCU还有HRTIM等。

(2)定时器挂在哪条总线上

        以STM32F10x系列的MCU为例,TIM1/8挂在APB2高速总线上,TIM2-7挂在APB1低速总线上。

 (3)定时器初始时钟频率

        有人说如果APB1和APB2经过HCLK都进行1分频,那么TIM1/8的时钟为APB2的时钟,TIM2-7的时钟为APB1的时钟。

        如果APB1和APB2经过HCLK进行分频,分频系数不为1,那么TIM1/8的时钟为APB2时钟的两倍,TIM2-7为APB1的时钟的两倍。

        在实际应用中,要根据不同的MCU和时钟系统配置得到相应定时器的初始时钟频率。

 (4)定时器计数时钟频率

        所谓计数时钟频率,就是上面提到每种外设具体时钟频率还要经过外设本身的外设分频(PSC)配置,从而得到最终定时器时钟频率,这个频率就是定时器每计数一次花的时间。

(5)在CubeMX中定时器的配置

        下面是对TIM2配置的实例:

                                                                        图1

         选中左侧TIM2,然后在Clock Source选择Internal Clock,在下面Parameter Settings处就会展示出相关参数,主要需要配置PSC,Mode,ARR值。

        PSC即Prescaler,预分频系数,就是上面第(4)点中提到的再次分频系数,基于第(3)点的时钟频率分频。比如要配置定时器每一微妙(最终定时器计数频率为1MHz)计数一次,那么首先看第(3)点的时钟频率是多少:

        从系统时钟树配置中可以看出,APB1总线时钟频率为72MHz,TIM2挂在APB1总线上,然后根据第(3)点的规则可知TIM2的初始频率为72MHz,那么要让TIM2的计数频率为1MHz,那么PSC就应该配置为72-1,这就是图1中72的由来。 注意这里是16bit位值,因此这个值不能超过65535,另外,STM32手册中提到这里PSC的值一定是你想要的值减1,可能是从0开始的吧,所以这里是72-1。

        Counter Mode可以选择计数模式,这里选择向上计数。

        Counter Period即上面提到的ARR值,即重装载值。即定时器计到这个值后响应一个事件(一般可以配置中断),然后重新开始计时。这个值也是有限制的,这里从图1中可以知道它是一个32bit的数。同样,由于TIM2是1us计数一次,如果我们希望500ms响应一次中断,那么就需要把ARR的值配置为500000,即TIM2计数500000次后响应中断,每次1us,500000刚好500ms。同样配置时的值是实际想要的值减1,即500000-1。

        如果要使用中断方式,需要配置中断:

         上图中可以选中TIM2的global interrupt,即使能了中断,然后根据实际应用配置中断优先级。                        

(6)Cube生成代码

        1) 开启定时器

        HAL_TIM_Base_Start_IT(&htimx); htimx是一个结构体变量,这里就是TIM2的句柄。这个函数的作用是开启定时器及溢出中断。

         HAL_TIM_Base_Stop_IT(&htimx); 这个函数是关闭定时器及溢出中断HAL库函数。

         HAL_TIM_Base_Start(&htimx); 这个函数是开启定时器,但是不开启中断的HAL库函数。

         HAL_TIM_Base_Stop(&htimx); 这个函数是关闭定时器的HAL库函数。

        2) 定时器中断处理函数

        当定时器的计数到达ARR的值后,就会产生溢出中断,最终调用到中断完成回调函数,我们一般都是会改写这个函数,在这个函数里面执行相应逻辑。根据上面的配置,这个回调函数将500ms调用一次。void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim)

        注意ST的定时器,调用HAL_TIM_Base_Start_IT(&htimX)后,当CNT加到ARR的值后会响应中断,这是我们想要的中断。但是实际上是只要一调用这个函数就会响应一次中断,这次中断是我们不想要的。

        因此,解决办法往往是初始化定时器后,调用__HAL_TIM_CLEAR_IT(&htimX,TI_IT_UPDATE)函数清除中断,否则会额外响应一次中断从而导致逻辑错误。为了保险起见,每次计数中断完成后,可以在中断处理函数中调用__HAL_TIM_SET_COUNTER(&htimX,0)把CNT值设置为0。这样重新开始计数的时候又重新从0开始计数。

3. STM32的定时器典型配置之PWM输出

(1)PWM占空比

        什么是PWM呢,比如LCD背光就可以用PWM方式控制,当高电平时间比低电平时间长,那么肉眼感受到LCD亮的时间就比灭的时间长,如果继续减少低电平时间,甚至感受不到LCD有灭,那么通过这样的方式就可以实现LCD亮度控制。

        一般来说PWM都是方波,那么高电平和低电平持续时间构成一个周期,高电平持续时间除以周期*100%,就是占空比,即Duty值。

(2)定时器比较值

        一般比较值位于0到ARR重装载值之间,当定时器计数值处于0到比较值之间时,定时器的某个通道对应的GPIO输出高电平,如果计数值处于比较值到ARR重装载值之间,那么定时器的某个通道对应的GPIO输出低电平,由此可知通过改变比较值的大小就可以改变定时器的占空比。

                                                                图2

(3)Cube配置

         选中左侧相应定时器,选中右侧的Activated(某些定时器设置开启选项是把Clock source选择为Internal Clock),然后选中PWM Generation CH1。

        参数那里PSC选择72-1,即计数频率是1MHz,即计数一次1us,ARR值为1000-1,即1000us响应一次中断,也就是1ms一次中断,中断频率就是1/0.1s=1000Hz。

        1000Hz的频率肉眼是看不出来的,最后就是定时器某个通道对应的GPIO输出引脚要配置为TIMx_CHx,假如定时器2的通道2作为PWM输出,那么就需要把定时器2通道2对应的GPIO复用功能配置为TIM2_CH2。

        PWM一个周期: 当CNT值向上计数到达ARR值,定时器溢出,重新向上计数...循环此过程,所以PWM周期为CNT计数到达ARR所花时间,它的倒数就是PWM频率。

        PWM周期算法: Tout = (arr+1) * (psc+1)/Tclk , Tclk就是APB总线上分配给TIM的时钟频率,(psc+1)/Tclk即TIM计数一次花多少时间。

TIM某通道PWM配置相关参数:https://blog.csdn.net/qq_36958104/article/details/83661117

(4)代码编写

        打开定时器PWM,HAL_TIM_WM_Start(&htimx,TIM_CHANNEL_2); 然后通过调用下面函数修改比较值,从而修改了占空比,从而实现亮度的控制。下面是常用读写定时器的相关函数:

重装载值ARR设置:__HAL_TIM_SetAutoreload

获取当前重装载值:__HAL_TIM_GetAutoreload

设置计数值:__HAL_TIM_SetCounter

获取当前计数值:__HAL_TIM_GetCounter

设置比较值:__HAL_TIM_SetCompare(&htimx,TIM_CHANNEL_2,比较值); 

获取当前比较值:__HAL_TIM_GetCompar

        如何让LCD或LED的亮度变亮呢,只需要不断增加PWM高电平持续时间,即让duty不断增加即可。我们设置重装载值为1000,根据图2所示,只需要让比较值在0-1000之间不断增加,那么duty就会不断增加实现逐渐变亮的效果。

        同理,让比较值从1000-0不断减少,就能实现灯不断变暗。需要注意的是,如果用一个循环让比较值不断增加或减少,那么最好对每一次循环做1ms的延时,这样肉眼才容易看到灯的变亮或变暗的效果。

(5)影子寄存器

        定时器中的影子寄存器是起到缓冲同步作用。影子寄存器在这里实际上才是在比较的时候起作用的,也就是为预装载寄存器做缓冲,目的是为了当预装载寄存器ARR中的值更新后,影子寄存器仍然保持原来的值(这是在ARPE=1的情况下),参与比较操作的是影子寄存器,所以这样不会影响到计数器的工作误差。它在更新事件发生之后(溢出中断)影子寄存器的值才从ARR寄存器那里获得更新,它也有同步的作用,使所有的相关寄存器数值都同时更新。 当ARPE为0时,影子寄存器是立即更新的,不等待更新事件 的发生。
        那为什么非得弄个影子寄存器呢?大家想想啊。因为定时器里面有这样一句话:“计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写”。在运行时就可以写自动装载寄存器,那么当你对自动装载寄存器进行写入新值时,如果你的没有影子寄存器,那么肯定的你的计数器在这次溢出就会不准确。而如果有了影子寄存器且ARPE=1,那就不会出现这种情况了。起到了一个缓冲作用。
        其实STM32里面的影子寄存器不止存在这个自动装载寄存器里面。还存在很多寄存器里面。比如预分频寄存器。就是因为这个机制的存在才使得STM32能够更安全稳定的工作。这不也给自己带来一种解决问题的机制吗。以后可以多考虑缓冲备份。

        上面关于影子寄存器的描述转自:STM32——影子寄存器_独自莫凭欄的博客-CSDN博客_stm32影子寄存器

(6)System tick timer

        这个定时器比较特殊,在Cube配置完后一般都会自动配置上这个定时器。这个定时器可以为整个HAL库提供时钟服务,比如生成代码中的delay函数就是基于这个定时器做的。默认情况下是被配置为1ms周期。在HalDelay中,首先获取System tick值,然后根据用户需要延时值,用一个while循环判断,当当前tick值大于了调用delal函数时的tick值+用户设置延时值,代表延时时间已到,退出while循环返回delay函数。

        Hal delay使用时,如果在中断服务函数中有调用到Hal delay函数,如果中断优先级高于delay函数使用到的System tick定时器中断优先级,那么就会导致delay函数中的while循环卡死从而导致整个系统卡死。

        因为调用delay函数的中断服务函数对应的中断的优先级高于delay使用到的System tick定时器的中断优先级,就会导致System tick定时器中断老是被抢占,从而System tick定时器时间无法增加,那么调用delay函数时,当前tick值就有可能永远小于调用delal函数时的tick值+用户设置延时值,从而导致delay函数的while循环无法退出导致系统看门狗超时导致系统崩溃。

        所以如果有中断ISR中直接或间接(其他HAL库函数中也有可能调用delay函数,ISR中又恰好调用了这个HAL库函数)调用了Hal delay函数,那么一定要保证这个中断的优先级要低于System tick定时器的中断优先级,否则容易出现卡死的问题。

        

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值