STM32F103C8T6定时器模块化
这篇主要是贴F103C8T6定时器定时时间的模块化程序,考虑到该单片机定时器资源,因此该定时器函数只能配置TIM2、TIM3、TIM4三个通用定时器的定时功能。该模块适配浮点数定时如上述展示的定时1.5ms,所以寻找合适的预分配系数要花不少CPU资源,因此该配置只适合初始化,不太适合出现在循环函数中。该定时函数的参数首项来自头文件中的枚举元素,次项是定时时间单位毫秒。该模块的特点看一下模块代码:T
这篇主要是贴F103C8T6定时器定时时间的模块化程序,考虑到该单片机定时器资源,因此该定时器函数只能配置TIM2、TIM3、TIM4三个通用定时器的定时功能。
先看一下它的使用方式:
Timer_Init_ms(TIMER2, 1.5f);//添加浮点数要加f,一般只做初始化
该模块适配浮点数定时如上述展示的定时1.5ms,所以寻找合适的预分配系数要花不少CPU资源,因此该配置只适合初始化,不太适合出现在循环函数中。该定时函数的参数首项来自头文件中的枚举元素,次项是定时时间单位毫秒。
该模块的特点
- 当时间很小(如 0.0001ms)时,可能存在浮点精度问题
- 最大可靠值:约几小时(超出 float 精度范围)
- 整型溢出保护:
- 72MHz 系统下最大定时:4294967295 / 72000 ≈ 59652 毫秒(约 59.6 秒)(FFFF FFFF =4294967295 )
- 超过这个时间需要额外处理(代码中的 else 分支)
- 负值处理:
- 当前方法不处理负值(ms 参数应为正)
看一下模块代码:Timer.c
#include "stm32f10x.h"
#include "Timer.h"
//#include <math.h> // 提供 fabsf 函数
//#include <float.h> // 提供 FLT_MAX
// 静态函数原型声明
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx);
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx);
// 获取定时器时钟源
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx)
{
if (TIMx == TIM1) return RCC_APB2Periph_TIM1;
else if (TIMx == TIM2) return RCC_APB1Periph_TIM2;
else if (TIMx == TIM3) return RCC_APB1Periph_TIM3;
else if (TIMx == TIM4) return RCC_APB1Periph_TIM4;
return 0; // 无效定时器
}
// 获取定时器IRQ号
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx)
{
// STM32F10x系列的IRQ映射关系
if (TIMx == TIM2) return TIM2_IRQn;
if (TIMx == TIM3) return TIM3_IRQn;
if (TIMx == TIM4) return TIM4_IRQn;
return NonMaskableInt_IRQn; // 默认返回不可屏蔽中断
}
// 优化后的定时器初始化函数
void Timer_Init_ms(Timer_Type timer_type, float ms) // 改为float类型
{
// 1. 定时器选择
TIM_TypeDef* TIMx;
switch(timer_type)
{
case TIMER2: TIMx = TIM2; break;
case TIMER3: TIMx = TIM3; break;
case TIMER4: TIMx = TIM4; break;
default: return; // 无效定时器
}
// 2. 时钟配置(智能识别APB1/APB2)
uint32_t timer_periph = get_timer_clock_source(TIMx);
// 如果定时器是TIM1或TIM8,它们位于APB2;否则在APB1
if (TIMx == TIM1 || TIMx == TIM8) {
RCC_APB2PeriphClockCmd(timer_periph, ENABLE);
} else {
RCC_APB1PeriphClockCmd(timer_periph, ENABLE);
}
// 3. 时基配置
TIM_TimeBaseInitTypeDef TIM_InitStruct = {
.TIM_ClockDivision = TIM_CKD_DIV1,
.TIM_CounterMode = TIM_CounterMode_Up,
.TIM_RepetitionCounter = 0
};
const uint32_t SystemClock = 72000000; // 72MHz系统时钟
const uint32_t MaxPeriod = 65535; // 16位最大值
// 计算所需的总时钟周期数(四舍五入)
uint32_t total_ticks = (uint32_t)(ms * (SystemClock / 1000.0f) + 0.5f);
if (total_ticks < 1) total_ticks = 1; // 防止零除错误
// 智能分解分频系数和重装载值
if (total_ticks <= (MaxPeriod + 1)) {
// 短定时:直接使用高精度模式
TIM_InitStruct.TIM_Prescaler = 0; // 无分频
TIM_InitStruct.TIM_Period = total_ticks - 1;
} else {
// 长定时:使用最佳优化组合
uint32_t best_prescaler = 0;
uint32_t best_period = 0;
uint32_t min_error = UINT32_MAX; // 初始化为最大整数ffff ffff
// 遍历可能的预分频值(仅遍历较小区间)
uint32_t prescaler_start = total_ticks / (MaxPeriod + 1);
uint32_t prescaler_end = total_ticks / 1;
if (prescaler_end > MaxPeriod) prescaler_end = MaxPeriod;
for (uint32_t prescaler = prescaler_start; prescaler <= prescaler_end; prescaler++) {
uint32_t period = total_ticks / (prescaler + 1);
// 检查是否在范围内
if (period > MaxPeriod) continue;
// 计算实际时钟周期数(整数运算)
uint32_t actual_ticks = (prescaler + 1) * period;
uint32_t error = (actual_ticks > total_ticks) ?
(actual_ticks - total_ticks) :
(total_ticks - actual_ticks);
// 更新最优解
if (error < min_error) {
min_error = error;
best_prescaler = prescaler;
best_period = period;
}
}
TIM_InitStruct.TIM_Prescaler = best_prescaler;
TIM_InitStruct.TIM_Period = best_period - 1; // 调整为寄存器值
}
TIM_TimeBaseInit(TIMx, &TIM_InitStruct);
// 4. 中断配置
TIM_ClearFlag(TIMx, TIM_FLAG_Update);
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
// 5. NVIC配置
NVIC_InitTypeDef NVIC_InitStruct = {
.NVIC_IRQChannel = get_timer_irqn(TIMx),
.NVIC_IRQChannelCmd = ENABLE,
.NVIC_IRQChannelPreemptionPriority = 1,
.NVIC_IRQChannelSubPriority = 1
};
NVIC_Init(&NVIC_InitStruct);
// 6. 启动定时器
TIM_Cmd(TIMx, ENABLE);
}
/*================================================
使用注意事项
资源限制:
在 STM32F103C8T6 等资源有限的 MCU 上,避免在中断中做复杂计算
定时器初始化只执行一次,性能开销可以接受
精度限制:
最小分辨率:约 13.89ns (72MHz 时钟)
实际误差:<1 个时钟周期
可维护性:
在头文件中更新函数声明:
// 初始化TIM2为1.5ms定时器
Timer_Init_ms(TIMER2, 1.5f);
// 初始化TIM3为0.75ms定时器
Timer_Init_ms(TIMER3, 0.75f);
// 初始化TIM4为100.5ms定时器
Timer_Init_ms(TIMER4, 100.5f);
浮点数精度限制:
当时间很小(如 0.0001ms)时,可能存在浮点精度问题
最大可靠值:约几小时(超出 float 精度范围)
整型溢出保护:
72MHz 系统下最大定时:4294967295 / 72000 ≈ 59652 毫秒(约 59.6 秒)
超过这个时间需要额外处理(您代码中的 else 分支)
负值处理:
当前方法不处理负值(ms 参数应为正)
可添加防护:
if(ms < 0) ms = 0.0f;
*/
Timer.h
#ifndef __TIMER_H
#define __TIMER_H
#pragma once
#include "stm32f10x.h"
// 支持的定时器枚举类型
typedef enum {
TIMER2 = 0,
TIMER3,
TIMER4
// 可扩展其他定时器...F103C8T6定时器资源TIM1、TIM2、TIM3、TIM4
} Timer_Type;
// 初始化函数声明
void Timer_Init_ms(Timer_Type timer, float ms); // 改为float类型
#endif
长定时模式(>65ms) - 资源优化配置
| 参数 | 值 | 物理意义 |
|---|---|---|
| 时钟源 | 72 MHz | 不变 |
| 自动重装载值 | 固定 59999 | 60,000 个时钟周期(≈0.833ms) |
| 预分频器 | ms×1000/60000 | 动态分频(扩大定时范围) |
| 实际定时时间 | ≈ms 毫秒 | 有小幅近似(但 < 1% 误差) |
工作原理:
定时时间 = (重装载值+1) × (预分频+1) / 时钟频率
= (59999+1) × (预分频+1) / 72,000,000
= 60,000 × (预分频+1) / 72,000,000
= (预分频+1)/1200 秒
= (预分频+1)/1.2 毫秒
这是无浮点数的长定时模式
通过程序得知:短时间定时取消了预分频的参数它的值被统一为0即:TIM_Prescaler = 0;它的系数设置都在TIM_Period 这项。长时间定时则有些不同看程序:
const uint32_t SystemClock = 72000000; // 72MHz系统时钟
const uint32_t MaxPeriod = 65535; // 16位最大值
// 计算所需的总时钟周期数(四舍五入)
uint32_t total_ticks = (uint32_t)(ms * (SystemClock / 1000.0f) + 0.5f);
else {
// 长定时:使用最佳优化组合
uint32_t best_prescaler = 0;
uint32_t best_period = 0;
uint32_t min_error = UINT32_MAX; // 初始化为最大整数4294967295u
// 遍历可能的预分频值(仅遍历较小区间)
uint32_t prescaler_start = total_ticks / (MaxPeriod + 1);
uint32_t prescaler_end = total_ticks / 1;
if (prescaler_end > MaxPeriod) prescaler_end = MaxPeriod;
for (uint32_t prescaler = prescaler_start; prescaler <= prescaler_end; prescaler++)
{
uint32_t period = total_ticks / (prescaler + 1);
// 检查是否在范围内
if (period > MaxPeriod) continue;
// 计算实际时钟周期数(整数运算)
uint32_t actual_ticks = (prescaler + 1) * period;
uint32_t error = (actual_ticks > total_ticks) ?
(actual_ticks - total_ticks) :
(total_ticks - actual_ticks);
// 更新最优解
if (error < min_error)
{
min_error = error;
best_prescaler = prescaler;
best_period = period;
}
}
TIM_InitStruct.TIM_Prescaler = best_prescaler;
TIM_InitStruct.TIM_Period = best_period - 1; // 调整为寄存器值
这个程序是什么意思呢?历遍所有可能的预分频值与由整型除法运算得到的计数周期的值period得到的乘积。这些乘积与total_ticks之间存在差值,差值最小的组合就是我们需要的设置的参数值。
然后分析一下程序为什么要这么写:
1. 无法整除导致的截断误差
uint32_t period = total_ticks / (prescaler + 1); // 整数除法截断小数
这是误差的核心来源:
- 理想值:理想的 period 应为浮点数
total_ticks / (prescaler + 1) - 实际值:整数除法截断小数部分(不是四舍五入)
- 截断误差:
period = floor(total_ticks / (prescaler + 1))
2. 系数放大效应
uint32_t actual_ticks = (prescaler + 1) * period;
- 误差放大倍数:预分频值
(prescaler + 1)就是误差放大系数 - 截断误差成本:
误差 = (prescaler + 1) × 舍弃的小数部分因此程序需要通过比较误差的绝对值来确定误差最小的一组参数值。而历遍所有的组合需要花不少的CPU资源因此该函数只适合出现在初始化的时候。
基于此再修改下程序,在传参的时候判断一下是否是浮点数来进入不同的配置函数,来提高定时模块的应用场景。
Timer.c
#include "stm32f10x.h"
#include "Timer.h"
#include <math.h> // 用于 fabsf
#include <stdlib.h> // 用于 abs
#include <float.h> // 用于 FLT_MAX
#include <stdint.h> // 用于 uint32_t 等
/*===================================================================
该函数目前只适用STM32F103C8T6,不同的单片机需要有相应的修改,修改主函数部分和头文件
该模块目前只支持TIMER2 TIMER3 TIMER4
精度限制:
最小分辨率:约 13.89ns (72MHz 时钟)
72MHz 系统下最大定时:4294967295 / 72000 ≈ 59652 毫秒(约 59.6 秒)
函数支持整数和浮点数输入
// 初始化TIM2为1.5ms定时器
Timer_Init_ms(TIMER2, 1.5f);定时1.5ms
Timer_Init_ms(TIMER2, 1);定时1ms
函数会自动判断传入是整数还是浮点数,浮点数一般只适合初始化因为消耗的CPU资源较多,
整数消耗的CPU资源小一般可以出现在各种场景
*/
// 静态函数原型声明
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx);
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx);
static int is_integer(float value);
// 辅助函数:判断浮点数是否为整数
static int is_integer(float value) {
return (fabsf(value - (float)(int)value) < 0.0001f);
}
// 获取定时器时钟源
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx)
{
if (TIMx == TIM1) return RCC_APB2Periph_TIM1;
else if (TIMx == TIM2) return RCC_APB1Periph_TIM2;
else if (TIMx == TIM3) return RCC_APB1Periph_TIM3;
else if (TIMx == TIM4) return RCC_APB1Periph_TIM4;
return 0; // 无效定时器
}
// 获取定时器IRQ号
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx)
{
// STM32F10x系列的IRQ映射关系
if (TIMx == TIM2) return TIM2_IRQn;
if (TIMx == TIM3) return TIM3_IRQn;
if (TIMx == TIM4) return TIM4_IRQn;
return NonMaskableInt_IRQn; // 默认返回不可屏蔽中断
}
// 优化后的定时器初始化函数
void Timer_Init_ms(Timer_Type timer_type, float ms)
{
// 1. 定时器选择
TIM_TypeDef* TIMx;
switch(timer_type)
{
case TIMER2: TIMx = TIM2; break;
case TIMER3: TIMx = TIM3; break;
case TIMER4: TIMx = TIM4; break;
default: return; // 无效定时器
}
// 2. 时钟配置(智能识别APB1/APB2)
uint32_t timer_periph = get_timer_clock_source(TIMx);
// 如果定时器是TIM1或TIM8,它们位于APB2;否则在APB1
if (TIMx == TIM1 || TIMx == TIM8) {
RCC_APB2PeriphClockCmd(timer_periph, ENABLE);
} else {
RCC_APB1PeriphClockCmd(timer_periph, ENABLE);
}
// 3. 时基结构体声明
TIM_TimeBaseInitTypeDef TIM_InitStruct;
// 4. 判断是否为整数毫秒
if (is_integer(ms)) {
// 整数毫秒模式(高效算法)
uint16_t ms_int = (uint16_t)ms;
// 使用高效整数算法
if (ms_int <= 65) {
// 短定时模式:高精度模式(1微秒分辨率)
TIM_InitStruct.TIM_Prescaler = 71; // 预分频72
TIM_InitStruct.TIM_Period = ms_int * 1000 - 1;
} else {
// 长定时模式
TIM_InitStruct.TIM_Period = 59999; // 固定周期值
// 计算基础分频值
uint32_t calculated_value = ms_int * 1000 / 60000;
// 应用减法并边界检查
if (calculated_value > 0xFFFF) {
TIM_InitStruct.TIM_Prescaler = 0xFFFF;
} else if (calculated_value > 0) {
TIM_InitStruct.TIM_Prescaler = calculated_value - 1;
} else {
TIM_InitStruct.TIM_Prescaler = 0;
}
}
} else {
// 浮点毫秒模式(高精度算法)
const uint32_t SystemClock = 72000000; // 72MHz系统时钟
const uint32_t MaxPeriod = 65535; // 16位最大值
// 计算所需的总时钟周期数(四舍五入)
uint32_t total_ticks = (uint32_t)(ms * (SystemClock / 1000.0f) + 0.5f);
if (total_ticks < 1) total_ticks = 1; // 防止零除错误
if (total_ticks <= (MaxPeriod + 1)) {
// 短定时:直接使用高精度模式
TIM_InitStruct.TIM_Prescaler = 0; // 无分频
TIM_InitStruct.TIM_Period = total_ticks - 1;
} else {
// 长定时:使用最佳优化组合
uint32_t best_prescaler = 0;
uint32_t best_period = 0;
uint32_t min_error = UINT32_MAX; // 初始化为最大整数
// 遍历可能的预分频值(仅遍历较小区间)
uint32_t prescaler_start = total_ticks / (MaxPeriod + 1);
uint32_t prescaler_end = total_ticks / 1;
if (prescaler_end > MaxPeriod) prescaler_end = MaxPeriod;
for (uint32_t prescaler = prescaler_start; prescaler <= prescaler_end; prescaler++) {
uint32_t period = total_ticks / (prescaler + 1);
// 检查是否在范围内
if (period > MaxPeriod) continue;
// 计算实际时钟周期数
uint32_t actual_ticks = (prescaler + 1) * period;
uint32_t error = (actual_ticks > total_ticks) ?
(actual_ticks - total_ticks) :
(total_ticks - actual_ticks);
// 更新最优解
if (error < min_error) {
min_error = error;
best_prescaler = prescaler;
best_period = period;
}
}
TIM_InitStruct.TIM_Prescaler = best_prescaler;
TIM_InitStruct.TIM_Period = best_period - 1; // 调整为寄存器值
}
}
// 5. 配置其他参数
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStruct.TIM_RepetitionCounter = 0;
// 6. 初始化时基
TIM_TimeBaseInit(TIMx, &TIM_InitStruct);
// 7. 中断配置
TIM_ClearFlag(TIMx, TIM_FLAG_Update);
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
// 8. NVIC配置
NVIC_InitTypeDef NVIC_InitStruct = {
.NVIC_IRQChannel = get_timer_irqn(TIMx),
.NVIC_IRQChannelCmd = ENABLE,
.NVIC_IRQChannelPreemptionPriority = 1,
.NVIC_IRQChannelSubPriority = 1
};
NVIC_Init(&NVIC_InitStruct);
// 9. 启动定时器
TIM_Cmd(TIMx, ENABLE);
}
头文件是通用的。
解释一整数判断函数的工作方式:
static int is_integer(float value) {
return (fabsf(value - (float)(int)value) < 0.0001f);
}
1. 强制转换为整数并回退
(int)value
- 将浮点数
value强制转换为整数 - 这种转换会截断小数部分(不是四舍五入)
- 例如:
3.75→3,4.0→4
2. 转回浮点数
(float)(int)value
- 将整数转回浮点数(创建整数表示)
- 例如:
3.75→ 截断为3→ 转回3.0f
3. 计算差值
value - (float)(int)value
- 计算原始值与其整数表示之间的差
- 这个差值就是小数部分
- 例如:
3.75→ 差值 =3.75 - 3.0 = 0.754.0→ 差值 =4.0 - 4.0 = 0.0
4. 取绝对值
fabsf(...)
- 获取差的绝对值(确保正数)
- 使用浮点版本绝对值函数
- 例如:
0.75→0.75-3.75→ 差值 =-3.75 - (-3.0) = -0.75→fabsf(-0.75) = 0.75
5. 比较判断
... < 0.0001f
判断差值是否小于一个小阈值(0.0001)
如果小于阈值,认为该浮点数 "本质上是整数"
返回:
1 (true):是整数
0 (false):不是整数
为什么需要阈值(0.0001f)?
-
浮点数精度问题:
- 有些整数在内存中存储为近似值
- 例如:
3.0可能实际存储为3.000001或2.999999 - 阈值允许这种微小误差
-
实际应用需求:
- 在定时器场景,千分之一毫秒精度足够
- 0.0001ms = 0.1μs ≈ 7.2 个时钟周期(72MHz)
但是呢这个函数有一点问题它会把3.00001认为是整数不会把2.99999认为是整数,因此这个判断函数还需要再修改一下。
static int is_integer(float value) {
// 计算向下取整和向上取整的差距
float floor_val = (float)(int)value;
float ceil_val = (float)(int)(value + 0.5f);
float floor_diff = fabsf(value - floor_val);
float ceil_diff = fabsf(value - ceil_val);
// 取最小值
float min_diff = (floor_diff < ceil_diff) ? floor_diff : ceil_diff;
// 检查是否足够接近某个整数
return (min_diff < 0.0001f); // 0.1微秒容差
}
现在函数能把近似的浮点数判断为整数,但是在后续强制转换那里就需要再修改一下即
// 4. 判断是否为整数毫秒
if (is_integer(ms)) {
// 整数毫秒模式(高效算法)
uint16_t ms_int = (uint16_t)ms;
改成如下
// 4. 判断是否为整数毫秒
if (is_integer(ms)) {
// 整数毫秒模式(高效算法)
uint16_t ms_int = (uint16_t)(ms+0.1f);
后续程序加的0.1f刚好能处理这种4.9999的数转换为5.
它的工作原理:
1. is_integer 的判断特性
假设容差阈值为 0.0001:
is_integer(4.00001f) == 1(小于 0.0001)is_integer(4.99999f) == 1(小于 0.0001)is_integer(4.90000f) == 0(远大于 0.0001)
2. 加 0.1f 的转换效果
| 输入值 | +0.1f 后 | 转为 uint16_t | 结果 |
|---|---|---|---|
| 4.00001f | 4.10001f | 4 | 正确 |
| 4.99999f | 5.09999f | 5 | 正确 |
| 5.00000f | 5.10000f | 5 | 正确(应为 5) |
| 4.90000f | 5.00000f | 5 | 但不会进入此分支 |
3. 关键安全措施
- 只对通过整数检测的值应用加 0.1f
- 非整数值(如 4.9)不会进入此分支
- 4.9 被判断为非整数 → 进入浮点分支 → 不会失真
因此最后的函数是:
#include "stm32f10x.h"
#include "Timer.h"
#include <math.h> // 用于 fabsf
#include <stdlib.h> // 用于 abs
#include <float.h> // 用于 FLT_MAX
#include <stdint.h> // 用于 uint32_t 等
/*===================================================================
该函数目前只适用STM32F103C8T6,不同的单片机需要有相应的修改,修改主函数部分和头文件
该模块目前只支持TIMER2 TIMER3 TIMER4
精度限制:
最小分辨率:约 13.89ns (72MHz 时钟)
72MHz 系统下最大定时:4294967295 / 72000 ≈ 59652 毫秒(约 59.6 秒)
函数支持整数和浮点数输入
// 初始化TIM2为1.5ms定时器
Timer_Init_ms(TIMER2, 1.5f);定时1.5ms
Timer_Init_ms(TIMER2, 1);定时1ms
函数会自动判断传入是整数还是浮点数,浮点数一般只适合初始化因为消耗的CPU资源较多,
整数消耗的CPU资源小一般可以出现在各种场景
*/
// 静态函数原型声明
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx);
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx);
static int is_integer(float value);
// 辅助函数:判断浮点数是否为整数
//static int is_integer(float value) {
// return (fabsf(value - (float)(int)value) < 0.0001f);
//}
static int is_integer(float value) {
// 计算向下取整和向上取整的差距
float floor_val = (float)(int)value;
float ceil_val = (float)(int)(value + 0.5f);
float floor_diff = fabsf(value - floor_val);
float ceil_diff = fabsf(value - ceil_val);
// 取最小值
float min_diff = (floor_diff < ceil_diff) ? floor_diff : ceil_diff;
// 检查是否足够接近某个整数
return (min_diff < 0.0001f); // 0.1微秒容差
}
// 获取定时器时钟源
static uint32_t get_timer_clock_source(TIM_TypeDef* TIMx)
{
if (TIMx == TIM1) return RCC_APB2Periph_TIM1;
else if (TIMx == TIM2) return RCC_APB1Periph_TIM2;
else if (TIMx == TIM3) return RCC_APB1Periph_TIM3;
else if (TIMx == TIM4) return RCC_APB1Periph_TIM4;
return 0; // 无效定时器
}
// 获取定时器IRQ号
static IRQn_Type get_timer_irqn(TIM_TypeDef* TIMx)
{
// STM32F10x系列的IRQ映射关系
if (TIMx == TIM2) return TIM2_IRQn;
if (TIMx == TIM3) return TIM3_IRQn;
if (TIMx == TIM4) return TIM4_IRQn;
return NonMaskableInt_IRQn; // 默认返回不可屏蔽中断
}
// 优化后的定时器初始化函数
void Timer_Init_ms(Timer_Type timer_type, float ms)
{
// 1. 定时器选择
TIM_TypeDef* TIMx;
switch(timer_type)
{
case TIMER2: TIMx = TIM2; break;
case TIMER3: TIMx = TIM3; break;
case TIMER4: TIMx = TIM4; break;
default: return; // 无效定时器
}
// 2. 时钟配置(智能识别APB1/APB2)
uint32_t timer_periph = get_timer_clock_source(TIMx);
// 如果定时器是TIM1或TIM8,它们位于APB2;否则在APB1
if (TIMx == TIM1 || TIMx == TIM8) {
RCC_APB2PeriphClockCmd(timer_periph, ENABLE);
} else {
RCC_APB1PeriphClockCmd(timer_periph, ENABLE);
}
// 3. 时基结构体声明
TIM_TimeBaseInitTypeDef TIM_InitStruct;
// 4. 判断是否为整数毫秒
if (is_integer(ms)) {
// 整数毫秒模式(高效算法)
uint16_t ms_int = (uint16_t)(ms+0.1f);
// 使用高效整数算法
if (ms_int <= 65) {
// 短定时模式:高精度模式(1微秒分辨率)
TIM_InitStruct.TIM_Prescaler = 71; // 预分频72
TIM_InitStruct.TIM_Period = ms_int * 1000 - 1;
} else {
// 长定时模式
TIM_InitStruct.TIM_Period = 59999; // 固定周期值
// 计算基础分频值
uint32_t calculated_value = ms_int * 1000 / 60000;
// 应用减法并边界检查
if (calculated_value > 0xFFFF) {
TIM_InitStruct.TIM_Prescaler = 0xFFFF;
} else if (calculated_value > 0) {
TIM_InitStruct.TIM_Prescaler = calculated_value - 1;
} else {
TIM_InitStruct.TIM_Prescaler = 0;
}
}
} else {
// 浮点毫秒模式(高精度算法)
const uint32_t SystemClock = 72000000; // 72MHz系统时钟
const uint32_t MaxPeriod = 65535; // 16位最大值
// 计算所需的总时钟周期数(四舍五入)
uint32_t total_ticks = (uint32_t)(ms * (SystemClock / 1000.0f) + 0.5f);
if (total_ticks < 1) total_ticks = 1; // 防止零除错误
if (total_ticks <= (MaxPeriod + 1)) {
// 短定时:直接使用高精度模式
TIM_InitStruct.TIM_Prescaler = 0; // 无分频
TIM_InitStruct.TIM_Period = total_ticks - 1;
} else {
// 长定时:使用最佳优化组合
uint32_t best_prescaler = 0;
uint32_t best_period = 0;
uint32_t min_error = UINT32_MAX; // 初始化为最大整数
// 遍历可能的预分频值(仅遍历较小区间)
uint32_t prescaler_start = total_ticks / (MaxPeriod + 1);
uint32_t prescaler_end = total_ticks / 1;
if (prescaler_end > MaxPeriod) prescaler_end = MaxPeriod;
for (uint32_t prescaler = prescaler_start; prescaler <= prescaler_end; prescaler++) {
uint32_t period = total_ticks / (prescaler + 1);
// 检查是否在范围内
if (period > MaxPeriod) continue;
// 计算实际时钟周期数
uint32_t actual_ticks = (prescaler + 1) * period;
uint32_t error = (actual_ticks > total_ticks) ?
(actual_ticks - total_ticks) :
(total_ticks - actual_ticks);
// 更新最优解
if (error < min_error) {
min_error = error;
best_prescaler = prescaler;
best_period = period;
}
}
TIM_InitStruct.TIM_Prescaler = best_prescaler;
TIM_InitStruct.TIM_Period = best_period - 1; // 调整为寄存器值
}
}
// 5. 配置其他参数
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStruct.TIM_RepetitionCounter = 0;
// 6. 初始化时基
TIM_TimeBaseInit(TIMx, &TIM_InitStruct);
// 7. 中断配置
TIM_ClearFlag(TIMx, TIM_FLAG_Update);
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
// 8. NVIC配置
NVIC_InitTypeDef NVIC_InitStruct = {
.NVIC_IRQChannel = get_timer_irqn(TIMx),
.NVIC_IRQChannelCmd = ENABLE,
.NVIC_IRQChannelPreemptionPriority = 1,
.NVIC_IRQChannelSubPriority = 1
};
NVIC_Init(&NVIC_InitStruct);
// 9. 启动定时器
TIM_Cmd(TIMx, ENABLE);
}
至此本案结束。
更多推荐



所有评论(0)