Logo

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

Avatar

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

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


隐私政策

Copyright © Skyone 2025