ST给的TIM例子都是实现一个固定频率的当时输出,如果想在每次定时中断后改变定时器值,那么需要动态的修改定时器周期配置。
9 n/ p5 @* M$ j8 Y d& Y实测中发现一有坑,给大家共享。' }$ E% l d( A7 h7 M( G0 U
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)- W% U2 _* I# P
- {
: j+ o6 }' `; F+ q# t9 J/ E3 E2 L6 I - /* USER CODE BEGIN Callback 0 */' i5 {; c, P' g
/ Q) U& w k# [- /* USER CODE END Callback 0 */
* F% _$ P( x6 I( j0 q* C& K - if (htim->Instance == TIM22) {$ u6 }1 O) Y0 O
- HAL_IncTick();; ~6 f! [9 p F7 R3 }" j; S- S
- }$ K2 Z$ E" P, t* [) i
- /* USER CODE BEGIN Callback 1 */
2 I; i) m& e& z: ?. } - if (htim->Instance == TIM2) {" b2 {+ ]3 t6 e$ I3 r/ h+ E1 @( `* B
- IrdaTransfer(pDataNEC);
/ J8 P. F/ @$ {2 Y5 S - }
% W% D# i4 J/ S q - /* USER CODE END Callback 1 */
9 N% _' R0 u0 J - }
) P0 f3 U4 a" @
复制代码 首先在HAL_TIM_PeriodElapsedCallback中添加TIM的Update事件中断回调函数,自己写IrdaTransfer(pDataNEC)实现NEC编码的红外遥控。
. K: |5 G/ R& w- uint16_t IrdaTransfer(uint8_t* pDataNEC) G" |6 f5 _4 }! m- F( r
- {
9 w: [3 ~8 Q4 s) x7 t# [; e: ] - PulseVal=TimeSerial(pDataNEC,PulseSteps);9 U- U5 R, s" D4 f. [5 {
- PulseSteps++;0 [) J$ j: k1 d* q4 e ?
- if(PulseVal>0)
* U, I. v4 W2 D0 q7 z$ R - {+ [' _4 g/ o( K: A3 @# S, _
- htim2.Init.Period = PulseVal&0x7fff;8 S0 a; y( u; P+ @8 Y
- HAL_TIM_Base_Init(&htim2);
7 c Y$ G5 ^: c$ M( p; t - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);4 z9 O; | C R4 r- Y0 A: L! O
- HAL_TIM_Base_Start_IT(&htim2);
R, \, c1 |+ m/ d - }! y' {; ]! ?0 V+ B1 M
- else
& O) ?5 n% N3 Q, e - {
6 g" }- v9 _, z& A. V$ u* a& [$ C - __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);9 h+ Q, T; k( d! E5 r
- HAL_TIM_Base_Stop_IT(&htim2);
3 C( z( ]0 j% {5 f8 \, h - }
% o2 \0 r1 V- q0 d0 P7 U. @* \ | - if(PulseVal&0x8000)0 u: R3 A' r* u: U& v( x# v
- HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_SET);$ W m3 X: }! p6 `' H
- else
, E( D1 l2 I y) L& [ - HAL_GPIO_WritePin(IrDA_Output_GPIO_Port, IrDA_Output_Pin, GPIO_PIN_RESET);
5 D1 s$ l/ ^ D - return PulseVal;. W( M* Q. ]2 o1 W
- }
' ^& x7 e' ~3 j) Y: U7 \
复制代码 注意这里的坑是:2 B( c2 O' r% \! {
htim2.Init.Period = PulseVal&0x7fff;3 a& C% `2 N" r9 ~# v: r7 s
HAL_TIM_Base_Init(&htim2);3 B$ Z. o7 ?% P/ I0 _ U
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);//这是坑,必须在此处加。9 Q5 v7 D4 z. J" _9 u9 i
HAL_TIM_Base_Start_IT(&htim2);, _ n0 Q# l2 j9 S9 k& z# z
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。: s, ]0 K9 U F
以上代码即可实现每次定时后立即更新定时值,实现任意的序列控制。具体的NEC红外序列实现功能我就不放了,留点自己动脑子的地方,填的坑与大家共享。
) J+ t! l1 ~- Y- n6 d& t+ r( M, w) E B, B8 U7 p
/ R$ C1 v5 [' H- o9 Z
: N5 {* ^6 ]3 G2 l1 n+ X |
! q s: F+ ^4 e: j
HAL_TIM_Base_Start_IT(&htim2);; U. n; E- c3 x. a ~
在初始化定时时长后,必须先清除中断标志,再启动定时器中断,否则会直接进中断而没有在定时结束时进,这个坑调了我一天,不明白为啥初始化定时器就会触发一次UPDATE中断。
==>不能算是个坑,应该是手册没看清楚。
因为有些寄存器比方PSC/RCR寄存器必须通过更新事件才能更新,我们初始化时只能操作预转载寄存器,所以让我们用户数据生效,就手动产生个更新事件,让预装载寄存器的数据拷贝到影子寄存器【实际寄存器】发挥作用。但这个操作会置位更新中断标志,所以我们在使能更新中断前有必要清除下该标志UIF,否则可能一使能更新中断就跳进更新中断服务程序。