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。