ST给的TIM例子都是实现一个固定频率的当时输出,如果想在每次定时中断后改变定时器值,那么需要动态的修改定时器周期配置。
% \7 W7 ^6 E, L1 h实测中发现一有坑,给大家共享。8 r# X# F0 Z3 \+ z# r
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)7 r# s9 ~; V! f0 A2 S
- {
+ t; R# k5 d2 d6 u0 k% ~ - /* USER CODE BEGIN Callback 0 */
( y# ~% B& z" [5 U" X1 ^
# y0 e* b. v2 ^5 U: c. e- /* USER CODE END Callback 0 */
5 @' `, K. |2 n9 K. w - if (htim->Instance == TIM22) {
9 `, o. `+ T# q2 i4 i5 n - HAL_IncTick();) V B# S9 d( h' f" u5 I% }
- }; Y+ ` i7 ?% T: \
- /* USER CODE BEGIN Callback 1 */5 F' W$ K2 {- x; I
- if (htim->Instance == TIM2) {8 g- |" N/ p. m" O3 Z$ J% B
- IrdaTransfer(pDataNEC);
* [0 ]* A" L1 \' B3 E [ C - }
+ N9 K+ h7 K; v" C( ]0 j' l - /* USER CODE END Callback 1 */
# G1 N! d% }5 @9 g - }
4 }) m& I$ p) Y2 M2 g4 L) ]
复制代码 首先在HAL_TIM_PeriodElapsedCallback中添加TIM的Update事件中断回调函数,自己写IrdaTransfer(pDataNEC)实现NEC编码的红外遥控。
. U4 H8 d4 Z/ p" o0 K- uint16_t IrdaTransfer(uint8_t* pDataNEC)
( g: ]8 ?# M- ?* T: t - {0 ?: o, Y4 c. K" e
- PulseVal=TimeSerial(pDataNEC,PulseSteps);
+ k4 \6 |, j! Q, j - PulseSteps++;
$ b4 T* }# [2 I' t( _ - if(PulseVal>0)
5 B/ }( f5 p% v: S( u - {
5 h3 u$ C2 p7 A: W - htim2.Init.Period = PulseVal&0x7fff;. @3 `4 l* J" w$ a, n! y% @
- HAL_TIM_Base_Init(&htim2);6 o2 K' k. z, {9 R. R% r3 d4 o% F
- __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);. _" X" H9 Y4 l7 T; R. i
- HAL_TIM_Base_Start_IT(&htim2);$ [) u+ i& K; u- E
- }) R# t& i# J9 V
- else) @3 D5 [0 |# P+ f6 {8 J7 A
- {7 E! z( z2 c P- _, M" V
- __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);1 R5 A/ g2 }* J6 E) i" R
- HAL_TIM_Base_Stop_IT(&htim2);
0 k. g2 i4 T/ ^ - }
" q+ N d+ C9 C4 T U - if(PulseVal&0x8000)
# l9 L- C' K$ v - HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_SET);1 D! g$ [( e: f* u
- else6 j$ Z0 N* G2 y* H5 j
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_RESET);8 @/ o6 |. l( O, ^1 I2 f9 I/ N
- return PulseVal;& r8 I( M& q9 y5 S
- }
9 b; |7 x: Z" i! Y
复制代码 注意这里的坑是:# p( U% g, O5 t
htim2.Init.Period = PulseVal&0x7fff;
" N3 |. y. |' m4 n" d1 ` HAL_TIM_Base_Init(&htim2);
, F" e( V& A' x$ @0 W: M6 ?+ p __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);//这是坑,必须在此处加。3 x5 p5 U# K) M- t& r
HAL_TIM_Base_Start_IT(&htim2);
* b w" E& j2 S3 c# j在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。7 |3 C$ R" }" ?* q7 y! I
以上代码即可实现每次定时后立即更新定时值,实现任意的序列控制。具体的NEC红外序列实现功能我就不放了,留点自己动脑子的地方,填的坑与大家共享。
+ ]; W1 A0 T# e1 t
; Y* e2 N" ?2 i0 G' v' V* r8 i2 L) l1 m4 E
+ a7 D) F, N, A3 H, w; r' W+ Y |
* ]$ B0 U9 \- c/ G6 R* o
HAL_TIM_Base_Start_IT(&htim2);" U; g% I, h5 n/ h
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。+ ]8 r" f0 N3 D! D6 E0 g
==>不能算是个坑,应该是手册没看清楚。
因为有些寄存器比方PSC/RCR寄存器必须通过更新事件才能更新,我们初始化时只能操作预转载寄存器,所以让我们用户数据生效,就手动产生个更新事件,让预装载寄存器的数据拷贝到影子寄存器【实际寄存器】发挥作用。但这个操作会置位更新中断标志,所以我们在使能更新中断前有必要清除下该标志UIF,否则可能一使能更新中断就跳进更新中断服务程序。