人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2024-09-10 20:33阅读: 35评论: 0推荐: 0

第25章 基本定时器定时实验

第二十五章 基本定时器定时实验

1. 硬件设计

本实验利用基本定时器 TIM6 定时 1s,1s 时间到 LED 翻转一次。基本定时器是单片机内部的资源, 没有外部 IO,不需要接外部电路,现只需要一个 LED 即可。

2. 软件设计

2.1 编程大纲

  1. TIM相关参数宏定义

  2. TIM工作参数设置及初始化

  3. 更新中断服务函数

  4. 主函数测试

通用定时器和高级定时器的定时编程要点跟基本定时器差不多,只是还要再选择下计数器的计数模式, 是向上还是向下。因为基本定时器只能向上计数,且没有配置计数模式的寄存器,默认是向上。

2.2 代码分析

2.2.1 TIM相关参数宏定义

/* 定时器基础配置 */
#define TIM_BASE TIM6
#define TIM_BASE_Period    999// ARR值
#define TIM_BASE_Prescaler 71 // 预分频系数
/* 中断相关 */
#define TIM_BASE_IRQ TIM6_IRQn
#define TIM_BASE_IRQHandler TIM6_IRQHandler

2.2.2 基本定时器配置

#include "TIMbase.h"

void TIM_BASE_Init(void)
{
    /* NVIC相关配置 */
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 设置中断优先组
    NVIC_InitStructure.NVIC_IRQChannel = TIM_BASE_IRQ; // 设置中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 设置抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 设置子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
    NVIC_Init(&NVIC_InitStructure); // 初始化NVIC

    /* TIM相关配置 */
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
    TIM_TimeBaseStructure.TIM_Period = TIM_BASE_Period;	     // ARR = 999
    TIM_TimeBaseStructure.TIM_Prescaler= TIM_BASE_Prescaler; // 预分频值71 
    TIM_TimeBaseInit(TIM_BASE, &TIM_TimeBaseStructure);
    TIM_ClearFlag(TIM_BASE, TIM_FLAG_Update); // 清除中断标志位
    TIM_ITConfig(TIM_BASE, TIM_IT_Update, ENABLE); // 开启计数器中断
    TIM_Cmd(TIM_BASE, ENABLE);
}
/*  具体计算过程
    定时器时钟源为APB1,APB1为72MHz
    定时器的频率为72/(71+1) = 1MHz
    定时器的计数周期为999+1,计数周期/定时器频率 = 1ms
*/

这里出现了几个新的库函数,我们来解释一下:

1. TIM_TimeBaseInit

原型:

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_InitStruct);

功能:

  • 初始化指定定时器的时间基准(计数周期和预分频器)。
  • TIMx 是要配置的定时器外设,例如 TIM2、TIM3 等。
  • TIM_InitStruct 是一个 TIM_TimeBaseInitTypeDef 结构体,包含了计数器的各种参数,如计数周期(TIM_Period)、预分频器(TIM_Prescaler)、时钟分频(TIM_ClockDivision)和计数模式(TIM_CounterMode

结构体定义:

typedef struct
{
    uint16_t TIM_Period;            // 自动重载寄存器的值
    uint16_t TIM_Prescaler;          // 预分频器的值
    uint16_t TIM_ClockDivision;      // 时钟分频
    uint16_t TIM_CounterMode;        // 计数模式
} TIM_TimeBaseInitTypeDef;

2. TIM_ClearFlag

原型:

void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);

功能:

  • 清除指定定时器的标志位。TIM_FLAG 是标志位参数,例如 TIM_FLAG_Update
  • 该函数用于重置定时器的状态标志,以便在下一次事件发生时能够重新捕捉。

标志位定义:

#define TIM_FLAG_Update ((uint16_t)0x0001)  // 更新中断标志位

3. TIM_ITConfig

原型:

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

功能:

  • 配置定时器的中断。TIM_IT 是中断源,例如 TIM_IT_Update
  • NewState 可以是 ENABLE 或 DISABLE,用于启用或禁用中断。

参数说明:

  • TIMx:要配置的定时器外设。
  • TIM_IT:中断源。
  • NewState:设置中断状态,ENABLE 启用,DISABLE 禁用。

4. TIM_Cmd

原型:

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

功能:

  • 启动或停止定时器。NewState 可以是 ENABLE 或 DISABLE
  • 启用定时器使其开始计数,或禁用定时器停止计数。

参数说明:

  • TIMx:要配置的定时器外设。
  • NewState:设置定时器状态,ENABLE 启用,DISABLE 禁用。

在初始化定时器的时候,我们定义了一个结构体:TIM_TimeBaseInitTypeDef, TIM_TimeBaseInitTypeDef 结构体里面有 5 个成员, TIM6 和 TIM7 的寄存器里面只有TIM_Prescaler 和 TIM_Period,另外三个成员基本定时器是没有的, 所以使用TIM6 和TIM7 的时候只需初始化这两个成员即可, 另外三个成员是通用定时器和高级定时器才有,具体说明如下:

typedef struct {
    TIM_Prescaler            // 都有
    TIM_CounterMode          // TIMx,x[6,7]没有,其他都有
    TIM_Period               // 都有
    TIM_ClockDivision        // TIMx,x[6,7]没有,其他都有
    TIM_RepetitionCounter    // TIMx,x[1,8,15,16,17]才有
} TIM_TimeBaseInitTypeDef;

其中 TIM15/16/17 只存在与互联型产品中,在 F1 大/中/小容量型号中没有。

2.2.3 定时器中断服务函数

void TIM_BASE_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM_BASE, TIM_IT_Update) != RESET) // 如果定时器产生计数溢出,也就是产生了中断
  {
    time++; // 1ms
    TIM_ClearITPendingBit(TIM_BASE, TIM_IT_Update); // 清除中断标志位
  }
}

定时器中断一次的时间是 1ms,我们定义一个全局变量 time,每当进一次中断的时候,让time 来记录进入中断的次数。 如果我们想实现一个 1s 的定时,我们只需要判断 time 是否等于 1000 即可,1000 个 1ms 就是 1s。 然后把 time 清0,重新计数,以此循环往复。在中断服务程序的最后,要把相应的中断标志位清除掉,切记。


解释一下中断函数中出现的新库函数:

1. TIM_GetITStatus

原型:

ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

功能:

  • 检查指定定时器的特定中断标志位是否被置位。
  • TIMx 是指定的定时器外设(例如 TIM2TIM3 等)。
  • TIM_IT 是要检查的中断源,例如 TIM_IT_Update,表示更新中断。

返回值:

  • 如果指定的中断标志位被置位,返回 SET
  • 否则,返回 RESET

2. TIM_ClearITPendingBit

原型:

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

功能:

  • 清除指定定时器的特定中断挂起标志位。
  • TIMx 是指定的定时器外设。
  • TIM_IT 是要清除的中断源,例如 TIM_IT_Update,表示更新中断。

作用:

  • 在中断服务程序中调用该函数可以将中断挂起标志位清除,从而避免重复触发同一中断。

2.2.4 主函数测试


#include "stm32f10x.h"
#include "led.h"
#include "TIMbase.h"

volatile uint32_t time = 0; // ms 计时变量 

int main(void)
{
  LED_Init();	
  TIM_BASE_Init();
  while(1)
  {
    if(time == 1000) // 1000 * 1 ms = 1s 
    {
      time = 0;
	  LED1_TOGGLE(); 
    }        
  }
}

主函数做一些必须的初始化,然后在一个死循环中不断的判断 time 的值,time的值在定时器中断改变, 每加一次表示定时器过了 1ms,当 time 等于 1000 时,1s时间到,LED1翻转一次,并把 time 清 0。

3. 小结

TIM基本定时器还算简单,合适的配置就行,下面我们来回顾一下基本流程:

3.1 配置基本定时器

3.1.1 使能定时器时钟

在开始配置定时器之前,你需要确保定时器的时钟已经被启用。这通常通过配置系统时钟(RCC)来完成。

// 使能定时器时钟,例如使能 TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

3.1.2 配置定时器基础参数

你需要设置定时器的预分频器、计数周期等参数。预分频器用于分频定时器时钟,计数周期用于设定定时器溢出的时间间隔。

TIM_TimeBaseInitTypeDef TIM_InitStructure;

// 配置定时器基础参数
TIM_InitStructure.TIM_Period = 9999; // 计数周期值(计数器溢出值)
TIM_InitStructure.TIM_Prescaler = 71; // 定时器时钟预分频值
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数器向上计数

// 初始化定时器
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);

3.2 配置中断

3.2.1 使能定时器更新中断

启用定时器的更新中断,使得定时器溢出时可以触发中断请求

// 使能定时器更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

3.2.2 配置中断优先级和使能中断

在 NVIC(嵌套向量中断控制器)中配置中断优先级,并使能定时器的中断请求。

NVIC_InitTypeDef NVIC_InitStructure;

// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

// 初始化 NVIC
NVIC_Init(&NVIC_InitStructure);

3.3 启动定时器

最后,你需要启动定时器,使其开始计数。

// 启动定时器
TIM_Cmd(TIM2, ENABLE);

3.4 编写中断服务函数

在中断服务程序中处理定时器溢出的事件。你需要确保在中断服务程序中清除中断挂起标志位。

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        // 执行定时器溢出后的任务
        // 例如:改变 LED 状态、更新计时器等
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

2024.9.10 第一次修订,后期不再维护

2025.1.23 优化内容和代码

本文作者:hazy1k

本文链接:https://www.cnblogs.com/hazy1k/p/18407125

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(35)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起