1、时基单元
STM32 微控制器中的时基单元(Time Base Unit)是用于生成定时事件的硬件模块,广泛应用于定时器、计数器、PWM(脉宽调制)生成、输入捕获和输出比较等功能。时基单元结构如下图所示,其实也就是基本定时器的硬件结构。
时基单元包括以下组件:内部时钟,预分频器(Prescaler, PSC),计数器(Counter, CNT)和自动重装载寄存器(Auto-Reload Register, ARR)。
- 内部时钟
对于基本定时器,只有内部时钟源,STM32F10X芯片默认的内部时钟源频率为72MHz。 - 预分频器
预分频器用于将定时器的输入时钟频率降低到一个合适的频率范围,通过TIMx_PSC 寄存器配置。经过预分频器分频之后,时钟频率变为原来的1/(1+PSC)。其中,PSC是TIMx_PSC 寄存器的值,TIMx_PSC是一个16位寄存器,最大值可设置为65535,这也就意味着时钟频率最多可以被分成原来的65536倍。
分频后的时钟信号传输给计数器。 - 计数器
计数器是定时器的核心部分,它根据预分频后的时钟频率进行计数,可通过 TIMx_CNT 寄存器读取和设置计数器的当前值。计数器的值可以配置为向上、向下或中心对齐模式进行计数。TIMx_CNT也是16位的寄存器,最大值可设置为65535。 - 自动重装载寄存器
自动重装载寄存器用于设置计数器的最大值,通过 TIMx_ARR 寄存器配置。当计数器的值达到 ARR 的值时,计数器会根据配置的计数模式进行重置或翻转,从而产生定时事件(定时器中断、PWM输出等)。TIMx_ARR也是16位寄存器,最大值也为63335。
时基单元工作流程总结:内部时钟→预分频器→计数器→自动重装载寄存器→触发定时中断。时钟信号进入到时基单元后,经过分频器分频,以及计数器计数到自动重装载寄存器的值才触发定时事件,于是定时频率为:
其中,PSC和ARR分别为预分频器和自动重装载寄存器的值。
我们在配置时基单元时,不需要一个个手动去配置各个组件的寄存器。SMT32库函数已经帮我们集成好了,这些寄存器全部被放在了一个结构体里:TIM_TimeBaseInitTypeDef。因此,配置时基单元,只需要通过TIM_TimeBaseInit()函数对TIM_TimeBaseInitTypeDef进行配置即可。
2、定时器分类
前边介绍的是基本定时器(TIM6和TIM7),基本定时器拥有定时中断和主模式触发DAC的功能。除了基本定时器之外,还有通用定时器(TIM2~TIM5)和高级定时器(TIM1和TIM8)。
相比基本定时器,通用定时器拥有基本定时器的全部功能,同时还具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能。对于通用定时器,前边讲到的基本定时器的四个组件之一的内部时钟应该替换为内外时钟源选择电路。具体配置时,通过调用TIM_InternalClockConfig()函数进行配置。这一步要放在时基单元的配置之前,因为内外时钟源的选择是在时基单元之前的。
高级定时器拥有通用定时器的全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能。高级定时器博主没用过,故不讲。
注:此图来自江科大stm32入门教程,并非原创
3、从定时器中断配置看外设配置的逻辑
我之前也看过不少有关外设配置的帖子,但都是针对某个具体外设的,而且讲得都比较乱,没有系统地总结外设配置的通用逻辑。下边以定时器中断配置为例,并结合之前讲过的外部中断的配置,讲讲我自己的一套方法和逻辑。
stm32外设配置的总的逻辑和流程是:
外设时钟使能→GPIO口初始化配置→外设初始化配置→中断源配置→NVIC配置→外设使能。
在实际进行外设配置时,可以按照这个流程走一遍,思考每个步骤是否需要,不是每个步骤都要进行。
定时器中断配置:
- 外设时钟使能
定时器中断需要用到时钟使能吗?当然需要,所以需要进行定时器时钟使能。这一步同时还要注意,如果下一步需要用到GPIO口的话,这里还需要进行GPIO口初始化。定时器中断不需要用到GPIO口,所以不需要对GPIO口进行时钟使能。
对比外部中断的配置,外部中断需要对GPIO和AFIO进行时钟使能,而不需要对EXTI进行时钟使能。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- GPIO口初始化
如前所述,定时器中断不需要用到GPIO口,所以跳过这一步。
对比外部中断的配置,外部中断需要进行GPIO口初始化。 - 外设初始化配置
对于基本定时器,只需要对时基单元进行配置即可:
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
如果是通用定时器,还需要在时基单元之前进行内外时钟源选择:
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
对比外部中断,外部中断有一点比较特殊,外部中断有外部中断线的概念,需要在进行外设配置之前,先进行外部中断线配置(GPIO_EXTILineConfig())。
- 中断源配置
在进行NVIC配置之前,需要进行中断源配置。在具体配置NVIC之前,当然需要进行中断源配置。中断源的观念和逻辑已经在前边的帖子里讲过,这里不再赘述。
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
- NVIC配置
请移步我之前写的帖子,这里不再赘述。 - 外设使能
TIM_Cmd(TIM2, ENABLE);
最后的中断源配置→NVIC配置→外设使能这三步,有两点需要注意:
- 如果不需要用到中断,比如PWM,那么不需要进行中断源配置和NVIC配置。
- 外部中断比较特殊,外部中断的中断源选择和外设使能都被集中到了外设配置当中,也就是集成到了EXTI_InitTypeDef结构体当中。调用EXTI_Init();即可,不需要单独配置中断源和进行外设。