STM32F1与STM32CubeIDE快速入门-定时器输入捕获模式(Input Capture Mode)实现频率计数

定时器输入捕获模式(Input Capture Mode)

1、定时器输入捕获模式介绍

在本文中,将介绍输入捕捉模式下的定时器模块操作。 如何配置定时器以在输入捕捉模式下运行。 以及如何启用其中一个输入捕捉通道在上升沿捕捉外部信号(使用NE555作为输入信号源),构建一个简单的频率计数器应用程序。

正如我们在之前的文章中所介绍的,定时器模块可以运行多种模式,其中一种是输入捕捉模式。 每当输入捕捉通道引脚上发生特殊事件时,定时器从内部源获取时钟,并捕捉其当前值并将其保存到输入捕捉寄存器。 下面简要介绍 STM32 定时器模块中的捕获比较通道。 另请注意,每个定时器模块都有多个(输入捕获/比较输出)通道(3、4、6 或数据表中的任何内容)。
在这里插入图片描述

1.1 STM32 定时器—捕捉/比较通道

每个捕捉/比较通道都围绕一个捕捉/比较寄存器(包括一个影子寄存器)、一个用于捕捉的输入级(带有数字滤波器、多路复用和预分频器)和一个输出级(带有比较器和输出控制)构建。 输入级对相应的 TIx 输入进行采样以生成滤波信号 TIxF。 然后,具有极性选择功能的边沿检测器生成一个信号 (TIxFPx),该信号可用作从模式控制器的触发输入或捕获命令。 它在捕获寄存器 (ICxPS) 之前预分频。如下图所示(捕获/比较通道输入级的示意图):

在这里插入图片描述

捕获/比较通道 1 全电路图如下:
在这里插入图片描述

1.2 输入捕捉模式下的 STM32 定时器

在输入捕捉模式下,捕捉/比较寄存器 (TIMx_CCRx) 用于在相应 ICx 信号检测到转换后锁存计数器的值。 当发生捕捉时,相应的 CCXIF 标志(TIMx_SR 寄存器)被置位,如果使能,则可以发送中断或 DMA 请求。 如果在 CCxIF 标志已经为高电平时发生捕捉,则设置过度捕捉标志 CCxOF(TIMx_SR 寄存器)。 CCxIF 可通过软件将其写入 0 或读取存储在 TIMx_CCRx 寄存器中的捕获数据来清除。 CCxOF 在写入 0 时被清零。

2、输入捕捉模式频率计数器实现

2.1 频率计数器功能介绍

在这个实例中,将构建一个系统,在输入捕捉模式下使用定时器模块测量数字信号的频率。 系统将经历我选择命名它们的几个状态(空闲、完成)。 在 IDLE 状态下,系统准备使用在后台运行的 TIM2 捕获第一个时间戳,直到外部信号在 IC1 输入通道引脚上发生上升沿转换。

在第一个上升沿,timer2 的值被捕获到 CCR1 寄存器并触发中断。 在 ISR 中,我们将 CCR1 值保存到名为 T1 的变量中。 并且系统状态从 IDLE 变为 DONE,

在 DONE 状态下,系统将正常运行,直到 IC1 引脚再次出现上升沿。 因此,在 ISR 中,将捕获的值保存到 T2 变量中。 而测得的周期为(T2-T1),所以频率计算为(1/(T2-T1))。 然后,它使用 UART1 打印到串行端口。 最后系统状态再次翻转为IDLE,整个过程重复。
在这里插入图片描述

值得注意的一点是,TIM2 在第二个上升沿到来之前很容易溢出。 这在频谱的低端尤其重要,因为测量的是长周期的低频。 因此,还必须启用 TIM2 溢出中断,并且我们将跟踪自从我们保存了 T1 值以来发生了多少溢出。 所以 OVF_Count 添加到 T2 值,每个溢出占 65536 个滴答。

2.2 设置

工程创建、芯片时钟设置使用最高频率72MHz,具体如何设置,请参考前面文章。本功能的定时器设置如下:

在这里插入图片描述

并开户定时器的中断功能:

在这里插入图片描述

保存并生成代码。

2.3 代码实现

1)基本定义

1
2
3
4
5
6
7
8
9
10
11
12
#define IDLE 0
#define DONE 1
#define F\_CLK 72000000UL

volatile uint8\_t gu8_State = IDLE;
volatile uint8\_t gu8_MSG[20] = { 0 };
volatile uint32\_t gu32_T1 = 0;
volatile uint32\_t gu32_T2 = 0;
volatile uint32\_t gu32_Ticks = 0;
volatile uint16\_t gu16_TIM2_OVC = 0;
volatile uint32\_t gu32_Freq = 0;

2)定时器输入捕捉回调实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void HAL\_TIM\_IC\_CaptureCallback(TIM_HandleTypeDef \*htim) {
if (gu8_State == IDLE) {
gu32_T1 = TIM2->CCR1;
gu16_TIM2_OVC = 0;
gu8_State = DONE;
} else if (gu8_State == DONE) {
gu32_T2 = TIM2->CCR1;
gu32_Ticks = (gu32_T2 + (gu16_TIM2_OVC \* 65536)) - gu32_T1;
gu32_Freq = (uint32\_t) (F_CLK / gu32_Ticks);
memset(gu8_MSG,0,35);
if (gu32_Freq != 0) {
sprintf(gu8_MSG, "Frequency = %lu Hz\r\n", gu32_Freq);
HAL\_UART\_Transmit(&huart1, gu8_MSG, sizeof(gu8_MSG), 100);
}
gu8_State = IDLE;
}
}

// 滴答计数
void HAL\_TIM\_PeriodElapsedCallback(TIM_HandleTypeDef \*htim) {
gu16_TIM2_OVC++;
}

最后在main.c中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_TIM2\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
// 启动定时器
HAL\_TIM\_IC\_Start\_IT(&htim2, TIM_CHANNEL_1);
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

2.4 运行结果

在这里插入图片描述

本次实例使用的信号生成器为NE555模块,对频率计数不做精度控制。

文章来源: https://iotsmart.blog.csdn.net/article/details/123383529