ST给的TIM例子都是实现一个固定频率的当时输出,如果想在每次定时中断后改变定时器值,那么需要动态的修改定时器周期配置。# p* p1 N' O* b$ f/ j- L
实测中发现一有坑,给大家共享。
! K2 l6 I9 @ L. b0 y7 _% @- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)+ [& q B/ J" w; |0 Z2 G! u
- {
6 t5 ^! o' k- T) g0 u3 j- C7 F - /* USER CODE BEGIN Callback 0 */
* a) W$ Z6 n% _0 J - . F9 v7 R& U* b4 M8 X* |7 K/ b
- /* USER CODE END Callback 0 */
' F; h" K" ?2 M/ f% ~! A( @& p - if (htim->Instance == TIM22) {
) p& b5 Z' \4 e, x! \ - HAL_IncTick();
+ U4 p& {; N" ^# A s. S; U. d4 k - }3 i6 J4 s( G2 H- ]' V7 ~# p
- /* USER CODE BEGIN Callback 1 */
1 c8 f, }; f; c# b5 U9 u - if (htim->Instance == TIM2) {) [: g' W1 W! ~
- IrdaTransfer(pDataNEC);
: J4 U6 O" J' u. Z+ x) b* ~ - }
' I3 o" G$ ]: G0 p - /* USER CODE END Callback 1 */
8 B* W! @! ?$ }- z+ j l - }
# W" u3 @7 ?' f) Y( ^+ g# T4 J7 }
复制代码 首先在HAL_TIM_PeriodElapsedCallback中添加TIM的Update事件中断回调函数,自己写IrdaTransfer(pDataNEC)实现NEC编码的红外遥控。# `# n8 z5 H- o" X" x, w# O
- uint16_t IrdaTransfer(uint8_t* pDataNEC)
. p1 i ^0 Z7 M1 l$ {1 @ - {
: Z# {$ Q# ?2 T8 c6 z; \5 f0 T, n - PulseVal=TimeSerial(pDataNEC,PulseSteps);0 H, I! r2 V1 q; k
- PulseSteps++;
9 n5 ]7 Y! U5 { - if(PulseVal>0)9 F8 }# j& c% U& y( Z0 L
- {
3 e$ I. n6 c4 s5 j. U/ s# F* w) K+ B/ u- q - htim2.Init.Period = PulseVal&0x7fff;+ x. ?1 }( L! r( U
- HAL_TIM_Base_Init(&htim2);
, V! A0 D3 c' f - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);
H% [% j9 D: V3 |" Q; j3 h+ m- B - HAL_TIM_Base_Start_IT(&htim2);& f" m6 i% ^/ l6 ^* O
- }1 m$ Q9 s. E' f2 ? i$ F% R9 b J; |. y
- else8 i m* D; b' Q" e% d! `- G
- {
* w; j# x7 h5 d% T1 C - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);1 A8 v. L q3 N+ y, W
- HAL_TIM_Base_Stop_IT(&htim2);. W( y( w. [2 }' {
- }
# M6 c x8 L0 f. b: S# N4 E, d - if(PulseVal&0x8000) R# M7 l2 ^2 ?& `, f
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_SET);
6 J" w$ o! J+ D( ]6 b! R - else! w! ^; P1 s ^+ ~
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_RESET);5 y5 H- e8 c! K# t3 H- ^% j0 E
- return PulseVal;
1 m) e$ t& u) S) ^ - }9 q1 V3 U, G( t* X* N
复制代码 注意这里的坑是:7 n a9 O& Y7 O% V% ]
htim2.Init.Period = PulseVal&0x7fff;# q" o& u8 y$ o' F
HAL_TIM_Base_Init(&htim2);
x! p! s* y5 g4 R __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);//这是坑,必须在此处加。
% M. t8 K) `0 C) c }2 Q7 BHAL_TIM_Base_Start_IT(&htim2);" a8 ]. e1 U& D y' ]
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。6 ?" v6 l5 q2 K" a. x
以上代码即可实现每次定时后立即更新定时值,实现任意的序列控制。具体的NEC红外序列实现功能我就不放了,留点自己动脑子的地方,填的坑与大家共享。
/ ]& I$ L# K" r& K1 ~& ?, z
1 j0 T( u/ s2 n4 v1 ^
" H/ C2 s. _9 q4 p7 U) o
5 s0 N0 c) p& c |
# e+ H( t- U' \/ A g, g/ C
HAL_TIM_Base_Start_IT(&htim2);1 i, h% J& ^- { F# Q- @# a
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。1 ]& X0 \- d2 k" t' O) w+ E# z u
==>不能算是个坑,应该是手册没看清楚。
因为有些寄存器比方PSC/RCR寄存器必须通过更新事件才能更新,我们初始化时只能操作预转载寄存器,所以让我们用户数据生效,就手动产生个更新事件,让预装载寄存器的数据拷贝到影子寄存器【实际寄存器】发挥作用。但这个操作会置位更新中断标志,所以我们在使能更新中断前有必要清除下该标志UIF,否则可能一使能更新中断就跳进更新中断服务程序。