时钟选择
计数器时钟可以由以下时钟源提供:
①. 内部时钟(CK_INT),在TIMx_SMCR寄存器中默认就是内部时钟
②. 外部时钟模式1:外部输入脚(TIx)
③. 外部时钟模式2:外部触发输入(ETR)
④. 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1来作为另一个定时器Timer2的预分频器。
内部时钟选择
![](https://gitee.com/matjenin/hexo_images/raw/master/images2/定时器中断_内部时钟选择.png)
时钟计算方法
除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍
默认调用SystemInit函数情况下:
SYSCLK=72M
AHB时钟=72M
APB1时钟=36M
所以APB1的分频系数是2,所以通用定时器时钟CK_INT=2*36M=72M
计数器模式
通用定时器可以向上计数、向下计数、向上向下双向计数模式。
向上计数模式
计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
当时钟分频=1时(CK_PSC=CK_CNT):
![](https://gitee.com/matjenin/hexo_images/raw/master/images2/定时器中断_向下计数.png)
向下计数模式
计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
当时钟分频=1时(CK_PSC=CK_CNT):
![](https://gitee.com/matjenin/hexo_images/raw/master/images2/定时器中断_向上计数.png)
中央对齐模式(向上/向下计数)
计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0重新计数。
当时钟分频=1(CK_PSC=CK_CNT),ARR=6时:
![](https://gitee.com/matjenin/hexo_images/raw/master/images2/定时器中断_中央计数.png)
定时器中断实验相关寄存器
计数器当前值寄存器 CNT
预分频寄存器 TIMx_PSC
自动重装载寄存器 TIMx_ARR
该寄存器在物理上实际对应着2个寄存器,一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器被称为影子寄存器。事实上真正起作用的是影子寄存器。根据TIMx_CR1寄存器中APRE位的位置:ARPE=1时,预装载寄存器的内容可以随时传送到影子寄存器。此时两者是连通的;而ARPE=0时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。
控制寄存器1 TIMx_CR1
DMA中断使能寄存器 TIMx_DIER
常用库函数
定时器参数初始化
void TIM_TimeBaseInit(TIM_TypeDef TIMx, TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct);
typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMOde;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
示例:
TIM_TimeBaseInitTypeDef
TIM_TimeBaseStucture.TIM_Period=4999;
TIM_TimeBaseStucture.TIM_Prescaler=7199;
TIM_TimeBaseStucture.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStucture.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
定时器使能函数
操作控制寄存器1 TIMx_CR1。
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
定时器中断使能函数
操作DMA中断使能寄存器 TIMx_DIER。
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
状态标志位获取和清除
void TIM_ClearFlag(TIM_TypeDef TIMx, uint16_t TIM_FLAG);
void TIM_ClearITPendingBit(TIM_TypeDef TIMx, uint16_t TIM_IT);
FlagStatus TIM_GetFlagStatus(TIM_TypeDef TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef TIMx, uint16_t TIM_IT);
定时器中断实现步骤
①. 使能定时器时钟。
RCC_APB1PeriphClockCmd();
②. 初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
③. 开启定时器中断,配置NVIC。
void TIM_ITConfig();
NVIC_Init();
④. 使能定时器。
TIM_Cmd();
⑤. 编写中断服务函数。
TIMx_IRQHandler();
定时器中断实验
led.c和led.h直接从跑马灯实验中移植即可。
by库函数
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr, u16 psc);
#endif
timer.c
#include "timer.h"
#include "sys.h"
#include "led.h"
void TIM3_Int_Init(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSturc;
NVIC_InitTypeDef NVIC_InitStucture;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitSturc.TIM_Period=arr;//重装载值
TIM_TimeBaseInitSturc.TIM_Prescaler=psc;//预分频系数
TIM_TimeBaseInitSturc.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInitSturc.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频因子为1
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitSturc);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能TIM3的更新中断
//中断优先级NVIC设置
NVIC_InitStucture.NVIC_IRQChannel=TIM3_IRQn;//TIM3中断通道
NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
NVIC_InitStucture.NVIC_IRQChannelSubPriority=2;//响应优先级2
NVIC_InitStucture.NVIC_IRQChannelCmd=ENABLE;//中断使能
NVIC_Init(&NVIC_InitStucture);
TIM_Cmd(TIM3,ENABLE);//TIM3定时器初始化
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)//通过中断标志位判断是否发生了定时器中断
{
LED1=!LED1;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除中断标志位
}
}
main.c
#include "stm32f10x.h"
#include "timer.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC中断优先级分组
delay_init();//延时函数初始化
LED_Init();//LED初始化函数
TIM3_Int_Init(4999,7199);//定时器500ms溢出时间
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
by寄存器
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr, u16 psc);
#endif
timer.c
#include "timer.h"
#include "led.h"
#include "sys.h"
void TIM3_Int_Init(u16 arr, u16 psc)
{
RCC->APB1ENR|=1<<1;//TIM3定时器时钟使能
TIM3->ARR=arr;//TIM3自动重装载值
TIM3->PSC=psc;//设置预分频值
TIM3->DIER|=1<<0;//允许TIM3更新中断
MY_NVIC_Init(2,2,TIM3_IRQn,2);//TIM3 NVIC中断设置
TIM3->CR1&=0<<4;//TIM3向上计数
TIM3->CR1|=1<<0;//使能TIM3计数器
}
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)//检测是否发生溢出中断
{
LED1=!LED1;
}
TIM3->SR&=~(1<<0);//清除中断标志位
}
main.c
#include "sys.h"
#include "delay.h"
#include "timer.h"
#include "led.h"
int main(void)
{
Stm32_Clock_Init(9);//系统时钟设置
delay_init(72);//延时初始化
LED_Init();//LED初始化函数
TIM3_Int_Init(4999,7199);//溢出时间500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}