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