文章参考有 https://www.bilibili.com/video/BV1th411z7sn?p=14&share_source=copy_web
一、简介
TIM (Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
1、基本定时器
主从触发模式:让内部的硬件在不受程序的控制下实现自动运行,使用得好可减轻CPU负担。 这个紫色部分就是主从触发模式的一个实例。 当我们使用DAC的时候,可能需要用DAC输出一段波形,就需要每隔一段时间就来触发一下来输出下一个电压点。也是会牵涉到中断。 使用主模式可以把这个定时器的更新事件,映射到这个触发输出TRGO,然后TRGO直接接到DAC的引脚上。(跟着动,不用再额外去申请中断) 预分频器、CNT计数器、自动重装载寄存器构成了最基本的计数电路,也叫做微时基单元。 预分频器、CNT计数器、自动重装载寄存器都是16位。 预分频器对收到的时钟(CK_PSC)进行分频:如果是写1,表示是2分频,输出=输入/2。预分频器和实际的分频系数差1。
(计数器计数频率)CK_CNT = CK_PSC(预分频器的值) / PSC(分频赋的值)+1
计数器可对预分频后的计数时钟进行计数。计数时钟每来一个上升沿,计数器就加1。达到自动重装器的值后产生中断信号,并且清零计数器,开启下一次计数。 当每一次计数的事件都一样,计数跟计时就没啥区别。
计数器溢出频率 f=计数器计数频率 / ARR(重装载寄存器的值)+1
定时的时间 T = 1 / f
在这里图上画的一个向上的折线箭头。就代表这里会产生中断信号 这种计数值等于自动重装值产生的中断,一般把他叫做更新中断。这个更新中断之后就会通到NVIC。我们再配置好NMC的定时器通道。那定时器的更新中断就能够得到CPU的响应。
向下的箭头代表事件,这里对应的叫更新事件。更新事件不会触发中断。但可以触发肉部其他电路的工作。
关于预分频系数从1变到2的计数器时序图
首先预分频系统的频率一直没变,是系统时钟。在CNT_EN置于0时(没开),定时器CK_CNT不启动。CNT_EN=1时CK_CNT启动。然后在开启后的第一个上升沿开始计数。 当预分频系数由1变为2时,首先写入预分频控制寄存器。在计数器完成这个周期(到达FC后),触发更新事件,预分频器缓冲器置为1.预分频计数器变为010101这么延续,在为0时CK_CNT上升沿触发一次。
我的理解是,预分频控制寄存器是虚假的皇帝,预分频控制缓存器才是摄政王,更新事件是玉玺。只要玉玺盖了剩下的才开始动。 当然这么也有可能存在问题,比如你在刚开始计数就更改时序,结果还要等到他把这一堆跑完才可以更改。所以在stm32库函数里这个摄政王是可以设置开不开的。
2、通用计时器
拥有定时中断、主模式触发DAC的功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
中间的部分就是基本的定时中断。 上部分是有时钟源选择,编码接口,主从模式。 下部分是输入捕获和输出比较。
当计数值计到自动重装值时。计数值清零同时产生更新中断和更新事件 对于通用定时器而言。这个计数器的计数模式就不止向上计数这一种(也就是计数器从开始,向上自增,计到重装值,清零同时申请中断。) 通用定时器和高级定时器还支持向下计数模式和中央对齐模式。 向下计数模武就是从重装值况始。向下自减,减到0,回到重装值同时申请中断,这样循环。 中央对齐模式:先上升到重装值,申请中断,再向下自减,减到0,再申请中断。继续下一轮循环。 不过一般都是向上计数用得多。
时钟选择
内部时钟源 外部时钟源模式1 当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。 外部时钟源模式2 选定此模式的方法为:令TIMx_SMCR寄存器中的ECE=1 计数器能够在外部触发ETR的每一个上升沿或下降沿计数
输入捕获和输出比较
https://blog.csdn.net/wei348144881/article/details/109091539
3、高级定时器
二、代码
#include "Timer.h"
#include "stm32f10x.h"
void TIM3_Init()
{
TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1;
TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
}
}
1、关于时钟总线的外设情况
TIM3挂载在APB1上
2、初始化定时器参数
溢出时间 = (自动重载值period + 1)*(分频系数prescaler + 1)/ 输入时钟频率
一般情况下这个输入时钟频率是内部时钟的情况下就是72MHz
输出频率(最后使用的频率)f = 输入时钟频率 / (分频系数prescaler + 1)
f的意思是,1s计X个数 。计1个数的时间是 :1 / f
所以溢出的总时间是 = 计一个数的时间(1/f) * 个数(重装载值)
1s = 1000ms = 1000000us M = 10的六次方
TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1;
TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
记1个数的时间=7200 / 72000000 = 0.0001s = 0.1ms 总时间 = 0.1ms * 10000 = 1s
注意这两个数都是16位,最大值是65535
3、关于中断
配置NVIC时选择TIM3中断 TIM3中断函数
4、一些别的函数
恢复缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);
时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
使能计数器
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);
选择ITRx其他定时器时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择TIx捕获通道的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
单独更改时基函数中的配置
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);
获取标志位和清楚标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
三、LED显示计数
led屏幕的使用函数是用的正点原子的函数
#include "stm32f10x.h"
#include "KEY.h"
#include "LED.h"
#include "delay.h"
#include "EXTI.h"
#include "lcd.h"
#include "Timer.h"
uint8_t num;
int main(void)
{
delay_init();
LCD_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
TIM3_Init();
while(1)
{
LCD_ShowxNum(30,40,num,7,24,0);
}
}
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
num++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
}
}
#include "Timer.h"
#include "stm32f10x.h"
void TIM3_Init()
{
TIM_TimeBaseInitTypeDef TIM3_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM3_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM3_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
TIM3_TimeBaseInitStructure.TIM_Period = 10000 -1;
TIM3_TimeBaseInitStructure.TIM_Prescaler = 7200 -1;
TIM3_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseInitStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE);
}
|