玩转STM32CubeMX | 定时器中断 1.定时器中断
# s: Q/ J9 N. }* f! B
# q% |$ E5 @* ~2 R# PSTM32的定时器功能十分强大,有高级定时器(TIM1和TIM8)、通用定时器(TIM2~TIM5)和基本定时器(TIM6和TIM7);本实验主要介绍难度适中的通用定时器,通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。每个定时器都是完全独立的,没有互相共享任何资源。
! B: @0 V' c: H: U4 O) l3 d! l通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:6 U/ m9 u7 F) m) {4 X B
/ k7 t+ N5 V% f X: X) ~2 ?4 F
4 ]: Z( d* B: M! q/ u- ~可编程的自由运行递减计算器( U B# K) S0 q' V& o+ L
; P- Z8 H/ x2 a6 |6 \; ~% q
; u* J6 ~8 s& u) {条件复位:当递减计数器的值小于0x40,则产生复位;当递减计数器在窗口外被重新装载,则产生复位4 L1 U o5 j7 d, `3 k$ w3 o+ N
0 o5 ]4 y& Z: L9 C& |- D. d
# u( Q9 ], o; y$ m3 e2 v& y如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可用于重新装载计数器以避免WWDG复位
/ G S) S( p! O. V! [
5 M: q1 _- K5 \8 b$ z; i* s) _: ~" Z) z; X+ E3 P- q; S+ i& d
16位向上、向下、向上/向下自动装载计数器, G* [' S) b: Z* z
+ `7 t3 A, a) O4 ?& A8 o
( _8 d: }( |0 E- B6 o: w& o* P16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
: \# C7 D% I8 Q3 O7 ^9 I; D$ x% [! [
, C9 f f$ N: A) d( c
4个独立通道:输入捕获;输出比较;PWM生成(边缘或中间对齐模式);单脉冲模式输出
; u/ P7 {- ]3 m6 s; K
9 V8 H$ j+ p4 J/ m9 X( z8 y& `
4 |/ N4 K7 y' w/ H' J2 v' Q使用外部信号控制定时器和定时器互连的同步电路
& d: f0 d7 Q* a* Y4 y9 T2 G* ~! p7 U8 s6 Y) h8 u3 o5 u& ?. i
) C- K# Z3 e* t# U; m% M; K- s. B8 {如下事件发生时产生中断/DMA:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发);触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);输入捕获;输出比较' S, e. r8 x% y( m/ N! q
6 e$ V0 f! ~7 x0 y/ s! R M$ R ^( D$ |3 z1 G4 l2 _
支持针对定位的增量(正交)编码器和霍尔传感器电路( l5 k1 h5 Y" Z+ R0 R9 y0 A
9 z; \+ @0 p* f/ P4 B+ o3 L" t
* \- u: M+ o- T# z! J
触发输入作为外部时钟或者按周期的电流管理
9 Q5 v' G6 t5 A) o+ d$ m3 E* ?7 U
" f3 Q, \- u+ Z, N通用定时器框图
' {8 }! ^! K7 D/ ]# `定时器时基部分
: V. B! n" F% t U* `5 j; d/ R5 B2.硬件设计1 \% z2 d# x" D5 V1 h& `# o$ w
) k! v/ C9 {4 r4 W
, ]4 N& Y( d% r [' d# p' R m( F! w& E2 _. h1 x$ P
! t& k5 d1 e( p" _0 l* F" v: i本实验通过TIM3的中断来控制D1的亮灭,需要用到的硬件资源有:
- E- r8 m0 O* o7 A
' X$ T+ ^, N" |* |& C, d
( O! }. F1 p$ x4 e& W% K·指示灯D1和D2
5 H0 ~- o5 W l5 g6 G8 N·定时器TIM3) [! M* r1 q' M. B2 ~. O( P- P
3.软件设计
' ?+ C2 X0 p7 F& ^( ?" u3 I$ E% w; t" n, z5 ?2 V
% w6 y* V# C! L# s- J
3.1 STM32CubeMX设置
+ I' ~1 D" \7 m- q \6 O& _' p8 o# G; `
' N+ u$ {# T6 d% ]; j➡️ RCC设置外接HSE,时钟设置为72M;TIM3的时钟挂载在APB1 Time Clocks上为72MHz
1 {" U5 t0 _. O( s: m* i- A: w0 l" d3 @
% J( U$ v& y2 V, @% e) C➡️ PC0和PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平- C4 U2 {7 C- t' O
; ]4 ~% N+ Y- g; \! E/ h0 c {
1 k) D/ B$ x0 w7 T! K' s
➡️ 激活TIM3定时器,时钟源选择为内部时钟,PSC预分频设置为7200-1,向上计数,自动重装载值(ARR)设置为10000-1,在NVIC设置中激活TIM3定时器中断;根据公式可算出:计数器时钟CK_CNT = 72M/7200 = 10000Hz,计时器中断时间为 ARR/10000 = 1s
: {0 a, [) j9 r1 {9 _➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码( B. e8 z8 A8 p. i" u5 G+ V
: g' z2 K% l. D& {# _2 ^
7 T& Y' J+ W% Q9 Z! `& H& X
3.2 MDK-ARM软件编程* z5 O( [( b. c9 x6 ^# X/ H, p% z
➡️ 在tim.c文件中可以看到定时器的初始化函数
5 Y$ h r6 R# a' z4 b- void MX_TIM3_Init(void){/ ?5 e- @5 \* M) o8 u5 Y7 ~3 n
- TIM_ClockConfigTypeDef sClockSourceConfig = {0};4 o( a; F7 f' l i1 \; K
- TIM_MasterConfigTypeDef sMasterConfig = {0};- _1 g, l6 w% W: B# A2 N5 n% q
- " T# @) `( v4 g' Z
- htim3.Instance = TIM3;
, F; ^; r. S4 ?4 f - htim3.Init.Prescaler = 7200-1;
; @! x, N& d y - htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
; z7 w' M% G' ? Q0 u - htim3.Init.Period = 10000-1;
" D7 r3 D0 k* i" q- ]( b. m - htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;* a l- Z' r3 X
- htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
. f' H1 P) E+ n- V - if (HAL_TIM_Base_Init(&htim3) != HAL_OK){9 d, E3 g3 f; D8 k$ n1 m
- Error_Handler();+ H! z; Y9 ^# |$ m! C
- }! d3 Y7 N5 d4 A% i
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
" N8 ^2 H+ ]: ~; S" D9 C9 a - if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK){. U" V( J# q( K5 ^9 s0 c! T
- Error_Handler();
# H: p& x! W: C - }
( }/ p: L: r1 |1 |- C$ M* F- l - sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;2 d2 O' W2 Z& ^1 I0 T8 q% Z
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
, @2 Y( i& @- [, r: [ - if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){
( l/ U d; f$ N" Y; b T1 J - Error_Handler();
: O/ Q" x" n6 ~8 n& s+ q$ g - }' M' W% s; d7 T
- }
复制代码 找到弱符号周期运行回调函数原型,并在tim.c中自定义该回调函数
$ m/ K5 W# D% S" r$ y__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- t) C9 S$ J/ y* \- I9 V- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ i( U# |, Q+ N3 g5 O: ?* _
- if(htim == &htim3){
+ T& @+ {- p' N2 ` - //LED1状态每1s翻转一次
! I+ j+ v2 {% g# S" E6 u( a - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0); R& M- M) p: p0 k+ f$ C" g
- }) ^2 w5 |9 v u' X' V* y0 n
- }
复制代码 ➡️ 在main函数中编写相关代码,在while中使LED2每500ms翻转一次
: B" u0 o. C3 J- int main(void){7 ~4 w( |, {% L
- HAL_Init();7 Q* }" X# [! }, V" P$ V3 f
- SystemClock_Config();
" g6 | ^9 I% k: P# C, G - MX_GPIO_Init();, v7 ` R1 O6 J
- MX_TIM3_Init();5 S: I* y5 B, N; ~# |+ y& b( e
- //启动定时器中断模式计数) B- l6 {2 L5 O: ?/ G- u: c/ @
- HAL_TIM_Base_Start_IT(&htim3);
% g7 J7 @1 O. X$ i8 D+ h - while (1){
: C; b, k" K9 h i2 M* s! X3 V - //LED2状态每500ms翻转一次 K6 r" X3 G' Z' ^! ^/ a5 i: [9 t
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1); - G9 u J0 J- k' p' q
- HAL_Delay(500);. q. S2 Q+ u# d7 Y7 p8 Q5 b
- }
$ m) w7 U- Q7 J- \ - }
复制代码 4.下载验证
: J) E& t6 e8 p4 D& u' j' G8 D) g1 a编译无误后下载到开发板,可以看到LED1每1s状态翻转一次,LED2每500ms状态翻转一次
; A- @$ `" h) @ G" T
! m8 B+ {3 ]$ D0 W% W p |