|
STM32CubeMX系列 | 定时器中断 1.定时器中断
% |' w, ?! _9 t& q: q2 |
# E4 t7 g/ m8 |1 _! S+ ~, Z( hSTM32的定时器功能十分强大,有高级定时器(TIM1和TIM8)、通用定时器(TIM2~TIM5)和基本定时器(TIM6和TIM7);本实验主要介绍难度适中的通用定时器,通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。每个定时器都是完全独立的,没有互相共享任何资源。. V6 n% [+ S4 K q9 r) X+ p K
通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
( n- X) u/ P. u0 n W5 }5 h
; g z+ z6 L( b9 j: H ~- A# q/ H6 ~, S8 ~6 ^5 E
可编程的自由运行递减计算器
q* H, m% R* B8 ]9 F2 @6 B: ~7 Y
; G) j5 r. \$ q2 n# H
# ^$ k' Q! d+ ?6 b: q条件复位:当递减计数器的值小于0x40,则产生复位;当递减计数器在窗口外被重新装载,则产生复位9 i& @ }1 C# h( D0 d3 O
/ b* Q: c s( Y9 _
; ?% H( a& V2 Y+ A! J% I7 F/ N如果启动了看门狗并且允许中断,当递减计数器等于0x40时产生早期唤醒中断(EWI),它可用于重新装载计数器以避免WWDG复位
3 a2 T. S; P" y8 C! \0 J7 V- p) `. B9 C( \! _( `; v
9 {8 M0 G2 l8 V2 u16位向上、向下、向上/向下自动装载计数器
% Z* J5 z. b+ U8 q! s, W v# s* ~' | s0 u. Q
' x, }! e3 }$ B1 j' o6 @/ X16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
" N& j' j- M% ]9 v2 A, \$ G& n! A' E, W4 c. S/ Z+ N/ t: J; ~
/ s7 G+ o5 r/ n9 f4个独立通道:输入捕获;输出比较;PWM生成(边缘或中间对齐模式);单脉冲模式输出+ z3 i: x% r- {
. ]4 g' P3 j3 C( M- K% R) {+ B( {2 V# x+ ?
使用外部信号控制定时器和定时器互连的同步电路' ?. ]7 q; L; V/ A% R6 {6 e g; A
- x; a# D2 i- K; Z. W
' v( ~* J# U- v% K r) ~如下事件发生时产生中断/DMA:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发);触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);输入捕获;输出比较/ R( I, F0 }% X$ H+ L
* [' @2 y, |+ j0 t5 |
, j$ N9 e E+ `: A支持针对定位的增量(正交)编码器和霍尔传感器电路# B5 e* I! p3 c
# B6 A; p% J/ C4 q2 p; B' V
. U5 v4 M l4 h! j. K触发输入作为外部时钟或者按周期的电流管理! t" }; f+ G' W$ }( K
( \. k; D4 z( S/ \4 b6 y3 [: c
# t+ z' c/ x( F7 Q
通用定时器框图
* T: }' [7 C. p5 K0 l定时器时基部分1 X' p0 L& c7 n
2.硬件设计7 n. p, v' ?6 U$ D/ |7 [- U% D
" J; X/ w2 D2 M! p, {: I
本实验通过TIM3的中断来控制D1的亮灭,需要用到的硬件资源有:
9 ~" m$ G& c3 s! g0 H
! ~2 r4 P& `9 l8 [$ C5 D3 T) P7 c) L( u4 y1 X- ^
指示灯D1和D2: i6 @! p$ q( q: V5 h
定时器TIM3) J. B7 R# ?2 a9 p. g
3.软件设计
1 R0 ]2 `5 l& l& e
5 l8 Q/ J2 G. j1 y0 {/ \& I- T0 O' w9 p# n5 z, ]# y8 Q( _
3.1 STM32CubeMX设置
- M2 H4 ? d/ H, y; T0 Y
. L8 c. C+ ?5 Z0 _5 L J
) K! O9 T5 G, s& e% `➡️ RCC设置外接HSE,时钟设置为72M;TIM3的时钟挂载在APB1 Time Clocks上为72MHz
" U( D! \* v9 p2 G4 ]0 h4 Z, N
+ ~5 a; s# m+ o+ `# Y+ V; v4 n7 @' d* t, B' u0 M/ N4 n! E# l1 l
➡️ PC0和PC1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平+ I, w. d1 _/ C$ @3 t& P
3 `5 S. K8 V2 A0 r! \: k. V
' |1 t( [4 E, X, r7 Z, @6 J➡️ 激活TIM3定时器,时钟源选择为内部时钟,PSC预分频设置为7200-1,向上计数,自动重装载值(ARR)设置为10000-1,在NVIC设置中激活TIM3定时器中断;根据公式可算出:计数器时钟CK_CNT = 72M/7200 = 10000Hz,计时器中断时间为 ARR/10000 = 1s3 L3 e5 [5 m6 @( U3 N b. y) k o
➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
# {9 f" `; x }( S' q+ m- D! N& t: S, V
$ h' e( k4 [& I0 U+ E: E* } k3.2 MDK-ARM软件编程1 z5 q+ ~1 `* Y) D6 _3 d, A
- T1 k1 h9 q: Z& f3 R/ s" u/ u* C# b8 h+ s& i8 _
➡️ 在tim.c文件中可以看到定时器的初始化函数
p0 {3 g. K3 e# H3 h- void MX_TIM3_Init(void){) Y! _7 W9 x5 {4 v
- TIM_ClockConfigTypeDef sClockSourceConfig = {0};9 o. }4 H. l% v# K8 U. {
- TIM_MasterConfigTypeDef sMasterConfig = {0};
$ G( Y6 m. I+ [, W8 W - * t. q# a7 M$ F3 H4 u4 o
- htim3.Instance = TIM3;! ]5 J# n T1 a/ q- r1 Z
- htim3.Init.Prescaler = 7200-1;
J0 V( a3 W: a7 C# k - htim3.Init.CounterMode = TIM_COUNTERMODE_UP;# a5 b4 x5 ~& n1 j" I
- htim3.Init.Period = 10000-1;% I+ }4 ^9 h+ j7 O" A
- htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
1 K- H) Y. Y3 @ - htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
4 h+ h. \- H, D6 U; H, r. h4 f0 A - if (HAL_TIM_Base_Init(&htim3) != HAL_OK){! c! X6 F, c3 d& l$ A+ t5 S3 A; U
- Error_Handler();
C9 m- E) [+ U! i - }- g* N' L. S2 V; B4 D+ m* R( N
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
- s$ |* n; i* F- h - if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK){
* b6 C6 i8 L% O# ] - Error_Handler();
/ f: K t2 A' t - }. c9 ]* a9 F1 x' l
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;. }7 b5 F: T' D+ Q* V9 Q
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
4 y$ C d, u0 y$ q( B - if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK){5 F, j U, T0 R% V9 G0 D
- Error_Handler();( X* @$ V' [: r0 n5 R8 v) D
- }
% H# q w8 e# | - }
复制代码 找到弱符号周期运行回调函数原型,并在tim.c中自定义该回调函数2 p- Q' ?8 [& D6 q
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
! |4 h, C8 {1 O. U# I- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
5 U/ H, K; S M$ N2 R8 n4 ~ _ - if(htim == &htim3){
+ C* D* f# Z. v2 o3 M+ S - //LED1状态每1s翻转一次
: U5 m4 R" D: E: \, Z* O - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
% L3 W" T5 b8 v - }# O6 a( P8 {* q. A/ U
- }
复制代码 ➡️ 在main函数中编写相关代码,在while中使LED2每500ms翻转一次
% Q6 C7 D" `5 c& Z% E5 X- int main(void){+ o( U+ L5 N3 H2 B" w4 _/ G5 Q* J
- HAL_Init();
) m9 J8 @9 I4 [+ N& m0 y - SystemClock_Config();
7 l) q# U6 i' e' g% W - MX_GPIO_Init();
' i r, p% ?6 K5 U1 z: Y+ ? - MX_TIM3_Init();2 a& E- Q! U; F/ D! y8 G% I
- //启动定时器中断模式计数
`( f! E: b& _3 J& Z" \$ W - HAL_TIM_Base_Start_IT(&htim3);
0 R( Z3 ]7 R% n, H9 O+ s. h - while (1){
% P! Y9 A/ {* W1 E$ n6 u0 j - //LED2状态每500ms翻转一次. |% P) t8 p2 Z9 U
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_1); ' e8 ]0 w# |/ |* u& r
- HAL_Delay(500);
* D; d! @7 n) V4 ^ - }
# Z1 C! \% S2 u8 s, N* K3 [ - }
复制代码 4.下载验证( `5 I3 x; P! Y) G: I
编译无误后下载到开发板,可以看到LED1每1s状态翻转一次,LED2每500ms状态翻转一次0 t5 Q/ j4 M; W/ s
5 x4 x% t0 N" Y9 r' a
3 \+ R& o# _5 l( A
6 R- @7 D$ ~- i: e$ h
% J" M* w9 R) }* A$ Q6 a! R" \4 E3 H2 U7 F+ V1 W' ]4 o8 H
& h7 _9 Q9 I" D; A1 u# M
|