5.0 TIM

TIM简介

1. 什么是定时器(TIM)?

  • 字面意思:TIM是Timer的缩写,就是用来计时的工具。

  • 核心原理 :它就像一个精密的"计数器",不停地对输入的时钟脉冲进行累加计数。当数字累加到程序员预先设定的数值时,它会立刻触发一个中断(打个比方:就像闹钟走到设定的时间就会响铃),通知CPU去执行相应的任务。

2. 核心硬件结构(如何实现长定时)

PPT里提到了三个关键硬件:16位计数器预分频器自动重装寄存器

  • 预分频器(Prescaler) :可以把输入的时钟频率"降速"。比如输入是72MHz,我们可以把它除以7200,变成10kHz。预分频器的作用是让定时器能计更久的时间

  • 16位计数器:它能计数的最大值是 216−1=65535216−1=65535。

  • 自动重装寄存器(Auto-reload):当计数器数到我们设定的值后,自动清0并重新开始下一轮计数。

  • 最大59.65秒是怎么来的?

    在72MHz的时钟下:

    • 最大预分频系数是 65536。

    • 最大计数值是 65536。

    • 最大定时时间 = 65536×65536÷72,000,000≈59.6565536×65536÷72,000,000≈59.65 秒。

    • 这意味着,仅靠这一个硬件,即使不借助软件扩展,也能实现将近1分钟的精准定时。

3. 多功能性

PPT特别强调了定时器的多功能性,它不止能干"定时报警"的活,还具备:

  • 输入捕获(Input Capture):可以测量外部信号的脉冲宽度(比如测量遥控器的信号)。

  • 输出比较(Output Compare):可以产生特定波形(比如控制舵机、电机调速的PWM波)。

  • 编码器接口:可以直接连接旋转编码器,用来读取电机转动的角度和速度。

  • 主从触发模式:可以让一个定时器作为"指挥官",去触发另一个定时器或者外设(比如ADC采样),实现同步操作。

定时器的分类(根据用途分级)

STM32的定时器并不是统一的,根据复杂程度分为三类:

  • 基本定时器(Basic):功能最简单,只能做基础定时,用来做简单的延时或触发DAC。

  • 通用定时器(General):功能中等,应用最广,具备输入捕获、输出比较(PWM)等功能,常用于电机控制、信号测量。

  • 高级定时器(Advanced) :功能最强大,除了具备通用定时器的所有功能外,还增加了死区控制(用于控制大功率MOSFET管防短路)、

  • 互补输出等,专门用于高级电机(如三相电机)的驱动控制。

基本定时器框图解析:

STM32 基本定时器(TIM6/TIM7)内部是怎么"数时间"的

RCC 时钟 → CK_PSC → 预分频器 PSC → CK_CNT → CNT计数器 → 自动重装载寄存器 ARR → 产生更新事件

先把很快的时钟"分频变慢",然后让计数器一个一个数,数到设定值以后产生事件。

  1. 最左边:RCC 给定时器一个时钟

来自RCC的TIMxCLK

这个就是定时器的输入时钟。

比如你的 STM32F103C8T6,假设某个定时器最终拿到:

TIMxCLK = 72 MHz

那么意味着:

定时器每秒钟接收到 72000000 个时钟脉冲。

但是这么快肯定没办法直接拿来做:

每1秒触发一次

所以要先经过下面这个东西。

  1. PSC:预分频器

图中:

CK_PSC → PSC预分频器 → CK_CNT

PSC 就是:

Prescaler,预分频器

它的作用非常简单:

把定时器时钟降下来

TIMxCLK = 72 MHz

PSC = 7199

实际上 STM32 这里的计算是:

CKCNT​=PSC+1TIMxCLK​

所以:

CKCNT​=7199+172MHz​ =10kHz

也就是说:

原来:

72000000 次/秒

经过 PSC:

10000 次/秒

于是计数器就舒服多了。

  1. CNT:真正"数数"的地方

图中这个:

+/- CNT计数器

就是定时器最核心的东西。

它本质上就是一个寄存器:

TIMx->CNT

它会随着 CK_CNT 的节拍:

0

1

2

3

4

5

6

...

不断变化。

基本定时器这里主要就是向上计数

所以你可以把它想成一个人在数:

1、2、3、4、5、6......

每来一个 CK_CNT,它就数一次。

  1. ARR:自动重装载寄存器

图中上面的:

自动重装载寄存器

也就是:

ARR(Auto Reload Register)

它的作用就是:

规定 CNT 数到多少。

例如:

ARR = 9999

那么 CNT 就会:

0

1

2

...

9998

9999

继续往下数就会产生一次更新事件

然后 CNT 重新开始:

0

1

2

...

这就是"自动重装载"。

所以:

ARR决定"数多少个"。

而:

PSC决定"数得多快"。

这两个你一定要分清。

  1. 为什么叫"更新事件"?

这个图里虽然没有特别突出写出来,但是这就是定时器最重要的一个动作。

比如:

CK_CNT = 10 kHz

ARR = 9999

也就是:

CNT每秒数10000次。

那么从:

0 → 9999

一共需要:

10000

个计数周期。

所以:

T=1000010000​=1s

于是:

每1秒产生一次更新事件。

这就是定时器。

你可以把它理解成:

时钟

分频

慢下来的时钟

CNT开始数

数到ARR

产生更新事件

重新开始数

  1. 图左上角那个"控制器"是什么?

图中还有:

触发控制器

下面还有:

控制器

这个部分你现在可以先把它理解成:

管理定时器什么时候运行、什么时候停止、什么时候复位、什么时候计数。

复位、使能、计数

所以你可以把它理解成定时器的"总管"。

比如:

TIM_Cmd(TIM2, ENABLE);

本质上就是:

告诉这个定时器:开始工作。

  1. TRGO 是干什么的?

图右上方有:

TRGO → 至 DAC

这个先不要被吓到。

TRGO:

Trigger Output
触发输出

也就是说,定时器产生某些内部事件之后,可以把这个事件作为一个触发信号,送给其他外设。

例如这里画到了 DAC。

定时器可以按照固定时间间隔去触发 DAC 工作。

所以定时器不只是:

"让我延时"

还可以:

"每隔固定时间让另一个外设干一件事。"

这个思想以后非常重要。

  1. 图里面两个"U"是什么意思?

你图里 ARR 和 CNT 附近有很多:

U

这个通常表示:

Update(更新)

也就是更新事件相关的信号。

比如 CNT 数满:

CNT = ARR

发生更新。

这个更新事件可以进一步:

  • 更新寄存器

  • 产生中断

  • 触发其他外设

  • 让一些内部状态发生改变

所以以后看到:

UI、U、Update

你就知道:

"定时器计数到头了,发生更新了。"

  1. 这个图最重要的地方

RCC提供时钟 → PSC把时钟分频 → CNT按照分频后的时钟开始计数 → CNT数到ARR → 产生更新事件 → CNT重新开始计数。

画成你脑子里的简图:

RCC

TIMxCLK

┌──────────┐

│ PSC │ ← 决定"数得多快"

│ 预分频器 │

└────┬─────┘

CK_CNT

┌──────────┐

│ CNT │ ← 真正负责数数

│ 计数器 │

└────┬─────┘

│ 数到 ARR

┌──────────┐

│ ARR │ ← 决定"数多少"

│自动重装载│

└────┬─────┘

更新事件

├──→ 中断

├──→ TRGO

└──→ CNT重新开始

最后给你一个特别重要的公式

定时器最终的溢出/更新周期:

例如:

TIMxCLK = 72MHz

PSC = 7199

ARR = 9999

那么:

T=720000007200×10000​=1s

所以这组参数就是:

每 1 秒产生一次更新事件。

更新事件 → 触发中断 → CPU进入中断函数。

通用定时器框图解析:

那为什么通用定时器突然这么复杂?

因为它不满足于:

"我每隔一段时间数一次。"

① 可以接外部信号

图左边:

TIMx_CH1

TIMx_CH2

TIMx_CH3

TIMx_CH4

TIMx_ETR

这些就是定时器的外部输入引脚

所以通用定时器可以:

接收外部脉冲,然后数这些脉冲。

例如:

外部传感器 → 定时器 → CNT

传感器来一个脉冲,定时器就数一次。

这对你之前学习的旋转编码器就特别重要。

② 它不光能"数时间",还能测外部信号

图下面这一大片:

输入滤波器和边沿检测器

输入捕获

捕获/比较寄存器

这玩意儿可以干:

测一个脉冲什么时候来。

例如:

定时器可以通过这些东西测:

  • 脉冲宽度

  • 周期

  • 频率

  • 相邻两个边沿之间的时间

这就是以后你会学的:

输入捕获

③ 它还能自己输出波形

图右边:

捕获/比较寄存器

输出控制

TIMx_CH1

这意味着定时器不仅能:

接收信号

还能:

自己生成信号

最典型的就是:

PWM

比如:

这就是 PWM 波。

所以通用定时器可以拿来:

  • 控制舵机

  • 调节 LED 亮度

  • 控制电机

  • 输出特定频率的方波


你现在真正需要理解的其实只有三层

别想着把整张图一次啃掉。

第一层:最基础

你现在先学:

定时器怎么"计时"

时钟

预分频器

CNT

ARR

更新事件

这就是你刚才那个基本定时器

第二层:通用定时器增加了"输入"

通用定时器在原来的基础上增加:

外部引脚

输入滤波

输入捕获

捕获/比较寄存器

于是它可以:

测外部信号。


第三层:通用定时器增加了"输出"

又增加:

CNT

捕获/比较寄存器

输出控制

CH1/CH2/CH3/CH4

于是它可以:

产生 PWM 等输出信号。

你前面学的:

基本定时器

实际上就是一个功能比较少的定时器

现在这个:

通用定时器
在"会计时"的基础上,又加了输入捕获、输出比较、PWM、外部触发等一大堆功能。

高级定时器框图解析:

通用定时器 + 一大堆专门给电机/PWM控制准备的功能。

你现在看这张图

先看它最核心的骨架

跟前面完全一样:

时钟

预分频器

CNT计数器

自动重装载寄存器

更新事件

所以:

基本定时器:

计时

通用定时器:

计时 + 输入捕获 + 输出比较/PWM

高级控制定时器:

通用定时器

重复计数器

互补输出

死区控制

刹车保护

更强的PWM控制

右边,多出来一堆:

① 重复次数计数器

图里这个:

重复次数计数器

以及:

REP寄存器

这个是高级定时器比较有代表性的东西。

普通定时器可能:

数满一次

更新事件

高级定时器可以:

数满一次

先不更新

再数满一次

还是不更新

......

重复若干次

才产生一次真正的更新

所以它多了一个:

"要重复几次才算真正更新"

的功能。

② 最重要的:互补输出

你看到右边:

TIMx_CH1

TIMx_CH1N

还有:

TIMx_CH2

TIMx_CH2N

这个 N 很关键。

例如:

CH1

CH1N

就是一对互补输出

可以理解成:

一个高的时候,另一个往往低。

这个东西特别适合:

控制电机、逆变桥、MOS管

所以高级定时器为什么叫"高级"?

不是它计时更高级。

而是它能玩功率控制

③ 死区时间

这张图里有一个很关键的:

DTG寄存器

对应的就是死区发生器

这个东西第一次看到会懵,但实际上非常简单。

假设有上下两个 MOS 管:

上管

电机

下管

理论上:

上管开

下管关

然后换状态:

上管关

下管开

但是现实中的 MOS 管:

不是说关就立刻完全关。

如果上管刚关,下管立刻就开,可能出现:

上管还没完全关

下管已经开了

两个管子同时导通

短路

这就很危险。

所以高级定时器故意插一个很短的:

"都关着的空白时间"

这就是:

死区时间

这个功能就是高级定时器特别重要的地方。

④ 刹车输入

图最下面:

TIMx_BKIN

这个也很重要。

它相当于一个:

紧急刹车/保护输入

例如电机发生异常:

过流

过压

故障

外部保护电路给 BKIN 一个信号。

高级定时器可以马上:

关闭 PWM 输出。

所以你可以把它理解成:

"出事了,先把功率管关了!"

这就是高级控制定时器的安全功能。


所以你现在看到三种定时器,可以这样理解

基本定时器

我只负责计时

核心:

时钟 → 预分频 → CNT → ARR → 更新

通用定时器

我除了计时,还能测信号、输出PWM

增加:

输入捕获

输出比较

PWM

4个通道

高级控制定时器

我主要是拿来控制电机、逆变器这些东西的

在通用定时器基础上增加:

重复计数

互补输出

死区时间

刹车保护

你现在千万别试图把这张图全看懂

真的没必要。

你现阶段直接把这张图砍成下面这一部分

PWM → 通用定时器 → 高级定时器

再一个一个把:

捕获/比较、互补输出、死区、刹车、重复计数

你会发现它根本不是"又来了一套新知识",而是在原来的定时器上不断加功能

所以你现在看到:

基本 → 通用 → 高级
会计时 → 会计时+测信号+发PWM → 会计时+PWM+专门搞电机的保护和控制

定时中断基本结构流程:

"谁给定时器提供时钟" → 决定计数规则 → CNT开始数 → 数到ARR → 产生更新事件 → 允许的话产生中断 → NVIC通知CPU。

第一步:先给定时器一个"时钟"

图最左边有很多可能的时钟来源:

RCC内部时钟

GPIO → ETR外部时钟

其他定时器 → ITRx

GPIO → TIx捕获通道

你可以把它理解成:

定时器有很多个"水龙头",但一次只能选择自己要用哪一个。

① RCC内部时钟

最上面:

RCC内部时钟

内部时钟模式

这是我们最常用、也最容易理解的。

比如:

RCC给TIM2一个72MHz的时钟

意思就是:

定时器自己按照系统提供的时钟节拍工作。

你平时写普通的定时中断,大多数就是走这条路。

还可以用外部时钟

图里面还有:

GPIO

ETR外部时钟

外部时钟模式2

就是:

不让定时器按照内部时钟计数,而是让外面的引脚给它脉冲。

比如外面来了:

↑ ↓ ↑ ↓ ↑ ↓

来了一个脉冲,定时器就可以数一次。

所以:

内部时钟 = 芯片自己打节拍
外部时钟 = 外面的设备来打节拍

其他定时器也能给它提供时钟

图中:

ITRx

其他定时器

外部时钟模式1

这个看着很高级,其实思想非常简单:

定时器之间可以串起来。

比如:

定时器1

定时器2

定时器3

定时器1产生一个事件,可以拿去驱动定时器2。

这以后做复杂定时、同步控制的时候特别有用。

你现在先知道:

一个定时器可以拿另一个定时器的信号当自己的输入。

捕获通道也可以参与

图左下:

GPIO

TIx捕获通道

编码器模式

这个是以后你学:

  • 输入捕获

  • 编码器

会用到的。

例如你之前玩的旋转编码器,A、B信号就可以进入定时器的捕获通道。

所以这张图其实已经把:

定时器 + 旋转编码器

这条路也画出来了。

最关键的一块:内部时钟模式选择

你看到中间这个黄色大框:

内部时钟模式

外部时钟模式2

外部时钟模式1

编码器模式

这实际上就是在做一件事:

选择"定时器到底采用哪一种计数方式"。

你可以把它理解成一个大总开关/模式选择器

比如你现在只想做普通定时:

选择:内部时钟模式

如果要让外部脉冲驱动:

选择:外部时钟模式

如果要接旋转编码器:

选择:编码器模式

本质就是:

选择谁来驱动CNT。


选完之后,进入"时基单元"

图中粉色的大框:

时基单元

这就是定时器真正负责"计时"的核心部分。

里面有三个你现在最应该记住的东西:

预分频器

计数器

自动重装载器

也就是:

PSC

CNT

ARR

预分频器:把时钟变慢

假设前面来的时钟是:

72MHz

经过预分频器:

72MHz → 10kHz

比如:

CKCNT​=PSC+1TIMxCLK​

所以:

预分频器决定CNT数得有多快。

你可以记成:

PSC = 调速度

CNT:真正开始数

经过预分频之后

时钟

预分频器

CNT计数器

CNT开始:

0

1

2

3

4

5

......

所以:

CNT就是"数数的人"。

ARR:决定"数到多少"

旁边这个:

ARR 自动重装器

它相当于告诉CNT:

你数到多少算一轮。

比如:

ARR = 9999

那么:

0

1

2

3

...

9999

到头以后产生一次更新。

所以:

ARR = 决定数多少

现在就到最关键的一步:更新事件

当CNT数到ARR规定的值以后:

CNT数满

更新事件

图上这个"U"和"UI",本质都和:

更新

有关。

这个更新事件可以理解为:

"这一轮计时结束了!"

例如:

72MHz

预分频

10kHz

CNT数10000次

更新事件

如果刚好是一秒,那就:

每秒产生一次更新事件。

这就是定时器定时的本质。

但是"更新事件"不等于"中断"

这个地方非常重要。

很多初学者容易把这两个东西混在一起:

事件 ≠ 中断

正确的关系是:

CNT数满

产生更新事件

如果开启了更新中断

产生中断请求

进入中断系统

NVIC

CPU进入中断函数

所以你前面学的:

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

干的事情其实就是:

允许"更新事件"去申请中断。

运行控制是干什么的?

图下方:

运行控制

这个你可以理解成:

定时器的"开关和控制面板"。

例如:

TIM_Cmd(TIM2, ENABLE);

就是:

启动定时器。

如果关闭:

TIM_Cmd(TIM2, DISABLE);

就是:

停止定时器。

所以:

运行控制

允许时基单元运行

CNT开始数

最后就是"中断输出控制"

图右边:

中断输出控制

NVIC

这里非常关键。

整个过程可以完整串起来:

RCC内部时钟

选择内部时钟模式

预分频器

CNT计数器

ARR判断

CNT数满

产生更新事件

开启更新中断

中断输出控制

NVIC

CPU

执行中断函数

这就是你现在学习的:

定时器中断完整流程

拿一个实际例子你就彻底明白了

假设我们要:

让STM32每1秒进入一次定时器中断。

假设:

定时器时钟 = 72MHz

PSC = 7199

ARR = 9999

先经过预分频:

72MHz÷(7199+1)=10kHz

所以CNT:

每秒数10000次。

ARR:

数到9999。

所以:

0

1

2

...

9999

更新事件

更新中断

NVIC

进入TIM中断函数

然后重新:

0

1

2

...

9999

又一次中断

所以最后就是:

每1秒执行一次中断函数。


你现在可以把整张图压缩成这张"脑内地图"

谁来提供时钟?

┌──────────┬──────────┐

↓ ↓ ↓

RCC 外部引脚 其他定时器

↓ ↓ ↓

模式选择

时基单元

预分频器 PSC

计数器 CNT

自动重装载 ARR

数到头了

更新事件

开启了更新中断?

↙ ↘

否 是

↓ ↓

结束 NVIC

CPU

中断函数

第一:时钟从哪里来。

第二:预分频器负责把时钟变慢。

第三:CNT负责数。

第四:ARR负责规定数到多少。

第五:数满产生更新事件,更新中断打开后,才会一路进 NVIC、进中断函数。

而你图上那些 ETR、ITRx、TIx、编码器模式、外部时钟模式,先不要全部背。它们主要是在回答一个问题:

"定时器的时钟/计数信号还可以从哪里来?"
时钟 → 预分频 → CNT计数 → ARR判断 → 更新事件 → 中断 → NVIC → CPU

预分频时序

计数器时序

计数器无预装时序

计数器有预装时序

定时器不停地拿时钟来数数,PSC决定它数得有多慢,CNT负责数,ARR决定数到多少;当CNT数满以后产生更新事件,而PSC、ARR这些"新参数"到底什么时候真正换上去,就是这几张时序图想告诉你的。

比如你原来 PSC=1,现在想改成 PSC=2。你虽然已经把新值写进寄存器了,但定时器不会毫无秩序地在当前计数过程中突然换节奏,它会先把这个新值放在那里,等到一个合适的更新时间再真正启用。所以第一张图其实就是在解释:

"我改了预分频值,什么时候真正影响CNT?"

然后 CNT 一直数,假设 ARR 是 36,它就从 00、01、02......一路数到 36。数到头以后回到 00,这就是一次溢出,同时产生更新事件。第二张图就是把这个过程画成波形,让你看到:

时钟来了 → CNT加一 → CNT数满 → 溢出 → 更新事件 → 更新中断标志。

接下来 ARR 又有两种情况。

如果不开预装载,你改 ARR,新的 ARR 就直接拿来用了,所以这一轮计数可能马上受到影响。

如果开预装载,你改的 ARR 不会立刻碰真正正在工作的那个值,而是先存起来。等这一轮计数结束,产生更新事件的时候,再把新 ARR 换进去。这样就相当于:

"这一轮先跑完,下一轮再换新参数。"

这就是后面那两张图真正想表达的东西。:

时钟来了 → 预分频 → CNT开始数 → ARR规定终点 → CNT数满 → 更新事件 → 新参数在这个时刻完成交接。

这样看,四张图其实是在描述同一个定时器,只不过把同一个过程的不同细节放大给你看。

RCC时钟树

RCC 时钟树可以理解成:

STM32内部有很多种时钟源,RCC负责从里面选一个、加工一下,再把不同频率的时钟分配给 CPU、总线、定时器、USB、ADC 等外设。

产生时钟 → 选择和倍频/分频 → 分发给不同模块。

定时器计数时钟配num++

cpp 复制代码
//TIME.c文件
#include "stm32f10x.h"                  // Device header
void Timer_Init()
{ 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//激活TIM3时钟
    TIM_InternalClockConfig(TIM3);//设置TIM时钟来源于内部
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//创造一个表
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时钟分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数模式为向上计数
	TIM_TimeBaseInitStructure.TIM_Period=9999;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=7199;//psc
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//高级功能:重复次数寄存器
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM3,TIM_FLAG_Update);//清除标志,防止刚刚触发就进入中断从1开始计数.
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//配置中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 配置NVIC优先级分组
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//事件
	
	NVIC_Init(&NVIC_InitStructure);
	TIM_Cmd(TIM3, ENABLE);
}
volatile uint8_t num=0;
void TIM3_IRQHandler()
{
  if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
  {
    num++;
   TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
  }



}	

Time.h文件

#ifndef __Timer_H
#define __Timer_H
void Timer_Init(void);
extern volatile uint8_t num;
#endif

//main.c文件
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
int main(void)
{
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
    OLED_Init();
    Timer_Init();
   
    while (1)
    {
        OLED_ShowNum(1,1,num,8);
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4)==0)
		{
		  num=0;
		
		}
		   
    
    }
	
}

定时器外部时钟(毛刺暂未克服)

cpp 复制代码
//主函数
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "ExternTimer+InfraredSensor.h"

int main(void)
{
OLED_Init();
OLED_ShowString(1,1,"counts:");
OLED_ShowString(3,1,"NUM:");
SensorInit();
EX_Timer();

while (1)
{



OLED_ShowNum(2,1,TIM_GetCounter(TIM2),8);
OLED_ShowNum(4,1,num,8);
}
}














//.c
#include "stm32f10x.h"     // Device header
volatile uint8_t num=0;


//对射式红外传感器GPIO配置
void SensorInit()//配置传感器GPIO口
{
    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);
	
}

void EX_Timer()
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//时钟
    TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x02);//外部时钟配置
    TIM_TimeBaseInitTypeDef Time_BaseInitStructure;//时基单元配置
	Time_BaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	Time_BaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	Time_BaseInitStructure.TIM_Period=10-1;
	Time_BaseInitStructure.TIM_Prescaler=0;
	Time_BaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&Time_BaseInitStructure);
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//防止上电就中断
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//中断配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//NVIC分组
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_Init(&NVIC_InitStructure);
	TIM_Cmd(TIM2,ENABLE);//打开时钟
}
volatile uint8_t lock = 0;

void TIM2_IRQHandler()
{
 if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
  
    {
		num++;
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}













//.h
#ifndef __ExternTimer+InfraredSensor_H
#define __ExternTimer+InfraredSensor_H
void SensorInit();
void EX_Timer();

extern volatile uint8_t num;



#endif
相关推荐
2601_966799044 小时前
CH398 技术解析:USB 3.0转千兆以太网单芯片方案与内置电源设计要点
单片机·嵌入式硬件
zlinear数据采集卡5 小时前
数据采集卡从入门到精通(38):上位机开发实战——Python/QT/LabVIEW的技术选型与分层架构
python·单片机·嵌入式硬件·qt·fpga开发·开源·labview
嵌入式阿蔡5 小时前
项目实战 02:智能家居环境网关——ESP32 做 BLE 中心 + 本地联动
网络·单片机·嵌入式硬件·esp32·智能家居·嵌入式实时数据库
自小吃多6 小时前
Capture软件原理图添加差分属性笔记
笔记·嵌入式硬件
简单电磁炮6 小时前
ADXL345调试
单片机·嵌入式硬件
LCG元6 小时前
STM32 软件模拟 I2C 驱动 AT24C02 EEPROM:从时序原理到实测读写全记录
stm32·单片机·嵌入式硬件
振南的单片机世界6 小时前
终端电阻硬核拆解:不接120Ω,RS485信号“撞墙反弹”乱码
arm开发·stm32·单片机·嵌入式硬件
恒锐丰科技韩生6 小时前
SS6625E|3.0‑20V 单通道 16A 峰值 H 桥直流电机驱动 SOP8 门锁玩具机器人专用
嵌入式硬件·硬件工程
国产HT1621B7 小时前
外挂 YL1621 SSOP48 芯片 vs MCU 内置 LCD 控制器深度对比
科技·单片机·嵌入式硬件·mcu·lcd驱动