前言:
本文章基于STM32标准库开发,先介绍tim.h常用库函数,再详细介绍了STM32微控制器怎么初始化和配置定时器,和定时器TIM2使用案例(实现变量1s加1)
最后的代码可以参考结尾的附件
一、STM32定时器配置流程大体思路
第一步,开启RCC时钟
第二步,选择时基单元的时钟源,对于定时器中断,这里我们选择内部时钟源
第三步,配置时基单元 (用一个结构体就可以配置好了)
第四步,配置输出中断控制,允许更新中断输出到NVIC
第五步,配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
第六步,运行控制,我们还需要使能一下计数器,不然计数器是不会运行的
最后再写一个定时器的中断函数,这样这个函数每隔一段时间就能自动执行一次了
二、tim.h库函数常用函数介绍
这里用的是STM32标准库,我们先打开tim.h库函数,可以看到,这里有非常多的库函数,本文章先介绍常用的和需要用到的库函数(图中蓝色标记的函数),剩下的后续可以慢慢学习
tim.h常用库函数作用介绍
void TIM_DeInit(TIM_TypeDef* TIMx);
恢复默认状态
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
时基单元初始化,用来配置时基单元,有两个参数:
第一个参数,选择某个定时器,
第二个参数,结构体,里面包含了配置时基单元的一些参数
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
这个函数可以把结构体变量赋一个默认值
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);用来使能计数器,有两个参数:
第一个参数,选择定时器
第二个参数,新的状态,也就是使能还是失能
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
用来使能中断输出信号的
第一个参数,选择定时器
第二个参数,选择要配置哪个中断输出
第三个参数,新的状态,使能还是失能
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择ITRx其他定时器的时钟
第一个参数,选择要配置的定时器
第二个参数,选择要接入哪个其他的定时器
void TIM_TIxExternalClockConfig
(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);选择TIx捕获通道的时钟
第一个参数,选择TIx
第二个参数,选择TIx具体的某个引脚
剩下两个参数,输入的极性选择和滤波器
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);选择ETR通过外部时钟模式1输入的时钟
第一个参数,外部触发预分频器
剩下两个参数,选择TIx具体的某个引脚
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);选择ETR通过外部时钟模式2输入的时钟
参数和上一个函数一模一样
tim.h下面这些函数,可以用来方便的更改某些参数的值
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
用来单独写预分频值的
第一个参数,要写入的预分频值
第二个参数,写入的模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
用来改变计数器的计数模式参数,选择新的计数器模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
自动重装器预装功能配置参数是使能还是失能
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
给计数器写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
给自动重装器写入一个值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
获取当前计数器的值,比如想看一下当前计数器计到哪里了
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
获取当前预分频器的值
以上就是tim.h库函数的一些常用的函数和介绍了
三、初始化和配置STM32定时器
接下来,是如何配置和初始化定时器
这里以初始化通用定时器TIM2为例
新建Timer.c和Timer.h文件,用来配置定时器
1.首先第一步RCC开启时钟
因为TIM2是APB1总线的外设,所以使用APB1的开启时钟函数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启时钟
2.选择时基单元时钟
打开定时器库函数,我们想选择内部时钟,用这个函数,复制。
粘贴,参数选择TIM2,这样TIM2选择为内部时钟(如果不调用这个函数,上电默认也是内部时钟)
TIM_InternalClockConfig(TIM2); //TIM2选择为内部时钟
3.配置时基单元
打开定时器库函数,选择下面这个函数来初始化时基单元,用这个函数,复制。
这个函数需要传递一个结构体参数来配置,我们先定义一个结构体,来配置时基单元,
这里结构体参数我们定义我们想要的模式,
具体如下
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 指定时钟分频,为1分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 选择计数模式,为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //周期,预重装器的值,这里定时1s
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器的值,这里预分频为7200
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值,这里是高级定时器才需要,这里给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //传入结构体,初始化
这样子我们时基单元就配置好了
4.使能更新中断
打开定时器库函数,找到使能更新中断的函数,用这个函数,复制。
粘贴,参数选择TIM2,三个参数:TIM2、更新中断模式、打开中断。这样子就开启了更新中断到NVIC的通路,代码如下
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //三个参数:TIM2,更新中断模式,打开
5.配置NVIC
配置NVIC,首先,进行NVIC优先级分组,然后定义结构体用来初始化NVIC,
具体代码如下
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //优先级分组选择2
NVIC_InitTypeDef NVIC_InitStructure; //NVIC初始化结构体
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择中断通道 定时器2再NVIC里的通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
6.启动定时器
打开定时器库函数,找到启动定时器的函数,用这个函数,复制。
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
粘贴,第一个参数选择TIM2,第二个参数ENABLE,这样定时器TIM2就打开了
代码如下
TIM_Cmd(TIM2,ENABLE); //启动定时器TIM2
这样子,定时器TIM2就开始工作了,到这里,整个定时器TIM2的初始化就完成了。
接下来编写中断函数
7.中断函数
打开打开启动文件,找到定时器TIM2的中断函数名,复制。
TIM2_IRQHandler
粘贴,编写中断函数
void TIM2_IRQHandler(void)
{
// 这里这个函数第一个参数选择TIM2中断,第二个参数选择TIM2的更新中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) //获取更新中断的标志位
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除TIM2的更新中断标志位
}
}
到此,中断函数的基本框架编写完成
最后,记得去头文件里面声明一下函数
四、定时器测试
我们可以把刚刚写的中断函数复制粘贴到我们main.c里面,来测试一下定时器TIM2
测试:定义一个全局变了Num,通过定时器实现每秒自动+1,再通过OLED屏显示效果
具体代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时器TIM2初始化
/*OLED显示*/
OLED_ShowString(1,1,"Num:");
while (1)
{
OLED_ShowNum(1, 5, Num,5);
}
}
void TIM2_IRQHandler(void)
{
// 这里这个函数第一个参数选择TIM2中断,第二个参数选择TIM2的更新中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) //获取更新中断的标志位
{
Num ++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除TIM2的更新中断标志位
}
}
这样子,就实现了用定时器TIM2对Num变量实现每1s加1的效果
实验现象:
STM32定时器实验现象
附件:Timer.c Timer.h main.c
附件1:Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启时钟
TIM_InternalClockConfig(TIM2); //TIM2选择为内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 指定时钟分频,为1分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 选择计数模式,为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //周期,预重装器的值,这里定时1s
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器的值,这里预分频为7200
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值,这里是高级定时器才需要,这里给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //传入结构体,初始化
TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除一下标志位,防止上电先执行一次中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //三个参数:TIM2,更新中断模式,打开
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //优先级分组选择2
NVIC_InitTypeDef NVIC_InitStructure; //NVIC初始化结构体
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择中断通道 定时器2再NVIC里的通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE); //启动定时器TIM2
}
/*
void TIM2_IRQHandler(void)
{
// 这里这个函数第一个参数选择TIM2中断,第二个参数选择TIM2的更新中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) //获取更新中断的标志位
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除TIM2的更新中断标志位
}
}
*/
附件2:Timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
附件3:main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Timer_Init(); //定时器TIM2初始化
/*OLED显示*/
OLED_ShowString(1,1,"Num:");
while (1)
{
OLED_ShowNum(1, 5, Num,5);
}
}
void TIM2_IRQHandler(void)
{
// 这里这个函数第一个参数选择TIM2中断,第二个参数选择TIM2的更新中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) //获取更新中断的标志位
{
Num ++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除TIM2的更新中断标志位
}
}