|
玩转STM32CubeMX | 定时器中断 1.定时器中断0 x9 G% E1 L0 r3 _8 [3 R; F
& K! \" F' h5 H! b, T- ESTM32的定时器功能十分强大,有高级定时器(TIM1和TIM8)、通用定时器(TIM2~TIM5)和基本定时器(TIM6和TIM7);本实验主要介绍难度适中的通用定时器,通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。每个定时器都是完全独立的,没有互相共享任何资源。; Y0 c* f3 M5 O4 B
通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:6 Q# U: h6 p# \" b. L2 ^
( U6 k) \3 \/ H; E' h$ _9 j* z; _+ R+ Q1 U
可编程的自由运行递减计算器4 O: x/ i) E" x/ W
. ]$ K, b, y7 H" K- e: W4 g0 w
9 d; `( K& v! l+ `7 c
条件复位:当递减计数器的值小于0x40,则产生复位;当递减计数器在窗口外被重新装载,则产生复位. W& z5 W- q6 O
6 y) a) o% w( N3 e1 A+ u) H8 Z$ u7 ]6 G5 Z5 ^# p: \- E5 j) P
如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可用于重新装载计数器以避免WWDG复位
' I# n+ n, R& M. D* W( z. x* h) r; r9 Q( w
# D% D$ I& r* e) P! h& Z16位向上、向下、向上/向下自动装载计数器, u! _, r8 d8 v: \3 i+ }- S: f* O
5 i9 ^9 ^, G& o/ d. Q9 e; R
! `' ?7 _5 V8 f7 A
16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值1 U( j- _4 \! ~ G$ X
" T+ y+ Q# |: @% I
8 |/ a% X" _- A) K) \# F
4个独立通道:输入捕获;输出比较;PWM生成(边缘或中间对齐模式);单脉冲模式输出$ W! T: _2 A' T m ^; R5 t& b) j
5 t9 u: h& T9 T' s. K5 g" `
/ {6 g* ? ^# s* P0 H/ i' S& l
使用外部信号控制定时器和定时器互连的同步电路 t h$ z4 z0 A- k* X# C$ h
; E3 j! n: D$ e/ f% P+ B: O
9 ^" g' A5 [9 T* L9 c# |如下事件发生时产生中断/DMA:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发);触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);输入捕获;输出比较9 E" ?0 s2 M$ j) l, g! P8 u' l3 r
N5 T1 r1 n/ o s* X) d
3 S9 N, X5 U3 ^ r5 t支持针对定位的增量(正交)编码器和霍尔传感器电路
; J e/ d+ a2 e2 h7 S" I$ v+ `) L( B9 K: _* s H
6 B6 }, q- W$ X$ z+ q" J触发输入作为外部时钟或者按周期的电流管理3 H- r: Z+ S: r; ^2 C \* S
& k+ K' U9 g* `1 j0 }& `' w
7 G3 J. b, m }8 {- a1 O9 R通用定时器框图
, A- C$ t/ d# @定时器时基部分& v9 V: {* V8 C/ k
2.硬件设计
# T0 i+ ]# N, q1 y' e M! M( `/ A# @1 ~2 E( C3 Y! m$ Z
- Q2 d5 s* y2 N' m J* I
1 q! g! m; ]4 S# c
$ @& i2 N" h, y$ R- D- M5 F+ G本实验通过TIM3的中断来控制D1的亮灭,需要用到的硬件资源有:) g8 q+ T; |9 R: H) Q; g; F# ?: h
2 Z* a, }( I% U( J" l4 I/ p
. M0 {8 T+ t2 x+ a# S% N' P/ T·指示灯D1和D2# r; o) \. ~4 }$ o0 e
·定时器TIM3
+ M! Y% x# K% b( ^" I6 W3.软件设计
8 \0 O' q$ T2 F
, B, W# |+ X+ @6 M
8 {/ g0 w: d: V# w: \+ w3.1 STM32CubeMX设置9 O5 R4 z4 x$ N$ u2 y' b
9 {: l- h5 X2 y8 s& B6 w8 ]3 U3 ^% W0 f- g
➡️ RCC设置外接HSE,时钟设置为72M;TIM3的时钟挂载在APB1 Time Clocks上为72MHz" U. I% z5 E# |; F1 `* T" {
1 i; ?) V! ^) I* n( t
9 \, ~3 G+ z: v% F6 N➡️ PC0和PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
* A* @0 h, r P6 _( I% n/ N* S1 p
( I O+ W* {9 E- G' N! x& I/ ]8 E2 _8 m) T
➡️ 激活TIM3定时器,时钟源选择为内部时钟,PSC预分频设置为7200-1,向上计数,自动重装载值(ARR)设置为10000-1,在NVIC设置中激活TIM3定时器中断;根据公式可算出:计数器时钟CK_CNT = 72M/7200 = 10000Hz,计时器中断时间为 ARR/10000 = 1s
4 I3 x' e0 P$ u! c* t➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码) e0 S5 J" G. W* h
. `$ x0 D& q, h, c& a" x. F! l) N' H2 p
3.2 MDK-ARM软件编程
: u' W" B$ ^: b: a( K: v➡️ 在tim.c文件中可以看到定时器的初始化函数
0 m, N. o) W& S- void MX_TIM3_Init(void){
. N5 T1 g7 ~) U* S - TIM_ClockConfigTypeDef sClockSourceConfig = {0};- S5 u8 M; p, d2 q# [
- TIM_MasterConfigTypeDef sMasterConfig = {0};5 }' ] W2 `6 S1 x
% l" Y) A) W( s6 l! q8 `- htim3.Instance = TIM3;, ~7 l( Z% j8 G* w6 a
- htim3.Init.Prescaler = 7200-1;
9 ~1 f2 ~" v/ n% |& [6 Z: u( u - htim3.Init.CounterMode = TIM_COUNTERMODE_UP;7 Q/ B, M- A! ~0 P7 u: g. F" z
- htim3.Init.Period = 10000-1;, X6 b7 B5 B2 k+ p$ V
- htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;( a# T: g0 [8 e9 |( \+ e
- htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;3 d R; m. E" V* U; x; G
- if (HAL_TIM_Base_Init(&htim3) != HAL_OK){
9 m" N7 Q/ H- P' A+ _ - Error_Handler();
. g6 Q; C" G2 `6 s0 @8 [ - }9 N. f }; i* U. l2 G
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;( x9 ?2 i4 s5 Y8 k3 G( t
- if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK){: v1 E9 c0 h/ M( c" g! K1 M; T( a
- Error_Handler();- J9 q5 j- T# i
- }- h/ N* R0 j P) w5 ^5 v
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;3 I$ p$ h' V+ m. p2 E! i( N
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
+ H" B+ x" ? M/ ~ - if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){" k' m- W) }0 @' O+ |
- Error_Handler();2 s3 D4 J! C/ C( a: e
- }
( u: _# ?2 g: Y b/ T0 H - }
复制代码 找到弱符号周期运行回调函数原型,并在tim.c中自定义该回调函数5 h2 e: `6 _6 G! _& _1 B2 |
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim), a; t7 y& e8 T a& {
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
: `! j7 L0 s6 J1 x4 f+ } k$ O - if(htim == &htim3){
: X/ G5 `* \" e" d4 h' C0 | - //LED1状态每1s翻转一次
2 N+ @0 x+ ~8 x H. p& Z - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
9 A9 l- ^& I, c2 p% { P8 O - }
9 @' Z5 D8 ^3 V, {1 N - }
复制代码 ➡️ 在main函数中编写相关代码,在while中使LED2每500ms翻转一次' M0 b+ J9 J G9 h
- int main(void){
$ ?) H7 W" f6 j/ T' H - HAL_Init();
n) N' B9 o4 E/ N5 } - SystemClock_Config();8 g. q& J* s I/ h1 h6 [' P. n
- MX_GPIO_Init();
/ D+ W: M; h1 S; r2 |6 ] - MX_TIM3_Init();; e0 c5 b( y; \- O# H
- //启动定时器中断模式计数
0 O8 G C0 ~. v7 S# e# d - HAL_TIM_Base_Start_IT(&htim3); g5 E" Q* k' ~7 X) f7 n9 N
- while (1){* E8 d& V2 j& D$ f5 O- B
- //LED2状态每500ms翻转一次
% o2 `4 P! r+ A( d - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1);
0 Q3 M" ?( j/ I; V& c - HAL_Delay(500);/ L4 L# p2 x/ A
- }, r4 i: ?& t* \( g
- }
复制代码 4.下载验证
$ J4 W+ Z. b6 j2 U$ b T编译无误后下载到开发板,可以看到LED1每1s状态翻转一次,LED2每500ms状态翻转一次3 h- u1 X {" f( {: f: U
- d' B* d; ? C' z6 r4 O
|