定时器除了使用内部时钟线(APB1/APB2)的电平信号进行计数,还能通过GPIO口引入外部信号,通过外设输出的电平信号进行计数。
本文中,采用TCRT5000(红外反射光电开关),作为外部信号的输入。
外部输入信号线

ETR(外部触发器)经过极性选择,边沿检测,预分频,输入滤波:
- 极性选择:可将上升沿和下降沿互换;
- 边沿检测:只能进行上升沿检测,但可以搭配极性选择实现下降沿检测;
- 预分频:降低ETR的频率;
- 输入滤波:过滤抖动信号,在CubeMX设置的Trigger Filter即是在设置此功能。
之后分为两支:
- ETR2:直接抵达触发控制器,属于外部时钟模式2,作为外部时钟源;
- ETR1:连接到触发器后经过选择再连接到从模式控制器,属于外部时钟模式1,作为从模式的一种输入信号。
以STM32F103为例,每个定时器有四个输入信号,TI1到TI4(TIM Input),其中TI1和TI2与触发控制器有关;
每条TI信号线会依次经过输入滤波器和边沿检测器:
- 输入滤波器:过滤抖动信号;
- 边沿检测器:可设置为上升沿触发或者下降沿触发,此外还能设置为双边沿触发,检测到上升沿/下降沿时输出一个脉冲信号。
之后会继续分出TI1FP1,TI1FP2,TI2FP1,TI2FP2,以及TI1_ED;
其中TI1FP1,TI2FP2,TI1_ED和ETR1共同接入到一个触发器,并选择其中一个信号接入到触发控制器中的从模式控制器,作为输入信号。
其中,TI1_ED固定只能为双边触发。
模式选择
在左侧Timers中选择TIM2,在右侧Mode中对模式进行配置:
外部时钟
- 外部时钟模式2:从GPIO口中获取外设发出的高低电平信号,并将其用于控制计数器。
从模式
在Slave Mode中选择不同的模式:
- 外部时钟模式1(External Clock Mode 1):与外部时钟模式2基本一致;
- 复位模式(Reset Mode):输入信号上升沿/下降沿时对计数器的寄存器进行复位,并触发定时器更新中断(在中断模式下);
- 门模式(Gated Mode):出现上升沿时开始计数,出现下降沿时停止计数,即仅在高电平时进行计数,若启用下降沿触发则反之:仅在低电平时进行计数;
- 触发模式(Trigger Mode):当输入信号为高电平时,开始计数;但由于无法主动停止计数,因此通常搭配单脉冲模式(One Pulse Mode)使用:启用后,在重装载后会停止计时;
在Trigger Source中可选择不同的外部输入信号线。
更多设置
- Trigger Polarity:设置上升沿触发/下降沿触发/双边沿触发;
- Trigger Filter:脉冲滤波设置,建议设置为15。
中断回调与寄存器
当自动重装载寄存器或者从模式控制器触发定时器更新中断时,会将状态寄存器中更新中断标志位置1,而从模式控制器接收到触发信号后,还会将触发器中断标志位(TIM_FLAG_TRIGGER) 置1;
借由此,我们可以判断是自动重装载寄存器导致的复位,还是从模式控制器导致的复位,进而做出不同的反应。
代码配置
基础计数
目的:TCTR5000每次检测到变化时,将其存入定时器TIM2的计数器并通过串口发送回上位机。
在CubeMX中,为Clock Source选择ETR2,或者Slave Mode选择External Clock Mode1并在Trigger Source中选择ETR1或TI1_ED或TI1FP1或TI2FP2,Prescaler则设置为0,Trigger Filter设置为15;
通过宏函数__HAL_TIM_GET_COUNTER直接读取计数器寄存器中的数值,若产生变化,则通过USRT2串口将其返回上位机:
HAL_TIM_Base_Start(&htim2);int counter=0,counter_temp=-1;char message[20]="";while (1){ /*Counter*/ counter=__HAL_TIM_GET_COUNTER(&htim2); if(counter!=counter_temp){ counter_temp=counter; sprintf(message, "counter:%d",counter); HAL_UART_Transmit_IT(&huart2, message, strlen(message)); }}将手指反复扫过TCTR5000,可以在串口助手中看到counter数值的递增:

从模式-复位模式
目的:定时器连接内部时钟正常计时,TCTR5000每次检测到变化时,将计数器重置。
在CubeMX中,为Slave Mode选择Reset Mode;
在死循环中写入以下代码块,以显示counter计数:
while(1){ /*Reset Mode*/ counter=__HAL_TIM_GET_COUNTER(&htim2); sprintf(message, "counter:%d",counter); HAL_UART_Transmit_IT(&huart2, (uint8_t*)message, strlen(message)); HAL_Delay(500);}复位模式会使用定时器更新中断回调函数,因此我们对其进行重定义,并且使用宏函数__HAL_TIM_GET_FLAG来确定是自动重装载寄存器还是从模式控制器导致的复位:
/*Reset Mode*/static const char *message_reload_auto="Reloaded(Auto).\n";static const char *message_reload_manual="Reloaded(Manual).\n";void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim==&htim2){ if(__HAL_TIM_GET_FLAG(htim,TIM_FLAG_TRIGGER)==SET){ __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_TRIGGER); HAL_UART_Transmit_IT(&huart2, (uint8_t*)message_reload_manual,strlen(message_reload_manual)); return; }else{ HAL_UART_Transmit_IT(&huart2, (uint8_t*)message_reload_auto,strlen(message_reload_auto)); return; } }}TIM_FLAG_TRIGGER并不会自动清零,因此在__HAL_TIM_GET_FLAG之后需要立刻将其清零,以防止之后误判。

从模式-门模式
目的:仅当TCTR5000输出高电平时计时,否则停止计数。
在CubeMX中,为Slave Mode选择Gated Mode;
门模式下,出现上升沿和下降沿均会将触发器中断标志置1,而并不会触发定时器更新中断,因此在定时器更新中断中对TIM_FLAG_TRIGGER操作毫无意义,将此段逻辑移动至while(1)中:
while (1){ /* Gated Mode */ const char *message_gate="Gated Mode triggered.\n"; if(__HAL_TIM_GET_FLAG(&htim2,TIM_FLAG_TRIGGER)==1){ __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_TRIGGER); HAL_UART_Transmit_IT(&huart2, (uint8_t*)message_gate, strlen(message_gate)); } counter=__HAL_TIM_GET_COUNTER(&htim2); sprintf(message, "counter:%d",counter); HAL_UART_Transmit_IT(&huart2, (uint8_t*)message, strlen(message)); HAL_Delay(500);}并删除定时器更新中断中的多余内容:
/* Gated Mode/Trigger Mode */static const char *message_reload_auto="Reloaded(Auto).\n";void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim==&htim2){ HAL_UART_Transmit_IT(&huart2, (uint8_t*)message_reload_auto,strlen(message_reload_auto)); return; }}
从模式-触发模式
目的:TCTR5000输出高电平时开始计时,计时达到自动重装载寄存器后归零并停止,等待下一次TCTR5000输出高电平。
在CubeMX中,为Slave Mode选择Trigger Mode,并为One Pulse Mode打勾,这样计数器在归零后会自动停止;
代码与门模式基本相同:
while (1){ /* Trigger Mode */ const char *message_trigger="Trigger Mode triggered.\n"; if(__HAL_TIM_GET_FLAG(&htim2,TIM_FLAG_TRIGGER)==1){ __HAL_TIM_CLEAR_FLAG(&htim2,TIM_FLAG_TRIGGER); HAL_UART_Transmit_IT(&huart2, (uint8_t*)message_trigger, strlen(message_trigger)); } counter=__HAL_TIM_GET_COUNTER(&htim2); sprintf(sendBuffer, "counter:%d",counter); HAL_UART_Transmit_IT(&huart2, (uint8_t*)sendBuffer, strlen(sendBuffer)); HAL_Delay(500);}
总结
相关函数
__HAL_TIM_GET_FLAG(__HANDLE__, __FLAG__);//读取触发器中断标志位__HAL_TIM_CLEAR_FLAG(__HANDLE__, __FLAG__);//清除触发器中断标志位外部时钟模式
- 外部时钟模式2:从GPIO口中获取外设发出的高低电平信号,并将其用于控制计数器。
从模式
- 外部时钟模式1(External Clock Mode 1):与外部时钟模式2基本一致;
- 复位模式(Reset Mode):输入信号上升沿/下降沿时对计数器的寄存器进行复位,并触发定时器更新中断(在中断模式下);
- 门模式(Gated Mode):出现上升沿时开始计数,出现下降沿时停止计数,即仅在高电平时进行计数,若启用下降沿触发则反之:仅在低电平时进行计数;
- 触发模式(Trigger Mode):当输入信号为高电平时,开始计数;但由于无法主动停止计数,因此通常搭配单脉冲模式(One Pulse Mode)使用:启用后,在重装载后会停止计时;
部分信息可能已经过时





