STM32中的定时器

一、定时器基本定时功能

1.定时器本质及应用方面

定时器本身就是计数器!定时器本身就是计数器!定时器本身就是计数器!但更厉害的是,它可以通过各种不同的计数姿势的巧妙组合,实现普通的定时功能。

2.定时器的分类

在STM32的F10系列芯片中,有八个定时器,这是专业介绍

那我们到底该怎么选择用哪个类型的定时器呢?

  • 只需要定时 → 基本定时器

  • 要PWM/测脉冲 → 通用定时器

  • 做电机驱动 → 高级定时器

这是一个大概,具体情况具体分析

3.如何实现定时?

芯片内部传送一个72MHz的方波信号,给计时器。由于72MHz是指一秒传输72000000个方波(一个高电平,一个低电平)而我们的计数器是16bit,1bit就是一位,只能写0和1;16bit=2的16次方=65536.远远小于72000000,所能计时的秒数很低。为了满足我们更高的计时需要,我们在计时器前加一个预分频器,起到一个分频的作用,当将预分频器设为1时,72MHz经过它的处理,会变成一秒输出36000000,给计时器。但预分频器也是有上限的,不能无限分频,它也是16bit(65536)。当两个器都是取值为65536时,我们可以计时59秒多。我们这样子就可以解决计时秒数低的问题。

但我们如何得到一个具体的时间,这里就要引出自动重装载寄存器,假设令自动重装载寄存器值为3,那么当计数器从0数到3时,自动重装载寄存器检测到计数器的值与它相同时,就会产生一个中断标志位,所以我们可以使用中断服务函数,让芯片去执行别的功能。而计数器又会从0开始数数,如此的进行重复。

举个例子:传过来一个72MHz的信号,我们一般将预分频器设为7200-1(因为从0开始数数,分频7200次就要将它赋值为7200-1),这样计数器就一秒中数10000次(72000000/7200=10000)。如果我们想实现1秒的计时,此时只需要让自动重装载寄存器的值为10000就可以达到此效果。

如图,图片来源哔哩哔哩上的keysking博主

二、定时器的外部时钟

输入滤波会过滤一些微小的抖动。

边沿检测器会根据我们的配置,检测输入信号的边沿并输出一个脉冲。有三个不同的配置,上升沿,下升沿,双边沿。

整体逻辑:通用或者高级定时器有四个输入通道,其中通道1和通道2在经过输入滤波和边沿检测后,分别输出了TI1FP1和TI2FP2(可以选择上升沿,下升沿,双边沿)进入触发器,另外通道1还输出了一个只能是双边沿触发产物的TI1_ED,然后还有个外部触发器输入ETR,它在一通操作后,可以通过触发器进入从模式控制器,也可以直接进入触发控制器。

三、配置代码实现定时器使得灯每秒亮一次灭一次

编程思路

系统内部原理:看上面这个图画红线的地方,它需要走到AHB系统总线,然后再走到APB1的TIM2; 同理小灯的GPIOA引脚在APB2上,AHB总线下面时时钟控制(RCC),我们同样也需要使能对应APB1和APB2上的时钟,也要配置。

1.使能时钟 定时器时钟(RCC)

2.配置定时器结构体

3.开启定时器中断,配置中断结构体

4.写中断服务函数,并清除中断标志位

进入代码部分

我在每行代码旁都写了相关的注释说明,请大家往下观看

led.c文件

#include "stm32f10x.h"    //包含led.h文件
#include "main.c"

//初始化LED灯的函数——定义(在led.c文件),声明(在led.h文件),调用(在main.c文件)
void Chion_Led_Init(void)	
{
    //初始化LED引脚GPIOA1

    GPIO_InitTypeDef led_initstruct;    //定义GPIO的结构体

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  , ENABLE );    //初始化时钟APB2的GPIOA

    led_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;      //配置GPIO1的引脚
	led_initstruct.GPIO_Pin = GPIO_Pin_1 ;            //配置引脚的输出速度为2MHZ
	led_initstruct.GPIO_Speed = GPIO_Speed_2MHz;      //配置引脚的模式为推挽输出
    GPIO_Init(GPIOA, &led_initstructt);               //初始化GPIO的结构体

}

这里我们在选择LED引脚模式的时候,通常使用推挽输出,因为推挽输出的特性就是推挽输出能够同时驱动高电平和低电平,这样我们能够很直观的看出小灯是否亮灭,不用担心输出不稳导致的问题。

tim.c文件

#include "tim.h"
#include "stm32f10x.h"

void tim_config(void)
{
	TIM_TimeBaseInitTypeDef TIMinitStructure;//定义定时器结构体
	NVIC_InitTypeDef NVICinitStructure;//定义中断结构体
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE );//初始化时钟

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1 );//选了优先级第一组

	
	TIMinitStructure.TIM_ClockDivision =TIM_CKD_DIV1;//表示不分频,直接使用APB1总线时钟作为            
    定时器的时钟源,可以实现更高的时间精度
	TIMinitStructure.TIM_CounterMode =TIM_CounterMode_Up;//选择向上计数模式,如果没有特殊需 
    求,那么向上计数模式是一个默认且合理的选择
	TIMinitStructure.TIM_Period = 10000-1;//配置自动重装载寄存器,达到一万进入中断
	TIMinitStructure.TIM_Prescaler = 7200-1;//在本例中系统时钟为72MHz,通过预分频器可以降低到 
    10kHz.算式为72000000/7200 =10000。为啥降到10000,是因为计数器最大只能数到65535,取一个中 
    间值。

    TIM_TimeBaseInit(TIM2,&TIMinitStructure);//初始化定时器结构体
    TIM_ITConfig( TIM2,TIM_IT_Update, ENABLE );//打开定时器中断
    TIM_Cmd( TIM2, ENABLE);//为了计算更精确,再引入一个时钟,TIM2是根据我们自己选择的
	
	
	
	NVICinitStructure.NVIC_IRQChannel =TIM2_IRQn;//中断向量,每数一秒,进入中断
	NVICinitStructure.NVIC_IRQChannelPreemptionPriority =1;//在优先级第一组里,随便选一个范 
    围内的数字,这个是抢占优先级
	NVICinitStructure.NVIC_IRQChannelSubPriority =1;//响应优先级
	NVICinitStructure.NVIC_IRQChannelCmd =ENABLE;//让中断开始工作
    NVIC_Init( &NVICinitStructure);//初始化中断结构体



}

因为定时器文件里包含定时器和中断,有点复杂,我们需要有个顺序,防止漏了什么代码没敲。

在这我写代码的顺序是这样的先定义定时器结构体,配置定时器结构,再初始化定时器,最后把相应的时钟使能;

再到中断,先定义中断结构体,配置中断向量,这里要注意的是当我们配置优先级时,一定不要忘了写这个代码在上面NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1 )这个是告诉电脑你选了什么组,要记得写,然后就是配置抢占优先级,响应优先级,由于我这里就用了一个中断,所以数字只要是在组的范围内都可以选,最后就是要让中断使能,并且初始化就好啦!

小灯为什么是1s灯亮,1s灯灭呢?

因为我在配置定时器,计数器开始工作,数到一万时,和自动重装载寄存器(赋值为一万)相同时,产生中断标志位,就进入中断函数(main.c中的void TIM2_IRQHandler(void)),实现灯1s闪烁。

原理:一般来说,都传输72MHz方波信号,也就是1秒传过来一个72MHz的信号,我们一般将预分频器设为7200-1(因为从0开始数数,分频7200次就要将它赋值为7200-1),这样计数器就一秒中数10000次(72000000/7200=10000)。

如果我们想实现1秒的计时,此时只需要让自动重装载寄存器的值为10000就可以达到此效果。

main.c文件

#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "tim.h"



int  main()
{
	
	 led_config();
	 tim_config();
 
GPIO_SetBits(GPIOA,  GPIO_Pin_1);//让小灯熄灭

	while (1)
	{
		 
	}
   
}
void TIM2_IRQHandler(void)//中断函数
	
{
	static uint16_t temp;//静态变量,所以第一次为0
	
  if(TIM_GetITStatus(TIM2,  TIM_IT_Update) !=RESET)//如果发生中断,产生中断标志位
	{
	
			if(temp++ %2 ==1)//这里表示0除以2,但经历完这个语句,temp变为1
			{
			 GPIO_ResetBits(GPIOA, GPIO_Pin_1);//灯亮

			
			}
			else
			{
				 GPIO_SetBits(GPIOA,  GPIO_Pin_1);//灯灭

			
			}
	}
 TIM_ClearITPendingBit(TIM2, TIM_IT_Update );//清除中断标志位



}

这里我首先让小灯熄灭,我后续更好判断,然后我们就要写一个中断函数了,需要用到一个静态变量,这个变量的好处就是可以从0开始数数,与定时器原理相同,不会有歧义。初学的小伙伴记下这个函数就好啦!因为我们是想实现小灯1s亮,1s灭,所以只要我们将这个函数配置为除以2,根据余数是0,让灯灭;余数是1,让灯亮就好了,最后一定要清除中断标志位!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值