开发板STM32 三轴联动 带插补 加减速 源代码 MDK 源码 分别基于STM32F1和STM32F4两套的三轴联动插补(直线圆弧两种带)加减速的源码,基于国外写的脱机简易雕刻机源码的项目修改,添加了大量的中文注释,可以很好帮助大家学习这个源码。

最近在捣鼓三轴运动控制系统,发现网上有个基于STM32的脱机雕刻机项目挺有意思。原版代码是国外开发者写的,但咱们团队把它扒拉过来魔改了一波——不仅移植到F1和F4两个平台,还往源码里怼了上万字的中文注释,现在连刚入门的小白都能看明白那些骚操作。

先看工程结构,项目里最带劲的是这个运动控制核心:

// 三轴联动主控函数(F4版本用DSP库加速)
void Motion_Core_Update(void)
{
    // 实时计算剩余步数
    int32_t steps_remaining = max(max(x_steps, y_steps), z_steps);
    
    // 动态调整脉冲频率(核心加速算法)
    if (steps_remaining > 200) {
        current_freq = max_freq;  // 全速飙车模式
    } else {
        current_freq = sqrt(2 * accel * steps_remaining);  // 减速曲线计算
    }
    
    TIM_SetAutoreload(STEP_TIMER, SystemCoreClock / current_freq / 2);
}

这段代码里藏着速度变化的灵魂。当剩余步数足够多时直接满速跑,接近终点时用平方根函数做平滑减速,实测效果比某些商用驱动器还丝滑。

直线插补的实现比想象中简单粗暴:

void Line_Interp(int32_t target[], float feedrate)
{
    // 计算各轴总步数
    x_steps = labs(target[X] - current_pos[X]);
    y_steps = labs(target[Y] - current_pos[Y]);
    z_steps = labs(target[Z] - current_pos[Z]);
    
    // 取最大步数作为基准
    step_count = max(x_steps, max(y_steps, z_steps));
    
    // 步进增量计算(避免浮点运算)
    x_inc = (target[X] - current_pos[X]) << 16 / step_count;
    y_inc = (target[Y] - current_pos[Y]) << 16 / step_count;
    z_inc = (target[Z] - current_pos[Z]) << 16 / step_count;
    
    // 启动加减速调度器
    Accel_Engine_Start(step_count);
}

这里用定点数运算替代浮点,F1版本直接硬算,F4版则用Q格式处理。看到那个<<16操作没?这其实是用65536作为定点数精度,既保证计算速度又不会溢出。

开发板STM32 三轴联动 带插补 加减速 源代码 MDK 源码 分别基于STM32F1和STM32F4两套的三轴联动插补(直线圆弧两种带)加减速的源码,基于国外写的脱机简易雕刻机源码的项目修改,添加了大量的中文注释,可以很好帮助大家学习这个源码。

圆弧插补就有点东西了,核心算法改编自Bresenham圆算法:

// 圆弧插补状态机
typedef struct {
    int32_t  x, y;
    int32_t  d;
    uint8_t  quadrant;
    uint16_t over90_flag;
} Arc_State;

void Arc_Interp(Arc_State *arc)
{
    while(arc->d <= 0) {
        step_X();  // 输出X轴脉冲
        arc->d += 2 * arc->x + 1;
        arc->x++;
        
        if(arc->d > 0) {
            step_Y();  // 输出Y轴脉冲
            arc->d += 2 * (arc->y - arc->x) + 1;
            arc->y--;
        }
        
        // 处理象限切换
        if(arc->x > arc->y && !arc->over90_flag) {
            arc->quadrant++;
            arc->over90_flag = 1;
        }
    }
}

这状态机跑起来跟贪吃蛇似的,通过误差项d的正负决定移动方向。代码里藏了个象限切换的骚操作,处理超过90度的圆弧时自动跳转,实测画圆精度能控制在±2个脉冲内。

F1和F4两套代码最大的区别在定时器配置上。F1用标准库操作TIM3:

// F1的定时器初始化(72MHz主频)
void STEP_TIM_Init(void)
{
    TIM_TimeBaseInitTypeDef  timer;
    timer.TIM_Prescaler = 0;  // 不分频
    timer.TIM_CounterMode = TIM_CounterMode_Up;
    timer.TIM_Period = 7200;  // 初始10kHz
    TIM_TimeBaseInit(TIM3, &timer);
    TIM_Cmd(TIM3, ENABLE);
}

而F4用HAL库+HALTIMOCStartDMA玩起了高级操作:

// F4的DMA脉冲发射(168MHz主频)
void STEP_DMA_Start(void)
{
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_OC_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t*)pulse_buffer, PULSE_BUF_SIZE);
}

F4的DMA传输配合预装载缓冲,实测脉冲频率能飚到200kHz不带丢步的。不过要注意,用DMA时GPIO必须配置为复用推挽模式,否则输出波形会变方波它二舅——方不起来。

源码里最实用的其实是这个加速度曲线生成器:

// 梯形加减速参数生成
void Accel_Engine_Calc(int32_t total_steps)
{
    // 计算加速段步数
    uint32_t accel_steps = (max_freq * max_freq) / (2 * accel);
    
    if(total_steps > 2 * accel_steps) {
        // 完整梯形:加速-匀速-减速
        cruise_steps = total_steps - 2 * accel_steps;
    } else {
        // 三角波模式:只有加减速
        accel_steps = total_steps / 2;
        cruise_steps = 0;
    }
    
    // 生成速度曲线表
    for(int i=0; i<accel_steps; i++){
        speed_table[i] = sqrt(2 * accel * i);  // 加速段
    }
}

这个算法妙在自动识别运动距离是否够跑完加速+匀速+减速三个阶段。当总步数太少时自动切换成三角波模式,实测在小距离移动时明显比固定梯形加速流畅。

想跑起来这套代码,记得改这几个地方:

  1. 在motor_pin.h里配置实际使用的GPIO引脚
  2. 根据机械结构调整stepspermm参数
  3. F4版本需要开启FPU和DSP库
  4. 定时器中断优先级要设为最高

最后说个坑:原版代码的圆弧插补只支持XY平面,要搞三维螺旋插补得自己改插补算法。不过拿这个做基础来魔改,比从头写至少省两个月咖啡钱。源码包里带了激光雕刻和CNC两种配置示例,拿烙铁的时候小心别把开发板烫出烤肉味就行。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐