ST给的TIM例子都是实现一个固定频率的当时输出,如果想在每次定时中断后改变定时器值,那么需要动态的修改定时器周期配置。
6 _4 p$ g; _0 {( Q+ K9 @实测中发现一有坑,给大家共享。. ]/ J- i& o4 N& t4 T8 ^. K0 p" ^" [0 A
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
) W1 q3 s. b2 ? - {
; ^0 r1 a9 X; X+ L' {& i2 t4 H' C3 { - /* USER CODE BEGIN Callback 0 */
2 F; w$ h8 E" b* _) m5 \( }
2 w _& _- @' F1 z/ U) Y- /* USER CODE END Callback 0 */
6 r. k8 g$ f- q# R J5 O - if (htim->Instance == TIM22) {6 \8 x6 I. I6 g
- HAL_IncTick();
* v7 d' E; c7 W( `5 {$ c - }: F4 m+ U. m5 U- g& D D; Q
- /* USER CODE BEGIN Callback 1 */% K: e$ y' d$ o- M
- if (htim->Instance == TIM2) {. V4 H* ^/ N9 Q, y6 B
- IrdaTransfer(pDataNEC);
* _5 A+ s( n$ t7 Z) ~- {8 V6 g% C - }
& i6 o, D% \( a, `; U- }8 [2 F - /* USER CODE END Callback 1 */
@: h: S( W1 A/ }2 c1 M/ @ - }, v' @4 a+ K @2 p' x/ `# d
复制代码 首先在HAL_TIM_PeriodElapsedCallback中添加TIM的Update事件中断回调函数,自己写IrdaTransfer(pDataNEC)实现NEC编码的红外遥控。
$ x/ I# T/ j$ z' e; _- uint16_t IrdaTransfer(uint8_t* pDataNEC)
! k' S- i2 ~! Q/ Q - {: r; j# Y* j8 N C
- PulseVal=TimeSerial(pDataNEC,PulseSteps);
9 O6 H/ S) I. P1 { - PulseSteps++;" O; h2 T5 B2 x( m+ _
- if(PulseVal>0)
+ x' k$ o$ ~% ` - {0 r, i, \4 {" K& O; b" q
- htim2.Init.Period = PulseVal&0x7fff;/ T9 K' B& \9 ? b
- HAL_TIM_Base_Init(&htim2);
+ e0 ^1 A' ^$ c/ l& C7 H- A) b - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);, j/ j! G% x! g. T" z( _: f6 r9 ~
- HAL_TIM_Base_Start_IT(&htim2);9 Z* X+ K' ^, w& T! W* w* j0 b
- }3 u+ d, v& }0 R/ I; k3 }4 [
- else$ c' d+ w! z5 h
- {
" @ i j5 Z+ |) {0 x$ h - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);% C, G' y0 m j( f9 A; N
- HAL_TIM_Base_Stop_IT(&htim2);' P7 `$ O \/ N: O/ w- M5 [: [
- }
0 ^; I1 q9 P& \: g5 s - if(PulseVal&0x8000)$ M6 P6 ?5 Y. D7 W4 R L# O5 V
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_SET);
- t" ]- n0 w4 H8 T# E1 F6 D7 m - else* s8 h7 @7 `' Z/ C+ H: I% \+ A
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_RESET);
& R0 n- L& T, ?+ s0 b% B - return PulseVal;
% o3 s( L) O$ f/ |* a" u - }
( ?0 b8 z+ f* u U: ?
复制代码 注意这里的坑是:
. e+ u! F0 U2 e9 k1 I/ { Khtim2.Init.Period = PulseVal&0x7fff;( P3 j' S# W' a- I! D
HAL_TIM_Base_Init(&htim2);
9 f( _" }* i) `# P8 X5 e __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);//这是坑,必须在此处加。$ g- X; [; ]* v' q5 Z; S V1 h
HAL_TIM_Base_Start_IT(&htim2);+ Q7 W$ e- `. E" v* e
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。- \5 i+ @$ q8 n9 P, Z8 G9 ^- _
以上代码即可实现每次定时后立即更新定时值,实现任意的序列控制。具体的NEC红外序列实现功能我就不放了,留点自己动脑子的地方,填的坑与大家共享。
: Y! p5 x3 Q5 ?) x* t( l" x% t8 _6 L1 D3 g6 Y: M7 ?, g
: k/ h. R, a3 V/ d0 _6 U
# Z/ r) T4 } v4 { |
. y# Z. [# o2 u1 y
HAL_TIM_Base_Start_IT(&htim2);
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。
==>不能算是个坑,应该是手册没看清楚。
因为有些寄存器比方PSC/RCR寄存器必须通过更新事件才能更新,我们初始化时只能操作预转载寄存器,所以让我们用户数据生效,就手动产生个更新事件,让预装载寄存器的数据拷贝到影子寄存器【实际寄存器】发挥作用。但这个操作会置位更新中断标志,所以我们在使能更新中断前有必要清除下该标志UIF,否则可能一使能更新中断就跳进更新中断服务程序。