上次讲了滴答定时器,这个我们来说下stm32 的其他定时器,分别有三种, 基本定时器,通用定时器和 高级定时器,在STM32F1 的定时器中分别由2 个基本定时器(TIM6、 TIM7) 、 4 个通用定时器(TIM2-TIM5) 和 2 个高级定时器(TIM1、 TIM8) 组成(这些资料也有),这接进入主题吧,这次我们讲的是stm32 的通用定时器(从简单的入门).
通用定时器包含一个 16 位自动重载计数器(CNT) ,就是可以计数2^16次,还有其计数的频率可以由分频系数 PSC 来控制,PSC的取值范围为1~65535,定时器的能实现什么功能就不说了,也找的到,这次通过定时器来控制led亮和灭。
开始之前我们要添加stm32f10x_tim.c 库文件,定时器器的所需要的配置函数都在这个库里面。
在stm中很多操作都是要先时钟使能,以通用定时器TIM3为例子,首先是通用定时器是挂载在APB1总线上,所以可以使用 APB1 总线时钟使能函数来使能 TIM3, 调用的库函数如下:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能 TIM3钟
其次就是初始化时钟(配置时钟)调用的函数是
void TIM_TimeBaseInit(TIM_TypeDef *TIMx,TIM_TimeBaseInitTypeDef *
TIM_TimeBaseInitStruct);
TIM_TypeDef *TIMx,指的是要配置那个时钟,后面一个是结构体的指针,结构体里面的内容就是要我们来设置:
{
uint16_t TIM_Prescaler; //定时器预分频器
uint16_t TIM_CounterMode; //计数模式
uint32_t TIM_Period; //定时器周期
uint16_t TIM_ClockDivision; //时钟分频
uint8_t TIM_RepetitionCounter; //重复计数器
} TIM_TimeBaseInitTypeDef;
结构体内含有 5 个成员变量, 前 4 个在通用定时器中会使用到, 最后一个是在高级定时器中才会用到。 分析如下:
TIM_Prescaler:就是我们前面所说的分频系数就是在这里设置,定时器的预分频器系数, 时钟源经过该预分频器后输出的才是定时器时钟, 设置值范围: 0-65535, 分频系数由于是除数, 分母不能为 0,所以会自动加 1, 最后实现 1-65536 分频;
TIM_CounterMode:计数模式的选择,可以选择为向上、 向下、中心对齐计数方式。 比较常用的是向上计数模式(TIM_CounterMode_Up) 和向下计数模式(TIM_CounterMode_Down),向上就是 从0 开始计数,向下反之;
TIM_Period: 我们设定的值(周期数),如我们要计数要15000,那么就赋值为15000,注意的是装载值得取值是0~2^16,
TIM_ClockDivision: 时钟分频因子, 设置定时器时钟 CK_INT 频率与数字
滤波器采样时钟频率分频比。(还没搞懂这个,也没用的)
TIM_RepetitionCounter: 重复计数器, 通过此参数可以非常简单的控制
PWM 输出个数。 此成员只针对于高级定时器配置, 基本定时器与通用定时器不用
设置
定时器配置好后,他是如何工作的呢,一开始我以为是在主函数里一直计数的吗,还是说他可以无时无刻的在进行(我理解是多线程,就是同时开工),后来才想明白是定时器是一直运行着的,只要使能并且配置好之后,那定时器计数到了指定的值后,如何来系统知道呢,这时刚就说说下定时器的中断了,当定时器完成一个周期后,他会更新装载值,他会产生一个中断,有了中断,那我们就可以在开始中断时做些什么,当然定时器产生中断的信号有很多,如更新中断 TIM_IT_Update(一个周期)、 触发中断 TIM_IT_Trigger、 输入捕获中断,在这里我们用到的是更新中断,现在我们说下定时器中断配置的函数:
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT,
FunctionalState NewState);
第一个参数是定时器的选择,第二个是中断的信号选择,第三个是使能中断类型
然后需要使能中断时钟
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
既然有了中断,那就应该要有中断优先级呀,就是NVIC的配置,我的另外一篇文章有说过(https://blog.csdn.net/rouse2617/article/details/104128437)
前面几个步骤已经将定时器配置好, 但还不能正常使用, 只有开启定时器了才能让它正常工作, 开启定时器的库函数如下:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
第一个参数是用来选择定时器。
第二个参数是用来使能或者失能定时器, 也就是开启或者关闭定时器功能。
同样可以选择 ENABLE 和 DISABLE。
在接下来就是编写中断函数,定时器的中断函数名为
void TIMX_IRQHandler()
{
}
x为数字1~8
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
此函数功能是判断 TIMx 的中断类型 TIM_IT 是否产生中断, 例如我们要判断
TIM3 的更新(溢出) 中断是否产生, 可以调用此函数:
if(TIM_GetITStatus(TIM3,TIM_IT_Update))
{
...//执行 TIM3 更新中断内控制
}
如果产生更新中断, 那么调用 TIM_GetITStatus 函数后返回值为 1, 就会进
入到 if 函数内执行中断控制功能程序。 否则就不会进入中断处理程序。
在编写定时器中断服务函数时, 最后都会调用一个清除中断标志位的函数,
如下:
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
里面的两个参数功能和前面读取定时器中断状态标识位函数一样, 例如我们
要清除 TIM3的更新中断标志位, 调用函数如下:
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
以上就是配置通用定时器的步骤了
void time_init(u16 pre,u16 psc)
{
TIM_TimeBaseInitTypeDef t_str;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
t_str.TIM_Period=pre;
t_str.TIM_Prescaler=psc;
t_str.TIM_ClockDivision=TIM_CKD_DIV1;
t_str.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3,&t_str);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=(ENABLE);
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3,ENABLE);
}```
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==1)
{
led=~led; //你想要做的是可以在这里写比如我想一个周期翻转一次led
//led是我我宏定义好的
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
最后就是主函数调用啦,应该知道,main里面要加个while循环o