Files
Simba-OmniInfantry/底盘/modules/comms/referee.c
T
LSMushui e3d432f3b5 refactor(modules): 按角色重排 modules/ 目录,消除命名不一致
原 modules/ 下目录大小写混用(BMI088/BUZZER/DR16/IST8310/LED/PID 为大写,
attitude/c_comm/filter/gimbal/ins/motor/referee/shoot 为小写),且把第三方
移植代码(QuaternionEKF/kalman_filter)与本工程封装(ins)混在同一目录。

新结构(全部小写,按角色分层,第三方代码单独隔离):

  modules/
    algorithm/   pid.c/h  filter.c/h          通用算法
    imu/         attitude.c/h  ins.c/h        姿态与惯导封装
    imu/qekf/    QuaternionEKF.c/h  kalman_filter.c/h
                                              (移植自 Wang Hongxi INS 例程)
    sensor/      bmi088.c/h  ist8310.c/h      板载传感器驱动
    comms/       dr16.c/h  c_comm.c/h  referee.c/h
    motor/       dji_motor.c/h                电机抽象
    control/     gimbal.c/h  shoot.c/h        整车控制逻辑
                 (底盘为 chassis.c/h)
    dev/         led.c/h  buzzer.c/h          板级指示/发声

共移动 58 个文件(每板 29 个),旧目录全部删除。

工程文件同步(每个工程各 2 个):
- .uvprojx: Modules 分组 16 个 <FilePath> 改为新路径;
  2 个非空 <IncludePath> 元素:15 个旧模块路径收敛为 8 个新目录,
  并顺带删除陈旧项 ../modules/AHRS(目录根本不存在,legacy 时代残留)
  与冗余项 ..\bsp(与 ../Bsp/Inc 重复)
- .uvoptx: 16 个 <PathWithFileName> 同步

关于"为什么不用改源码": Keil 的 include 靠 IncludePath 解析,本工程所有
#include 都是纯文件名形式(无路径限定),所以移动目录只需改工程配置。
另外 .h 完全不登记在工程里(只有 16 个 .c 被登记),故头文件只需移动。

回归验证(无法在此环境编译,用等价判据):
- 两个工程引用的 98 / 95 个 <FilePath> 全部真实存在
- 磁盘上 47 / 45 个 .c 全部仍被工程引用 —— 即没有任何文件掉出编译
- 2 个 IncludePath 元素均含全部 8 个新模块目录,无旧路径残留
- 所有 IncludePath 指向的目录均真实存在
- .uvprojx/.uvoptx XML 良构
- 两棵树结构一致(chassis/gimbal 等板级差异符合预期)

⚠️ 仍需在 Keil 里点一次编译确认(见 README 的构建章节)。
2026-09-12 11:51:41 +08:00

219 lines
6.7 KiB
C

/* =============================================================================
* referee.c - 裁判系统协议解析(0x0201 机器人状态 / 0x0202 实时功率 /
* 0x0304 自定义控制器键鼠)
* 输入: referee_parse_byte() 流式喂字节(USART6 整帧回调里循环调用)
* 校验: CRC8(多项式0x31,初值0xFF) / CRC16(0x1021,0xFFFF),位运算实现,
* 与 legacy 查表算法等价
* 字段偏移与 legacy 逐一对应:
* 0x0201: data[0]=robot_id, data[10..11]=功率上限, data[12]&0x04=发射机构上电
* 0x0202: data[4..7]=实时功率(float), data[8..9]=缓冲能量(J)
* 0x0304: 12 字节键鼠数据 -> keyboard 钩子(仅云台板注册)
* 解析在接收中断内完成,任务层只读 referee_data_t 全局量
* ========================================================================== */
#include "referee.h"
#include "main.h"
/* 帧结构: SOF(1) data_len(2) seq(1) CRC8(1) | cmd_id(2) data(n) CRC16(2)
* 校验算法与 DJI 裁判系统协议一致:
* CRC8 多项式 0x31,初值 0xFF,输入输出不反转
* CRC16 多项式 0x1021,初值 0xFFFF,输入输出不反转 */
#define REF_FRAME_MAX_LEN 160
static uint8_t crc8_update(uint8_t crc, uint8_t data)
{
crc ^= data;
for (uint8_t i = 0; i < 8; i++)
crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x31) : (uint8_t)(crc << 1);
return crc;
}
static uint16_t crc16_update(uint16_t crc, uint8_t data)
{
crc ^= (uint16_t)data << 8;
for (uint8_t i = 0; i < 8; i++)
crc = (crc & 0x8000) ? (uint16_t)((crc << 1) ^ 0x1021) : (uint16_t)(crc << 1);
return crc;
}
static uint8_t verify_crc8(const uint8_t * data, uint16_t len)
{
uint8_t crc = 0xFF;
for (uint16_t i = 0; i < len - 1; i++)
crc = crc8_update(crc, data[i]);
return crc == data[len - 1];
}
static uint8_t verify_crc16(const uint8_t * data, uint16_t len)
{
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len - 2; i++)
crc = crc16_update(crc, data[i]);
return (uint8_t)(crc & 0xFF) == data[len - 2] && (uint8_t)(crc >> 8) == data[len - 1];
}
referee_data_t referee = {
.robot_id = 0,
.shooter_power_on = 1,
.chassis_power_limit = 60,
.chassis_power = 0,
.buffer_energy = 60.0f,
.link_status = 0,
.last_update_tick = 0,
};
static referee_keyboard_hook_f keyboard_hook = NULL;
void referee_set_keyboard_hook(referee_keyboard_hook_f hook)
{
keyboard_hook = hook;
}
/* 解析状态机 */
typedef enum
{
REF_STATE_SOF = 0,
REF_STATE_LEN_LOW,
REF_STATE_LEN_HIGH,
REF_STATE_SEQ,
REF_STATE_HEADER_CRC8,
REF_STATE_CMD_LOW,
REF_STATE_CMD_HIGH,
REF_STATE_DATA,
REF_STATE_CRC16_LOW,
REF_STATE_CRC16_HIGH,
} ref_state_e;
static ref_state_e ref_state = REF_STATE_SOF;
static uint16_t ref_data_len = 0;
static uint16_t ref_cmd_id = 0;
static uint16_t ref_data_index = 0;
static uint8_t ref_frame_buf[REF_FRAME_MAX_LEN];
static uint16_t ref_frame_index = 0;
static void referee_dispatch(uint16_t cmd_id, const uint8_t * data, uint16_t len)
{
switch (cmd_id)
{
case 0x0201: // 机器人状态(与 legacy 偏移一致)
if (len >= 13)
{
referee.robot_id = data[0];
referee.chassis_power_limit = (uint16_t)(data[10] | (data[11] << 8));
referee.shooter_power_on = (data[12] & 0x04) >> 2;
referee.last_update_tick = HAL_GetTick();
referee.link_status = 1;
}
break;
case 0x0202: // 实时功率(与 legacy 偏移一致)
if (len >= 10)
{
float power;
memcpy(&power, &data[4], 4);
referee.chassis_power = power;
referee.buffer_energy = (float)((uint16_t)(data[8] | (data[9] << 8)));
referee.last_update_tick = HAL_GetTick();
referee.link_status = 1;
}
break;
case 0x0304: // 自定义控制器(经图传链路转发)
if (len >= 12 && keyboard_hook != NULL)
keyboard_hook(data);
referee.last_update_tick = HAL_GetTick();
referee.link_status = 1;
break;
default:
break;
}
}
void referee_parse_byte(uint8_t byte)
{
switch (ref_state)
{
case REF_STATE_SOF:
if (byte == 0xA5)
{
ref_frame_index = 0;
ref_frame_buf[ref_frame_index++] = byte;
ref_state = REF_STATE_LEN_LOW;
}
break;
case REF_STATE_LEN_LOW:
ref_data_len = byte;
ref_frame_buf[ref_frame_index++] = byte;
ref_state = REF_STATE_LEN_HIGH;
break;
case REF_STATE_LEN_HIGH:
ref_data_len |= (uint16_t)byte << 8;
ref_frame_buf[ref_frame_index++] = byte;
if (ref_data_len > REF_FRAME_MAX_LEN - 9) // 帧过长,重新找帧头
ref_state = REF_STATE_SOF;
else
ref_state = REF_STATE_SEQ;
break;
case REF_STATE_SEQ:
ref_frame_buf[ref_frame_index++] = byte;
ref_state = REF_STATE_HEADER_CRC8;
break;
case REF_STATE_HEADER_CRC8:
ref_frame_buf[ref_frame_index++] = byte;
if (verify_crc8(ref_frame_buf, 5))
ref_state = REF_STATE_CMD_LOW;
else
ref_state = REF_STATE_SOF;
break;
case REF_STATE_CMD_LOW:
ref_cmd_id = byte;
ref_frame_buf[ref_frame_index++] = byte;
ref_state = REF_STATE_CMD_HIGH;
break;
case REF_STATE_CMD_HIGH:
ref_cmd_id |= (uint16_t)byte << 8;
ref_frame_buf[ref_frame_index++] = byte;
ref_data_index = 0;
if (ref_data_len == 0)
ref_state = REF_STATE_CRC16_LOW;
else
ref_state = REF_STATE_DATA;
break;
case REF_STATE_DATA:
ref_frame_buf[ref_frame_index++] = byte;
if (++ref_data_index >= ref_data_len)
ref_state = REF_STATE_CRC16_LOW;
break;
case REF_STATE_CRC16_LOW:
ref_frame_buf[ref_frame_index++] = byte;
ref_state = REF_STATE_CRC16_HIGH;
break;
case REF_STATE_CRC16_HIGH:
ref_frame_buf[ref_frame_index++] = byte;
if (verify_crc16(ref_frame_buf, ref_frame_index))
referee_dispatch(ref_cmd_id, &ref_frame_buf[7], ref_data_len);
ref_state = REF_STATE_SOF;
break;
default:
ref_state = REF_STATE_SOF;
break;
}
}
void referee_parse_bytes(const uint8_t * data, uint16_t len)
{
while (len--)
referee_parse_byte(*data++);
}