跳到主要内容

STM32学习笔记(2)通用计时器

· 阅读需 5 分钟
Skyone
科技爱好者

本文章适用于STM32F103RBT6,其他型号可能有所不同

开发模板见我的GitHub仓库skyone-wzw@STM32F103RBT6-template

如果你觉得这篇文章有帮助,请star仓库或评论回复吧~

如果文章有任何错误(包括错别字)可以在这里提交Issue或邮件联系我

使能定时器时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIMx, ENABLE);

使能定时器时钟,对于中容量STM32F103,TIMx可以是:

  • TIM1:高级定时器
  • TIM2~TIM4通用定时器

例如

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 使能通用定时器3

初始化定时器

TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitTypeDef)

定义计时器的重装载值、预分频系数、计数方式等

对于TIM_TimeBaseInitTypeDef结构体

typedef struct {
uint16_t TIM_Prescaler;
// 预分频系数,下面有解释
uint16_t TIM_CounterMode;
/* 计数方式,可以是
* 向上计数: `TIM_CounterMode_Up`
* 向下计数: `TIM_CounterMode_Down`
* 中心计数: `TIM_CounterMode_CenterAligned1`
* | `TIM_CounterMode_CenterAligned2`
* | `TIM_CounterMode_CenterAligned3`
*/
uint16_t TIM_Period;
// 重装载值,即tick的次数

// TODO 下面两个暂时不理解,待更新
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;

例如:

TIM_TimeBaseInitTypeDef tim3;
tim3.TIM_Period = 4999;
// 重装载值为 4999, tick 5000 次
tim3.TIM_Prescaler = 7199;
// 预分频系数为 7200 = 7199 + 1
// 系统时钟为72MHz, TIM3 的时钟为 72000000 / 2 * 2 / 7200 = 10kHz
// 即一次 0.1ms
tim3.TIM_CounterMode = TIM_CounterMode_Up;
tim3.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &tim3);

开启定时器中断

TIM_ITConfig(TIMx, TIM_IT, ENABLE);

设置什么情况下中断,可以为:

#define TIM_IT_Update                      ((uint16_t)0x0001)
// 更新中断
#define TIM_IT_CC1 ((uint16_t)0x0002)
#define TIM_IT_CC2 ((uint16_t)0x0004)
#define TIM_IT_CC3 ((uint16_t)0x0008)
#define TIM_IT_CC4 ((uint16_t)0x0010)
#define TIM_IT_COM ((uint16_t)0x0020)
#define TIM_IT_Trigger ((uint16_t)0x0040)
#define TIM_IT_Break ((uint16_t)0x0080)

当然,用到中断就要设置中断优先级,此外,不要忘记在main函数开始时设置中断优先级分组

使能定时器

TIM_Cmd(TIMx, ENABLE);

非常简单,无需解释

写中断服务函数

对于高级定时器,中断服务函数的名字为:

  • void TIMx_BRK_IRQHandler()
  • void TIMx_UP_IRQHandler()
  • void TIMx_TRG_COM_IRQHandler()
  • void TIMx_CC_IRQHandler()

对于通用定时器,中断服务函数的名字为:

  • void TIMx_IRQHandler()

TIMx中的x换成对应计时器ID

例程

下面给出一个完整的例子

  • main.c
#include <stm32f10x.h>
#include <system_stm32f10x.h>

void TIM3_IRQHandler() {
if (TIM_GetFlagStatus(TIM3, TIM_FLAG_Update) == SET) {
// 判断发生了 `TIM_FLAG_Update` 中断
// 翻转LED状态
if (GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_0) == 1) {
GPIO_ResetBits(GPIOC, GPIO_Pin_0);
} else {
GPIO_SetBits(GPIOC, GPIO_Pin_0);
}
// 取消中断标志位
TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update);
}
}

int main() {
SetSysClock();
// 初始化系统时钟为 72MHz
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
// 中断优先级分组为 2

/*
* 初始化通用计时器3
* init_tim3 {
*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 使能通用计时器 3

TIM_TimeBaseInitTypeDef tim3;
tim3.TIM_Period = 4999;
// 重装载值为 4999, tick 5000 次
tim3.TIM_Prescaler = 7199;
// 预分频系数为 7200
// 系统时钟为72MHz, TIM3 的时钟为 72000000 / 2 * 2 / 7200 = 10kHz
// 即一次 0.1ms
tim3.TIM_CounterMode = TIM_CounterMode_Up;
tim3.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &tim3);

TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 配置更新中断

NVIC_InitTypeDef nvic_tim3;
nvic_tim3.NVIC_IRQChannel = TIM3_IRQn;
nvic_tim3.NVIC_IRQChannelPreemptionPriority = 2;
nvic_tim3.NVIC_IRQChannelSubPriority = 2;
nvic_tim3.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_tim3);
// 设置中断优先级

TIM_Cmd(TIM3, ENABLE);
// 使能定时器
/*
* 初始化通用计时器3
* }
*/

/*
* 初始化LED0
* init_LED0 {
*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef led0;
led0.GPIO_Pin = GPIO_Pin_0;
led0.GPIO_Mode = GPIO_Mode_Out_PP;
led0.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &led0);
GPIO_ResetBits(GPIOC, GPIO_Pin_0);
/*
* 初始化LED0
* }
*/

for (;;) { }
}

没有附加依赖,使用定时器中断使LED闪烁,LED用的是GPIOC_0

为了方便分享和复习,所有代码写在一个文件里,实际开发这样做非常不对!