1. 直线插补任务框架搭建

任务目标

在RTOS环境中创建一个直线插补任务,完成插补数据结构定义与任务主循环框架。任务从输入缓冲区获取目标点坐标,调用直线插补算法逐周期生成中间插补点,并将结果写入输出缓冲区。本案例聚焦任务框架与数据结构,不涉及具体电机驱动或通信协议。

数据结构定义

/* 插补点坐标结构 */
typedef struct {
    float x;    /* X轴位置,单位mm */
    float y;    /* Y轴位置,单位mm */
} Point_t;

/* 直线插补任务输入:目标位置 */
typedef struct {
    Point_t target;      /* 目标点 */
    float   feedrate;    /* 进给速度 mm/s */
    float   accel_time;  /* 加速时间 s,0表示匀速 */
} LineInput_t;

/* 直线插补任务输出:周期插补点 */
typedef struct {
    Point_t point;       /* 当前插补点 */
    uint8_t done;        /* 1=插补完成 */
} LineOutput_t;

/* 插补任务控制块 */
typedef struct {
    LineInput_t  input;   /* 当前目标 */
    LineOutput_t output;  /* 当前输出 */
    float        step_period;  /* 插补周期 s */
    float        total_dist;   /* 总距离 */
    float        dist_done;    /* 已插补距离 */
    uint8_t      running;      /* 1=正在插补 */
} LineInterpolator_t;

直线插补任务实现

#include <stdio.h>
#include <math.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

static LineInterpolator_t g_line = {0};
static QueueHandle_t g_input_q, g_output_q;

/* 直线插补核心算法:匀速直线插补 */
static void LineInterp_Step(LineInterpolator_t *li) {
    if (!li->running) return;

    float step_dist = li->input.feedrate * li->step_period;
    li->dist_done += step_dist;

    if (li->dist_done >= li->total_dist) {
        /* 到达终点 */
        li->output.point = li->input.target;
        li->output.done = 1;
        li->running = 0;
    } else {
        /* 按比例计算中间点 */
        float ratio = li->dist_done / li->total_dist;
        li->output.point.x = ratio * li->input.target.x;
        li->output.point.y = ratio * li->input.target.y;
        li->output.done = 0;
    }
}

/* 直线插补任务主体 */
static void LineInterp_Task(void *arg) {
    LineInput_t new_cmd;
    BaseType_t rc;

    g_line.step_period = 0.001f;  /* 1ms插补周期 */

    for (;;) {
        /* 尝试接收新指令(非阻塞) */
        rc = xQueueReceive(g_input_q, &new_cmd, 0);
        if (rc == pdPASS) {
            /* 启动新插补 */
            g_line.input = new_cmd;
            g_line.total_dist = sqrtf(new_cmd.target.x * new_cmd.target.x +
                                      new_cmd.target.y * new_cmd.target.y);
            g_line.dist_done = 0;
            g_line.running = 1;
            g_line.output.done = 0;
        }

        if (g_line.running) {
            LineInterp_Step(&g_line);
            xQueueSend(g_output_q, &g_line.output, 0);
        }

        vTaskDelay(pdMS_TO_TICKS(1));  /* 1ms周期 */
    }
}

/* 初始化:创建队列与任务 */
void LineInterp_Init(void) {
    g_input_q = xQueueCreate(4, sizeof(LineInput_t));
    g_output_q = xQueueCreate(16, sizeof(LineOutput_t));

    xTaskCreate(LineInterp_Task, "LineInterp", 256, NULL, 5, NULL);
}

运行与验证

依赖说明:需要FreeRTOS头文件与库支持,在嵌入式工程中接入RTOS内核即可,本代码不依赖特定硬件平台。

调用方式:

/* 在应用初始化中调用 */
LineInterp_Init();

/* 发送插补指令 */
LineInput_t cmd = {{10.0f, 5.0f}, 20.0f, 0};
xQueueSend(g_input_q, &cmd, portMAX_DELAY);

/* 在另一任务中接收插补结果 */
LineOutput_t out;
if (xQueueReceive(g_output_q, &out, portMAX_DELAY) == pdPASS) {
    printf("X=%.2f Y=%.2f done=%d\n", out.point.x, out.point.y, out.done);
}

验证方法:发送目标点(10, 5),进给速度20mm/s,插补周期1ms。预期输出序列中,X与Y坐标比值始终为2:1(直线斜率),最终输出点等于(10, 5)且done=1。可通过串口打印观察插补点是否沿直线均匀分布,终点标志是否正确置位。

通过条件:任务能周期运行,正确接收指令并输出插补点;所有输出点满足直线方程 y = 0.5x;最后一个输出点坐标为(10.0, 5.0)且done=1。