键鼠控制(此前 WASD 无反应): - 云台 robot_cmd_task:0x0304 与 USART3 帧内键鼠合并为共享视图 keyboard_merged(0x0304 优先); 键鼠模式由帧内键鼠动作进入,动云台左摇杆退出 - c_comm_task/c_comm:键鼠在控时底盘摇杆由 WASD+鼠标合成,改读共享视图(此前只认 0x0304) - dr16/调试输出:新增帧内键鼠原始字节诊断(key_lo/key_hi/mouse_*/s1/frames) 自定义UI(底盘板经裁判系统 USART6 绘制到选手端): - 新增 底盘/modules/comms/referee_ui.c|h:15 字节图形组包(带编译期断言), v5 卡片式 HUD = 俯仰梯(pitch) + 罗盘刻度带(底盘朝向) + 预估底盘功率 + 超级电容 + 摩擦轮 L/R 转速 + 小陀螺状态,yaw 在刻度带右侧;5Hz 刷新,约 50 图元/8 包 - referee.c 新增 referee_send_frame()(cmd 0x0301 机器人交互数据,CRC8+CRC16,USART6 TX DMA) - 新增 CToC 0x1B9:云台把 pitch 与摩擦轮转速带给底盘(底盘本地没有这两个量) - 版式避开官方保留区:顶部比分条/左下小地图/右下战车模块/中部两块官方保留 验证:云台 Code=99540 RO=1764 RW=420 ZI=62572;底盘 Code=99452 RO=1928 RW=416 ZI=62584;均 0 错误 0 警告。 未做实机验证(当前无板/无图传);UI 协议常量(UI_RECEIVER_ID/UI_SEND_HZ)与版式坐标集中在 referee_ui.h 宏区,便于按协议 PDF 一处校正。
218 lines
7.9 KiB
C
218 lines
7.9 KiB
C
/* =============================================================================
|
|
* c_comm.c - 云台<->底盘 板间通讯(CToC,CAN2,帧 ID 与 legacy 完全一致)
|
|
* 帧: 0x149 云->底 摇杆(4 个大端 u16) 0x189 云->底 控制状态
|
|
* 0x019 底->云 发射机构上电状态
|
|
* 云台板(主机): c_comm_master_send() 每 2ms 发摇杆帧+状态帧;
|
|
* 键鼠在线时按 legacy 规则把 WASD/鼠标合成进摇杆量;
|
|
* Data[0] 必须实时判定 dr16_offline()(dr16_status 掉线后不会自己清零)
|
|
* 底盘板(从机): c_comm_slave_send() 每 2ms 回传发射机构状态;
|
|
* 中断内解析 0x149/0x189 填充 ctoc_remote(底盘的"虚拟遥控器")
|
|
* ========================================================================== */
|
|
#include "c_comm.h"
|
|
#include "robot_param.h"
|
|
#include "referee.h"
|
|
#include <string.h>
|
|
|
|
volatile ctoc_remote_t ctoc_remote;
|
|
volatile uint8_t ctoc_shooter_power = 1;
|
|
volatile uint32_t ctoc_reply_last_tick = 0;
|
|
volatile uint8_t ctoc_remote_fault = 0;
|
|
volatile float ctoc_gimbal_yaw = 0.0f;
|
|
volatile uint8_t ctoc_att_ready = 0;
|
|
static volatile uint32_t ctoc_att_last_tick = 0;
|
|
/* 云台自检下发的编码器方向:+1=tick增向CCW -1=CW 0=未检测(0x1A9 flags) */
|
|
volatile int8_t ctoc_encoder_ccw = 0;
|
|
|
|
/* 0x1A9 帧新鲜判定(两板公共:底盘跟随用它门控,云台留作诊断) */
|
|
uint8_t ctoc_att_fresh(void)
|
|
{
|
|
return (ctoc_att_last_tick != 0 &&
|
|
HAL_GetTick() - ctoc_att_last_tick < CTOC_OFFLINE_TIME_MS);
|
|
}
|
|
|
|
static void c_comm_pack_uint16(uint8_t data[8], const uint16_t val[4])
|
|
{
|
|
for (uint8_t i = 0; i < 4; i++)
|
|
{
|
|
data[2 * i] = (uint8_t)(val[i] >> 8);
|
|
data[2 * i + 1] = (uint8_t)(val[i] & 0xFF);
|
|
}
|
|
}
|
|
|
|
static void c_comm_send_frame(hcan_t * hcan, uint32_t std_id, const uint8_t data[8])
|
|
{
|
|
CanCtrlData_s msg;
|
|
msg.hcan = hcan;
|
|
msg.tx_header.StdId = std_id;
|
|
msg.tx_header.IDE = CAN_ID_STD;
|
|
msg.tx_header.RTR = CAN_RTR_DATA;
|
|
msg.tx_header.DLC = 8;
|
|
msg.tx_header.TransmitGlobalTime = DISABLE;
|
|
for (uint8_t i = 0; i < 8; i++)
|
|
msg.tx_data[i] = data[i];
|
|
CAN_SendTxMessage(&msg);
|
|
}
|
|
|
|
#if defined(ROBOT_TYPE_GIMBAL)
|
|
|
|
/* ============================ 云台板(主机) ============================ */
|
|
|
|
static void c_comm_rx_0x019(uint32_t std_id, const uint8_t rx_data[8], uint32_t dlc)
|
|
{
|
|
(void)std_id;
|
|
(void)dlc;
|
|
ctoc_shooter_power = rx_data[0]; // 发射机构上电状态
|
|
ctoc_remote_fault = rx_data[1] & 0x01; // 底盘电机故障位
|
|
ctoc_reply_last_tick = HAL_GetTick();
|
|
}
|
|
|
|
void c_comm_init(void)
|
|
{
|
|
can_rx_register(&hcan2, CTOC_MASTER_ID1, c_comm_rx_0x019);
|
|
}
|
|
|
|
uint8_t ctoc_chassis_fault(void)
|
|
{
|
|
/* 从未收到过 0x019 视为底盘不在场,不判故障(便于仅云台台架调试);
|
|
* 收到过之后,故障位或帧超时都判故障 */
|
|
if (ctoc_reply_last_tick == 0)
|
|
return 0;
|
|
if (HAL_GetTick() - ctoc_reply_last_tick > CTOC_OFFLINE_TIME_MS)
|
|
return 1;
|
|
return ctoc_remote_fault;
|
|
}
|
|
|
|
/* 云台板:0x1A9 姿态帧 */
|
|
void c_comm_master_send_attitude(float yaw_deg, uint8_t flags)
|
|
{
|
|
uint8_t data[8] = {0};
|
|
memcpy(data, &yaw_deg, 4); // float 小端
|
|
data[4] = flags;
|
|
c_comm_send_frame(&hcan2, CTOC_SLAVE_ID3, data);
|
|
}
|
|
|
|
void c_comm_master_send(uint8_t keyboard_online, const uint8_t kb[12], uint8_t motor_fault, uint8_t spin_enable)
|
|
{
|
|
uint8_t data[8];
|
|
|
|
if (!keyboard_online)
|
|
{
|
|
// 遥控器控制:直接转发原始摇杆值
|
|
const uint16_t sticks[4] = {dr16.rc_r_RL, dr16.rc_r_UD, dr16.rc_l_RL, dr16.rc_l_UD};
|
|
c_comm_pack_uint16(data, sticks);
|
|
}
|
|
else
|
|
{
|
|
// 键鼠控制:合成摇杆量(与 legacy 一致)
|
|
uint8_t key_go = kb[8] & 0x01;
|
|
uint8_t key_back = (kb[8] >> 1) & 0x01;
|
|
uint8_t key_left = (kb[8] >> 2) & 0x01;
|
|
uint8_t key_right = (kb[8] >> 3) & 0x01;
|
|
int16_t mouse_rl = (int16_t)(((uint16_t)kb[1] << 8) | kb[0]);
|
|
int16_t mouse_du = (int16_t)(((uint16_t)kb[3] << 8) | kb[2]);
|
|
|
|
const uint16_t sticks[4] = {
|
|
(uint16_t)(1024 + (key_right - key_left) * 660), // AD->右摇杆RL
|
|
(uint16_t)(1024 + (key_go - key_back) * 660), // WS->右摇杆UD
|
|
(uint16_t)(1024 + mouse_rl * PC_MOUSE_RL_SENSITIVITY), // 鼠标->左摇杆RL
|
|
(uint16_t)(1024 + mouse_du * PC_MOUSE_DU_SENSITIVITY), // 鼠标->左摇杆UD
|
|
};
|
|
c_comm_pack_uint16(data, sticks);
|
|
}
|
|
c_comm_send_frame(&hcan2, CTOC_SLAVE_ID1, data);
|
|
|
|
/* 在线状态必须实时判定:dr16_status 只在收帧时置 1,掉线后不会自己清零,
|
|
* 若直接转发会让底盘永远认为遥控器在线(停车保护失效) */
|
|
data[0] = (dr16_status && !dr16_offline()) ? 1 : 0;
|
|
data[1] = dr16.s2;
|
|
data[2] = dr16.key_push_ctrl;
|
|
data[3] = dr16.key_push_shift;
|
|
data[4] = dr16_start_flag;
|
|
data[5] = dr16.s1;
|
|
data[6] = (motor_fault ? 0x80 : 0) | (spin_enable ? 0x04 : 0) |
|
|
(dr16.key_alpha1 ? 0x01 : 0) |
|
|
(dr16.key_alpha2 ? 0x02 : 0); // bit7=故障 bit2=陀螺使能(扩展)
|
|
data[7] = 0;
|
|
c_comm_send_frame(&hcan2, CTOC_SLAVE_ID2, data);
|
|
}
|
|
|
|
/* 0x1B9 云台->底盘:自定义UI 专用量(底盘本地没有,见 referee_ui)
|
|
* [0..1] pitch(0.01°) [2..3] 左摩擦轮(rpm/100) [4..5] 右摩擦轮(rpm/100)
|
|
* [6] bit0=小陀螺使能 */
|
|
void c_comm_master_send_ui(float pitch_deg, float fric_l_rpm, float fric_r_rpm, uint8_t spin_on)
|
|
{
|
|
uint8_t data[8] = {0};
|
|
int16_t p = (int16_t)(pitch_deg * 100.0f);
|
|
int16_t fl = (int16_t)(fric_l_rpm / 100.0f);
|
|
int16_t fr = (int16_t)(fric_r_rpm / 100.0f);
|
|
|
|
data[0] = (uint8_t)(p & 0xFF);
|
|
data[1] = (uint8_t)((uint16_t)p >> 8);
|
|
data[2] = (uint8_t)(fl & 0xFF);
|
|
data[3] = (uint8_t)((uint16_t)fl >> 8);
|
|
data[4] = (uint8_t)(fr & 0xFF);
|
|
data[5] = (uint8_t)((uint16_t)fr >> 8);
|
|
data[6] = spin_on ? 0x01 : 0;
|
|
c_comm_send_frame(&hcan2, CTOC_SLAVE_ID4, data);
|
|
}
|
|
|
|
#elif defined(ROBOT_TYPE_CHASSIS)
|
|
|
|
/* ============================ 底盘板(从机) ============================ */
|
|
|
|
static void c_comm_rx_0x149(uint32_t std_id, const uint8_t rx_data[8], uint32_t dlc)
|
|
{
|
|
(void)std_id;
|
|
(void)dlc;
|
|
ctoc_remote.rc_r_rl = ((uint16_t)rx_data[0] << 8) | rx_data[1];
|
|
ctoc_remote.rc_r_ud = ((uint16_t)rx_data[2] << 8) | rx_data[3];
|
|
ctoc_remote.rc_l_rl = ((uint16_t)rx_data[4] << 8) | rx_data[5];
|
|
ctoc_remote.rc_l_ud = ((uint16_t)rx_data[6] << 8) | rx_data[7];
|
|
ctoc_remote.last_tick = HAL_GetTick();
|
|
}
|
|
|
|
static void c_comm_rx_0x189(uint32_t std_id, const uint8_t rx_data[8], uint32_t dlc)
|
|
{
|
|
(void)std_id;
|
|
(void)dlc;
|
|
ctoc_remote.status = rx_data[0];
|
|
ctoc_remote.s2 = rx_data[1];
|
|
ctoc_remote.ctrl_push = rx_data[2];
|
|
ctoc_remote.shift_push = rx_data[3];
|
|
ctoc_remote.start_flag = rx_data[4];
|
|
ctoc_remote.s1 = rx_data[5];
|
|
ctoc_remote.spin_flag = (rx_data[6] >> 2) & 0x01; // 小陀螺使能位
|
|
ctoc_remote.gimbal_fault = (rx_data[6] & 0x80) ? 1 : 0; // 云台电机故障位
|
|
ctoc_remote.last_tick = HAL_GetTick();
|
|
}
|
|
|
|
static void c_comm_rx_0x1a9(uint32_t std_id, const uint8_t rx_data[8], uint32_t dlc)
|
|
{
|
|
(void)std_id;
|
|
(void)dlc;
|
|
float yaw;
|
|
memcpy(&yaw, rx_data, 4);
|
|
ctoc_gimbal_yaw = yaw;
|
|
ctoc_att_ready = rx_data[4] & 0x01;
|
|
if (rx_data[4] & 0x04) // 云台自检完成位
|
|
ctoc_encoder_ccw = (rx_data[4] & 0x02) ? 1 : -1; // tick 增方向
|
|
ctoc_att_last_tick = HAL_GetTick();
|
|
}
|
|
|
|
void c_comm_init(void)
|
|
{
|
|
can_rx_register(&hcan2, CTOC_SLAVE_ID1, c_comm_rx_0x149);
|
|
can_rx_register(&hcan2, CTOC_SLAVE_ID2, c_comm_rx_0x189);
|
|
can_rx_register(&hcan2, CTOC_SLAVE_ID3, c_comm_rx_0x1a9);
|
|
}
|
|
|
|
void c_comm_slave_send(uint8_t motor_fault)
|
|
{
|
|
uint8_t data[8] = {0};
|
|
data[0] = referee.shooter_power_on; // 发射机构上电状态
|
|
data[1] = motor_fault ? 0x01 : 0; // 底盘电机故障位(扩展)
|
|
c_comm_send_frame(&hcan2, CTOC_MASTER_ID1, data);
|
|
}
|
|
|
|
#endif
|