基本定时器分为三个部分
(1)时钟源:时钟源来自RCC的TIMXCLK,就是内部时钟(CK_INT)直接经过控制器传给时基单元充当CK_PSC
(2)控制器:控制定时器的复位、使能、计数、DAC触发
(3)时基单元(最基本的计数计时电路):
a、预分频器:分频、得到计时器的时钟,即CNT计数1次所需要的时间,预分频器时16位的寄存器、所以可分频为1-65536
b、计数器:用来计数、基本定时器的CNT计数器只能向上计数
c、自动装载寄存器ARR:CNT加到ARR的值之后,会产生一个事件或中断或DMA请求,中断用得比较多
预分频器
基本定时器只能选择内部时钟,故预分频器直接输入端,即内部时钟CK_INT(一般为72MHz)
预分频器(PSC)写0(1分频或者不分频):则SK_CNT输出72MHz,
写1(2分频):输出36MHz,写2(3分频):输出24MHz
故实际分频系数=预分配系数+1(分频器是16位,故最大位65525)
计数器
对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值就+1,计数器是16位的,故当中的值可为0~65535,65535后若再加,则又从0开始。
计数器计数到目标值后,就会产生中断
时基单元模块箭头
向上折线箭头:会产生中断信号,像计数值等于自动重装值时产生的中断,一般叫做更新中断,更新中断后会通向NVIC,我们再配置好NVIC的定时器通道,那定时器的更新中断就能得到CPU的响应
向下的箭头:代表会产生一个事件,称为"更新事件",更新事件不会触发中断,但可以触发内部其他电路的工作
简单说,定时器只需要知道。定时频率=72M/(预分频器+1)/(ARR+1)
代码演示
#include "stm32f10x.h" // 包含STM32固件库的头文件
void Timer_Init(void)
{
// 第一步:RCC开启时钟
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; // ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // PSC预分频器的值
// 预分频对72M进行7200分频,得到10K的计数频率,在10k的频率下,计数10k个数,就是1s
// 可以预分频给少点,自动重装给多一点,这样就是以一个比较高的频率计比较多的数,反之则反
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器的值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 第四步:配置输出中断控制,允许更新中断输出到NVIC
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除向上溢出中断标志
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能更新中断
// 第五步:配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 选择分组2
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 中断通道,定时器2的通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 设置抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 设置响应优先级为1
NVIC_Init(&NVIC_InitStructure);
// 第六步:启动定时器
TIM_Cmd(TIM2, ENABLE);
}
#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);
OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);//计数器的值
}
}
//对于定时中断而言,中断函数就是为别的文件服务的,故可以移到别的文件中使用
void TIM2_IRQHandler(void)
{
//获取中断标志位
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num ++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}