
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 → 产生更新事件
先把很快的时钟"分频变慢",然后让计数器一个一个数,数到设定值以后产生事件。
- 最左边:RCC 给定时器一个时钟
来自RCC的TIMxCLK
这个就是定时器的输入时钟。
比如你的 STM32F103C8T6,假设某个定时器最终拿到:
TIMxCLK = 72 MHz
那么意味着:
定时器每秒钟接收到 72000000 个时钟脉冲。
但是这么快肯定没办法直接拿来做:
每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 次/秒
于是计数器就舒服多了。
- CNT:真正"数数"的地方
图中这个:
+/- CNT计数器
就是定时器最核心的东西。
它本质上就是一个寄存器:
TIMx->CNT
它会随着 CK_CNT 的节拍:
0
1
2
3
4
5
6
...
不断变化。
基本定时器这里主要就是向上计数。
所以你可以把它想成一个人在数:
1、2、3、4、5、6......
每来一个 CK_CNT,它就数一次。
- ARR:自动重装载寄存器
图中上面的:
自动重装载寄存器
也就是:
ARR(Auto Reload Register)
它的作用就是:
规定 CNT 数到多少。
例如:
ARR = 9999
那么 CNT 就会:
0
1
2
...
9998
9999
继续往下数就会产生一次更新事件。
然后 CNT 重新开始:
0
1
2
...
这就是"自动重装载"。
所以:
ARR决定"数多少个"。
而:
PSC决定"数得多快"。
这两个你一定要分清。
- 为什么叫"更新事件"?
这个图里虽然没有特别突出写出来,但是这就是定时器最重要的一个动作。
比如:
CK_CNT = 10 kHz
ARR = 9999
也就是:
CNT每秒数10000次。
那么从:
0 → 9999
一共需要:
10000
个计数周期。
所以:
T=1000010000=1s
于是:
每1秒产生一次更新事件。
这就是定时器。
你可以把它理解成:
时钟
↓
分频
↓
慢下来的时钟
↓
CNT开始数
↓
数到ARR
↓
产生更新事件
↓
重新开始数
- 图左上角那个"控制器"是什么?
图中还有:
触发控制器
下面还有:
控制器
这个部分你现在可以先把它理解成:
管理定时器什么时候运行、什么时候停止、什么时候复位、什么时候计数。
复位、使能、计数
所以你可以把它理解成定时器的"总管"。
比如:
TIM_Cmd(TIM2, ENABLE);
本质上就是:
告诉这个定时器:开始工作。
- TRGO 是干什么的?
图右上方有:
TRGO → 至 DAC
这个先不要被吓到。
TRGO:
Trigger Output
触发输出
也就是说,定时器产生某些内部事件之后,可以把这个事件作为一个触发信号,送给其他外设。
例如这里画到了 DAC。
定时器可以按照固定时间间隔去触发 DAC 工作。
所以定时器不只是:
"让我延时"
还可以:
"每隔固定时间让另一个外设干一件事。"
这个思想以后非常重要。
- 图里面两个"U"是什么意思?
你图里 ARR 和 CNT 附近有很多:
U
这个通常表示:
Update(更新)
也就是更新事件相关的信号。
比如 CNT 数满:
CNT = ARR
发生更新。
这个更新事件可以进一步:
-
更新寄存器
-
产生中断
-
触发其他外设
-
让一些内部状态发生改变
所以以后看到:
UI、U、Update
你就知道:
"定时器计数到头了,发生更新了。"
- 这个图最重要的地方
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