STM32定时器详解及应用指南

文章目录

  • 一、定时器介绍
  • 1.简介
  • 2.定时器类型
  • 3.基本定时器
  • a.时钟源
  • b.控制器
  • c.基本时基单元
  • d.主模式触发DAC功能
  • 时序
  • 4.通用定时器
  • 二、实例
  • 1. 基本思路
  • 2.定时器中断
  • 3.定时器外部时钟
  • 4.其他知识
  • 一、定时器介绍

    1.简介

    TIM(Timer)定时器
    定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
    16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
    不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
    根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

    2.定时器类型

  • STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
  • 3.基本定时器


    基本定时器可分为3部分

    a.时钟源

    时钟源来自RCC的TIMXCLK,就是内部时钟(CK_INT)直接经过控制器传给时基单元充当CK_PSC

    b.控制器

    控制定时器的复位、使能、计数、DAC触发

    c.基本时基单元

    1>预分频器(PSC)perscaler
    分频、得到计时器的时钟,即CNT计数1次所需要的时间,预分频器时16位的寄存器、所以可分频为1-65536。
    基本定时器只能选择内部时钟,故预分频器直接输入端,即内部时钟CK_INT(一般为72MHz)
    预分频器(PSC)写0(1分频或者不分频):则SK_CNT输出72MHz,
    写1(2分频):输出36MHz,写2(3分频):输出24MHz。
    故实际分频系数=预分配系数+1(分频器是16位,故最大位65525)。

    2>计数器(CNT)counter
    用来计数,到达设定值后,会产生中断。

    3>自动重装寄存器(ARR)Auto Reload Register
    CNT加到ARR的值之后,会产生一个事件或中断或DMA请求,中断用得比较多

    UI(向上的箭头)中断响应,更新中断后会通向NVIC,再通向CPU
    U(向下的箭头)事件响应:触发其他外设工作

    d.主模式触发DAC功能

    1)能让内部的硬件再不受程序的控制下实现自动运行,若利用好,可以极大地减轻CPU的负担。
    2)用途:使我们使用DAC的时候,用DAC输出一段波形,则需要每隔一段事件触发一次DAC,让它输出下一个电压点。
    用正常思维实现:先设置一个定时器产生中断,每隔一段时间再中断程序中调用代码手动触发一次DAC转换,然后DAC输出。这样会使主程序处于频繁被中断的状态,会影响主程序的运行和其他中断的响应
    故定时器设置了一个主模式,使用主模式可以把定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不需要用中断来触发DAC(数模转换)转换了
    仅仅需要将更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用

    时序


    有框框的有影子寄存器,即预分频缓冲器,可以进行缓存,更改的值下个周期才生效(比如你改手机套餐,这个月内你还是原套餐,你改的套餐下个月才生效)

    4.通用定时器

    区别:
    1.计数器支持向上、向下、中央对齐计数模式
    2.可用外部时钟作为主频率
    3.时钟来自其他定时器

    未完待续…

    二、实例

    1. 基本思路

    第一步:RCC开启时钟
    第二步:选择时基单元的时钟源,对于定时中断,选择内部时钟源
    第三步:配置时基单元,包括预分频器、自动重装器、计数模式(用结构体配置)
    第四步:配置输出中断控制,允许更新中断输出到NVIC
    第五步:配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
    第六步:运行控制,整个模块配置完成后,使能计数器,否则计数器不运行, 计数器使能后,计数器就会开始计数,当计数器更新时,触发 中断,最后写个定时器中断函数,这样中断中断函数就能每隔一段时间自动执行一次

    2.定时器中断

    Timer.c

    #include "stm32f10x.h"                  // Device header
    
    void Timer_Init(void)
    {
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //RCC开启时钟
    	
    	TIM_InternalClockConfig(TIM2);  //配置内部时钟
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  //配置时基单元
    	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;  //不分频
    	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;  //向上计数模式
    	TIM_TimeBaseInitStructure.TIM_Period=10000-1;  //周期,即重装值(减一原因见时序表)
    	TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;  //预分频值(对72Mhz进行7200分频率,即10K计数频率,再计数10000次,得到1s)
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;  //多少次触发才触发一次中断(高级定时器才有此功能)
    	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
    	
    	TIM_ClearFlag(TIM2,TIM_FLAG_Update);  //清除向上溢出的中断标志
    	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);  //使能更新中断
    	
    	//配置NVIC
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    	NVIC_Init(&NVIC_InitStructure);
    	
    	TIM_Cmd(TIM2,ENABLE);  //启动定时器
    	
    }
    
    /* 中断函数模板,可以放到主函数中
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }
    */
    

    main. c

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Timer.h"
    
    uint16_t num;
    
    int main(void)
    {
    	OLED_Init();
    	Timer_Init();
    	OLED_ShowString(1,1,"NUM:");
    	
    	while(1)
    	{
    		OLED_ShowNum(1,5,num,5);
    	}
    }
    
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    		num++;
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }
    

    3.定时器外部时钟

    Timer.c

    #include "stm32f10x.h"                  // Device header
    
    void Timer_Init(void)
    {
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //配置GPIO口
    	
    	GPIO_InitTypeDef GPIO_InitSturcture;
    	GPIO_InitSturcture.GPIO_Mode=GPIO_Mode_IPU;
    	GPIO_InitSturcture.GPIO_Pin=GPIO_Pin_0;
    	GPIO_InitSturcture.GPIO_Speed=GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA,&GPIO_InitSturcture);
    	
    	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);  //更改为外部中断2模式(相较于1模式(4条通道),该模式只有1条通道,且可分频,比较简单,推荐使用)
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
    	TIM_TimeBaseInitStructure.TIM_Period=10-1;
    	TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
    	
    	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
    	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
    	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    	
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	NVIC_InitTypeDef NVIC_InitStructure;
    	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    	NVIC_Init(&NVIC_InitStructure);
    	
    	TIM_Cmd(TIM2,ENABLE);
    	
    }
    
    uint16_t Timer_getcounter(void)  //看CNT的值
    {
    
    	return TIM_GetCounter(TIM2);
    }
    
    /*
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }
    */
    

    Timer.h

    #ifndef  __TIMER_H__
    #define  __TIMER_H__
    
    void Timer_Init(void);
    uint16_t Timer_getcounter(void);
    
    #endif
    
    

    main.c

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Timer.h"
    
    uint16_t num;
    
    int main(void)
    {
    	OLED_Init();
    	Timer_Init();
    	OLED_ShowString(1,1,"NUM:");
    	OLED_ShowString(2,1,"CNT:");
    	while(1)
    	{
    		OLED_ShowNum(1,5,num,5);
    		OLED_ShowNum(1,5,Timer_getcounter(), 5);
    	}
    }
    
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    		num++;
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }
    

    4.其他知识

    extern:告诉编译器,已经在别的文件中定义了该变量,(可以实现在文件之间传递值)

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32定时器详解及应用指南

    发表评论