STM32(五):TIMER定时器 (标准库函数)
前言
上一篇文章已经介绍了如何用STM32单片机中的Systick嘀嗒定时器来控制LED灯的交替闪烁,实现了点灯的第四种方式。这篇文章我们来介绍一下如何用STM32单片机中的TIMER定时器来控制LED灯的交替闪烁。
一、实验原理
1.定时器的功能
STM32的定时器是一种强大的外设,具有以下几种主要功能:
定时功能:可以设置定时时间,用于在特定时刻执行任务。 计数功能:对输入的时钟脉冲进行计数。 PWM信号产生:用于控制电机速度、灯光亮度等。 输入捕获:捕获并记录外部信号的时间参数。 定时器中断:在计数值达到设定值时触发中断,进行相应处理。
2.定时器的类型
STM32中的定时器根据功能和复杂度分为三种类型:
高级定时器:TIM1和TIM8,支持更复杂的控制需求和高精度的操作。 通用定时器:TIM2、TIM3、TIM4、TIM5,适用于常规的定时和计数任务。 基本定时器:TIM6和TIM7,主要用于简单的定时任务,没有高级控制功能。
3.定时器工作原理
以下是定时器的工作原理图,主要分为5个部分,分别是时钟产生、定时器、输入、输出和寄存器部分。
时钟产生来源:
1. 内部时钟 PB1(CLK_INT) (*)
2. TIMx_ETR 外部引脚输入
3. ITRx 内部触发输入,定时器集联
4. TL1FP1/2 来自于定时器外部通道
主要是内部时钟,最后输出 [ CK_PSC ]
输出比较:
比如说现在定时器是从 0-100 向上计数
如果把 [ 捕获比较寄存器 ] 的值设置为 v = 50;
如果 当前值 < v 则输出高电平, 当前值 > v 则输出低电平
那么效果就是 0-50 输出高电平, 50-100 输出低电平, 实现通道输出
通过控制 V 的值 就能改变占空比, 实现PWM。
定时器主要分为3种计数模式,向上计数、向下计数以及中央对齐模式。
1.向上计数模式
2.向下计数模式
3.中央对齐模式
3.定时器系统框图
以下是定时器的系统框图:
主要有4块组成部分:
1.振荡器
HSI RC:内部高速时钟,8MHz。
HSE Osc:外部高速时钟,频率依赖于外部晶振。
LSI RC:内部低速时钟。
LSE Osc:外部低速时钟,通常是32.768kHz,用于实时时钟(RTC)。
2.时钟源选择和配置
PLL(相位锁环):可以将HSI或HSE的频率乘以一个系数(如图中所示,最高可达72MHz)。
SYSCLK(系统时钟):可以选择HSI、HSE或PLLCLK作为源。
MCO(微控制器时钟输出):可以输出不同源的时钟,如HSI、HSE或PLLCLK的一部分。
3.总线和接口时钟
AHB Prescaler:高性能总线预分频器,可对HCLK进行分频。
APB1 Prescaler和APB2 Prescaler:两个外设总线预分频器,分别对PCLK1和PCLK2进行分频。
ADC Prescaler:模数转换器预分频器,提供给ADC的时钟。
4.定时器
TIM1、TIM8(高级定时器):接收PCLK2时钟,可以选择直接时钟或通过倍增器增倍(x1或x2)。
TIM2、TIM3、TIM4(通用定时器):接收PCLK1时钟,同样有倍增器选择。
系统时钟与这些定时器的关系,决定了定时器的计数速度和精度。
二、实验步骤
1.时钟初始化
首先是对时钟的初始化,其中我们要知道以下计算的式子
定时器频率
=((1+TIM_Prescaler )/72M)*(1+TIM_Period )•
例:如果想要设置定时器频率为
1秒,可以设置TIM_Prescaler=35999,TIM_Period=1999 //2Khz的频率,计数到
2000
•
或•
TIM_Prescaler=7199,TIM_Period=9999 //10KHz的频率,计数到10000
参数如下所示:
TIM_Prescaler 用来设置分频系数 TIM_CounterMode 用来设置计数方式 TIM_Period 设置自动重载计数周期值 TIM_ClockDivision 用来设置时钟分频因子
void GENERAL_TIMx_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
/* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
GENERAL_TIMx_NVIC_Configuration();
/* 自动重装载寄存器周的值(计数值) */
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;
/* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
/* 重复计数器的值,通用定时器没有,不用管 */
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/* 初始化定时器TIMx, x[2,3,4,5] */
TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
/* 清除计数器中断标志位 */
TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
/* 开启计数器中断 */
TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);
/* 使能计数器: */
TIM_Cmd(GENERAL_TIMx, ENABLE);
}
2.时钟中断
时钟中断函数GENERAL_TIMx_NVIC_Configuration(void),这边是对中断来源以及优先级的配置,前面在Systick中有所介绍,可以看一下之前的连接:STM32(四):Systick (标准库函数)-CSDN博客
void GENERAL_TIMx_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 设置中断组为0 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* 设置中断来源 */
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;
/* 设置主优先级为 0 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 设置抢占优先级为3 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
/*定时器使能 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
但到这里我们的中断配置还没结束!!重点!!易踩坑!!
我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 GENERAL_TIM_INT_FUN (),记得加上头文件,例如:
#include "bsp/GeneralTIM/bsp_GeneralTIM.h"
extern __IO uint16_t timer_count;
void GENERAL_TIM_INT_FUN (void)
{
if ( TIM_GetITStatus(GENERAL_TIMx,TIM_IT_Update) != RESET )
{
timer_count++;
TIM_ClearITPendingBit(GENERAL_TIMx , TIM_IT_Update);
}
}
三、实操代码
程序分为3个文件:bsp_GeneralTIM.c、bsp_GeneralTIM.h、main.c
1.bsp_GeneralTIM.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp/GeneralTIM/bsp_GeneralTIM.h"
/**
* 函数功能: 通用定时器 TIMx,x[2,3,4,5]中断优先级配置
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
void GENERAL_TIMx_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 设置中断组为0 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* 设置中断来源 */
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM_IRQ;
/* 设置主优先级为 0 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
/* 设置抢占优先级为3 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
/*定时器使能 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void GENERAL_TIMx_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* 开启TIMx_CLK,x[2,3,4,5],即内部时钟CK_INT=72M */
GENERAL_TIM_APBxClock_FUN(GENERAL_TIM_CLK, ENABLE);
/* 通用定时器 TIMx,x[2,3,4,5]中断优先级配置 */
GENERAL_TIMx_NVIC_Configuration();
/* 自动重装载寄存器周的值(计数值) */
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;
/* 计数器计数模式,基本定时器TIM6和TIM7只能向上计数,没有计数模式的设置,不用管 */
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
/* 重复计数器的值,通用定时器没有,不用管 */
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/* 初始化定时器TIMx, x[2,3,4,5] */
TIM_TimeBaseInit(GENERAL_TIMx, &TIM_TimeBaseStructure);
/* 清除计数器中断标志位 */
TIM_ClearITPendingBit(GENERAL_TIMx, TIM_IT_Update);
/* 开启计数器中断 */
TIM_ITConfig(GENERAL_TIMx,TIM_IT_Update,ENABLE);
/* 使能计数器: */
TIM_Cmd(GENERAL_TIMx, ENABLE);
}
2.bsp_GeneralTIM.h
#ifndef __GENERAL_TIM_H__
#define __GENERAL_TIM_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include <stm32f10x.h>
/* 类型定义 ------------------------------------------------------------------*/
/* 宏定义 --------------------------------------------------------------------*/
/********************通用定时器TIM参数定义,只限TIM2 & TIM3 & TIM4 & TIM5************/
#define GENERAL_TIMx TIM2
#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM_CLK RCC_APB1Periph_TIM2
#define GENERAL_TIM_IRQ TIM2_IRQn
#define GENERAL_TIM_INT_FUN TIM2_IRQHandler
//#define GENERAL_TIMx TIM3
//#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK RCC_APB1Periph_TIM3
//#define GENERAL_TIM_IRQ TIM3_IRQn
//#define GENERAL_TIM_INT_FUN TIM3_IRQHandler
//#define GENERAL_TIMx TIM4
//#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK RCC_APB1Periph_TIM4
//#define GENERAL_TIM_IRQ TIM4_IRQn
//#define GENERAL_TIM_INT_FUN TIM4_IRQHandler
//#define GENERAL_TIMx TIM5
//#define GENERAL_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
//#define GENERAL_TIM_CLK RCC_APB1Periph_TIM5
//#define GENERAL_TIM_IRQ TIM5_IRQn
//#define GENERAL_TIM_INT_FUN TIM5_IRQHandler
/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void GENERAL_TIMx_Configuration(void);
#endif /* __GENERAL_TIM_H__ */
3.main.c
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"
#include "bsp/led/bsp_led.h"
#include "bsp/key/bsp_key.h"
#include "bsp/delay/delay.h"
#include "bsp/systick/bsp_SysTick.h"
#include "bsp/GeneralTIM/bsp_GeneralTIM.h"
/* 函数体 --------------------------------------------------------------------*/
__IO uint16_t timer_count=0;
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
LED_GPIO_Init();
//KEY_GPIO_Init();
// KEY1_EXIT_Config();
// KEY2_EXIT_Config();
// KEY3_EXIT_Config();
/* 初始化系统滴答定时器 */
// SysTick_Init();
/* 通用定时器初始化:1ms中断一次 */
GENERAL_TIMx_Configuration();
while (1)
{
if(timer_count==500)
{
LED1_ON;
LED2_OFF;
LED3_OFF;
}
if(timer_count==1000)
{
LED1_OFF;
LED2_ON;
LED3_OFF;
}
if(timer_count==1500)
{
timer_count=0;
LED1_OFF;
LED2_OFF;
LED3_ON;
}
}
}
四、实验效果
Timer定时器点灯
结束语
本文以STM32VET6为例讲解了用TIMER定时器控制LED灯的交替闪烁的实现方法,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!
作者:是覆盖对于变化