stm32定时器(TIM)学习笔记
制作不易,求个点赞!
定时器本质上就是一个计数器:
现在小明与小红在一起玩游戏,小明每隔一秒从1数到100,小明在每数两个数之后小红会数一次,但是小红数到5之后又会从零开始数。
例如,小明数从1数到12后,小红会从1数到5之后归零从1开始数。
在这个游戏中,小明就是输入的时钟,小红就是计数器。
Part.1 TIM定时中断
1.时基单元
- 计数器CNT :当有时钟源输入时,边开始计数。
- 预分频器PSC :对于输入的时钟源,选择是每隔几个数计一次数。
- 自动重装寄存器ARR: 当计数器计到最大值时,清零计数器。
2.定时器类型
stm32F103C8T6仅仅有TIM 1~4。
3.计数模式
对于基本计时器,仅仅只有向上计数这一种计数模式。
而通用定时器与高级定时器有向上计数、向下计数、中央对齐的计数模式。
4.
5.代码实现
5.1 TimerInit
Step 1. 因为Tim2在APB1上,所以开启APB1的外设时钟:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_Tim2,ENABLE);
Step 2.选择输入的时钟源,这里有这么几个函数可供选择:
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
//选择内部时钟源
void TIM ITRxExternalclockCoffig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//选择其他定时器作为时钟源
void TIM_TIxExternalclockConfig(TIM TypeDef* TIMx, uint16_t TIM TIxExternalCLKSource
uint16_t TIM ICPolarity, uint16 t ICFilter);
外部引脚时钟
即同过检测引脚上的电平然后作为时钟源
TIM TIxExternalCLKSource 选择具体的某个引脚
uint16_t TIM ICPolarity, uint16 t ICFilter极性与滤波器
void TIM ETRClockModelConfig(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler, uint16 t TIM E
uint16 t ExtTRGFilter);
//ExtTRGPrescaler再做一次分频率
void TIM ETRClockMode2Config(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler,
uint16 t TIM ExtTRGPolarity, uint16 t ExtTRGFilter);
//和CLOCK1一样,可以互换
void TIM ETRConfig(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler, uint16 t TIM ExtTRGPolar
uint16 t ExtTRGFilter);
//单独配置Prescaler之类的东西
由于我们这里选择使用内部时钟,所以直接输入如下代码即可
TIM_InternalClockConfig(TIM2);
Step 3.时基单元的配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//定义初始化结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// 时钟分分频,可以滤除波动的干扰,可以取值1,2,4
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 配置计数器的模式。有Up,Down,CenterAligned3种。这选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
//周期的配置
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
// 预分频的配置
/*
假使我们要有一个一秒定时器,那么要怎么配置呢?
这里可以使用如下公式:
多少秒计数一次 = 时钟源/(预分频+1)(自动重装值+1)
这里设置为1秒一次,那么 1 = 72*10^6/10000/7200
*/
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
//高级计数器重复计数功能,实现多个周期后才进行一次中断的操作
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
5.2 TIM_ITconfig
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
使用该命令可以开启定时器中断
5.3 NVIC的配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//选择分组2,这里不懂的可以等过几天作者写出来关于中断的笔记,不过感觉大家伙应该都不是很能从作者这里收获到知识,这样吧,点赞破50就更新NVIC
NVIC_InitTypeDef NVIC_InitStructure;
//定义NVIC结构体
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择中断是哪一个中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//配置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//配置子优先级
NVIC_Init(&NVIC_InitStructure);
//定义结构体
5.4 TIM_Cmd
TIM_Cmd(TIM2, ENABLE);
打开TIM
5.5 配置中断函数
每个中断函数都不能由自己定义,都是由stm32自己定义的。如果想要编写中断函数,我们需要做的是查询到这个函数的名称,然后继续写入这个函数。
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
6.全代码:
Timer.c文件:
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
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_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
Tim.h文件
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
main.c文件
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
OLED_ShowNum(1, 5, Num, 5);
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
这里需要注意,我们所引用的Num变量在两个文件中出现过,这种情况要么是在另外一个文件中加上extern int num,要么是把中断函数移动到main.c中。
此外,在初始化TIM后,程序会立即启动TIM的中断一次,为了防止这一现象,应该加上clearflag这句话。