STM32教程:定时器的配置与使用案例(基于STM32F103C8T6最小系统板标准库开发)*详细教程*

前言:

本文章基于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的更新中断标志位
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值