我们知道【如果不知道先假设知道】,STM32定时器中的四个带影子特性的寄存器组,每组寄存器分别由 影子寄存器【即实际控制寄存器】和预装寄存器组成。其中,影子寄存器是真正起作用的控制寄存器,用户访问不到它。而预装寄存器是用户访问的为实际影子寄存器准备数据或指令的寄存器。它们分别是: & Y$ G6 b2 X( w/ p& l. ?8 u6 n6 j& y TIMx_PSC 分频寄存器 TIMx_ARR 自动重装载寄存器. B7 ^: Z+ n- g3 o, h, k 0 r+ z, P( T0 b7 q2 Y0 W TIMx_CCR捕捉寄存器 TIMx_RCR 重复计数寄存器[高级定时器有】0 n4 C4 E, w6 N) _5 M, \- n ! ~) X9 A R) |* f* i% ~ 其中,ARR、CCR寄存器带预装载使能控制位: / Q" R4 r2 F; P- [0 c! h: y* p TIMx_ARR 带预装载使能控制位 ARPE@TIMx_CR1 8 Y7 O2 s7 ?0 {6 q. L2 U TIMx_CCR 带预装载使能控制位 OCxPE@TIMx_CCMR S5 j# v/ n( r v0 g9 d' J 当相应预装使能位置1时,即开启预装功能时,此时影子寄存器的数据更新必须且只能通过更新事件实现从预装寄存器到影子寄存器的数据拷贝。3 _; |9 l" b1 N/ f# q0 x# C' { k ( [. j Y: y* y, h9 W# A 当相应预装使能位置0时,即关闭预装功能时,用户修改预装寄存器的数据后会立即被拷贝进影子寄存器【实际寄存器】。也就是说,此时我们用户操作预装寄存器就相当于访问实际影子寄存器。$ h. F4 q' }) c O5 q) f 另外的PSC、RCR寄存器是不带预装使能控制位的,也就是说,二者的影子寄存器的预装功能始终开启,对于PSC/RCR影子寄存器的数据更新就只能通过更新事件唯一途径实现从预装寄存器到影子寄存器的数据拷贝更新。; R# c" `- h7 {1 N8 ]( y1 T4 r/ W 1 l) |* m+ w& `& j% z. p c% x + b% s+ A0 \! V% A- u! I) I7 K# c 那么有哪些操作可以产生更新事件呢?或者有哪些更新事件源呢? $ v a5 D% B6 r" H 这里把更新操作跟更新事件区别开来,以便于概念上的理解。 . X4 D! h+ ]( \/ ^' h1 A! K' s0 ~ 更新操作是一种动作,是更新事件的源头,即事件源; ) t$ d/ Q% E8 n" T/ K* k6 z9 d# x 更新事件是基于更新操作所导致的后续影响或结果。 : b# o( X4 y5 ^7 {4 \, r) q 可能的更新操作【事件源】有3类:5 q8 X8 ^3 m/ u# n 1、核心计数器的溢出【上溢或下溢】 e5 G7 e1 N* a( q 2、软件复位操作【对UG@TIMX_EGR置位】3 ?+ V! G+ |- X1 g6 ? C 6 A) i. o. A3 ^# l; C4 W; D 3、工作在复位模式下的定时器收到触发信号【即复位触发信号】 【特别提醒,对于高级定时器必须发生RCR+1次溢出动作后才可以产生更新事件。对于通用或基本定时器,每溢出一次就可以产生更新事件。】 那么更新操作何时可以升级为更新事件呢?这里涉及到一个控制寄存器的控制位,UDIS@TIMx_CR1. 1 g/ }( B1 I8 R$ X; _ $ z. o* E4 a& s6 ~ 当该控制位UDIS@TIMx_CR1为0时,更新操作升级为更新事件,更新事件会产生如下影响或效果:& ~/ r" D. ^. t0 W7 f- u; } 6 u J+ m7 _* ~3 P 1、实现从预装寄存器的数据到影子寄存器的内容拷贝,即完成影子寄存器的内容更新; : x" B+ o+ g( X U) P9 A; ? 2、实现计数器【预分频计数器、核心计数器、重复计数器】的重新初始化; - U7 G. g( s- t# e! m7 S* G3 e 3、置位状态寄存器的更新中断请求【UIF@TIMx_SR】位,并可以触发定时器更新中断或触发DMA请求; 0 z8 ? X0 }. y ' P& y0 r+ w% F9 ]8 q 当该控制位UDIS@TIMx_CR1为1时,更新操作不能升级为更新事件,其相应的结果或影响: 1 N6 r. F/ Z) m$ d* u1 X 0 G+ W F6 d' \" e 仅限于计数器的重新初始化,影子寄存器不做内容更新;- t2 B0 [2 _2 t Q' X1 Z2 u% C3 H 无更新标志的置位,不触发中断或DMA请求 6 X+ e' Q: f) p- r8 P: i- X 那么发生更新操作时计数器的重新初始化具体是指什么呢?5 N. E L( d& ]( p. T+ \- U: ] 3 g l A7 Q: G0 W' j0 ?5 C- ` * F" B' j+ R, Q* f ~ 1、分频计数器重装为0,然后重新开始计数;) d. C1 ^5 F) V9 v o$ P, x! T 2、重复计数器重装为RCR寄存器里的值,然后重新递减计数; ) J6 t4 T; h2 t) p5 v( C B) a 6 c" _- l5 }* E0 h' o2 U- n( }* @ 3、核心计数器的初始化由计数模式来定,如果是向上计数或中心对齐计数模式,CNT归0;如果是向下计数器模式,CNT重装为ARR,然后重新向下计数;) u" p& t$ d" t6 C; {6 i7 ]$ r' s: q# k 2 F1 B+ l/ w. L: T' X Q 2 z* e/ h: S3 O/ w0 b$ q 发生更新事件时,影子寄存器的更新与计数器的重装有先后顺序吗? & }8 ]9 }; R4 Q H5 W, J' k6 G 有!影子寄存器【ARR/CCR….】的更新操作在前,计数器的重装操作在后! & t0 n" i# P8 z, Y2 Q! ] 因为这样可以保障计数器的重装值使用更新过的数据。该个细节要特别注意!8 Z0 ~ O+ E3 M- [1 C- C * J& I6 W( V* r! D8 t/ x+ _$ v 最后,不妨做个基于更新事件的案例分享: 9 g+ @7 s# w. ~% j& | 问题描述:TIMER初始化阶段,经常有人反馈,不管定时器周期的长短,只要一使能更新中断,就立即进中断服务程序?令人不解,往往给开发带来些困扰,原因可能是什么?如何解决?$ v' O7 s' {8 J7 ]: u; S % Q# K- V/ \) O6 v; m 我们知道,定时器应用的初始化时,往往需要对有关时基寄存器进行些基本的数据赋值。比方对ARR/PSC/RCR这些寄存器赋予初始值。结合前面的介绍,这些寄存器都是些带预装功能的寄存器,我们用户操作的寄存器都是预装寄存器,还不是实际起作用的影子寄存器。 8 c0 q3 b* ?+ Y3 |( D 6 J b8 E; e0 J1 K 对于ARR寄存器倒还好,因为芯片复位后默认状态下,ARR寄存器的预装功能是关闭的【CCR寄存器的预装载功能默认条件下也是关闭的】,那么我们用户给ARR赋值就相当于给其实际影子寄存器赋值了。' P2 v# S* `+ ]. [- D ) p4 a& o, ]5 ]9 ?5 ^ 但PSC/RCR寄存器是不带预装控制位的,前面也说了,它们两个的影子寄存器的更新必须借助于更新事件。所以,在定时器的时基参数的初始化代码里,为了让用户写进预寄存器的数据生效,就用到了上面提到过的软件复位操作,即对UG@TIMx_EGR进行置位而产生更新事件,从而完成影子寄存器的数据更新。5 i9 f- V/ O, _, p1 n+ D 在STM32标准库里的TIM_TimeBaseInit( )函数里都有如下代码:4 V0 x- u `( W( y: o; m# W TIMx->EGR =TIM_PSCReloadMode_Immediate; 在Cube库里的HAL_TIM_Base_Init( )函数里的函数有如下代码: & N; P5 `+ L% |1 a3 C TIMx->EGR = TIM_EGR_UG;; B+ K" V% e% N 3 T/ E1 u& t' X1 x 结合前面的介绍,这两行代码使用的软件更新操作产发了更新事件,但它不仅仅实现了影子寄存器的数据更新,同时呢,还置位了状态寄存器的更新中断请求标志位UIF@TIMx_SR。那么,如果在这之后,我们使能定时器更新中断的话,进入更新中断服务程序就再自然不过了。为了规避这个问题,我们在时基参数初始化完成之后、使能定时器更新中断之前,可以先做更新中断标志的清除操作。+ Z6 m. E+ t2 U3 y- l% H# a5 m * A6 s: a/ k: q 基于这个案例,我们可以对定时器的影子特性以及不同影子寄存器的预装特性差异有进一步的了解。总的来讲,STM32定时器的寄存器预装载特性也是其一特色,定时器的更新事件也是个非常重要的事件,更新事件本身及相关中断或DMA功能也是STM32开发应用中常用的工具。8 A1 o+ a( z. k4 {& Z7 N/ M% u& X 0 `6 u! d8 s5 J' O& F) C |