1.基础
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
slave mode(从模式): STM32定时器的Slave Mode(从模式) 是指一个定时器(从定时器)可以被另一个定时器(主定时器)或外部信号控制其工作状态(如启动/停止/复位/计数等)。这种设计主要用于实现多定时器同步、事件联动或复杂波形生成等高级功能。
trigger mode(触发模式):是指控制从定时器(Slave Timer) 行为的外部信号来源。它决定了从定时器如何响应外部事件(如启动/停止/复位等)。
clock source(时钟源): 决定了定时器计数器(CNT)的计数脉冲来源
channels(通道): 是定时器与外部世界交互的核心接口,而组合通道(Combined Channels) 则是实现高级控制功能的关键技术。
/**/定时器模式:主模式、从模式
日常口语里 “主 / 从” 天然理解成:主机主动跑、从机跟着主机跑,但 ST 手册里的Master mode / Slave mode不是这个含义,它是针对内部触发信号流向定义的,不是定时器是否独立运行。
原始定义(ST 的视角,它是站在「内部触发信号」这个信号链路起名字)
- Slave mode(从模式):定时器作为触发信号的接收方(信号的从节点),接收触发信号来控制 CNT;
- Master mode(主模式):定时器作为触发信号的发送方(信号的主节点),产生 TRGO 信号输出给其他外设;
重点:它描述的是在触发信号链路里的角色,而不是定时器本身是不是 “主控设备”。
所以就会出现非常反常识的组合: 一个定时器既可以是触发信号的接收者 (Slave 开),同时又是触发信号的发送者 (Master 开),一边听别人,一边向外发信号。 这个是最容易把人绕晕的地方。
如果重新起名,改成下面这套就清晰很多(工程上很多老工程师私下就这么叫)
| ST 官方名字 | 推荐替换名称 | 含义 |
|---|---|---|
| Slave mode | 触发接收模式 | 接收外部触发信号,控制 CNT 复位 / 启停 |
| Master mode | 触发输出模式 | 内部事件发生时,输出 TRGO 触发脉冲给别的外设 |
| Slave mode = Disable | 触发接收关闭 | 不受外部触发控制,CNT 自由计数 |
为什么 ST 没有改名?
这是传统定时器 IP 的历史遗留命名,不是 ST 独创。早年 TI、Motorola 的老定时器 IP,内部同步互联的信号链路就沿用 Master/Slave 这套术语,ST 直接沿用了这套寄存器定义。 在多器件同步链路这个场景下,这个命名原本是合理的;但当新手单独看一个定时器,很容易按嵌入式系统 “主机 / 从机(I2C 主从那种)” 去理解,直接冲突。
I2C 的主从:是收发数据的设备角色; TIM 的主从:只是内部触发脉冲的发送 / 接收角色;两套主从完全不是一回事,术语撞车,是混乱的根源!
1.1基础通道功能(Channel 1~4)
每个通用定时器(TIMx)通常包含4个独立通道,每个通道均可独立配置为输入或输出模式:
输入模式(捕获功能)
应用场景:测量脉冲宽度、频率捕获
输出模式(比较功能)
应用场景:PWM生成、精确时间控制
1.2组合通道(Combined Channels)
组合通道是多个通道协同工作实现复杂功能的高级模式,主要在高级定时器(TIM1/TIM8)中实现:
1. 互补通道(Complementary Channels)
应用:电机驱动H桥电路
2. 三相PWM控制(3-Phase PWM)
3. 编码器接口模式(组合通道1+2)
2.定时器中断--1秒
控制LED0闪烁,周期为1S
1)基本配置
因为,有Tclk=72MHz,选择arr=9999,psc=7199,则溢出时间为Tout=1S
2)编辑代码
触发中断时系统会调用定义于stm32f1xx_it.c的void TIM2_IRQHandler(void);可以看到该中断服务函数又调用定义于stm32f1xx_hal_tim.c中的HAL_TIM_IRQHandler(&htim2);函数HAL_TIM_IRQHandler(&htim2)又调用回调函数PeriodElapsedCallback(htim)
重定义void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器溢出中断 { if (htim== &htim2) //确定是否是定时器2触发中断 { HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin); } }需要在主函数中启动定时器,用函数HAL_TIM_Base_Start_IT(&htim2);
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(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }3)分析
3.输出PWM波--呼吸灯
PWM即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
1)进行基本配置
2)进行编码
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_TIM1_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//启动PWM输出 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ uint16_t pwmVal=0; //PWM占空比 while (1) { /* USER CODE END WHILE */ while (pwmVal< 1000) { pwmVal++; __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pwmVal); //修改比较值,修改占空比 HAL_Delay(1); } while (pwmVal > 0) { pwmVal--; __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pwmVal); //修改比较值,修改占空比 HAL_Delay(1); } HAL_Delay(200); //完全熄灭后,等待200ms } /* USER CODE BEGIN 3 */ /* USER CODE END 3 */ }3)分析
4.测量PWM信号的频率及占空比
在定时计数器的输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道 可配置为PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
1)进行基本配置
time1测量周期和占空比,用其输出捕获通道1和2
time2、time3分别输出不同频率PWM波
2)进行编码
重定义time1的 捕获中断3处理函数,计算频率和占空比
// 捕获值变量 uint32_t IC1Value = 0; // 通道 1 当前捕获值(上升沿) uint32_t Last_IC1Value = 0; // 通道 1 上一次捕获值(上升沿) uint32_t IC2Value = 0; // 通道 2 捕获值(下降沿) uint32_t Period = 0; // 周期时间 uint32_t HighTime = 0; // 高电平时间 uint32_t Frequency = 0; // 计算的 PWM 频率 uint32_t DutyCycle = 0; // 计算的 PWM 占空比 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM1) // 确认是 TIM1 { if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // 通道 1 上升沿捕获 { Last_IC1Value = IC1Value; // 保存上一次捕获值 IC1Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 当前捕获值 // 计算周期 if (IC1Value > Last_IC1Value) { Period = IC1Value - Last_IC1Value; } else // 计数器溢出处理 { Period = (htim->Init.Period - Last_IC1Value) + IC1Value + 1; } } else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) // 通道 2 下降沿捕获 { IC2Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // 捕获值(下降沿) // 计算高电平时间 if (IC2Value > IC1Value) { HighTime = IC2Value - IC1Value; } else // 计数器溢出处理 { HighTime = (htim->Init.Period - IC1Value) + IC2Value + 1; } // 计算频率和占空比 if (Period != 0) // 避免除零 { Frequency = HAL_RCC_GetPCLK2Freq() / (htim->Init.Prescaler + 1) / Period; DutyCycle = (HighTime * 100) / Period; } } } }主函数主要是设置time2和time3的参数,并启动输出,以及其他time1
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_TIM1_Init(); MX_TIM2_Init(); MX_TIM3_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); //启动PWM输出 __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,50); //ARR为100,设置占空比为50% HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); //启动PWM输出 __HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,600); //ARR为1000,设置占空比为60% HAL_TIM_Base_Start(&htim1); HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ sprintf(massage, "Frequency: %lu Hz, Duty Cycle: %lu%%\r\n", Frequency, DutyCycle); printf("%s",massage); HAL_Delay(1000); // 每 100 毫秒发送一次 /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }