ST给的TIM例子都是实现一个固定频率的当时输出,如果想在每次定时中断后改变定时器值,那么需要动态的修改定时器周期配置。
7 p: l( f# w. m# j/ O1 K* E$ v2 A实测中发现一有坑,给大家共享。% @: Z2 F4 |$ y) {0 b `+ U2 H5 |, `
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)2 _' O! d0 ~8 m, |3 B
- { [9 @/ Q" n" t& r9 u$ e8 G
- /* USER CODE BEGIN Callback 0 */% E% g: Y4 n6 x4 y4 D! c! O, R
& B6 n h9 \& b! s9 @$ ~- /* USER CODE END Callback 0 */
' ?' ^. ^2 _0 Y9 } |1 Q - if (htim->Instance == TIM22) {+ e4 l8 b. a0 p
- HAL_IncTick();7 C4 Z4 W- w& U; Z7 |3 x
- }
) F( d# u# H' [( _ n7 V* ~ - /* USER CODE BEGIN Callback 1 */* v( o O0 d) V& v" k$ ?
- if (htim->Instance == TIM2) {9 p/ d5 D% @+ c" o5 D
- IrdaTransfer(pDataNEC);
8 ^. k0 D* \4 G( s" n - }
& n) [7 l$ j2 \; M( {/ | o - /* USER CODE END Callback 1 */5 d: |5 P5 q+ ~+ G% ]
- }' C3 X% n7 u k9 ]- A
复制代码 首先在HAL_TIM_PeriodElapsedCallback中添加TIM的Update事件中断回调函数,自己写IrdaTransfer(pDataNEC)实现NEC编码的红外遥控。) Q0 S/ D+ t8 f! Z
- uint16_t IrdaTransfer(uint8_t* pDataNEC)
8 V2 \) D" q( H. Y - {: s( i% j) c/ h4 j# A2 z) t+ r; v/ y
- PulseVal=TimeSerial(pDataNEC,PulseSteps);( l$ E6 r% D$ P5 X! H
- PulseSteps++;
a6 ]3 b; n( E; f- Z T4 g. w - if(PulseVal>0)
0 v1 e: r( b% x: I+ W - {
b& O. t$ s% f1 f8 T1 { - htim2.Init.Period = PulseVal&0x7fff;, W: ?. l) e( P5 F7 m9 H8 b5 b
- HAL_TIM_Base_Init(&htim2);& w( q% m) c3 z+ j; s+ T
- __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);
: X7 v2 p" f s9 F& v - HAL_TIM_Base_Start_IT(&htim2);
: U1 \' ]7 e# F K2 b% O6 k3 Q; | - }
+ f8 H$ @% _) |. y* Q1 `8 V - else
$ P2 D* z/ ?; h - {
7 Y) G" \5 w# h1 Z - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);
6 w% R6 w7 U( f9 L* L. b8 ~ - HAL_TIM_Base_Stop_IT(&htim2);. @4 \' M# ?) z
- }9 r* U4 L/ K/ `* T0 D* T# M. Z
- if(PulseVal&0x8000)
3 G; [' D4 R7 F6 w) F - HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_SET);
6 q. b- s1 C/ Y) F - else& a$ l6 \4 M( @7 r9 V" }
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_RESET);/ d* E* u$ b- U
- return PulseVal;
! K! v$ n6 \3 ~1 G) h& B% N4 l5 ~ - }# V; j* _* H% A D2 ^( y) Q' C+ P
复制代码 注意这里的坑是:
- k+ x+ U8 L$ mhtim2.Init.Period = PulseVal&0x7fff;
# U: V" _# j7 U. a( m/ J K+ v: z p HAL_TIM_Base_Init(&htim2);) u ~5 X& t/ g8 \) c6 @# Y7 a: ^% v
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);//这是坑,必须在此处加。 a. |6 d' Q/ u5 z% [2 {
HAL_TIM_Base_Start_IT(&htim2);
. Y$ Y, D5 D+ R6 N6 ], T g# b5 y在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。+ U/ Z8 j$ a0 w O6 U' n
以上代码即可实现每次定时后立即更新定时值,实现任意的序列控制。具体的NEC红外序列实现功能我就不放了,留点自己动脑子的地方,填的坑与大家共享。( Y% n/ h) |* c! u
0 X) W2 o! c/ U: x
0 t; m1 `) D, G3 t
! V+ ]& N4 J% H& K |
& J, H- l! ?+ d( s
HAL_TIM_Base_Start_IT(&htim2);
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。 B/ N+ Q5 M* y9 @
==>不能算是个坑,应该是手册没看清楚。
因为有些寄存器比方PSC/RCR寄存器必须通过更新事件才能更新,我们初始化时只能操作预转载寄存器,所以让我们用户数据生效,就手动产生个更新事件,让预装载寄存器的数据拷贝到影子寄存器【实际寄存器】发挥作用。但这个操作会置位更新中断标志,所以我们在使能更新中断前有必要清除下该标志UIF,否则可能一使能更新中断就跳进更新中断服务程序。