stm32学习笔记---TIM输出比较(代码部分)定时器定时中断/定时器外部时钟

目录

第一个代码:定时器定时中断

Timer.c

初始化函数

初始化定时器的步骤

定时器的库函数

TIM_DeInit

TIM_TimeBaseInit

TIM_TimeBaseStructInit

TIM_Cmd

TIM_ITConfig

TIM_InternalClockConfig

TIM_ITRxExternalClockConfig

TIM_InputTriggerSource

TIM_TIxExternalClockConfig

TIM_ICPolarity和ICFilter

TIM_ETRClockMode1Config

TIM_ETRClockMode2Config

TIM_ETRConfig

TIM_PrescalerConfig

TIM_CounterModeConfig

TIM_SetCounter

TIM_SetAutoreload

TIM_GetCounterr

TIM_GetPrescaler

其他四个函数

第一步,RCC开启时钟

第二步,选择时基单元的时钟源

第三步,配置时基单元

第四步,配置输出中断控制

第五步,配置NVIC

第六步,运行控制

中断函数

Timer.h

main.c

第二个代码:定时器外部时钟

Timer.c

选择时钟源

配置GPIO

预分频和自动重装值

封装CNT计数器的值

Timer.h

main.c


声明:本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记,我之所以记录下来是为了方便自己日后复习。如果你也是跟着江科大的视频学习的,可以配套本专栏食用,如有问题可以QQ交流群:963138186

本节我们来学习一下定时中断和内外时钟段选择的代码部分。

第一个代码:定时器定时中断

接线图:

复制OLED显示屏那一节的工程并改名

定时器不涉及外部的硬件,所以可以把它放到system文件夹里面。当然你也可以放在其他文件夹里,这个都没问题。

Timer.c

老规矩,上来就写初始化函数。

初始化函数

我们要初始化定时器的话,就看这张图

这个是定时中断的整个框架结构,我们只需要把这里面的每个模块都打通,就可以让定时器工作了。

初始化定时器的步骤

大体上的步骤就是:

第一步,RCC开启时钟这个基本上每个代码都是第一步。在这里打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了。

第二步选择时基单元的时钟源,对于定时中断,我们就选择内部时钟源。

第三步配置时基单元包括这里的预分频器、自动重装器、计数模式等等。这些参数用一个结构体就可以配置好了。

第四步配置输出中断控制,允许更新中断输出到NVIC。

第五步配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。这部分在上节我们也用过,流程基本是一样的。

第六步运行控制整个模块配置完成后,我们还需要使能一下计数器。要不然计数器是不会运行的,当定时器使能后,计数器就会开始计数了。当计数器更新时触发中断。最后我们再写一个定时器的中断函数,这样这个中断函数每隔一段时间就能自动执行一次了。

这些就是我们初始化定时器的大体思路了。

定时器的库函数

打开tim.h文件,拖到最后,可以看到库函数的数量是非常多。但是我们先把我们本节需要用的函数挑出来学习,其他的之后再慢慢学。

TIM_DeInit

TIM_DeInit恢复缺省配置,这个不用多说。

TIM_TimeBaseInit

TIM_TimeBaseInit时基单元初始化,这函数比较重要,它就是用来配置这个图里这里的时基单元的。

这里面有两个参数,第一个TIMx选择某个定时器。第二个是结构体,里面包含了配置时基单元的一些参数。

我们在这里先把我们讲过的函数都做个标记,这样等会儿方便查找。我们可以把光标放在我们想做标记的行,然后点这个按钮。这样就可以在这行代码左边添加一个书签,如果再点一下,就会清除书签。这个对代码的运行没有任何影响。

TIM_TimeBaseStructInit

TIM_TimeBaseStructInit这个函数可以把结构体变量赋一个默认值。这个之前我们也都见过,没啥说的,我们也做个标记。

TIM_Cmd

TIM_Cmd这个是用来使能计数器的,对应的就是我们这个图里的这个位置:运行控制。

它有两个参数,第一个TIMx选择定时器,第二个NewState新的状态,也就是使能还是失能,使能计数器就可以运行,失能计数器就不运行,我们也标记一下。

TIM_ITConfig

TIM_ITConfig这个是用来使能中断输出信号的,对应的就是这个位置:中断输出控制。

它的参数看一下,第一个TIMx选择定时器。第二个TIM_IT选择要配置哪个中断输出,第三个,NewState新的状态,失能还是失能。这种TIM_ITConfig函数之后,还会经常遇到,就是使能外设的中断输出。

我们继续接下来看一下这下面的六个函数。

这六个函数对应的就是这里时基单元的时钟选择部分

可以选择RCC内部时钟,ETR外部时钟,ITRx其他定时器,TIx捕获通道这些。

TIM_InternalClockConfig

选择内部时钟,参数只有一个TIMx,调用一下这里的连接就是这样的

TIM_ITRxExternalClockConfig

选择ITRx其他定时器的时钟。参数是TIMx,选择要配置的定时器和

TIM_InputTriggerSource

选择要接入哪个其他的定时器,调用一下,这里的连接就是这样的

TIM_TIxExternalClockConfig

选择TIx捕获通道的时钟。参数第一个TIMx不用说了,第二个TIM_TIxExternalCLKSource选择TIx具体的某个引脚。

TIM_ICPolarity和ICFilter

输入的极性和滤波器。对于外部引脚的波形,一般都会有极限选择和滤波器,这样更灵活一些,调用一下这个函数,这个图里就是这样连接的

TIM_ETRClockMode1Config

选择ETR通过外部时钟模式1输入的时钟,也就是这一路

它的参数TIM_ExtTRGPrescaler外部触发预分频器,这里可以对ETR的外部时钟再提前做一个分频。

TIM_ETRClockMode2Config

选择ETR通过外部时钟模式2输入的时钟,对应的就是这一路

它的参数和上面一个函数一模一样,对于ETR输入的外部时钟而言,这两个函数是等效的,参数也是一样的,如果不需要触发输入的功能,两个函数可以互换。

TIM_ETRConfig

这个不是用来选择时钟的,用单独用来配置ETR引脚的预分频器,极性,滤波器这些函数的。

这个图里关键部分的函数基本就讲完了,时钟源选择就用这里的六个函数

时基单元用TIM_TimeBaseInit的函数;

中断输出控制,用TIM_ITConfig函数;

NVIC用上节讲过的函数;

运行控制用TIM_Cmd函数。

这样初始化基本上就ok了。

接下来我们再看几个函数。

因为在初始化结构体里有很多关键的参数,比如自动重装值和预分频值等等。这些参数可能会在初始化之后还需要更改。如果未来改某个参数,还要再调用一次初始化函数,太麻烦了,所以这里有一些单独的函数,可以方便的更改这些关键参数。

TIM_PrescalerConfig

比如这里的TIM_PrescalerConfig就是用来单独写预分频值的

参数Prescaler就是要写入的预分并值,TIM_PSCReloadMode写入的模式。我们上节说了,预分频器有个缓冲器写入的值是在更新事件发生后才有效的,所以这里有个写入的模式,可以选择是听从安排,在更新事件生效。或者是在写入后手动产生一个更新事件,让这个值立刻生效。不过这些都是细节问题,影响不大,只要知道这个是写预分频值的函数就行了。

TIM_CounterModeConfig

用来改变计数器的计数模式,参数TIM_CounterMode选择新的计数器模式。TIM_ARRPreloadConfig自动重装器预装功能配置,前面介绍了这个计数器的预装功能,有预装还是无预装是可以自己选择的。调用一下这个函数给个参数,使能还是失能就行了。

TIM_SetCounter

给计数器写入一个值。如果你想手动给一个计数值,就可以用这个函数。

TIM_SetAutoreload

给自动重装器写一个值。如果你想手动给一个自动重装值,就可以用这个函数。

TIM_GetCounterr

获取当前计数器的值。如果你想看当前计数器计到哪里了,就可以调用一下这个函数。返回值就是当前的计数器的值。

TIM_GetPrescaler

获取当前的预分频器的值。如果想看预分频值,就调一下这个函数。

其他四个函数

最后再看一下后面的这四个函数,

这四个函数熟悉,上节我们也见过。这些就是用来获取标志位和清除标志位的。

本小节我们就暂时介绍这么多。

在这里我准备初始化的是TM2,也就是通用定时器。

第一步,RCC开启时钟

这里注意要使用APB1开启时钟函数,因为TIM2APB1总线的外设

第二步选择时基单元的时钟源

打开tim.h文件,调用一下这个函数,选择内部时钟源

这样TM2的时基单元就由内部时钟来驱动了,其实不写这行代码也行,因为会默认是内部时钟

第三步配置时基单元

打开tim.h文件,调用一下这个函数。

第二个参数是个结构体就需要转到它的定义找到它的结构体类型,然后取个结构体变量名,再引出成员,并赋值,最后调用这个初始化时基单元的函数,将结构体的地址传过去。

TIM_ClockDivision这个成员的作用是什么呢?

我们之前说了,在这个定时器的外部信号输入引脚一般都会有一个滤波器,它可以滤掉信号的抖动干扰。

它工作的原理是在一个固定的时钟频率f下进行采样。如果连续n个采样点都为相同的电平,就代表输入信号稳定了,就把这个采样值输出出去。如果这n个采样值不全都相同,就说明信号有抖动,这时就保持上一次的输出或者直接输出低电平也行,这样就能保证输出信号在一定程度上的滤波。这里的采样频率f和采样点数n都是滤波器的参数,频率越低,采样点数越多,滤波效果就越好,不过相应的信号延迟就越大。这就是这个滤波器的工作原理。

f和n的关系在手册的这里也有说明,需要可以去看一下

现在关键的地方来了,这个采样频率f从哪来?

手册里写的是,它可以是由内部时钟加一个时钟直接而来,也可以是由内部时钟加一个时钟分频而来。

分频多少就是由我们这个参数可TIM_ClockDivision决定的。我们在理论部分并没有提到这个参数,可见这个参数其实跟时基单元关系并不大,它的取值随便配一个就行了。我们可以选择1分频。

TIM_CounterMode这个成员的取值分别是向上计数,向下计数和三种中央对齐的模式。我们选择向上计数。

这里并没有CNT计数器的成员,这个如果我们之后需要的话,可以用之前说的set counter和get counter这两个函数来操作计数器。

TIM_Period的取值可以参照这个式子,比如定时1s,也就是定时频率为1Hz,那么PSC可以是10000-1,ARR可以是7200-1,这样预分频是对72M进行7200分频,得到的就是10k的计数频率,在10k的频率下计10000个数,不就是一秒的时间吗?

数值不是唯一的,可以预分频给少点,自动重装给多点,这样就是以一个比较高的频率,记比较多的数。也可以预分频给多点,自动重装给少点,这样就是以一个比较低的频率,记比较少的数。

到这里,时基单元就配置好了,接下来我们就需要使能更新中断了。

第四步配置输出中断控制

这个函数就是用来使能中断的,

这样就开启了更新中断到NVIC的通路。

第五步配置NVIC

下一步自然就是NVIC

第六步运行控制

最后一步启动定时器,到tim.h文件中找一下这个函数使能TIM

这样定时器就可以开始工作了。当产生更新时,就可以触发中断。

到这里整个定时中断的初始化代码就完成了。

中断函数

接下来我们就可以写中断函数了,我们打开启动文件,找到这个TIM2的中断函数名字

这样我们的代码就已经基本完事了。

#include "stm32f10x.h"                  // Device header/*** 函    数:定时中断初始化* 参    数:无* 返 回 值:无*/
void Timer_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟/*配置时钟源*/TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值//因为预分频器和计数器都有1个数的偏差,所以这里都要再减个1TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到,我们现在不需要,直接给0TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	/*中断输出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位//TIM_TimeBaseInit函数末尾,手动产生了更新事件//若不清除此标志位,则开启中断后,会立刻进入一次中断//如果不介意此问题,则不清除此标志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//想看更新中断的标志位,就写TIM_IT_Update{TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/

Timer.h

#ifndef __TIMER_H
#define __TIMER_Hvoid Timer_Init(void);#endif

main.c

我们想让定时器每秒自动帮我们加一下number这个变量。所以我先定义一个变量.

然后这个number要在中断函数里执行加加,但是这个中断函数是在timer模块里的。如果直接在这里写number加加,number就是跨越不同点c文件的变量了,这样编译就会报错。解决方法有两种。

第一种是如果你想跨文件使用变量可以在使用变量的那个文件的上面用extern声明一下要用的变量。就是告诉编译器,现在有number这个变量,它在别的文件里定义了,至于在哪里,编译器要自己去找。注意这个过程并没有定义新的变量,它操作的还是main.c里的这个number。

我们在Timer.c里面声明了这个变量之后,就可以在该文件下方的中断函数中使用这个变量了

第二种方式就是直接把这个中断函数挪到main.c文件下,就不需要声明了。

我们一般都是直接把中断函数放到使用它的文件下,所以还是挪到main.c下

最后在屏幕中显示一下Number的值

但是这样运行的结果是每次复位后都从1开始计数,看不到0这个数值,这是为什么呢?

我们打开tim.h,找到TIM_TimeBaseInit函数,然后跳转到它的定义,可以看到它的定义里有这一句注释:

意思是生成一个更新事件来立刻重新装载预分频器和重复计数器的值,为什么要加这一句?我们知道这个预分频器是有一个缓冲寄存器的,我们写的值只有在更新事件时才会真正其作用。所以这里为了让值立刻起作用,就在这最后手动生成了一个更新时件,这样预分频器的值就有效了。但同时它的副作用就是更新事件和更新中断是同时发生的,更新中断会置更新中断标志位。一旦初始化晚了,更新中断就会立刻进入。

这就是我们刚一上电就立刻进中断的原因。

解决方案也非常简单,就是在TIM_TimeBaseInit后面,和开启中断的前面,在这里调用一下这个函数,这样再手动把更新中断,标志位清除一下,就能避免刚初始化完就进中断的问题了。

这样就没问题了,上电后数字从零开始增加。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"uint16_t Num;			//定义在定时器中断里自增的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化Timer_Init();		//定时中断初始化/*显示静态字符串*/OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:while (1){OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//看一下CNT计数器值}
}/*** 函    数:TIM2中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断{Num ++;												//Num变量自增,用于测试定时中断TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}

运行结果:

STM32-定时器定时中断

在这里我们还可以看一下CNT计数器值的变化情况

这时可以看到CNT的值在飞速变化,变化范围应该就是从0一直到自动重装值。我们的自动重装值刚刚写的是10000-1,所以这个值就是从0一直自增到9999,总共是一万个数,记一万次就是1秒。如果我们把自动重装值改成1000,就是由原来记一万个数变成了记一千个数,这时,这个值就是从零加到了999了,对应的就是0.1秒,那就可以看到上面这个数字(也就是刚刚Num的值)加加的速度也是比原来快了十倍。

如果我们把这里改成这样:

就是以原来十倍的计数频率记10000个数,这时number加加的速度也是原来的10倍和刚才是一样的。这就是预分频值和自动重装值对中断频率的影响。

接下来我们就来开始写第二个代码。

第二个代码:定时器外部时钟

接线图:

右下角是一个OLED显示屏,上面接了一个对射式红外传感器

复制上一个工程改名:

现在我们的基本任务仍然是定时中断,但是这个时钟部分我们就不使用内部时钟了

Timer.c

其他都不变,只需要修改这些就可以了:

选择时钟源

我们到tim.h里找一下选择时钟的一个函数

我们要通过ETR引脚的外部时钟模式2配置,第二个参数是时外部触发预分频器,我们选择不需要分频。第三个参数是外部触发的极性(触发极性不是指触发信号本身的正负,而是指由它的上升沿或下降沿触发或者其他),我们可以选择不反向。第四个参数是外部触发滤波器,我们这里就暂时不用滤波器了,所以这个位置写0x00就行了。

这样通过ETR的外部时钟模式2就配置好了。

配置GPIO

这个GPIO_Mode这个可以看一下手册的配置表,手册中推荐配置是浮空输入。但是浮空输入一旦悬空,电平就会跳个没完。

所以我们可以配置为上拉输入。

什么时候需要用浮空输入?

如果外部的输入信号功率很小,内部的这个上拉电阻可能会影响到这个输入信号,这时就可以用一下浮空输入,防止影响外部输入的电平。

预分频和自动重装值

下面这个预分频和自动重装值也改小点,我们手动模拟的没那么快。预分频就给1,不需要分频。自动重装值给10,从0寄到9就行了。

封装CNT计数器的值

我们想实时看一下CNT计数器的值,我们也把它的函数也封装一下:

完整代码:

Timer.c

#include "stm32f10x.h"                  // Device header/*** 函    数:定时中断初始化* 参    数:无* 返 回 值:无* 注意事项:此函数配置为外部时钟,定时器相当于计数器*/
void Timer_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/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);						//将PA0引脚初始化为上拉输入/*外部时钟配置*/TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//选择外部时钟模式2,时钟从TIM_ETR引脚输入//注意TIM2的ETR引脚固定为PA0,无法随意更改//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	/*中断输出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位//TIM_TimeBaseInit函数末尾,手动产生了更新事件//若不清除此标志位,则开启中断后,会立刻进入一次中断//如果不介意此问题,则不清除此标志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}/*** 函    数:返回定时器CNT的值* 参    数:无* 返 回 值:定时器CNT的值,范围:0~65535*/
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2);	//返回定时器TIM2的CNT
}/* 定时器中断函数,可以复制到使用它的地方
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_Hvoid 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();		//OLED初始化Timer_Init();		//定时中断初始化/*显示静态字符串*/OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:OLED_ShowString(2, 1, "CNT:");			//2行1列显示字符串CNT:while (1){OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量OLED_ShowNum(2, 5, Timer_GetCounter(), 5);		//不断刷新显示CNT的值}
}/*** 函    数:TIM2中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断{Num ++;												//Num变量自增,用于测试定时中断TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位//中断标志位必须清除//否则中断将连续不断地触发,导致主程序卡死}
}

运行结果:

STM32-定时器外部时钟

用挡块片挡一下CNT加1,因为现在时基单元没有预分频,所以每次遮挡CNT都会加1。如果有预分频,就是遮挡几次才能加1次。

然后加到9后自动清零,同时申请中断,Num就加1。

本节的代码部分到这里就结束了,下节继续。

QQ交流群:963138186

本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓

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

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

相关文章

淘宝扭蛋机小程序开发,探索市场新的发展方向

如今,潮玩已经成为了年轻人娱乐消费的首选方式之一,发展态势也在不断上升,吸引了众多年轻人的关注。在小程序的发展下,也推动了扭蛋机市场的创新,淘宝扭蛋机小程序就是一个新的模式,为扭蛋机市场带来了新的…

时钟的抖动(Jitter)与偏移(Skew)

时钟的抖动(Jitter)与偏移(Skew)是数字系统时序分析中的两个重要概念,它们对系统的性能和稳定性有着显著的影响。以下是关于时钟抖动和偏移的详细解释: 时钟抖动(Jitter) 定义&…

VB求高于平均成绩的分数

有3个学生,每个学生4门课。 先求每个学生的平均成绩,然后展示高于平均成绩的分数。 Public Class Form1Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.ClickDim pj%, i%, n%, sum%, say$Dim cj(0 To 3) As Integeri 1…

Linux—LVM与磁盘配额

目录 一、LVM 1、LVM概念 2、LVM逻辑卷核心组件 3、LVM管理命令 二、LVM操作主要命令步骤 1、添加硬盘 2、新建分区,并修改分区类型 3、新建物理卷(PV) 4、新建卷组(VG) 5、新建逻辑卷(LV&#xff0…

帮您理解PostgreSQL(WAL、XLOG、CheckPoint进程、LSN、PITR、SR)

文章目录 一、WAL、XLOG、LSN二、检查点进程与pg_control文件-负责脏页刷盘、数据库恢复三、基础备份与时间点恢复PITR四、原生复制功能与流复制(SR Streaming Replication) 一、WAL、XLOG、LSN 在计算机领域,WAL是Write Ahead Logging的缩写…

Typora配置自建的兰空图床

文章目录 Typora配置自建的兰空图床 - 前言先看效果1、搭建兰空图床 - docker2、配置兰空图床3、登录进入兰空图床后台4、Typora配置兰空图床安装兰空插件获取兰空图床的Token编辑PigGO的配置文件 使用 Typora配置自建的兰空图床 - 前言 Typora插入的图片默认存储在本地&#…

仓库管理系统07--顶部标题设计

1、创建全局变量 2、应用全局变量 1)主窗体应用 2)登录窗体应用 3、自定义弹窗 弹窗中各按钮的事件代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows…

odoo17 tree视图添加按钮

需求描述 点击下图中tree视图上的同步退货单按钮,弹出相应的form视图进行退货单同步,然后点击同步按钮调用后端python代码处理。 实现步骤 主要文件目录结构 js文件的创建 /** odoo-module **/ import { registry } from "web/core/registry&quo…

证件照制作工具有哪些?分享当下热门的证件照制作工具

无论是考证、出国旅游还是应聘,一张符合标准的证件照成了必备之物。 如果手头的证件照尺寸不符合要求,不必惊慌,现在有多种证件照制作软件可以帮助你迅速解决问题。 今天,本文就为大家分享几个证件照制作教程,让你的…

基于单片机的智能温控风扇设计

摘 要 : 本次设计是基于单片机的智能温控风扇 。 以 STC89C52 单片机为核心 , 可以实现对风扇的有效控制 。 可以根据需要设置不同的温度 ,如果温度在设定值最大值和最小值之间时则启动风扇弱风档, 如果温度超过设定的数值时将会变到大风档…

一文学会用Helm部署rancher 高可用集群

rancher集群架构图 Helm部署rancher 高可用集群 Helm简介 Helm是Kubernetes的一个包管理工具,用来简化Kubernetes应用的部署和管理。可以把Helm比作CentOS的yum工具。 Helm有如下几个基本概念: Chart: 是Helm管理的安装包,里面包含需要部署的安装包资源。可以把Chart比作C…

什么牌子的开放式耳机好?五大优质机型,新手必看!小白闭眼入系列

音乐技术的不断进步为耳机市场的发展有了更多的选择,开放式耳机成为音乐爱好者们新的一个选择。从最初的基础音质到如今的高解析度音频,开放式耳机经历了一次次的技术革新和升级。这类耳机以开放式不入耳的设计,舒适的佩戴体验著称&#xff0…

44.商城系统(二十五):k8s基本操作,ingress域名访问,kubeSphere可视化安装

上一章我们已经配置好了k8s集群,如果没有配置好先去照着上面的配。 一、k8s入门操作 1.部署一个tomcat,测试容灾恢复 #在主机器上执行 kubectl create deployment tomcat6 --image=tomcat:6.0.53-jre8#查看k8s中的所有资源 kubectl get all kubectl get all -o wide#查看po…

探索 PrimeVue——开源项目的卓越之旅

嗨,大家好,我是徐小夕。之前一直在社区分享零代码&低代码的技术实践,也陆陆续续设计并开发了多款可视化搭建产品,比如: H5-Dooring(页面可视化搭建平台)V6.Dooring(可视化大屏搭…

【数学建模】—【Python库】—【Numpy】—【学习】

目录 ​编辑 1. NumPy安装 2. ndarray对象 1. 创建ndarray 1.从列表或元组创建: 2.使用内置函数创建: 2. ndarray属性 3. 数组运算 1. 基本运算 2. 数学函数 3.统计函数 4. 数组索引与切片 1. 一维数组索引与切片 2.多维数组索引与切片 5.…

如何在 CentOS 上卸载 Nginx?

本章教程,主要介绍如何彻底卸载删除nginx 一、停止nginx服务 sudo systemctl stop nginx二、卸载nginx服务 sudo yum remove nginx三、查找nginx相关文件 sudo find / -name *nginx*将nginx相关文件进行删除 四、删除nginx相关文件 这里是常见的一些nginx相关文件 s

机器学习/pytorch笔记:time2vec

1 概念部分 对于给定的标量时间概念 t,Time2Vec 的表示 t2v(t)是一个大小为 k1的向量,定义如下: 其中,t2v(t)[i]是 t2v(t)的第 i 个元素,F是一个周期性激活函数,ω和 ϕ是可学习的参数。 以下是个人理解&am…

Shark!一个基于遗传算法的自动因子挖掘平台

DolphinDB 推出的 CPU-GPU 异构计算平台 Shark,将 DolphinDB 上的复杂指标计算能力无缝切换到 GPU 算力平台,大幅提升了计算性能。Shark 最新版本给开发者提供了两个主要功能:因子挖掘和因子计算。通过使用遗传算法,因子挖掘功能能…

编程哲学——抽象

主要参考资料: App Image Format: https://docs.espressif.com/projects/esp-idf/zh_CN/release-v4.4/esp32s3/api-reference/system/app_image_format.html# 目录 简介抽象:从现实到模型类和对象:现实与模型的映射封装:隐藏复杂性继承&#…

年薪超过30万的网工,需要具备什么技能?

网工是一个各行各业都需要的职业,工作内容属性决定了它不会只在某一方面专精,需要掌握网络维护、设计、部署、运维、网络安全等技能。 那么,网络工程师的技术水平体现在哪些方面?今天就跟你唠唠这个。 01 先来测测你的网络设计能力…