赞
踩
定时器(Timer)是微控制器中的一个重要模块,用于生成定时和延时信号,以及处理定时事件。在STM32系列微控制器中,定时器通常用于以下几个方面:
定时器功能: 定时器可以生成精确的定时信号,用于定时器中断、PWM(脉冲宽度调制)、计时等应用。它可以产生周期性的计数器溢出事件,也可以产生比较匹配和捕获事件。
PWM生成: 定时器可以用于产生PWM信号,用于控制电机速度、调光、音频产生等应用。
计时功能: 定时器可以用于测量时间间隔,计算时间延迟,或者用于定时测量外部事件的频率。
输入捕获和输出比较: 定时器可以用于捕获外部事件的时间戳,也可以用于与比较器进行比较,并产生相应的事件。
在STM32系列微控制器中,定时器模块非常灵活,通常包括多个独立的定时器单元,每个定时器单元都有自己的计数器、自动重载寄存器、预分频器、比较器等功能。此外,定时器模块通常还支持多种工作模式、计数模式和时钟源选择,可以满足各种不同的应用需求。
高级定时器
、通用定时器
、基本定时器
三种类型为何PSC+1?PSC为0则不分频即72MHz,为1 则分频率为36Mhz也即是72MHz/1+1 =36MHz,为2则分频为72Mhz/2+1=24MHz …最大值分频值为为65535 则72MHz/65535+1
默认情况下,定时器的输入时钟源(CK_INT)与定时器预分频器的输入时钟(CK_PSC)的时钟频率是相同的。
在STM32系列微控制器中,默认情况下,定时器的输入时钟源是微控制器的主时钟(一般是内部时钟源,比如HSI或者HSI16),而定时器预分频器的输入时钟则是来自于定时器输入时钟源。因此,如果没有对定时器的时钟源进行特别的配置,那么默认情况下,CK_INT和CK_PSC的时钟频率是相同的。
CK_CNT_OV:
时器计数器溢出频率,即定时器溢出的频率,通常以 Hz(赫兹)为单位。
CK_CNT:
定时器计数器时钟频率,即定时器计数器的输入时钟频率,通常以 Hz 为单位。
ARR :
自动重载寄存器的值,决定了定时器计数器溢出的周期。
CK_PSC:
定时器预分频器的输入时钟频率,通常也是定时器的输入时钟频率,在这里是72MHz,不需要我们处理。
PSC :
定时器预分频器的分频系数,决定了定时器计数器时钟频率。
这些参数的英文全称分别是:
- CK_CNT_OV: Timer Counter Overflow Frequency
- CK_CNT: Timer Counter Clock Frequency
- ARR: Auto-reload Register Value
- CK_PSC: Timer Prescaler Clock Frequency
- PSC: Prescaler Value
公式(重要 计算定时用):
输入时钟频率:CK_CNT = CK_PSC / (PSC + 1)
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
将CK_CNT= CK_PSC / (PSC + 1)带入得到下式= CK_PSC / (PSC + 1) / (ARR + 1)
需要一个一个配置,打通所在的线路配置即可。
计时1s如何设置计时?
1s=1/1Hz 即 t=1/f
f(频率)=计数器溢出频率; t(时间)=1/f; 计时1s等于t=1/1(CK_CNT_OV) 故而f=CK_CNT_OV=1 ;此时CK_PSC / (PSC + 1) * (ARR + 1)=1
又因为 CK_PSC 是预分频器的输入时钟频率,为72MHz,故而(PSC + 1) * (ARR + 1)=CK_PSC=72000000,凑PSC和ARR的值(但不要超过65535)使得式子成立即可。
这里PSC可以给7200-1 为什么-1?因为(PSC + 1) * (ARR + 1)式子PSC+1了,为了凑整数好计算取PSC=7200(注意:不要超过65535)。ARR+1=72000000/(PSC+1)=10000;
以分频或者不分频后的的频率进入计数到ARR,到达ARR后,清零PSC,同时进入中断函数执行中断函数
如此 1ms ,1 us的配置也就通过计算可以计算出了。
故而配置时基单元就可以这样写:
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//配置为向上计数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//这里随便配置个其中的参数
TIM_TimeBaseInitStructure.TIM_Period=10000;//Auto-Reload,重装值 ARR 其值不得超过65536
TIM_TimeBaseInitStructure.TIM_Prescaler=7200;//预分频系数 根据上方公式计算 PSC,其值不得超过65536
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//这个是高级定时器才会用到的,通用随便给个直接给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
使用定时器,每1s进入一次定时器中断函数,完成Num++操作,并将它显示到OLED显示屏幕上。
Timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num; //Extern 声明变量在其他文件里(在main.c定义了),让编译器自己去找,这里引用的是main.c定义过的变量
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//TIM2,通用计时器使能
TIM_InternalClockConfig(TIM2);//配置为内部时钟模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//配置为向上计数
//@72MHz 1s
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//这里随便配置个其中的参数
TIM_TimeBaseInitStructure.TIM_Period=10000;//Auto-Reload,重装值 ARR 其值不得超过65536
TIM_TimeBaseInitStructure.TIM_Prescaler=7200;//预分频系数PSC,其值不得超过65536
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//这个是高级定时器才会用到的,通用随便给个直接给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能定时器中断
//下面这行程序后面还有中断使能开关,中断标志置1了,但后面的中断使能没开,也进不了中断,所以在使能开关前面清除就能达到目的
TIM_ClearFlag( TIM2,TIM_FLAG_Update);//清除标志位,因为TIM_TimeBaseInit函数里有这样一句话
/* Generate an update event to reload the Prescaler and the Repetition counter
values immediately->立即 */ //就会导致刚初始化就进入中断,导致下方Num不是从0开始,而是从1开始。
// TIMx->EGR = TIM_PSCReloadMode_Immediate; 因此需要清除标志位。
//NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//配置为TIM2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能NVIC
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init(& NVIC_InitStructure);
//一定记得启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
Num++;
Num%=100;//100重新计时
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清楚标志位
}
}
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Timer.h"
uint16_t Num;//定义Num,在Timer引用
int main()
{
OLED_Init();
Timer_Init();
while(1)
{
OLED_ShowNum(1,1,Num,4);
}
}
现象:每1s加一次。一直加到99,再加清零 继续从0开始
对射式红外,遮挡一次cnt加1次,满十次cnt清零 ,同时Num+1。
因为使用的是ETR外部时钟模式2,故而接线图Do->PA0(PA0有复用TIM2_ETR功能)
Timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num; //Extern 声明变量在其他文件里(在main.c定义了),让编译器自己去找,这里引用的是main.c定义过的变量
void Timer_Init(void)
{
//TIM2通用定时器 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//TIM2,通用计时器使能
//GPIO时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//外部时钟模式2使能
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_Inverted,0xff);//外部时钟模式2,下降沿触发,滤波
//GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
//模式看手册的 GPIO章节的8.1.11 外设的GPIO配置中的TIM ETR
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入,手册给的浮空输入,但不建议,因为电平会跳个不停
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//PA0端口,因为PA0有TIM2_ETR复用功能
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//时基单元配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//配置为向上计数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//这里随便配置个其中的参数
TIM_TimeBaseInitStructure.TIM_Period=10-1;//Auto-Reload,重装值 ARR 其值不得超过65536
TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;//预分频系数PSC,其值不得超过65536
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//这个是高级定时器才会用到的,通用随便给个直接给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能定时器中断
//这个程序后面还有中断使能开关,中断标志置1了,但后面的中断使能没开,也进不了中断,所以在使能开关前面清除就能达到目的
TIM_ClearFlag( TIM2,TIM_FLAG_Update);//清除标志位,因为TIM_TimeBaseInit函数里有这样一句话
/* Generate an update event to reload the Prescaler and the Repetition counter
values immediately->立即 */ //就会导致刚初始化就进入中断,导致下方Num不是从0开始,而是从1开始。
// TIMx->EGR = TIM_PSCReloadMode_Immediate; 因此需要清除标志位。
//NVIC配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//配置为TIM2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能NVIC
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init(& NVIC_InitStructure);
//一定记得启动定时器
TIM_Cmd(TIM2,ENABLE);
}
/**
* @brief 得到TIM2的计数值
* @param 无
* @retval 计数器的值
*/
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
//模板
/*
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清楚标志位
}
}
*/
解释一下Period与Prescaler的值的意义(以红外对射为例)
上述情况:Period为10-1(重传值9 为10时清零) ,Prescaler为1-1 不分频(频率),假如改为2-1,频率就会比1-1变慢2倍,此时红外遮挡两次CNT才增加一次。
表示当红外对射遮挡一次,Timer_GetCounter加1
当Timer_GetCounter到9时,再加1,就进入TIM更新事件中断,同时Timer_GetCounter清除为0
拓展:Period为M-1 ,Prescaler为N-1
表示当红外对射遮挡N次,Timer_GetCounter加1
个人总结复习使用,如果对你也有帮助,那可真是小舞的荣幸。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。