Files
car_stm32f103vet6/Core/Bsp/protocol.c
wangbeihong 48a443c6c1 Update FreeRTOS configuration and enhance upper computer AI documentation
- Increased total heap size in FreeRTOSConfig.h from 10000 to 18000.
- Modified stack sizes for various tasks in freertos.c to improve performance:
  - initTask: 128 to 256
  - CarCtrlTask: 256 to 1024
  - timerTask: 512 to 1024
  - sr04Task: 128 to 512
  - rc522Task: 128 to 512
- Added comprehensive upper computer AI guide for TCP client implementation, detailing protocol contracts, command sets, telemetry, architecture requirements, and error handling strategies.
- Introduced a Python web upper computer AI guide with a recommended tech stack and project structure.
- Created a Chinese version of the upper computer AI guide for local developers.
- Updated STM32 project configuration to reflect new task stack sizes and heap settings.
- Adjusted configuration files for various tools to ensure compatibility with the new project structure.
2026-04-17 22:15:54 +08:00

716 lines
22 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file protocol.c
* @brief 协议处理函数实现
* @details 该文件包含了协议解析和处理的相关函数,主要用于处理从 ESP12F
* 模块接收到的数据。
* @author Beihong Wang
* @date 2026-04-01
*/
#include "protocol.h"
#include "bsp_motor.h"
#include "bsp_hall.h"
#include "bsp_pid.h"
#include "bsp_uart.h"
#include "bsp_rc522.h"
#include "bsp_beep.h"
#include "bsp_sr04.h"
#include "bsp_track_ir.h"
#include "track_ir_algo.h"
#include "checksum.h"
#include "cmsis_os.h"
#include "elog.h"
#include <stdio.h>
#include <string.h>
/* 定义日志 TAG */
#define Protocol_TAG "Protocol"
/* 小车控制状态相关定义 */
#define CAR_PWM_MAX 3599U // PWM最大值对应100%
/* PID 闭环参数,针对低速高摩擦环境优化 */
#define MOTOR_KP 6.5f
#define MOTOR_KI 2.5f // 微增 KI消除最后 2-3 RPM 的静差
#define MOTOR_KD 0.1f
#define TARGET_MAX_RPM 50.0f // 100% 对应 50 RPM
#define MOTOR_PWM_DEADZONE 1200 // 保持死区,克服静摩擦
#define MOTOR_PWM_MAX_ARR 3599 // 定时器重装载值
#define CARCTRL_LOOP_MS 50U
#define OBSTACLE_STOP_DISTANCE_CM 18.0f
#define OBSTACLE_RECOVER_DISTANCE_CM 22.0f
/* 手动控制方向斜向速度缩放,避免斜向合速度过大 */
#define MANUAL_DIAG_SCALE 0.7071f
/* 电机位置定义 (基于用户规定) */
#define MOTOR_LR MOTOR_1 // 左后 (Left Rear)
#define MOTOR_LF MOTOR_2 // 左前 (Left Front)
#define MOTOR_RF MOTOR_3 // 右前 (Right Front)
#define MOTOR_RR MOTOR_4 // 右后 (Right Rear)
/* 运动解算临时变量 (RPM 单位) */
static float target_v_x = 0; // 前进后退分量 (-TARGET_MAX_RPM ~ TARGET_MAX_RPM)
static float target_v_y = 0; // 左右横移分量
static float target_v_w = 0; // 原地旋转分量
static uint8_t car_running = 0; // 小车运行状态1=运行0=停止)
static uint8_t car_speed_percent = 0; // 当前整体速度百分比0~100
static uint16_t car_target_station = 0; // 目标站点编号
static uint8_t car_target_reached = 0; // 到站锁存,避免重复触发
static uint8_t obstacle_locked = 0; // 避障锁1=有障碍锁定)
static track_ir_ctrl_output_t track_output = {0};
static uint8_t track_line_mask = 0;
static void CarCtrl_StopAll(void);
typedef enum {
CTRL_MODE_AUTO = 0,
CTRL_MODE_MANUAL
} ctrl_mode_t;
typedef enum {
MAN_DIR_STOP = 0,
MAN_DIR_FWD,
MAN_DIR_BWD,
MAN_DIR_LEFT,
MAN_DIR_RIGHT,
MAN_DIR_LF,
MAN_DIR_RF,
MAN_DIR_LB,
MAN_DIR_RB,
MAN_DIR_CW,
MAN_DIR_CCW
} manual_dir_t;
static ctrl_mode_t car_ctrl_mode = CTRL_MODE_AUTO;
static manual_dir_t manual_dir = MAN_DIR_STOP;
#define CAR_STATION_MIN ((uint16_t)STATION_1)
#define CAR_STATION_MAX ((uint16_t)STATION_2)
/* 4个电机的 PID 控制器 */
static PID_TypeDef motor_pid[MOTOR_COUNT];
static const char *CarCtrl_ModeToString(ctrl_mode_t mode)
{
return (mode == CTRL_MODE_MANUAL) ? "MAN" : "AUTO";
}
static const char *CarCtrl_ManualDirToString(manual_dir_t dir)
{
switch (dir) {
case MAN_DIR_FWD: return "FWD";
case MAN_DIR_BWD: return "BWD";
case MAN_DIR_LEFT: return "LEFT";
case MAN_DIR_RIGHT:return "RIGHT";
case MAN_DIR_LF: return "LF";
case MAN_DIR_RF: return "RF";
case MAN_DIR_LB: return "LB";
case MAN_DIR_RB: return "RB";
case MAN_DIR_CW: return "CW";
case MAN_DIR_CCW: return "CCW";
case MAN_DIR_STOP:
default:
return "STOP";
}
}
static void CarCtrl_SetManualDirection(manual_dir_t dir)
{
manual_dir = dir;
if (dir == MAN_DIR_STOP) {
car_running = 0U;
target_v_x = 0.0f;
target_v_y = 0.0f;
target_v_w = 0.0f;
CarCtrl_StopAll();
} else {
car_running = 1U;
car_target_reached = 0U;
}
}
/**
* @brief 初始化闭环控制系统
*/
void CarCtrl_InitClosedLoop(void)
{
for(int i=0; i<MOTOR_COUNT; i++) {
// 恢复较高的积分限幅(2500)。要想达到稳态单靠积分项必须能填补3599(满载) - 1200(死区) = 2399 的差距。
pid_init(&motor_pid[i], MOTOR_KP, MOTOR_KI, MOTOR_KD, (float)MOTOR_PWM_MAX_ARR, 2500.0f);
}
track_ir_algo_init();
obstacle_locked = 0U;
car_ctrl_mode = CTRL_MODE_AUTO;
manual_dir = MAN_DIR_STOP;
BEEP_Off();
}
/**
* @brief 停止所有电机
*/
static void CarCtrl_StopAll(void)
{
for(int i=0; i<MOTOR_COUNT; i++) {
motor_stop(i);
pid_reset(&motor_pid[i]);
}
}
/**
* @brief 到站蜂鸣提示
* @param times 蜂鸣次数
*/
static void CarCtrl_BeepTimes(uint8_t times)
{
for (uint8_t i = 0; i < times; i++) {
BEEP_On();
osDelay(120);
BEEP_Off();
osDelay(120);
}
}
/**
* @brief 运行期间检测是否到达目标站点
*/
static void CarCtrl_CheckTargetStation(void)
{
rc522_card_info_t card;
station_id_t station;
if (car_ctrl_mode != CTRL_MODE_AUTO) {
return;
}
if (car_running == 0U || car_target_reached != 0U || car_target_station == 0U) {
return;
}
if (rc522_get_new_card(&card) == 0U) {
return;
}
station = rc522_match_station(card.uid, card.uid_len);
if ((uint16_t)station == car_target_station) {
car_running = 0U;
car_target_reached = 1U;
CarCtrl_StopAll();
elog_i(Protocol_TAG, "到达目标站点%u停车并蜂鸣", car_target_station);
CarCtrl_BeepTimes(3U);
}
}
/**
* @brief 执行一步 PID 闭环计算并更新电机 (建议 10ms-20ms 调用一次)
*/
void CarCtrl_UpdateClosedLoop(void)
{
float dist;
if (!car_running) {
obstacle_locked = 0U;
BEEP_Off();
track_ir_algo_reset();
CarCtrl_StopAll();
return;
}
dist = sr04_get_distance();
if ((dist > 0.0f) && (dist < OBSTACLE_STOP_DISTANCE_CM)) {
obstacle_locked = 1U;
}
if (obstacle_locked != 0U) {
if ((dist > OBSTACLE_RECOVER_DISTANCE_CM) || (dist <= 0.0f)) {
obstacle_locked = 0U;
BEEP_Off();
elog_i(Protocol_TAG, "障碍解除,恢复循迹");
} else {
target_v_x = 0.0f;
target_v_y = 0.0f;
target_v_w = 0.0f;
BEEP_On();
CarCtrl_StopAll();
return;
}
}
BEEP_Off();
if (car_ctrl_mode == CTRL_MODE_AUTO) {
track_line_mask = track_ir_get_line_mask_filtered();
track_ir_algo_step(track_line_mask, &track_output);
// 从前进速度分量计算基础速度(自动循迹)
target_v_x = (float)car_speed_percent * TARGET_MAX_RPM / 100.0f;
target_v_x = target_v_x * (float)track_output.speed_scale_percent / 100.0f;
target_v_y = 0.0f;
target_v_w = track_output.yaw_correction_rpm;
} else {
float base_rpm = (float)car_speed_percent * TARGET_MAX_RPM / 100.0f;
track_line_mask = 0U;
memset(&track_output, 0, sizeof(track_output));
/* 手动模式下,依据上位机方向指令生成 Vx/Vy/Vw */
switch (manual_dir) {
case MAN_DIR_FWD:
target_v_x = base_rpm; target_v_y = 0.0f; target_v_w = 0.0f;
break;
case MAN_DIR_BWD:
target_v_x = -base_rpm; target_v_y = 0.0f; target_v_w = 0.0f;
break;
case MAN_DIR_LEFT:
target_v_x = 0.0f; target_v_y = base_rpm; target_v_w = 0.0f; // 修正左移Vy为正
break;
case MAN_DIR_RIGHT:
target_v_x = 0.0f; target_v_y = -base_rpm; target_v_w = 0.0f; // 修正右移Vy为负
break;
case MAN_DIR_LF:
target_v_x = base_rpm * MANUAL_DIAG_SCALE;
target_v_y = -base_rpm * MANUAL_DIAG_SCALE;
target_v_w = 0.0f;
break;
case MAN_DIR_RF:
target_v_x = base_rpm * MANUAL_DIAG_SCALE;
target_v_y = base_rpm * MANUAL_DIAG_SCALE;
target_v_w = 0.0f;
break;
case MAN_DIR_LB:
target_v_x = -base_rpm * MANUAL_DIAG_SCALE;
target_v_y = -base_rpm * MANUAL_DIAG_SCALE;
target_v_w = 0.0f;
break;
case MAN_DIR_RB:
target_v_x = -base_rpm * MANUAL_DIAG_SCALE;
target_v_y = base_rpm * MANUAL_DIAG_SCALE;
target_v_w = 0.0f;
break;
case MAN_DIR_CW:
target_v_x = 0.0f; target_v_y = 0.0f; target_v_w = base_rpm;
break;
case MAN_DIR_CCW:
target_v_x = 0.0f; target_v_y = 0.0f; target_v_w = -base_rpm;
break;
case MAN_DIR_STOP:
default:
target_v_x = 0.0f; target_v_y = 0.0f; target_v_w = 0.0f;
break;
}
}
/* 麦克纳姆轮运动解算 (RPM单位)
* 根据你的电机位置规定:
* LR: M1, LF: M2, RF: M3, RR: M4
*/
float target_rpms[MOTOR_COUNT];
// 麦轮全向解算公式:
// LF = Vx + Vy - Vw
// RF = Vx - Vy + Vw
// LR = Vx - Vy - Vw
// RR = Vx + Vy + Vw
target_rpms[MOTOR_LF] = target_v_x + target_v_y - target_v_w; // 左前
target_rpms[MOTOR_RF] = target_v_x - target_v_y + target_v_w; // 右前
target_rpms[MOTOR_LR] = target_v_x - target_v_y - target_v_w; // 左后
target_rpms[MOTOR_RR] = target_v_x + target_v_y + target_v_w; // 右后
for(int i=0; i<MOTOR_COUNT; i++) {
float actual_rpm = hall_get_speed(i);
// 限幅目标值,防止叠加后超出物理极限
if (target_rpms[i] > TARGET_MAX_RPM) target_rpms[i] = TARGET_MAX_RPM;
if (target_rpms[i] < -TARGET_MAX_RPM) target_rpms[i] = -TARGET_MAX_RPM;
float pid_out = pid_calculate(&motor_pid[i], target_rpms[i], actual_rpm);
// 优化死区逻辑当目标速度不为0时叠加死区偏移
float final_pwm = 0;
if (target_rpms[i] > 1.0f || target_rpms[i] < -1.0f) {
if (pid_out >= 0) {
final_pwm = pid_out + MOTOR_PWM_DEADZONE;
} else {
final_pwm = pid_out - MOTOR_PWM_DEADZONE;
}
// 最终限幅,防止总 PWM 超过 ARR (3599)
if (final_pwm > 3599) final_pwm = 3599;
if (final_pwm < -3599) final_pwm = -3599;
}
motor_set_speed(i, (int16_t)final_pwm);
// 修改打印逻辑:使用位置别名让日志更易读
const char* motor_names[] = {"LR", "LF", "RF", "RR"};
static uint32_t last_log_time = 0;
if (HAL_GetTick() - last_log_time > 500) {
elog_d(Protocol_TAG, "M[%s] T:%.1f A:%.1f Pw:%d",
motor_names[i], target_rpms[i], actual_rpm, (int16_t)final_pwm);
if (i == MOTOR_COUNT - 1) last_log_time = HAL_GetTick();
}
}
}
/**
* @brief 解析并执行协议命令
* @param cmd_payload 队列传入的命令字符串(如 "ST:RUN"、"SP:080"、"GS:005"
*/
static void CarCtrl_HandleCommand(const char *cmd_payload)
{
const char *arg = NULL;
if (cmd_payload == NULL || cmd_payload[0] == '\0') {
return;
}
// 查找冒号分隔符,分割命令类型和参数
arg = strchr(cmd_payload, ':');
if (arg == NULL) {
elog_w(Protocol_TAG, "未知控制指令: %s", cmd_payload);
return;
}
// 启动/停止命令(仅作为自动循迹任务的启动/暂停信号,不直接控制电机)
if (strncmp(cmd_payload, "ST", 2) == 0) {
if (strcmp(arg + 1, "RUN") == 0) {
if (car_target_reached != 0U) {
car_running = 0U;
CarCtrl_StopAll();
elog_w(Protocol_TAG, "已到达站点,请先重新设置目标站点 (GS:xxx) 之后再启动");
CarCtrl_BeepTimes(2U);
Protocol_SendFeedback("ST", 0);
return;
}
if (car_target_station < CAR_STATION_MIN || car_target_station > CAR_STATION_MAX) {
car_running = 0U;
CarCtrl_StopAll();
elog_w(Protocol_TAG, "未设置有效站点,拒绝启动。请先下发 GS:001 或 GS:002");
CarCtrl_BeepTimes(2U);
Protocol_SendFeedback("ST", 0);
return;
}
car_running = 1;
car_target_reached = 0;
elog_i(Protocol_TAG, "小车自动循迹启动, speed=%u%%, station=%u", car_speed_percent,
car_target_station);
Protocol_SendFeedback("ST", 1);
} else if (strcmp(arg + 1, "STOP") == 0) {
car_running = 0;
car_target_reached = 0;
CarCtrl_StopAll();
elog_i(Protocol_TAG, "小车自动循迹暂停");
Protocol_SendFeedback("ST", 1);
} else {
elog_w(Protocol_TAG, "未知启动/停止命令: %s", cmd_payload);
Protocol_SendFeedback("ST", 0);
}
return;
}
// 设置整体速度命令
if (strncmp(cmd_payload, "SP", 2) == 0) {
unsigned int speed = 0;
// 解析速度参数百分比0~100
if (sscanf(arg + 1, "%u", &speed) == 1) {
if (speed > 100U) {
speed = 100U;
}
car_speed_percent = (uint8_t)speed;
elog_i(Protocol_TAG, "设置整体速度: %u%%", car_speed_percent);
Protocol_SendFeedback("SP", 1);
} else {
elog_w(Protocol_TAG, "速度参数解析失败: %s", cmd_payload);
Protocol_SendFeedback("SP", 0);
}
return;
}
// 设置目标站点命令
if (strncmp(cmd_payload, "GS", 2) == 0) {
unsigned int station = 0;
// 解析站点编号0~999
if (sscanf(arg + 1, "%u", &station) == 1) {
if (station < CAR_STATION_MIN || station > CAR_STATION_MAX) {
elog_w(Protocol_TAG, "站点%03u未配置目前仅支持 %03u~%03u", station,
CAR_STATION_MIN, CAR_STATION_MAX);
CarCtrl_BeepTimes(2U);
return;
}
car_target_station = (uint16_t)station;
car_target_reached = 0;
elog_i(Protocol_TAG, "设置目标站点: %03u", car_target_station);
CarCtrl_BeepTimes(1U);
Protocol_SendFeedback("GS", 1);
} else {
elog_w(Protocol_TAG, "站点参数解析失败: %s", cmd_payload);
Protocol_SendFeedback("GS", 0);
}
return;
}
// 控制模式切换MD:AUTO / MD:MAN
if (strncmp(cmd_payload, "MD", 2) == 0) {
if (strcmp(arg + 1, "AUTO") == 0) {
car_ctrl_mode = CTRL_MODE_AUTO;
manual_dir = MAN_DIR_STOP;
car_running = 0U;
obstacle_locked = 0U;
track_ir_algo_reset();
CarCtrl_StopAll();
elog_i(Protocol_TAG, "已切换为自动循迹模式(AUTO)");
Protocol_SendFeedback("MD", 1);
} else if (strcmp(arg + 1, "MAN") == 0) {
car_ctrl_mode = CTRL_MODE_MANUAL;
obstacle_locked = 0U;
CarCtrl_SetManualDirection(MAN_DIR_STOP);
elog_i(Protocol_TAG, "已切换为手动麦轮模式(MAN)");
Protocol_SendFeedback("MD", 1);
} else {
elog_w(Protocol_TAG, "未知模式命令: %s", cmd_payload);
Protocol_SendFeedback("MD", 0);
}
return;
}
// 手动运动方向命令MV:FWD/BWD/LEFT/RIGHT/LF/RF/LB/RB/CW/CCW/STOP
if (strncmp(cmd_payload, "MV", 2) == 0) {
if (car_ctrl_mode != CTRL_MODE_MANUAL) {
elog_w(Protocol_TAG, "当前非手动模式拒绝执行MV。请先下发 MD:MAN");
Protocol_SendFeedback("MV", 0);
return;
}
if (strcmp(arg + 1, "FWD") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_FWD);
} else if (strcmp(arg + 1, "BWD") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_BWD);
} else if (strcmp(arg + 1, "LEFT") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_LEFT);
} else if (strcmp(arg + 1, "RIGHT") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_RIGHT);
} else if (strcmp(arg + 1, "LF") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_LF);
} else if (strcmp(arg + 1, "RF") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_RF);
} else if (strcmp(arg + 1, "LB") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_LB);
} else if (strcmp(arg + 1, "RB") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_RB);
} else if (strcmp(arg + 1, "CW") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_CW);
} else if (strcmp(arg + 1, "CCW") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_CCW);
} else if (strcmp(arg + 1, "STOP") == 0) {
CarCtrl_SetManualDirection(MAN_DIR_STOP);
} else {
elog_w(Protocol_TAG, "未知手动方向命令: %s", cmd_payload);
Protocol_SendFeedback("MV", 0);
return;
}
elog_i(Protocol_TAG, "手动方向=%s, speed=%u%%", CarCtrl_ManualDirToString(manual_dir),
car_speed_percent);
Protocol_SendFeedback("MV", 1);
return;
}
// 未知命令类型
elog_w(Protocol_TAG, "未支持的控制命令: %s", cmd_payload);
}
/**
* @brief 打包并发送协议帧到上位机 (TCP)
* @param payload 有效载荷文本 (不含帧头 LOGI:, 分隔符 :, 校验位 CS 和 帧尾 #)
*/
static void Protocol_SendPacket(const char *payload) {
char packet[192] = {0};
uint8_t cs = 0;
// 1. 组装基础段: LOGI:PAYLOAD
snprintf(packet, sizeof(packet), "LOGI:%s", payload);
// 2. 计算校验和 (从 LOGI 开始到 payload 结束的累加和)
cs = Calculate_CheckSum((uint8_t *)packet, 0, (uint16_t)strlen(packet));
// 3. 拼接校验位和帧尾
size_t current_len = strlen(packet);
snprintf(packet + current_len, sizeof(packet) - current_len, ":%02X#", cs);
// 4. 直接发送
ESP12F_TCP_SendMessage(packet);
}
/**
* @brief 发送状态报告
* 格式示例: STAT:SP:080,STA:001,RUN:1,DIS:12.5,TRK:0010,RPM:25:25:25:25
*/
void Protocol_SendStatusReport(void) {
char payload[160] = {0};
float dist = sr04_get_distance();
uint8_t trk_mask = track_line_mask;
// STAT:SP:xxx,STA:xxx,RUN:x,DIS:xxx.x,TRK:xxxx,RPM:m1:m2:m3:m4
// TRK 此时上报 4 位二进制掩码 (H4 H3 H2 H1)
// RPM 为各个电机的实际 RPM保留整数
snprintf(payload, sizeof(payload),
"STAT:SP:%03u,STA:%03u,RUN:%u,MODE:%s,MAN:%s,DIS:%.1f,TRK:%u%u%u%u,DEV:%d,OBS:%u,RPM:%d:%d:%d:%d",
car_speed_percent, car_target_station, car_running,
CarCtrl_ModeToString(car_ctrl_mode),
CarCtrl_ManualDirToString(manual_dir),
dist,
(trk_mask & TRACK_IR_H4_BIT) ? 1 : 0,
(trk_mask & TRACK_IR_H3_BIT) ? 1 : 0,
(trk_mask & TRACK_IR_H2_BIT) ? 1 : 0,
(trk_mask & TRACK_IR_H1_BIT) ? 1 : 0,
(int)track_output.deviation,
obstacle_locked,
(int)hall_get_speed(MOTOR_1),
(int)hall_get_speed(MOTOR_2),
(int)hall_get_speed(MOTOR_3),
(int)hall_get_speed(MOTOR_4));
Protocol_SendPacket(payload);
}
/**
* @brief 发送反馈
* 格式示例: FB:ST:1 (ST成功), FB:GS:0 (GS失败)
*/
void Protocol_SendFeedback(const char *cmd_type, uint8_t status) {
char payload[16] = {0};
snprintf(payload, sizeof(payload), "FB:%s:%u", cmd_type, status);
Protocol_SendPacket(payload);
}
/* 引用在 freertos.c 中定义的消息队列句柄 */
extern osMessageQueueId_t CmdQueueHandle;
/**
* @brief 协议处理函数
* @details 严格按照协议文档:校验范围 = 帧头 + 命令 + 数据
* 即从下标 0 开始,一直加到最后一个冒号之前
*/
void Protocol_HandleMessage(uint8_t *data, uint16_t len) {
if (data == NULL)
return;
// 1. 基础检查:长度必须足够,且必须以 '#' 结尾
if (len < 10 || data[len - 1] != '#') {
elog_w(Protocol_TAG, "协议错误:长度不足或帧尾错误 (len: %d)", len);
return;
}
// 2. 寻找校验位前的分隔符
// 协议格式LOGI:CMD:DATA:CS#
// 我们需要找到最后一个冒号 ':' 的位置,它前面是数据,后面是校验位
int last_colon_pos = -1;
for (int i = 0; i < len; i++) {
if (data[i] == ':') {
last_colon_pos = i;
}
}
// 如果找不到冒号,说明格式错误
if (last_colon_pos == -1 || last_colon_pos < 5) {
elog_w(Protocol_TAG, "协议错误:找不到分隔符 ':' 或位置非法");
return;
}
// 3. 提取接收到的校验位 (从 ASCII 转为 Hex 数值)
// 校验位紧跟在 last_colon_pos 之后,长度为 2 字节
char recv_cs_hex_str[3] = {0};
// 防止越界
if (last_colon_pos + 3 >= len) {
elog_w(Protocol_TAG, "协议错误:校验位数据越界");
return;
}
recv_cs_hex_str[0] = data[last_colon_pos + 1];
recv_cs_hex_str[1] = data[last_colon_pos + 2];
unsigned int received_checksum = 0;
sscanf(recv_cs_hex_str, "%02X", &received_checksum);
// 4. 计算本地校验和
// 【核心修改点】
// 严格按照协议文档:从下标 0 开始,长度为 last_colon_pos
// 也就是计算 "LOGI:SP:080" 的累加和
uint8_t calculated_checksum =
Calculate_CheckSum(data, 0, (uint16_t)last_colon_pos);
// 5. 对比校验和
if (calculated_checksum == (uint8_t)received_checksum) {
elog_i(Protocol_TAG, "✅ 校验通过!执行指令: %s", (char *)data);
/* 提取有效载荷发送到消息队列 */
char cmd_payload[16] = {0};
// 将 "LOGI:" 之后到最后一个冒号之前的内容作为指令
uint16_t payload_len = last_colon_pos - 5;
uint16_t copy_len = (payload_len > 15) ? 15 : payload_len;
if (copy_len > 0) {
memcpy(cmd_payload, &data[5], copy_len);
}
osStatus_t status = osMessageQueuePut(CmdQueueHandle, cmd_payload, 0, 0);
if (status != osOK) {
elog_e(Protocol_TAG, "Protocol: Queue put failed: %d", status);
}
} else {
elog_w(Protocol_TAG, "❌ 校验失败!计算值: 0x%02X, 接收值: 0x%02X",
calculated_checksum, (uint8_t)received_checksum);
// 辅助调试:打印实际参与计算的数据段
char debug_buf[32] = {0};
if (last_colon_pos < 32) {
memcpy(debug_buf, data, last_colon_pos);
elog_i(Protocol_TAG, " -> 单片机正在计算这段数据的校验和: [%s]",
debug_buf);
elog_i(Protocol_TAG, " -> 请检查上位机是否也是按照此范围计算累加和");
}
}
}
#define CarCtrlTask_TAG "CarCtrlTask"
void CarCtrl_Task(void *argument) {
/* USER CODE BEGIN CarCtrl_Task */
char cmd_payload[16] = {0};
uint32_t last_report_tick = 0;
/* 初始化闭环 PID 控制器 */
CarCtrl_InitClosedLoop();
/* Infinite loop */
for (;;) {
uint32_t now = HAL_GetTick();
/* 1. 处理控制指令 (非阻塞获取,如果没有指令则继续执行闭环) */
if (osMessageQueueGet(CmdQueueHandle, cmd_payload, NULL, 0) == osOK) {
elog_d(CarCtrlTask_TAG, "CarCtrl: Command %s", cmd_payload);
CarCtrl_HandleCommand(cmd_payload);
}
/* 1.1 运行中检查 RFID 是否到达目标站点 */
CarCtrl_CheckTargetStation();
/* 2. 执行 PID 闭环控制更新 */
CarCtrl_UpdateClosedLoop();
/* 3. 定期向应用层/上位机发送状态报告 (每 500ms) */
if (now - last_report_tick >= 500) {
Protocol_SendStatusReport();
last_report_tick = now;
}
/* 与测速任务保持同周期,确保 PID 输入输出时基一致。 */
osDelay(CARCTRL_LOOP_MS);
}
/* USER CODE END CarCtrl_Task */
}