stm32——定时器

文章介绍了STM32F103C8T6中的定时器类型,包括基本定时器和通用定时器的结构和功能,如时钟源、预分频器、计数器和自动重装寄存器。重点讲述了基本定时器的主模式触发DAC功能以及如何通过配置实现定时器中断。文中给出了定时器初始化、中断配置和外部时钟源使用的示例代码。
摘要由CSDN通过智能技术生成

一、定时器介绍

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:告诉编译器,已经在别的文件中定义了该变量,(可以实现在文件之间传递值)

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值