TIM定时器

news2024/11/19 17:52:11

简介

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

定时器类型 

        •STM32F103C8T6定时器资源:TIM1TIM2TIM3TIM4 

基本定时器

内部时钟的来源是RCC_TIMxCLK,这里的频率值一般都是系统的主频72MHz ,所以通向时基单元的计数基准频率是72MHz。

预分频器:可以对这个72MHz的计数时钟进行预分频,这个寄存器写0,就是1分频,输出频率=输入频率=72MHz;写1,则是2分频,输出频率=输入频率/2=36MHz,以此类推。

计数器:对预分频后的计数时钟进行计次,计数时钟每来一个上升沿,计数器的值就加1,因为是16位的,所以最大可以加到65535,然后从0开始,当自增运行达到目标值时,产生中断。

自动重装载寄存器:16位寄存器,存的是我们写入的计数目标,在运行过程中,计数值不断自增,自动重装值就是固定的目标,当计数值达到自动重装值时,计时时间就到了,则会产生中断,并清零计数器。

更新中断:即图中自动重装载寄存器旁边的箭头UI,其为计数值等于自动重装值产生的中断,被称为更新中断,更新中断之后就会通往我们配置好的NVTI,则定时器的更新中断就可以得到CPU的响应了。

事件中断:即UI下边的向下的箭头U,会产生一个事件,被称为更新事件,不会触发中断,但是可以触发内部其他电路的工作。

通用计时器

通用定时器不仅可以使用内部时钟72MHz,还可以使用外部时钟TIMx_ETR;

外部时钟TIMx_ETR

查看引脚定义可以看到TIM2_ETR定义在引脚PA0上

 我们可以在TIM2_ETR()PA0上接一个外部方波时钟,再配置一下极性选择、边沿检测和预分频器,再配置输入滤波电路(对输入的波形进行滤波)

级联 

除了外部ETR引脚可以提供时钟(外部时钟模式2),还可以 通过TRGI这一路来提供外部时钟(外部时钟模式1),通过这一路的外部时钟有:1、ETR引脚的信号;2、ITR信号,这一部分的时钟是来自其他定时器的,主模式的输出TRGO可以通向其他定时器,即通向其他定时器的ITR引脚上来了,各个定时器每个通道通向的定时器如下图所示。

通过这一路,我们就可以实现定时器级联的功能

比如,我们可以先初始化定时器3,如何使用主模式把它的更新事件映射到TRGO上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,然后再选择时钟为外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,便是吸纳了定时器的级联。

TI1F_ED

CH1引脚的边沿

我们还可以选择TI1F_ED,连接着CH1引脚,并从该引脚获得时钟,通过这一路输入的时钟,上升沿和下降沿均有效。

TI1FP1和TI2FP2

其分别是CH1和CH2的时钟。

定时中断的基本结构

时序问题 

预分频器的时序

这里STM32为了不让定时器时钟因为预分频控制寄存器的变化而打乱之前的波形,增加了一个预分频缓冲器,即当在一个计时周期内(更新事件未执行时),预分频发生变化,不会立即是定时器时钟发生变化,而是做一个缓冲,当一个计时周期结束后(更新事件被执行),预分频才会作用(计数频率改变),如图所示。

预分频控制寄存器在一个计时周期内发生变化,定时器时钟的计数频率仍然会保持为原来的频率,知道本轮计数完成,在下一轮计数时,计数频率才会改变。

预分频器内部其实也是靠计数来分频的,当预分频值为0时,计数器就一直为0,预分频值为1时,就会01010101这样计数,在回到0时输出脉冲,定时器时钟下降沿。

 计数器时序

 计数器无预装时序

自动重装载寄存器在定时器时钟运行的一个周期中将FF改为36,无预装的计数器将直接在这个周期执行该计划,即计数到36就重新计数

  计数器有预装时序

相比无预装,多了一个自动重装载影子寄存器(缓冲寄存器),在一个计数周期内改变自动重装载寄存器的值,将会在下一个计数周期才会执行。 

RCC时钟树

 代码实操

6-2定时器中断

初始化定时器:按照下图顺序进行初始化

先查看定时器的库函数都有哪些

void TIM_DeInit(TIM_TypeDef* TIMx);
//恢复初始配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//时基单元初始化
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//给结构体变量赋予默认值
void 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);
//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//选择ITRx其他定时器的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
//选择TIx捕获通道的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//选择ETR通过外部时钟模式2输入的时钟
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                   uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//配置ETR引脚的预分频器、极性、滤波器

更改某些关键参数的函数

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, 
                         uint16_t TIM_PSCReloadMode);
//单独写预分频值(指定的TIM, 要写入的预分频值, 写入预分频的模式)(缓冲或者不缓冲)

void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
//改变计数器的计数模式(指定的TIM, 新的计数器模式)

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//是否启用计数器的有预装模式(指定TIM, 使能或者失能)

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);

开始配置 

1、RCC开启时钟

这样定时器的基准时钟和整个外设的工作时钟就会同时打开了

这里要使用APB1来开启时钟函数,因为TIM2是APB1总线的外设

	//开启TIM2的时钟
	//TIM2是APB1总线的外设,所以
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
2、选择时基单元的时钟源

对应定时中断,我们选择内部时钟模式

	//选择时基单元的时钟,选择为内部时钟
	//如果不写选择时钟源的代码,默认为内部时钟
	TIM_InternalClockConfig(TIM2);
3、配置时基单元
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
	//指定时钟分频(与本次操作没太大关系)
	TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//计数器模式
	TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	
	//下面三个就是时基单元里每个关键寄存器的参数
	//按照公式CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1)
	//定时频率 = 72M/(PSC+1)/(ARR+1)
	//定时1s,即定时频率为1Hz,则Period给10000,Prescaler给7200
	//预分频是对72M进行7200分频,即10k的计数频率,在10k的频率下计10000个数,即1s
	//因为两个参数都有一个数的偏差,所以都需要-1
	//ARR自动重装载器的值
	TimeBaseInitStructure.TIM_Period = 10000 - 1;
	//PSC预分频器的值
	TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
	//重复计数器的值(高级计数器特有的,我们没有直接赋0)
	TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TimeBaseInitStructure);
	
4、配置输出中断控制,允许更新中断输出到NVIC
	//使能更新中断
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	//这样就开启了更新中断到NVIC的通道
5、配置NVIC,在NVIC中打开定时器中断的通道,并分配优先级
	//NVIC
	//优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//NVIC初始化
	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);
6、启动定时器
	//启动定时器
	TIM_Cmd(TIM2, ENABLE);
7、编写中断函数

在启动文件中找到定时器2的中断函数

                DCD     TIM2_IRQHandler            ; TIM2
//中断函数
void TIM2_IRQHandler(void)
{	
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//判断是否是指定端口产生的中断
	{//第二个参数是想看哪个中断的标志位
		//内容写在这
		
		
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
	
}
在main函数中使用初始化函数
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"



int main(void)
{
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1,1,"Hello, World!");
	while(1)
	{
		
	}
}
问题 1

问题来了,要实现在OLED上显示一个1s加一的数字,就要定义一个数字,而这个定义的数字必须在main函数中定义,在中断函数中++,为了修改方便和解决这个被定义的数字不用再在Timer中再定义一遍,可以直接把中断函数移到main函数中,我们在51单片机中就是这样做的。

#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, 4, Num, 3);
	}
}

//中断函数
void TIM2_IRQHandler(void)
{	
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//判断是否是指定端口产生的中断
	{//第二个参数是想看哪个中断的标志位
		//内容写在这
		
		Num++;
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
	
}

这样就基本完成了我们想在OLED上显示一个1s加1的数字的目标。

问题2 

但是我们会发现OLED上的数字是从1开始,而不是从0开始,问题出自

	TIM_TimeBaseInit(TIM2, &TimeBaseInitStructure);

查看其定义中有

生成一个更新事件,来立刻重新装载预分频器和重复计数器的值 ;

因为有缓冲器,只有产生了事件,我们重新装载的值才会起作用,为了让值立刻起作用,就在该函数的最后手动生成了一个事件,这样预分频器的值就有效了。但是会产生副作用,更新事件和更新中断时同时发生的,更新中断会置中断标志位,一旦我们初始化完后,关系中断就会立即进入,即刚上电就立刻进入中断。

解决方法:

在TIM_TimeBaseInit之后,在NVIC之前,添加一个清除中断标志位的函数

	TIM_ClearFlag(TIM2, TIM_FLAG_Update);

这样就可以从0开始计数了

总体 

main

#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);
	}
	
}

Timer

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	//开启TIM2的时钟
	//TIM2是APB1总线的外设,所以
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	//选择时基单元的时钟,选择为内部时钟
	//如果不写选择时钟源的代码,默认为内部时钟
	TIM_InternalClockConfig(TIM2);
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
	//指定时钟分频(与本次操作没太大关系)
	TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//计数器模式
	TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	
	//下面三个就是时基单元里每个关键寄存器的参数
	//按照公式CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1)
	//定时频率 = 72M/(PSC+1)/(ARR+1)
	//定时1s,即定时频率为1Hz,则Period给10000,Prescaler给7200
	//预分频是对72M进行7200分频,即10k的计数频率,在10k的频率下计10000个数,即1s
	//因为两个参数都有一个数的偏差,所以都需要-1
	//ARR自动重装载器的值
	TimeBaseInitStructure.TIM_Period = 10000 - 1;
	//PSC预分频器的值
	TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
	//重复计数器的值(高级计数器特有的,我们没有直接赋0)
	TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
	
	//使能更新中断
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	//这样就开启了更新中断到NVIC的通道
	
	//NVIC
	//优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//NVIC初始化
	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);
	}
	
}
*/

拓展:

我们还可以加上查看CNT(计数器的值)的值的函数

OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);

可以看到这列数字加到10000时归零,且Num加1,这其中时长为1s。

我们可以修改初始化函数中的代码,观察有什么变化

仅修改Timer中的ARR自动重装载的值

TimeBaseInitStructure.TIM_Period = 1000 - 1;

可以看到现象为,Num增加的速度变为了原来的10倍,即1s加10,TIM2的值也变为加到1000置0,并Num加1

仅修改PSC预分频器的值

	TimeBaseInitStructure.TIM_Prescaler = 720 - 1;

会发现现象和上面的一样,这就得以验证我们的公式

 6-3定时器外部中断

 我们以先前的代码为基础做出修改

首先先把内部时钟模式改为外部时钟

    //内部时钟
	TIM_InternalClockConfig(TIM2);
    //外部时钟
    

拓展知识:外部触发滤波器,以一个采样频率f采样N个点,若N个点的值都一样,才会输出有效。

	//时钟配置——外部时钟
	//通过ETR引脚外部时钟模式2配置
	//第二个参数是外部触发预分频器,我们这里不需要分频
	//第三参数是外部触发的极性,1、反向:低电平或下降沿有效,2、不反向,高电平或者上升沿有效
	//第四个参数是外部触发滤波器,这个参数就是决定f和N的
	//具体填什么由手册中的14.4.3中的对应关系可知
	//我们这里不会用到滤波器,则填0x00
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted,  0x00);

因为我们的对射式红外传感器接在PA0上,所以还要初始化GPIO口

	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//模式要看数据手册,在8.1.11外设的GPIO配置中
	//手册中推荐浮空输入,但是我们选择上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

因为是手动的,所以ARR和PSC就可以不用给这么大

	//ARR自动重装载器的值
	TimeBaseInitStructure.TIM_Period = 10 - 1;
	//PSC预分频器的值
	TimeBaseInitStructure.TIM_Prescaler = 1 - 1;

为了更好的观察CNT的值,我们可以为其封装一个函数

uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);
}

总体

main

#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(2, 5, Timer_GetCounter(), 5);
	}
}

//中断函数
void TIM2_IRQHandler(void)
{	
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//判断是否是指定端口产生的中断
	{//第二个参数是想看哪个中断的标志位
		//内容写在这
		
		Num++;
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
	
}

Timer

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//模式要看数据手册,在8.1.11外设的GPIO配置中
	//手册中推荐浮空输入,但是我们选择上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	
	//开启TIM2的时钟
	//TIM2是APB1总线的外设,所以
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	//时钟配置——外部时钟
	//通过ETR引脚外部时钟模式2配置
	//第二个参数是外部触发预分频器,我们这里不需要分频
	//第三参数是外部触发的极性,1、反向:低电平或下降沿有效,2、不反向,高电平或者上升沿有效
	//第四个参数是外部触发滤波器,这个参数就是决定f和N的
	//具体填什么由手册中的14.4.3中的对应关系可知
	//我们这里不会用到滤波器,则填0x00
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted,  0x00);
	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
	//指定时钟分频(与本次操作没太大关系)
	TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//计数器模式
	TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	
	//下面三个就是时基单元里每个关键寄存器的参数
	//按照公式CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1)
	//定时频率 = 72M/(PSC+1)/(ARR+1)
	//定时1s,即定时频率为1Hz,则Period给10000,Prescaler给7200
	//预分频是对72M进行7200分频,即10k的计数频率,在10k的频率下计10000个数,即1s
	//因为两个参数都有一个数的偏差,所以都需要-1
	//ARR自动重装载器的值
	TimeBaseInitStructure.TIM_Period = 10 - 1;
	//PSC预分频器的值
	TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
	//重复计数器的值(高级计数器特有的,我们没有直接赋0)
	TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
	
	//使能更新中断
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	//这样就开启了更新中断到NVIC的通道
	
	//NVIC
	//优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//NVIC初始化
	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)
{
	return TIM_GetCounter(TIM2);
}


/*
//中断函数
void TIM2_IRQHandler(void)
{	
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//判断是否是指定端口产生的中断
	{//第二个参数是想看哪个中断的标志位
		//内容写在这
		
		
		//清除中断标志位,回归主函数,以防一直卡在中断函数中
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
	
}
*/

现象:因为没有预分频,所以每遮挡一次红外线CNT都会加1,当CNT加到10后,Num加1。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

R语言贝叶斯非参数模型:密度估计、非参数化随机效应META分析心肌梗死数据...

全文链接:http://tecdat.cn/?p23785 最近,我们使用贝叶斯非参数(BNP)混合模型进行马尔科夫链蒙特卡洛(MCMC)推断(点击文末“阅读原文”获取完整代码数据)。 概述 相关视频 在这篇文…

R语言逻辑回归、决策树、随机森林、神经网络预测患者心脏病数据混淆矩阵可视化...

全文链接:https://tecdat.cn/?p33760 众所周知,心脏疾病是目前全球最主要的死因。开发一个能够预测患者心脏疾病存在的计算系统将显著降低死亡率并大幅降低医疗保健成本。机器学习在全球许多领域中被广泛应用,尤其在医疗行业中越来越受欢迎。机器学习可…

若依前后端分离如何解决匿名注解启动报错?

SpringBoot2.6.0默认是ant_path_matcher解析方式,但是2.6.0之后默认是path_pattern_parser解析方式。 所以导致读取注解类方法需要对应的调整,当前若依项目默认版本是2.5.x,如果使用大于2.6.x,需要将info.getPatternsCondition().getPatterns()修改为info.getPathPatterns…

保研CS/软件工程/通信专业问题汇总(搜集和自己遇到的)

机器学习 1.TP、TN、FP、FN、F1 2.机器学习和深度学习的区别和联系 模型复杂性:深度学习是机器学习的一个子领域,其主要区别在于使用深层的神经网络模型。深度学习模型通常包含多个隐层,可以学习更加复杂的特征表示,因此在某些任…

使用python处理MNIST数据集

文章目录 一. MNIST数据集1.1 什么是MNIST数据集1.2MNIST数据集文件格式1.3使用python访问MNIST数据集文件内容 附录程序源码 一. MNIST数据集 1.1 什么是MNIST数据集 MNIST数据集是入门机器学习/识别模式的最经典数据集之一。最早于1998年Yan Lecun在论文:[Gradient-based l…

Qt5开发及实例V2.0-第十九章-Qt.QML编程基础

Qt5开发及实例V2.0-第十九章-Qt.QML编程基础 第19章 QML编程基础19.1 QML概述19.1.1 第一个QML程序19.1.2 QML文档构成19.1.3 QML基本语法 19.2 QML可视元素19.2.1 Rectangle(矩形)元素19.2.2 Image(图像)元素19.2.3 Text&#xf…

JavaScript中的代理对象(proxy)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 创建代理对象⭐ 使用代理对象⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友…

Helm 的简单使用 wordpress install

概述 尝试使用Helm部署wordpress博客服务 Helm | Helm Helm命令 bash自动补全 Helm | Helm补全 - bash wordpress案例 install helm repo add bitnami https://charts.bitnami.com/bitnamihelm install wordpress bitnami/wordpress \ --namespacewordpress \ --create-…

缓存(cache)与缓冲区(buffer)的主要区别

缓存(cache)与缓冲区(buffer)的主要区别 1.Buffer的核心作用是用来缓冲,缓和冲击(对输出设备的冲击,包括磁盘、打印机、显示器)。比如你每秒要写100次硬盘,对系统冲击很大,浪费了大…

SAP PO运维(三):XML消息监控

登录到SAP ECC系统,输入事务码:SXI_MONI,点击“Technical Monitor for Processed XML Messages”,或直接输入事务码SXI_MONITOR: 2、在XML消息处理页面输入查询条件,查看接口XML报错消息: 3、检…

Spring Cloud Alibaba Sentinel流量防卫兵

文章目录 Spring Cloud Alibaba Sentinel流量防卫兵1. 分布式遇到的问题2.解决的方法 Sentinel: 分布式系统的流量防卫兵1. 简介和特折 Sentinel流量防卫兵的搭建1.引入依赖2.添加配置类3.运行类上添加SentinelResource,并配置blockHandler和fallback4. linux中放入…

手摸手图解 CodeWhisperer 的安装使用

CodeWhisperer 是亚⻢逊出品的一款基于机器学习的通用代码生成器,可实时提供代码建议。 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术,观点…

KNN-K近邻算法(K-Nearest Neighbors)

k近邻算法的特点 思想极度简单应用数学知识少(近乎为零)效果好(缺点?)可以解释机器学习算法使用过程中的很多细节问题更完整的刻画机器学习应用的流程 k近邻算法 k近邻算法整体是这样的一个算法,我们已经知道的这些数据点其实是…

服务注册发现_Eureka概述

Spring Cloud Eureka 是Netflix 开发的注册发现组件,本身是一个基于 REST 的服务。提供注册与发现,同时还提供了负载均衡、故障转移等能力。 Eureka3个角色 服务中心服务提供者服务消费者。 注意: Eureka Server:服务器端。它提…

【Linux】系统编程线程读写者模式(C++)

目录 一、读写锁 二、读写锁接口 【2.1】设置读写优先 【2.2】初始化 【2.3】销毁 【2.4】加读锁 【2.5】加写锁 【2.6】解锁 三、读写锁实例 一、读写锁 在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相…

接口自动化测试之Mock

【软件测试面试突击班】如何逼自己一周刷完软件测试八股文教程,刷完面试就稳了,你也可以当高薪软件测试工程师(自动化测试) 1.Mock实现原理和实现机制 在某些时候,后端在开发接口的时候,处理逻辑非常复杂&a…

基于Java+SpringBoot+Vue+Uniapp奶茶在线下单小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户微信小程序端的主要功能有:管理员的主要功能有:具体实现截图详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考论文参考源码获取 前言 💗博主介绍:✌…

Linux 用户 用户组管理

用户 Linux系统是一个多用户多任务的分时操作系统,任何要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。每个用户账号都拥有一个唯一的用户名和各自的口令。用户在登录时键入正确的用户名和口令后&a…

ubuntu22.04编译DPDK19.08.2注意事项

下载:http://fast.dpdk.org/rel/dpdk-19.08.2.tar.xz 解压:tar -xvf dpdk-19.08.2.tar.xz (1)设置环境变量和编译 cd dpdk-stable-19.08.2 export RTE_SDKpwd export RTE_TARTGETx86_64-native-linuxapp-gcc make config Tx86…

Ae 效果:CC Simple Wire Removal

Keying/CC Simple Wire Removal Keying/CC Simple Wire Removal CC Simple Wire Removal (CC 简单线条移除)通过在两点之间创建一条指定宽度(厚度)的连线,然后将连线区域内的像素按指定方式进行填充,从而实…