STM32 定时器的几种基本使用

本文详细介绍了STM32微控制器中定时器的各种应用,包括基本定时、PWM输出、输入捕获、驱动直流电机、超声波模块信号处理及编码器模式。深入探讨了定时器的时基、配置与中断处理,适用于初学者和进阶用户。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: STM32——外设Timer定时器
date: 2020-05-20 14:43:52
tags:
categories: STM32学习记录


对定时器的基本认识

先来看看这种MCU有多少定时器:

STM32定时器分类

定时器分为3类:

  1. 基本定时器的功能最少,只能充当基本的时基,甚至都没有外部引脚。
  2. 通用定时器拥有基本定时器的全部功能,同时有输入捕获模式,用以接收外部的PWM,脉冲之类的信息,也有
  3. 高级定时器又有通用定时器的全部功能,又有互补输出模式,功能最为强大

先具体看一下夹在中间的通用定时器的官方文档中的描述:

为什么需要定时器,并且需要这么多定时器呢?这是因为STM32的处理器是一种单线程的模式,这这时如果没有一个专门的外设,那么在软件定时期间就无法处理其他的工作。于是ST就提供了这些可以独立工作的定时器来完成需要的定时工作。

定时器的心脏:时基

1、内部时钟(CK_INT)

2、外部时钟模式 1:外部输入脚(TIx)

3、外部时钟模式 2:外部触发输入(ETR)

4、内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。 这些时钟,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置。

定时器时基

在这里呢,最基本的定时功能就是功能1,如上图,基本定时器和通用定时器是挂载在APB1低速总线的,和PCLK共用来自APB1预分频器的时钟源,这个总线的最大频率是36M,但是由于预分频器分频倍数是1,所以在这里就由系统自动给双倍频率了,因此基本定时器和通用定时器的时钟频率都是72MHz的。

实践1:定时器溢出中断

原理

这个功能就和传统8051单片机的定时器功能一样,计数,溢出,触发中断,简单明了,多了个自动重装载功能,应该是最简单的定时器功能了,甚至基本定时器都能够完成,我就从这里开始学习定时器吧。

和GPIO,串口,外部中断一样,创建并添加src文件和inc文件:

TIMER.h

#ifndef _TIMER_H
#define _TIMER_H

extern TIM_HandleTypeDef TIM3_Handler;      //定时器句柄

void TIM3_Init(u16 arr,u16 psc);
#endif

TIMER.c

首先是初始化程序,一切是那么熟悉,从GPIO起,我们都使用HAL库提供的初始化结构体来初始化外设。

#include "TIMER.h"
#include "LED.h"

TIM_HandleTypeDef TIM3_Handler;      //定时器句柄 

//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
void TIM3_Init(uint16_t arr,uint16_t psc)		//arr:自动重装值	psc:时钟预分频数
{
     
    TIM3_Handler.Instance=TIM3;                          //通用定时器3
    TIM3_Handler.Init.Prescaler=psc;                     //分频系数
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器
    TIM3_Handler.Init.Period=arr;                        //自动装载值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
    HAL_TIM_Base_Init(&TIM3_Handler);			//时基初始化
    
    HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE   
}

追究底层原理的话,这些给结构体成员赋值的过程中,主要就是在操作CR1,CR2控制寄存器,完成定时器最基本的设置基础计时频率的过程,经过初始化函数上,就完成了这些配置。

CounterMode:TIM_COUNTERMODE_UP TIM_COUNTERMODE_DOWN下

Channel:用来设置活跃通道。每个定时器最多有四个通道可 以用来做输出比较,输入捕获等功能之用。这里的 Channel 就是用来设置活跃通道的,取值范 围为:HAL_TIM_ACTIVE_CHANNEL_1~ HAL_TIM_ACTIVE_CHANNEL_4。

这里呢,和串口类似,在时基初始化函数中,会调用一个__WEAK弱定义过的空函数:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
   
    if(htim->Instance==TIM3)
	{
   
		__HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3时钟
		HAL_NVIC_SetPriority(TIM3_IRQn,1,3);    //设置中断优先级,抢占优先级1,子优先级3
		HAL_NVIC_EnableIRQ(TIM3_IRQn);          //开启ITM3中断   
	}
}

这样,初始化才正式完成,然后是中断向量表中的中断服务函数:

void TIM3_IRQHandler(void)
{
   
    HAL_TIM_IRQHandler(&TIM3_Handler);
}

又是熟悉的感觉,直接调用HAL库的处理函数,我们按照惯例,进去看看:

HAL库定时器中断处理函数

这个函数居然有300多行,不过还好,机智的我把它折叠起来了,如果忽略具体实现细节,这个其实很清晰了,这个函数的作用是,通过判断定时器的寄存器,判断触发中断的到底是什么事件,是捕获比较1234,还是定时器更新事件,还是输入捕获事件,然后调用相应的回调函数。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
   
    if(htim==(&TIM3_Handler))
    {
   
		HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
		HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
    }
}

在这里我写的是一个闪灯程序(我在前面包含了LED.h头文件)

在主程序中初始化:

#include "main.h"
#include "stm32f1xx.h"		//芯片选型的功能,里面包含了stm32f1xx_hal.c

#include "LED.h"
#include "TIMER.h"


int main(void)
{
   
	HAL_Init();
	SystemClock_Config();
	LED_Init();
	TIM3_Init(9999, 7199);
	while(1)
	{
   
	}
}

可以看到初始化的两个重要参数:

void TIM3_Init(uint16_t arr,uint16_t psc)		//arr:自动重装值	psc:时钟预分频数
  • 自动重载值:指的是计数多少次自动重装,16位寄存器储存这个值,范围是0 - 65535,这个是向上计数,就是0到arr就会触发重装中断事件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值