TIM5定时器在ODrive中的详细分析
概述
TIM5是STM32F405的一个32位通用定时器,在ODrive固件中专门用于PWM输入捕获功能。它允许ODrive通过标准的RC PWM信号接收外部控制指令,大大增强了系统的易用性和兼容性。
目录
TIM5的基本作用
TIM5在ODrive中承担外部PWM信号捕获的核心功能:
- 信号测量:精确测量外部PWM信号的高电平时间
- 控制接口:提供标准的RC PWM控制接口
- 多通道支持:最多支持4个独立的PWM输入通道
- 信号处理:包括滤波、有效性检查、范围限制等
硬件配置
定时器参数
- 类型:32位通用定时器
- 时钟源:APB1总线,84MHz(TIM_APB1_CLOCK_HZ)
- 计数模式:向上计数(TIM_COUNTERMODE_UP)
- 周期值:0xFFFFFFFF(32位最大值,约51秒)
- 分辨率:11.9ns(1/84MHz)
输入捕获配置
cpp
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE; // 双边沿捕获
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接输入
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不分频
sConfigIC.ICFilter = 15; // 15个时钟周期滤波
GPIO连接
| 硬件版本 | 有效通道 | 对应GPIO | 定时器通道 |
|---|---|---|---|
| v3.1-3.2 | 通道4 | GPIO4 (PA3) | TIM5_CH4 |
| v3.3+ | 通道1-4 | GPIO1-4 | TIM5_CH1-CH4 |
代码中的具体使用位置
1. 全局对象定义(Board/v3/board.cpp)
cpp
// v3.1-3.2版本
PwmInput pwm0_input{&htim5, {0, 0, 0, 4}};
// v3.3及以上版本
PwmInput pwm0_input{&htim5, {1, 2, 3, 4}};
2. 中断处理函数(Board/v3/board.cpp)
cpp
void TIM5_IRQHandler(void) {
COUNT_IRQ(TIM5_IRQn);
pwm0_input.on_capture();
}
3. 定时器初始化(Board/v3/Src/tim.c)
cpp
void MX_TIM5_Init(void) {
htim5.Instance = TIM5;
htim5.Init.Prescaler = 0;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 0xFFFFFFFF;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// ... 通道配置
}
4. 中断使能配置(Board/v3/Src/tim.c)
cpp
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle) {
if(tim_icHandle->Instance==TIM5) {
__HAL_RCC_TIM5_CLK_ENABLE();
HAL_NVIC_SetPriority(TIM5_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM5_IRQn);
}
}
5. 初始化调用(MotorControl/main.cpp)
cpp
static void rtos_main(void*) {
// ... 其他初始化
pwm0_input.init(); // 初始化PWM输入捕获
// ... 后续代码
}
6. PWM输入类实现(MotorControl/pwm_input.cpp)
cpp
void PwmInput::init() {
// 配置并启动所有通道的输入捕获
for (size_t i = 0; i < 4; ++i) {
HAL_TIM_IC_ConfigChannel(htim_, &sConfigIC, channels[i]);
HAL_TIM_IC_Start_IT(htim_, channels[i]);
}
}
输入捕获工作原理
基本流程
外部PWM信号 → GPIO引脚 → TIM5输入捕获 → 记录时间戳 → 中断处理 → 计算脉宽 → 映射控制值
详细过程
- 定时器运行:TIM5从0开始向上计数
- 边沿检测:配置为双边沿捕获(上升沿和下降沿)
- 时间戳记录 :
- 上升沿:记录当前计数值到CCRx寄存器
- 下降沿:再次记录当前计数值到CCRx寄存器
- 中断触发:产生捕获中断
- 脉宽计算 :
高电平时间 = 下降沿时间戳 - 上升沿时间戳
数学计算
高电平时间(计数) = CCR_下降沿 - CCR_上升沿
高电平时间(秒) = 高电平时间(计数) / 84,000,000 Hz
PWM信号标准
信号参数定义
cpp
#define PWM_MIN_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 1000UL) // 1.0ms = 全反向
#define PWM_MAX_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2000UL) // 2.0ms = 全正向
#define PWM_MIN_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 500UL) // 0.5ms = 最小合法值
#define PWM_MAX_LEGAL_HIGH_TIME ((TIM_2_5_CLOCK_HZ / 1000000UL) * 2500UL) // 2.5ms = 最大合法值
为什么是1.0-2.0ms?
- 历史标准:继承RC伺服系统数十年的行业标准
- 设备兼容:兼容所有RC发射机、接收机、飞控
- 明确中点:1.5ms作为明确的零位/停止点
- 安全容错:0.5-2.5ms的合法范围提供故障容错
信号处理流程
- 有效性检查:0.5-2.5ms为合法范围,之外忽略
- 范围限幅:强制限制到1.0-2.0ms有效范围
- 比例计算 :
fraction = (高电平时间 - 1.0ms) / (2.0ms - 1.0ms)
控制映射机制
映射公式
cpp
float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) /
(float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME);
float value = odrv.config_.pwm_mappings[channel].min +
(fraction * (odrv.config_.pwm_mappings[channel].max -
odrv.config_.pwm_mappings[channel].min));
配置示例
python
# 通过ODrive Tool配置
odrv.config_.pwm_mappings[0].endpoint = "axis0.controller.input_pos"
odrv.config_.pwm_mappings[0].min = -90.0 # 1.0ms对应-90度
odrv.config_.pwm_mappings[0].max = 90.0 # 2.0ms对应90度
支持的控制类型
- 位置控制:PWM脉宽 → 目标位置
- 速度控制:PWM脉宽 → 目标速度
- 电流控制:PWM脉宽 → 目标电流
- 自定义变量:任意可配置的端点
中断处理流程
中断服务程序
cpp
void PwmInput::on_capture() {
if(__HAL_TIM_GET_FLAG(htim_, TIM_FLAG_CC1)) {
__HAL_TIM_CLEAR_IT(htim_, TIM_IT_CC1);
on_capture(0, htim_->Instance->CCR1);
}
// ... 类似处理通道2、3、4
}
通道处理函数
cpp
void PwmInput::on_capture(int channel, uint32_t timestamp) {
static uint32_t last_timestamp[4] = {0};
static bool last_pin_state[4] = {false};
// 读取当前引脚电平
bool current_pin_state = get_gpio(gpios_[channel]).read();
// 检测高电平结束(下降沿)
if (last_sample_valid[channel] &&
(last_pin_state[channel] != PWM_INVERT_INPUT) &&
(current_pin_state == PWM_INVERT_INPUT)) {
uint32_t high_time = timestamp - last_timestamp[channel];
handle_pulse(channel, high_time);
}
// 更新状态
last_timestamp[channel] = timestamp;
last_pin_state[channel] = current_pin_state;
last_sample_valid[channel] = true;
}
应用场景
场景1:RC遥控器控制
RC遥控器 → RC接收机 → PWM信号 → ODrive TIM5 → 电机控制
- 摇杆最下:1.0ms → 全反向
- 摇杆中间:1.5ms → 停止
- 摇杆最上:2.0ms → 全正向
场景2:飞控集成
飞行控制器 → PWM输出 → ODrive TIM5 → 无人机电机
- 支持多旋翼、固定翼等无人机应用
- 兼容Betaflight、INAV、ArduPilot等飞控
场景3:自动化控制
PLC/单片机 → PWM信号 → ODrive TIM5 → 工业设备
- 简单的模拟量控制接口
- 无需复杂通信协议
场景4:机器人控制
机器人控制器 → PWM信号 → ODrive TIM5 → 关节电机
- 机械臂关节控制
- 移动机器人轮毂电机控制
技术参数
性能指标
| 参数 | 值 | 说明 |
|---|---|---|
| 时钟频率 | 84MHz | TIM_APB1_CLOCK_HZ |
| 时间分辨率 | 11.9ns | 1/84MHz |
| 最大测量时间 | 约51秒 | 32位计数器 |
| 滤波器带宽 | 15个周期 | 约178ns滤波 |
| 通道数量 | 4个 | 独立配置 |
| 中断优先级 | 1 | 中等优先级 |
信号规格
| 参数 | 值 | 说明 |
|---|---|---|
| 合法范围 | 0.5-2.5ms | 之外忽略 |
| 有效范围 | 1.0-2.0ms | 映射范围 |
| 标准中点 | 1.5ms | 零位/停止 |
| 典型周期 | 20ms | 50Hz,但ODrive不限制 |
计算示例
时钟频率:84,000,000 Hz
1.0ms计数值:84,000,000 × 0.001 = 84,000
2.0ms计数值:84,000,000 × 0.002 = 168,000
1.5ms计数值:84,000,000 × 0.0015 = 126,000
总结
TIM5在ODrive中的重要性
- 用户友好接口:提供熟悉的RC PWM控制方式
- 高精度测量:硬件级时间测量,精度达11.9ns
- 灵活配置:支持多通道、可配置映射
- 可靠稳定:内置滤波、范围检查、故障容错
- 广泛兼容:兼容大量现有RC设备
设计哲学
ODrive选择1.0-2.0ms标准而非理论上"更完美"的0-100%占空比,体现了工程实用主义:
- 继承传统:利用成熟的RC生态系统
- 降低门槛:用户无需学习新协议
- 提高可靠性:经过数十年实践验证
扩展可能性
虽然当前实现基于RC标准,但TIM5的输入捕获功能具有高度灵活性:
- 可修改固件支持其他PWM范围
- 可扩展更多控制模式
- 可集成更复杂的信号处理算法
TIM5作为ODrive与外部世界的重要桥梁,将简单的PWM信号转换为精确的数字控制,是系统易用性和功能性的关键组成部分。
文档生成时间:2026年4月21日
基于ODrive固件v0.5.6分析