定时器编码器程序

← 返回 MOC | ← 主页

代码来源工程:Intelligent_Agricultural_Equipment_Innovation_CompetitionMycode/encoder.c


TIM 编码器模式读取转速(差分法)

TIM 配置为 TIM_ENCODERMODE_TI12(双通道四倍频),预分频 = 0,周期 = 65535。每个采样周期读取计数器当前值,与上次差分得到脉冲增量,再换算为 RPM。

16bit 计数器用 short 类型做差分,溢出时自动回绕,无需额外处理。

/* 关键参数(按实际电机修改) */
#define ENCODER_LINES    11      /* 编码器线数 */
#define ENCODER_MULTI    4       /* 倍频(TI12 模式固定为 4) */
#define GEAR_RATIO       14      /* 减速比 */
#define SAMPLE_MS        10      /* 采样周期 ms */
 
/* 每圈脉冲数 */
#define PPR  (ENCODER_LINES * ENCODER_MULTI * GEAR_RATIO)  /* = 616 */
 
/* RPM 换算系数:60 / (SAMPLE_MS/1000) / PPR */
#define RPM_COEF  (60000.0f / SAMPLE_MS / PPR)             /* = 6000/616 ≈ 9.74 */
 
static short last_cnt = 0;
 
/**
 * @brief 单路编码器读取转速(在固定周期定时器中断里调用)
 * @return 转速 RPM(正转为正)
 */
float encoder_read_rpm(TIM_HandleTypeDef *htim)
{
    short cur = (short)__HAL_TIM_GetCounter(htim);  /* 读当前值 */
    short diff = cur - last_cnt;                     /* short 差分,自动处理溢出 */
    last_cnt = cur;
    return (float)diff * RPM_COEF;
}
 
/* 使用示例 */
/*
// 在 10ms 定时中断中调用:
float rpm = encoder_read_rpm(&htim1);
*/

四路编码器读取(多路差分)

四路电机各占一个 TIM(TIM1/TIM3/TIM4 为 16bit,TIM5 为 32bit)。安装方向相反的轴读取后取负,统一正方向。

  typedef struct {
    float speedA;  /* 左前,RPM */
    float speedB;  /* 右前,RPM */
    float speedC;  /* 左后,RPM */
    float speedD;  /* 右后,RPM */
} EncoderSpeed_t;
 
static short  last16[3] = {0};   /* TIM1/3/4 上次计数 */
static int32_t last32   = 0;     /* TIM5 上次计数 */
 
/**
 * @brief 四路编码器读取,在 10ms 定时中断中调用
 * @param out 输出四路转速(RPM)
 */
void encoder_calc(EncoderSpeed_t *out)
{
    /* 1. 读取当前计数值 */
    short   cur16[3];
    int32_t cur32;
    cur16[0] = (short)  __HAL_TIM_GetCounter(&htim1);
    cur16[1] = (short)  __HAL_TIM_GetCounter(&htim3);
    cur16[2] = (short)  __HAL_TIM_GetCounter(&htim4);
    cur32    = (int32_t)__HAL_TIM_GetCounter(&htim5);
 
    /* 2. 差分:16bit 用 short 自动回绕,32bit 显式处理 */
    short   diff16[3];
    int32_t diff32;
    for (int i = 0; i < 3; i++) {
        diff16[i]  = cur16[i] - last16[i];
        last16[i]  = cur16[i];
    }
    diff32    = cur32 - last32;
    last32    = cur32;
 
    /* 32bit 溢出保护(正常运行基本不触发) */
    if (diff32 >  2147483647) diff32 -= (int64_t)4294967296;
    if (diff32 < -2147483648) diff32 += (int64_t)4294967296;
 
    /* 3. 换算 RPM,B/C/D 安装方向相反取负 */
    out->speedA =  (float)diff16[0] * RPM_COEF;
    out->speedB = -(float)diff16[1] * RPM_COEF;
    out->speedC = -(float)diff16[2] * RPM_COEF;
    out->speedD = -(float)diff32    * RPM_COEF;
}
 
/* 使用示例 */
/*
EncoderSpeed_t raw_speed;
 
// 10ms 定时中断中:
encoder_calc(&raw_speed);
// raw_speed.speedA ~ speedD 即为四路原始转速
*/

三级滤波(去极值 + 幅值限制 + 斜率限制)

与超声波滤波同一套算法,参数按电机转速场景调整(窗口 10,去极值 2,幅值上限 500 rpm,斜率限制 30 rpm/次)。

typedef struct {
    uint8_t count;       /* 环形缓冲写入位置 */
    uint8_t complete;    /* 缓冲区已满标志 */
    float   last_valid;  /* 上次有效输出 */
    float   buf[32];     /* 历史数据缓冲 */
} EncoderFilter_t;
 
/**
 * @brief 三级滤波
 *
 * @param f          滤波器状态
 * @param new_val    本次原始转速(RPM)
 * @param win        滤波窗口大小
 * @param remove_n   每端去极值个数
 * @param max_amp    幅值上限(RPM),超出则保持上次有效值
 * @param max_slope  单周期最大变化量(RPM)
 * @param out        输出滤波后转速
 */
void encoder_filter(EncoderFilter_t *f, float new_val,
                    uint8_t win, uint8_t remove_n,
                    float max_amp, float max_slope,
                    float *out)
{
    /* 1. 写入环形缓冲 */
    f->buf[f->count % win] = new_val;
    f->count++;
    if (f->count >= win) f->complete = 1;
 
    uint8_t n = f->complete ? win : f->count;
 
    /* 2. 初始化阶段直接透传 */
    if (!f->complete) { *out = f->last_valid = new_val; return; }
 
    /* 3. 复制并冒泡排序 */
    float tmp[32];
    uint8_t idx = f->count % win;
    for (uint8_t i = 0; i < n; i++) {
        tmp[i] = f->buf[idx];
        if (++idx >= win) idx = 0;
    }
    for (uint8_t i = 0; i < n - 1; i++)
        for (uint8_t j = 0; j < n - 1 - i; j++)
            if (tmp[j] > tmp[j+1]) { float t = tmp[j]; tmp[j] = tmp[j+1]; tmp[j+1] = t; }
 
    /* 4. 去极值求均值 */
    float sum = 0;
    uint8_t valid_n = n - 2 * remove_n;
    if (valid_n < 1) valid_n = 1;
    for (uint8_t i = remove_n; i < n - remove_n; i++) sum += tmp[i];
    float avg = sum / valid_n;
 
    /* 5. 幅值限制 */
    if (avg > max_amp || avg < -max_amp) { *out = f->last_valid; return; }
 
    /* 6. 斜率限制 */
    float diff = avg - f->last_valid;
    if (diff >  max_slope) avg = f->last_valid + max_slope;
    if (diff < -max_slope) avg = f->last_valid - max_slope;
 
    f->last_valid = avg;
    *out = avg;
}
 
/* 使用示例 */
/*
static EncoderFilter_t filt_A = {0};
float speed_A_filtered;
 
// 每次 encoder_calc 后调用:
encoder_filter(&filt_A, raw_speed.speedA,
               10, 2, 500.0f, 30.0f, &speed_A_filtered);
// 四路各维护一个 EncoderFilter_t 实例
*/