一、定时器简介
STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
1.1 基本定时器功能
①时钟源
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
②计数器时钟
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
③计数器
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
④自动重装载寄存器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
1.2 基本定时器时基
基本定时器的核心是时基,不仅基本定时器有,通用定时器和高级定时器也有。基本定时器与通用定时器的时基有3个(TIMx_CNT、TIMx_PSC、TIMx_ARR)。高级定时器的时基有4个(TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR)。
二、基本定时器使用流程
2.1 NVIC 设置
/**
@brief NVIC初始化(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为 0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2.2 定时器中断配置
/**
@brief 定时器中断配置(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*
可以从上图看出基本定时器和通用定时器使用APB1总线,
高级定时器使用APB2总线。
*/
// 开启定时器时钟,即内部时钟 CK_INT=72M
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*
预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
计数值 TIM_Period = 1000,
则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
即每1ms产生一次中断。
*/
// 自动重装载寄存器周的值(计数值)
TIM_TimeBaseStructure.TIM_Period = 1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断
// 时钟预分频数为 71,
// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
TIM_TimeBaseStructure.TIM_Prescaler = 71;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/*
完成时基设置
*/
// 初始化定时器
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/*
为了避免在设置时进入中断,这里需要清除中断标志位。
如果是向上计数模式(基本定时器采用向上计数),
则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
清除向上溢出中断标志。
*/
// 清除计数器中断标志位
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
// 使能计数器
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 开启计数器
TIM_Cmd(TIM6, ENABLE);
// 暂时关闭定时器的时钟,等待使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}
2.3 中断服务程序
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
// 1ms发生一次中断,time 记录中断次数
uint16_t time;
void BASIC_TIM_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
time++;
TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
}
}
三、设计实例
board_timer.h
#ifndef BOARD_TIMER_H
#define BOARD_TIMER_H
/**************************************************
* INCLUDES
*/
#include “stm32f10x.h”
/**************************************************
* DEFINITIONS
*/
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
/**************************************************
* API FUNCTIONS
*/
void BASIC_TIM_Config(void);
void BASIC_TIM_NVIC_Config(void);
#endif
board_timer.c
#include “board_timer.h”
/**************************************************
* GLOBAL VALUE
*/
uint16_t time;
/**************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief NVIC初始化(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为 0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
@brief 定时器中断配置(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*
可以从上图看出基本定时器和通用定时器使用APB1总线,
高级定时器使用APB2总线。
*/
// 开启定时器时钟,即内部时钟 CK_INT=72M
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*
预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
计数值 TIM_Period = 1000,
则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
即每1ms产生一次中断。
*/
// 自动重装载寄存器周的值(计数值)
TIM_TimeBaseStructure.TIM_Period = 1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断
// 时钟预分频数为 71,
// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
TIM_TimeBaseStructure.TIM_Prescaler = 71;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/*
完成时基设置
*/
// 初始化定时器
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/*
为了避免在设置时进入中断,这里需要清除中断标志位。
如果是向上计数模式(基本定时器采用向上计数),
则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
清除向上溢出中断标志。
*/
// 清除计数器中断标志位
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
// 使能计数器
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 开启计数器
TIM_Cmd(TIM6, ENABLE);
// 暂时关闭定时器的时钟,等待使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}
main.c
#include “board_timer.h”
extern uint16_t time;
int main()
{
/* 基本定时器 TIMx,x[6,7] 定时配置 */
BASIC_TIM_Config();
/* 配置基本定时器 TIMx,x[6,7]的中断优先级 */
BASIC_TIM_NVIC_Config();
/* 基本定时器 TIMx,x[6,7] 重新开时钟,开始计时 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
while(1)
{
if(time == 1000)
{
/* 1000 * 1 ms = 1s 时间到 */
time = 0;
/* LED1 取反 */
static uint8_t tmp = 0;
/*LED1 反转*/
if(tmp == 0)
{
// 该函数在GPIO输出章节中
UseLibSetOutput(GPIOB, GPIO_Pin_0,0);
tmp = 1;
}
else
{
// 该函数在GPIO输出章节中
UseLibSetOutput(GPIOB, GPIO_Pin_0,1);
tmp = 0;
}
}
}
return 0;
}
一、定时器简介
STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
1.1 基本定时器功能
①时钟源
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
②计数器时钟
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
③计数器
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
④自动重装载寄存器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
1.2 基本定时器时基
基本定时器的核心是时基,不仅基本定时器有,通用定时器和高级定时器也有。基本定时器与通用定时器的时基有3个(TIMx_CNT、TIMx_PSC、TIMx_ARR)。高级定时器的时基有4个(TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR)。
二、基本定时器使用流程
2.1 NVIC 设置
/**
@brief NVIC初始化(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为 0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
2.2 定时器中断配置
/**
@brief 定时器中断配置(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*
可以从上图看出基本定时器和通用定时器使用APB1总线,
高级定时器使用APB2总线。
*/
// 开启定时器时钟,即内部时钟 CK_INT=72M
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*
预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
计数值 TIM_Period = 1000,
则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
即每1ms产生一次中断。
*/
// 自动重装载寄存器周的值(计数值)
TIM_TimeBaseStructure.TIM_Period = 1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断
// 时钟预分频数为 71,
// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
TIM_TimeBaseStructure.TIM_Prescaler = 71;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/*
完成时基设置
*/
// 初始化定时器
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/*
为了避免在设置时进入中断,这里需要清除中断标志位。
如果是向上计数模式(基本定时器采用向上计数),
则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
清除向上溢出中断标志。
*/
// 清除计数器中断标志位
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
// 使能计数器
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 开启计数器
TIM_Cmd(TIM6, ENABLE);
// 暂时关闭定时器的时钟,等待使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}
2.3 中断服务程序
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
// 1ms发生一次中断,time 记录中断次数
uint16_t time;
void BASIC_TIM_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
time++;
TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
}
}
三、设计实例
board_timer.h
#ifndef BOARD_TIMER_H
#define BOARD_TIMER_H
/**************************************************
* INCLUDES
*/
#include “stm32f10x.h”
/**************************************************
* DEFINITIONS
*/
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
/**************************************************
* API FUNCTIONS
*/
void BASIC_TIM_Config(void);
void BASIC_TIM_NVIC_Config(void);
#endif
board_timer.c
#include “board_timer.h”
/**************************************************
* GLOBAL VALUE
*/
uint16_t time;
/**************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief NVIC初始化(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为 0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
@brief 定时器中断配置(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*
可以从上图看出基本定时器和通用定时器使用APB1总线,
高级定时器使用APB2总线。
*/
// 开启定时器时钟,即内部时钟 CK_INT=72M
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*
预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
计数值 TIM_Period = 1000,
则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
即每1ms产生一次中断。
*/
// 自动重装载寄存器周的值(计数值)
TIM_TimeBaseStructure.TIM_Period = 1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断
// 时钟预分频数为 71,
// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
TIM_TimeBaseStructure.TIM_Prescaler = 71;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/*
完成时基设置
*/
// 初始化定时器
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/*
为了避免在设置时进入中断,这里需要清除中断标志位。
如果是向上计数模式(基本定时器采用向上计数),
则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
清除向上溢出中断标志。
*/
// 清除计数器中断标志位
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
// 使能计数器
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 开启计数器
TIM_Cmd(TIM6, ENABLE);
// 暂时关闭定时器的时钟,等待使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}
main.c
#include “board_timer.h”
extern uint16_t time;
int main()
{
/* 基本定时器 TIMx,x[6,7] 定时配置 */
BASIC_TIM_Config();
/* 配置基本定时器 TIMx,x[6,7]的中断优先级 */
BASIC_TIM_NVIC_Config();
/* 基本定时器 TIMx,x[6,7] 重新开时钟,开始计时 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
while(1)
{
if(time == 1000)
{
/* 1000 * 1 ms = 1s 时间到 */
time = 0;
/* LED1 取反 */
static uint8_t tmp = 0;
/*LED1 反转*/
if(tmp == 0)
{
// 该函数在GPIO输出章节中
UseLibSetOutput(GPIOB, GPIO_Pin_0,0);
tmp = 1;
}
else
{
// 该函数在GPIO输出章节中
UseLibSetOutput(GPIOB, GPIO_Pin_0,1);
tmp = 0;
}
}
}
return 0;
}
举报