【STM32】TIM定时器基本定时功能

第一部分:定时器基本定时的功能;

第二部分:定时器的输出比较功能;

第三部分:定时器输入捕获的功能;

第四部分:定时器的编码接口。

1 TIM简介

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

1.1 定时器类型

类型

编号

总线

功能

高级定时器

TIM1TIM8

APB2

拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能

通用定时器

TIM2TIM3TIM4TIM5

APB1

拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能

基本定时器

TIM6TIM7

APB1

拥有定时中断、主模式触发DAC的功能

STM32F103C8T6定时器资源:TIM1TIM2TIM3TIM4

1.1.1 基本定时器

基本定时器框图:拥有定时中断、主模式触发DAC的功能

下面三部分预分频器计数器自动重装载寄存器构成最基本的计数计时电路,因此叫时基单元。

预分频器之前连接的是基准计数时钟的输入,可以认为连接到了输入端,也就是CK_INT(72MHz)

预分频器对72MHz的计数时钟进行分频,预分频器写0就是不分频,写1就是二分频36MHz......,所以预分频器的值和实际的分频系数相差1,即实际分频系数=预分频器的值 + 1。这个预分频器是16位的,所以最大值是65535,也就是65536分频。

然后是计数器,计数器可以对预分频器后的计数时钟进行计数,计数时钟每来一个上升沿,计数器就加1,这个计数器也是16位的,所以里面的值可以从0一直加到65535,再加的话,计数器就会从0开始。所以计数器的值在计时过程中会不断的自增运行,当自增运行到目标值时,产生中断,那就完成了定时的任务,所以还需要一个存储目标值的寄存器,那就是自动重装载寄存器。

自动重装载寄存器也是16位的,它存储的就是我们写入的计数目标,在运行过程中,计数器值不断自增,自动重装载寄存器的值是固定的,当计数值等于自动重装值时,也就是计时时间到了,此时会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时。

UI那里向上的折线,代表这里会产生中断信号,像这种计数值等于重装值产生的中断,称为"更新中断",这个更新中断会通往NVIC,再配置好NVIC的定时器通道,那定时器的更新中断就能够得到CPU的响应了。

U向下的折线,代表这里会产生一个事件,这里对应的事件称为"更新事件",更新事件不会触发中断,但是可以触发内部其它电路的工作。

总结:从基准时钟,到预分频器,再到计数器,计数器自增,同时不断与自动重装寄存器进行比较,它俩值相等时,即计时时间到,这时会产生一个更新中断和更新事件,CPU响应更新中断,就完成定时中断的任务了。

主模式触发DAC(数字/模拟转换模块)功能:它能让内部的硬件在不受程序的控制下实现自动运行。

用途:在使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发一次DAC,让它输出下一个电压点。如果用正常的思路实现,就是先设置一个定时器产生中断,每隔一段时间在中断函数中调用代码手动触发一次DAC转换,然后DAC输出,这样也是没问题的,但是会使主程序处于频繁被中断的状态,会影响主程序的运行和其它中断的响应,所以定时器设置了一个主模式,使用这个主模式可以把定时器的更新事件映射到触发输出(Trigger Out, TRFO)的位置,然后TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不需要通过中断来触发DAC转换了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用。

1.1.2 通用定时器

复杂很多,中间部分还是时基单元

通用计数器那种模式是向上计数,即自增;通用定时器和高级定时器还支持向下计数模式(向下自减,减到0,再回到重装值申请中断)和中央对齐模式(先向上自增到重装值,申请中断,再向下自减,减到0,申请中断)

这部分是内外时钟源选择和主从触发模式的结构了。对于基本定时器而言,定时只能选择内部时钟,也就是系统频率72MHz;到了通用定时器这里,时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟。第一个外部时钟来自TIMx_ETR引脚上的外部时钟,这个ETR(External)引脚的位置,参考引脚定义表。

TIM2_CH1_ETR的意思是TIM2的CH1和ETR都是复用在这个位置,即PA0引脚。

这里就可以在TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路(对输入波形进行滤波),滤波后的信号,兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了,这一路电路称为"外部时钟模式2"。

除了外部ETR引脚可以提供时钟外,下面还有一路可以提供时钟,就是TRGI(Trigger In),它主要的作用是用作触发输入来使用的,这个触发输入可以触发定时器的从模式。暂时可以把TRGI看作外部时钟的输入来看,这一路称为"外部时钟模式1"。通过这一路的外部时钟有ETR引脚的信号ITR信号(ITR信号来自其他定时的TRGO处,连接方式见下图:TIM2的ITR0连到TIM1的TRGO上,TIM2的ITR1连到TIM8的TRGO上,TIM2的ITR2连到TIM1的TRG3上,TIM2的ITR3连到TIM4的TRGO上)可以实现定时器级联的功能。

通过这一路的外部时钟有TI1F_ED这里连接的是输入捕获单元的CH1引脚,也就是从CH1引脚获得时钟,后面加ED,是边沿的(Edge)意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效。

最后还能通过TI1FP1TI2FP2获得。其中TI1FP1连接到CH1引脚的时钟,其中TI2FP2连接到CH2引脚的时钟。

总结:外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚;一般情况外部时钟通过ETR引脚就可以了。

如果使用外部时钟,首选ETR引脚外部时钟模式2的输入。

定时的编码器接口,可以读取正交编码器的输出波形。

再看下面的电路,主要是两部分:

输出比较电路:总共有4个通道,分别对应CH1-CH4的引脚,可以用于输出PWM波形,驱动电机

输入捕获电路:总共有4个通道,分别对应CH1-CH4的引脚,可以用来测量输入方波的频率等

中间寄存器是捕获/比较寄存器,是输入捕获和输出比较共用的电路。输入捕获和输出比较不能同时使用。

1.1.3 高级定时器

和通用定时器相比多的地方,第一个是申请中断的地方,增加了重复次数计数器,实现每个几个计数周期才发生一次更新事件和更新中断(原来是每个计数周期都会发生更新),相当于对输出的更新信号又做了一次分频(59s*65536)

下面的是高级定时器对输出比较模块的升级了

DTG(Dead Time Generate)死区生成电路,右边的输出引脚由原来的一个变为两个互补的输出,可以输出一对互补的PWM波,这些电路是为了驱动三相无刷电机的(四轴飞行器、电动车的后轮、电钻等)

最后一部分就是刹车输入功能,为了给电机驱动提供安全保障的。如果外部引脚TIMx_BKIN(break in)产生刹车信号或者内部时钟失效,产生了故障,那么控制电路就会自动切断电机的输出,防止意外的发生。

1.2 定时中断基本结构

本小结的两任务-定时中断内部时钟源选择

左边是为时基单元提供时钟的部分,可以选择RCC内部时钟,也可以选择ETR引脚提供的外部时钟模式2。

第一个案例定时中断使用的是RCC内部时钟

第二个定时器外部时钟使用的是外部时钟模式2这一路。

中断输出控制就是一个中断输出的允许位。

1.2.1 预分频器时序

预分频器可以将计数器的时钟频率按1到65536之间的任意值分频。它是基于一个(在TIMx_PSC
寄存器中的)16位寄存器控制的16位计数器。这个控制寄存器带有缓冲器,它能够在工作时被改
变。新的预分频器参数在下一次更新事件到来时被采用。

第一行CK_PSC,预分频器的输入时钟,选内部时钟的话,一般是72MHz

第二行CNT_EN,计数器使能,高电平计数器正常运行,低电平计数器停止运行。

第三行CK_CNT,计数器时钟,它既是预分频器的时钟输出,也是计数器的时钟输入;开始时计数器未使能,计数器时钟不运行;然后使能后,前半段,预分频器系数为1,计数器的时钟等于预分频器前的时钟;后半段,预分频器系数变为2,计数器的时钟也就变为预分频器前时钟的一半了。

第四行计数器寄存器,在计数器时钟的驱动下,计数器寄存器也跟着时钟的上升沿不断自增。在中间位置FC之后,计数值变为0,可以推断出ARR(自动重装值)就是FC,当计数值和重装值相等,并且下一个时钟来临时,计数值才清零,同时第五行产生一个更新事件(UEV)。这就是一个计数周期的工作流程。

下面还有三行时序。

这里描述的是预分频器的一种缓冲机制,也就是预分频器实际是有两个,一个是供我们读写使用的,它并不直接决定分频系数;另外还有一个缓冲寄存器(影子寄存器),它才是真正起作用的。为了防止在一个周期中间修改了分频系数,这时这个变化不会立即生效,而是等到本次计数周期结束时产生了跟新事件,预分频器的值才会被传递到缓冲寄存器里面,这时才会生效。

最后预分频器内部实际也是靠计数来分频的。当预分频值是0时,计数器就一直为0,直接输出原频率;当预分频值为1时,计数器就一直0、1、0、1、0、1......这样计数,在回到0的时候输出一个脉冲。这样输出频率就是输入频率的二分频。预分频器的值和实际的分频系数之间有一个数的偏移。

计数器时钟:CK_CNT

预分频器的输入时钟:CK_PSC

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

PSC就是0分频、1分频、2分频......

1.2.2 计数器时序

第一行CK_INT是内部时钟72MHz;

第二行是时钟使能,高电平正常运行,低电平停止运行。

第三行是计数器时钟,因为分频系数位2,所以这个分频是上面的1/2;然后第四行计数器寄存器的值在上升沿自增,增到0036时,发生溢出,此时再来一个上升沿,计数器清零,计数器溢出(第五行),产生一个更新事件脉冲(第六行),另外还会更新中断标志(UIF),这个标志位只要置1了,就会去申请中断,然后中断响应后,需要在中断程序中手动清零。

计数器溢出(中断)频率:CK_CNT_OV = CK_CNT / (ARR + 1)

                                                    = CK_PSC / (PSC + 1) / (ARR + 1)

ARR自动重装值

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

寄存器图中有阴影的都有影子寄存器(缓冲寄存器)

1.2.3 计数器无预装时序

计数器时序图,当ARPE=0时的更新事件(TIMx_ARR没有预装入),即没有影子寄存器(缓冲寄存器)

计数器正在自增计数,突然更改自动加载寄存器,就是自动重装寄存器,由FF变成36,所以上面计到36就直接更新,开始下一轮计数。

1.2.4 计数器有预装时序

计数器时序图,当ARPE=1时的更新事件(预装入了TIMx_ARR),即有影子寄存器(缓冲寄存器)

在计数的中途,把计数目标由F5改成36,下面有个影子寄存器,这个影子寄存器才是真正起作用的,它还是F5,等计数到F5时,产生更新事件;同时要更改的36才被传到影子寄存器,在下一个计数周期更改的36才有效。引入影子寄存器实际为了同步,就是让值的变化和更新事件同步发生,防止在运行中途更改造成错误。图中改成36,而F1大于36,如果没有影子寄存器,计数就会一直加,加到FF,再从0开始加到36,才能产生更新,这就造成一些小问题。

1.2.5 RCC时钟树

这个时钟树就是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统。时钟是所有外设运行的基础,所以时钟也是最先需要配置的东西。主函数之前的SystemInit中配置时钟。

在时钟产生电路有四个振荡源,分别是:

(1)内部的8MHz高速RC振荡器(提供系统时钟);

(2)外部的4-16MHz高速石英晶体振荡器,也就是晶振,一般接8MHz(提供系统时钟);

(3)外部的32.768KHz低速晶振,一般给RTC提供时钟;

(4)最后是内部的40KHz低速RC振荡器,可以给看门狗提供时钟。

AHB、APB1、APB2的时钟都是来自前两个高速晶振。外部的石英晶振要比内部的RC振荡器更加稳定,所以一般使用外部晶振。

SystemInit配置的流程是:首先会启动内部时钟,选择内部8MHz为系统时钟,暂时以内部8MHz的时钟运行;然后再启动外部时钟,进入PLL锁相环进行倍频,8MHz的9倍是72MHz,等到锁相环输出稳定后,选择锁相环输出为系统时钟,这样就把系统时钟由8MHz切换成72MHz。

锁相环:

为什么是72MHz:

如果外部晶振出问题了,程序时钟大概慢10倍(1s到10s),会以内部的8MHz运行(不是9倍吗)

CSS(Clock Security System)时钟安全系统:负责切换时钟,可以检测外部时钟的运行状态,一旦外部时钟失效,它就会自动把外部时钟切换回内部时钟,保证系统时钟的允许,防止程序卡死造成事故。

看右边的时钟分配电路。系统时钟72MHz进入AHB总线,AHB总线有个预分频器,在SystemInit中配置的分配系数是1,那AHB的时钟就是72MHz;然后进入APB1总线,这里的配置的分配系数是2,所以APB1总线的时钟是36MHz。

之前一直说无论高级定时器、通用定时器、基本定时器时钟都是72MHz,可以看下面的分支,APB1的预分频系数如果是1,则频率不变,否则频率*2,即36 * 2 = 72MHz。

APB2的分频系数是1,所以APB2的时钟和AHB的时钟一样,都是72MHz。

外部时钟使能对应代码

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

手册

2 TIM定时器之定时器中断

2.1 接线图

2.2 模块化

按照这个图进行初始化

步骤:

(1)RCC开启时钟;

(2)选择时基单元的时钟源(内部时钟);

(3)配置时基单元(预分频器、自动重装器、计数模式等);

(4)配置输出中断控制,允许更新中断输出到NVIC;

(5)配置NVIC,在NVIC中打开定时器中断通道,并分配一个优先级;

(6)启动计数器。

定时器的库函数

// 恢复缺省配置
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);
// 选择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); 
// 外部时钟模式1
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t             TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// ETR外部时钟2                       
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// 配置参数
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t     TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);                                        // 单独写预分频值
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);

Timer.c

#include "stm32f10x.h"                  // Device header// 定时器初始化
void Timer_Init(void)
{// 第一步RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 第二步选择时基单元的时钟源(内部时钟)TIM_InternalClockConfig(TIM2);// 第三步配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数,如果想定时1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)
//			 1 = 72000000 / (PSC + 1) / (ARR + 1)
//		     1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				// ARR自动重装器的值 两个合起来计数1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				// PSC预分频器的值,7200分频,得到10k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);                           // 防止复位从1开始// 第四步配置输出中断控制,允许更新中断输出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打开定时器中断通道,并分配一个优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        			// 分组NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;           			// 定时器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;			// 抢占优先级NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;					// 响应优先级NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;					// 使能NVIC_Init(&NVIC_InitStruct);// 第六步启动计数器TIM_Cmd(TIM2, ENABLE);
}// 中断函数
//void TIM2_IRQHandler(void)
//{
//	// 检测中断标志位,确保是设置的中断源触发的这个函数
//	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
//	{
//	
//		// 清除中断标志位
//		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//	}
//}

2.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中断函数
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init();								// 初始化OLEDTimer_Init();								// 初始化定时器OLED_ShowString(1, 1, "Num:");   			// 显示字符串while (1){OLED_ShowNum(1, 5, num, 5);   	    			// 显示计数OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);    // 显示CNT计数器,最大值9999}
}// 中断函数
void TIM2_IRQHandler(void)
{// 检测中断标志位,确保是设置的中断源触发的这个函数if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中断处理num++;// 清除中断标志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

现象:定时1s在计数

重点在这里

	// 第三步配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数,如果想定时1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)
//			 1 = 72000000 / (PSC + 1) / (ARR + 1)
//		     1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				// ARR自动重装器的值 两个合起来计数1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				// PSC预分频器的值,7200分频,得到10k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);      

3 TIM定时器之定时器外部时钟

3.1 接线图

3.2 模块化

任务依然是定时中断,但是时钟部分,不使用内部时钟了,

Timer

#include "stm32f10x.h"                  // Device header// 定时器初始化
void Timer_Init(void)
{// 第一步RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      // 上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第二步选择时基单元的时钟源(外部时钟)// 不分频,不反向(高电平/上升沿有效),外部触发滤波器:不用TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);// 第三步配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数,如果想定时1S
//	CK_CNT_OV = CK_CNT / (ARR + 1)
//			  = CK_PSC / (PSC + 1) / (ARR + 1)TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					// ARR自动重装器的值,0-9TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				// PSC预分频器的值,不分频TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);                           // 防止复位从1开始// 第四步配置输出中断控制,允许更新中断输出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打开定时器中断通道,并分配一个优先级NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        			// 分组NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;           			// 定时器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;			// 抢占优先级NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;					// 响应优先级NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;					// 使能NVIC_Init(&NVIC_InitStruct);// 第六步启动计数器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);
//	}
//}

3.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中断函数
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init();								// 初始化OLEDTimer_Init();								// 初始化定时器OLED_ShowString(1, 1, "Num:");   			// 显示字符串OLED_ShowString(2, 1, "Cnt:");   			// 显示字符串while (1){OLED_ShowNum(1, 5, num, 5);   	    			// 显示计数OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);    // 显示CNT计数器,最大值9999}
}// 中断函数
void TIM2_IRQHandler(void)
{// 检测中断标志位,确保是设置的中断源触发的这个函数if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中断处理num++;// 清除中断标志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

现象:挡一下对射式红外传感器,计数器加1,加到9时,触发定时中断,定时中断加1。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验报告-实验四(时序系统实验)

软件模拟电路图 说明 SW:开关,共六个Q1~Q3:输出Y0~Y3:输出 74LS194 首先,要给S1和S0高电位,将A~D的数据存入寄存器中(如果开始没有存入数据,那么就是0000在里面移位,不…

“新KG”视点 | 知识与大模型融合技术在电信领域应用探索

OpenKG 大模型专辑 导读 知识图谱和大型语言模型都是用来表示和处理知识的手段。大模型补足了理解语言的能力,知识图谱则丰富了表示知识的方式,两者的深度结合必将为人工智能提供更为全面、可靠、可控的知识处理方法。在这一背景下,OpenKG组织…

SI24R03 高度集成低功耗SOC 2.4G 收发一体芯片

今天给大家介绍一款Soc 2.4G 收发一体模块-SI24R03 Si24R03是一款高度集成的低功耗无线SOC芯片,芯片为QFN32 5x5mm封装,集成了资源丰富的MCU内核与2.4G收发器模块,最低功耗可达1.6uA,极少外围器件,大幅降低系统应用成本…

DNS协议(DNS规范、DNS报文、DNS智能选路)

目录 DNS协议基本概念 DNS相关规范 DNS服务器的记录 DNS报文 DNS域名查询的两种方式 DNS工作过程 DNS智能选路 DNS协议基本概念 DNS的背景 我们知道主机通信需要依靠IP地址,但是每次通过输入对方的IP地址和对端通信不够方便,IP地址不好记忆 因此提…

【Spring Boot】如何在IntelliJ IDEA中由同一份spring boot源码运行多个不同端口的实例

我们需要使用一个服务有多个实例的测试场景,那么我们就需要在IntelliJ IDEA中通过不同的端口运行不同的实例,并且运行时的源代码是一样的,那么我们可以在IntelliJ IDEA这样操作,接下来以UserApplication服务为例: 复制…

华为OD机试 - 九宫格按键输入 - 逻辑分析(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷&#…

Linux系统之部署Plik临时文件上传系统

Linux系统之部署Plik临时文件上传系统 一、Plik介绍1.1 Plik简介1.2 Plik特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、下载Plik软件包4.1 创建下载目录4.2 下载Plik软件包4.3 查看下载的Plik软件…

Python sorted函数及用法以及如何用json模块存储数据

Python sorted函数及用法 sorted() 函数与 reversed() 函数类似,该函数接收一个可迭代对象作为参数,返回一个对元素排序的列表。 在交互式解释器中测试该函数,可以看到如下运行过程: >>> a [20, 30, -1.2, 3.5, 90, 3.…

【已解决】Win10端口被占用

​ 我总是在启动项目的时候失败,被告知端口号被占用,明明没有被占用(可能是系统卡了或者其它问题),但是又不想改端口号,或者重启电脑,那怎么办呢? 第一步:打开命令行窗口,以管理员…

植物单细胞基础工程之标记基因数据库

前 言 单细胞转录组这把火已经爆燃到了植物领域。 单次实验即可获得上万个细胞的基因表达数据(大老远都觉得香),直接把分辨率从组织水平干到了单个细胞水平(当然,最后说事大多数还是基于细胞cluster或者sub cluster&…

vr工业制造流程3D模拟仿真可视化展示

工业仿真3D数字化展示系统具有多方面的独特之处,主要体现在以下几个方面: 1、真实感和交互性:该系统可以将实际的工业设备、产品、场景等进行数字化建模,通过三维图形技术将其呈现在计算机屏幕上,使用户可以在虚拟环境…

Matlab 加权均值质心计算(WMN)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 思路很简单,即将之前的均值中心,引入相关的权重函数(通常与距离有关),以此为每个点进行赋权,最后即可得到一个加权均值中心: 二、实现代码 %% ********<

vue+elementUI的tabs与table表格联动固定与滚动位置

有个变态的需求&#xff0c;要求tabs左侧固定&#xff0c;右侧是表格&#xff0c;点击左侧tab&#xff0c;右侧表格滚动到指定位置&#xff0c;同时&#xff0c;右侧滚动的时候&#xff0c;左侧tab高亮相应的item 上图 右侧的高度非常高&#xff0c;内容非常多 常规的瞄点不适…

stm32项目(11)——基于stm32的俄罗斯方块游戏机

1.功能设计 使用stm32f103zet6平台&#xff0c;以及一块LCD屏幕&#xff0c;实现了一个俄罗斯方块游戏机。可以用按键调整方块的位置、还可以控制方块下降的速度&#xff01; 2.视频演示 俄罗斯方块 3.俄罗斯方块发展史 俄罗斯方块是一种经典的拼图游戏&#xff0c;由苏联俄罗…

SpringBoot_02

Web后端开发_07 SpringBoot_02 SpringBoot原理 1.配置优先级 1.1配置 SpringBoot中支持三种格式的配置文件&#xff1a; application.propertiesapplication.ymlapplication.yaml properties、yaml、yml三种配置文件&#xff0c;优先级最高的是properties 配置文件优先级…

LoadRunner12.55的简介与安装

提示&#xff1a;https://mp.weixin.qq.com/s/iK-fh0VP7v8mNSDNxjkBow 文章目录 LoadRunner的简介与安装loadrunner概述loadrunner的下载与安装 LoadRunner的使用启用VuGen LoadRunner的简介与安装 LoadRunner官网&#xff1a;https://www.microfocus.com/zh-cn/products/load…

ESP Multi-Room Music 方案:支持音频实时同步播放 实现音乐互联共享

项目背景 随着无线通信技术的发展&#xff0c;针对不同音频应用领域的无线音频产品正不断涌现。近日&#xff0c;乐鑫科技推出了基于 Wi-Fi 的多扬声器互联共享音乐通信协议——ESP Multi-Room Music 方案。该方案使用乐鑫自研的基于 Wi-Fi 局域网的音频同步播放技术&#xff…

Pycharm修改文件默认打开方式 + CSV Editor插件使用

1、File —> Settings —> Editor —> File Types 然后将*csv添加到最上面 在plugins中下载插件&#xff0c;CSV Editor 备注&#xff1a;不在上一步的“File Types”中将*.csv设置为CSV格式&#xff0c;插件是不起作用的 就可以使用了

CRM在设备制造行业的应用,优化资源配置

设备制造业竞争激烈&#xff0c;公司要以客户为中心&#xff0c;搞好售后服务。CRM管理软件是设备制造业客户关系管理的重要工具。以下是CRM在设备制造业里的典型应用。 1.营销管理 制订市场策略&#xff1a;设备制造通常涉及较长的决策周期和销售周期。客户可能会在多家供货商…

PHP+vue+elementui高校学生社团信息管理系统o7q4a

社团是由高校用户依据兴趣爱好自愿组成&#xff0c;按照章程自主开展活动的用户组织。高校社团是实施素质教育的重要途径和有效方式&#xff0c;在加强校园文化建设、提高用户综合素质、引导用户适应社会、促进用户交流等方面发挥着重要作用&#xff0c;是新形势下有效凝聚用户…