这篇主要是贴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 → 34.0 → 4
2. 转回浮点数
(float)(int)value
  • 将整数转回浮点数(创建整数表示)
  • 例如:3.75 → 截断为 3 → 转回 3.0f

3. 计算差值

value - (float)(int)value
  • 计算原始值与其整数表示之间的差
  • 这个差值就是小数部分
  • 例如:
    • 3.75 → 差值 = 3.75 - 3.0 = 0.75
    • 4.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)?

  1. 浮点数精度问题

    • 有些整数在内存中存储为近似值
    • 例如:3.0 可能实际存储为 3.000001 或 2.999999
    • 阈值允许这种微小误差
  2. 实际应用需求

    • 在定时器场景,千分之一毫秒精度足够
    • 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);
}


至此本案结束。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐