diff --git a/.vscode/launch.json b/.vscode/launch.json index 58fc8ea..29cdabb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,7 +19,8 @@ "configFiles": [ "openocd_dap.cfg", // 配置文件已经在根目录提供,若要修改以此类推,openocd的路径下的share/scripts中有各种写好的配置文件 ], - "runToEntryPoint": "main" // 调试时在main函数入口停下 + "runToEntryPoint": "main", // 调试时在main函数入口停下 + //"preLaunchTask": "build task",//先运行Build任务编译项目,取消注释即可使用 //"preLaunchTask": "log", // 调试时同时开启RTT viewer窗口 // 若想要在调试前编译并且打开log,可只使用log的prelaunch task并为log任务添加depends on依赖 }, @@ -36,7 +37,9 @@ "servertype": "jlink", "interface": "swd", "svdFile": "STM32F407.svd", - //"preLaunchTask": "log", // 调试时同时开启RTT viewer窗口 + "rtos": "FreeRTOS", + // "preLaunchTask": "build task",//先运行Build任务,取消注释即可使用 + //"preLaunchTask": "log", // 调试时同时开启RTT viewer窗口 // 若想要在调试前编译并且打开log,可只使用log的prelaunch task并为log任务添加depends on依赖 }, ], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9ff5211..4aef523 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,5 +30,15 @@ "isDefault": false, } }, + { + "label": "log", + "type": "shell", + "command":"JlinkRTTClient", + "args": [], + "problemMatcher": [], + // "dependsOn":[ + // "build task", // 可以添加多个. + // ] + } ] } \ No newline at end of file diff --git a/Bug_Report.md b/Bug_Report.md index 4223b3f..a517636 100644 --- a/Bug_Report.md +++ b/Bug_Report.md @@ -1,5 +1,7 @@ # 异常报告 +已知可能出现的bug将会列在此处,并指明修复期限和任务执行者。 + 使用中遇到的bug和错误放在此处。参照下列格式: ## 标题用简短的一句话描述 @@ -32,6 +34,8 @@ ## 使用LK电机并挂载在hcan2上时会出现HardFault +> 已修复此问题。修复日志请查看当前目录下的“如何定位bug.md”。 + 使用MF9025v2电机,并将其配置在CAN2上。经过一次LKMotorControl,第二次进入时hcan->instance会在HAL_CAN_Add_Tx_Message()结束时被未知的语句修改成奇怪的值,造成HardFault ### 尝试解决的方案 diff --git a/HAL_N_Middlewares/Src/freertos.c b/HAL_N_Middlewares/Src/freertos.c index a7799d2..27d6ec9 100644 --- a/HAL_N_Middlewares/Src/freertos.c +++ b/HAL_N_Middlewares/Src/freertos.c @@ -197,7 +197,7 @@ void StartROBOTTASK(void const * argument) { // 200Hz RobotTask(); - osDelay(5); + osDelay(10);//syh此处暂时将时间改为10ms,原因在于未使用缓冲区发送,发送时延时5ms } } diff --git a/Makefile b/Makefile index 1139182..f6a50ab 100644 --- a/Makefile +++ b/Makefile @@ -132,7 +132,7 @@ modules/motor/servo_motor/servo_motor.c \ modules/motor/motor_task.c \ modules/oled/oled.c \ modules/referee/crc_ref.c \ -modules/referee/referee.c \ +modules/referee/rm_referee.c \ modules/referee/referee_UI.c \ modules/referee/referee_communication.c \ modules/remote/remote_control.c \ @@ -145,6 +145,7 @@ modules/ps_handle/ps_handle.c \ application/gimbal/gimbal.c \ application/chassis/chassis.c \ application/shoot/shoot.c \ +application/referee/referee.c \ application/cmd/robot_cmd.c \ application/balance_chassis/balance.c \ application/robot.c @@ -228,6 +229,7 @@ C_INCLUDES = \ -Iapplication/gimbal \ -Iapplication/cmd \ -Iapplication/balance_chassis \ +-Iapplication/referee \ -Iapplication \ -Ibsp/dwt \ -Ibsp/can \ diff --git a/VSCode+Ozone使用方法.md b/VSCode+Ozone使用方法.md index 1f90a70..5001519 100644 --- a/VSCode+Ozone使用方法.md +++ b/VSCode+Ozone使用方法.md @@ -8,7 +8,6 @@ > > 1. 添加一键编译+启用ozone调试脚本,使得整个进一步流程自动化 > 2. 增加更多的背景知识介绍 -> 3. 增加VSCode下RTT viewer的支持 @@ -481,6 +480,12 @@ VSCode `ctrl+,`进入设置,通过`搜索`找到cortex-debug插件的设置。 +### RTT Viewer日志功能 + +本框架添加了vscode下Segger RTT client的支持。在`.vscode/task.json`中已经添加了启动rtt viewer client的任务。你也可以将此任务作为附加启动任务和调试一起启动,方便查看日志。要使用日志,请包含`bsp_log.h`。注意,需要将jlink的安装目录添加到环境变量中。 + + + ### 更好的编辑体验 建议安装以下插件: diff --git a/application/balance_chassis/balance.c b/application/balance_chassis/balance.c index 1d27e57..d587381 100644 --- a/application/balance_chassis/balance.c +++ b/application/balance_chassis/balance.c @@ -1,4 +1,10 @@ +// app #include "balance.h" +#include "vmc_project.h" +#include "gain_table.h" +#include "robot_def.h" +#include "general_def.h" +// module #include "HT04.h" #include "LK9025.h" #include "bmi088.h" @@ -6,9 +12,8 @@ #include "super_cap.h" #include "controller.h" #include "can_comm.h" +// standard #include "stdint.h" -#include "robot_def.h" -#include "general_def.h" #include "arm_math.h" // 需要用到较多三角函数 /* 底盘拥有的模块实例 */ @@ -47,7 +52,7 @@ static float T_leg_left, T_leg_right; // 左右驱动电机力矩 * */ static void CalcLQR() -{ +{ } /** @@ -55,15 +60,15 @@ static void CalcLQR() * */ static void VMCProject() -{ +{ // 拟将功能封装到vmc_project.h中 } /** * @brief 腿部角度控制:转向和抗劈叉 * */ -static PIDInstance swerving_pid; -static PIDInstance anti_crash_pid; +static PIDInstance swerving_pid; // 转向PID,有转向指令时使用IMU的加速度反馈积分以获取速度和位置状态量 +static PIDInstance anti_crash_pid; // 抗劈叉,将输出以相反的方向叠加到左右腿的上 static void SynthesizeMotion() { @@ -73,15 +78,16 @@ static void SynthesizeMotion() * @brief 腿长控制:长度 + roll轴补偿(保持机体水平),用PD模拟弹簧的传递函数 * */ -static PIDInstance leg_length_pid; -static PIDInstance roll_compensate_pid; +static PIDInstance leg_length_pid; // 用PD模拟弹簧的传递函数,不需要积分项(弹簧是一个无积分的二阶系统),增益不可过大否则抗外界冲击响应时太"硬" +static PIDInstance roll_compensate_pid; // roll轴补偿,用于保持机体水平 static void LegControl() { } /** - * @brief 离地监测和?跳跃控制 + * @brief 离地监测和?跳跃控制? + * 通过模型解算地面的支持力完成离地检测 * */ static void FlyDetect() @@ -89,7 +95,7 @@ static void FlyDetect() } /** - * @brief 功率限制 + * @brief 功率限制,一般不需要 * */ static void WattLimit() @@ -134,7 +140,7 @@ void BalanceInit() .cali_mode = BMI088_CALIBRATE_ONLINE_MODE, .work_mode = BMI088_BLOCK_PERIODIC_MODE, }; - imu = BMI088Register(&imu_config); + // imu = BMI088Register(&imu_config); SuperCap_Init_Config_s cap_conf = { // 超级电容初始化 @@ -162,10 +168,28 @@ void BalanceInit() Motor_Init_Config_s driven_conf = { // 写一个,剩下的修改方向和id即可 + .can_init_config.can_handle = &hcan1, + .controller_param_init_config = { + .current_PID = { + .Kp = 1, + .Ki = 0, + .Kd = 0, + .MaxOut = 500, + }, + }, + .controller_setting_init_config = { + .angle_feedback_source = MOTOR_FEED, + .speed_feedback_source = MOTOR_FEED, + .outer_loop_type = CURRENT_LOOP, + .close_loop_type = CURRENT_LOOP, + }, + .motor_type = LK9025, }; + driven_conf.can_init_config.tx_id=1; l_driven = LKMotorInit(&driven_conf); + driven_conf.can_init_config.tx_id=2; r_driven = LKMotorInit(&driven_conf); CANComm_Init_Config_s chassis_comm_conf = { @@ -224,6 +248,7 @@ void BalanceInit() PIDInit(&roll_compensate_pid, &roll_compensate_pid_conf); } +/* balanceTask可能需要以更高频率运行,以提高线性化的精确程度 */ void BalanceTask() { } diff --git a/application/balance_chassis/gain_table.h b/application/balance_chassis/gain_table.h index 1449e93..b2086bc 100644 --- a/application/balance_chassis/gain_table.h +++ b/application/balance_chassis/gain_table.h @@ -2,13 +2,14 @@ #pragma once #include "stdint.h" +#include "stm32f407xx.h" #include "arm_math.h" #include "math.h" #define GAIN_TABLE_SIZE 100 // 增益表大小 // K 2x6,6个状态变量2个输出(Tp关节电机和T驱动轮电机) -static float leglen2gain [GAIN_TABLE_SIZE][2][6] = {}; +static float leglen2gain [GAIN_TABLE_SIZE][2][6] = {0}; static interpolation_flag = 0; // 插值方式:1 线性插值 0 关闭插值 diff --git a/application/balance_chassis/vmc_project.h b/application/balance_chassis/vmc_project.h new file mode 100644 index 0000000..b74b239 --- /dev/null +++ b/application/balance_chassis/vmc_project.h @@ -0,0 +1,14 @@ +#ifndef VMC_PROJECT_H +#define VMC_PROJECT_H + +#include "stm32f407xx.h" +#include "arm_math.h" +#include "math.h" +#include "general_def.h" + +// 将五连杆和直杆的vmc映射放在此处,方便修改和调试,balance.c不会太长 +#endif // !VMC_PROJECT_H + + + + diff --git a/application/chassis/chassis.c b/application/chassis/chassis.c index 93e4a6e..1af1908 100644 --- a/application/chassis/chassis.c +++ b/application/chassis/chassis.c @@ -16,10 +16,16 @@ #include "dji_motor.h" #include "super_cap.h" #include "message_center.h" + +// referee需要移动到module层 +///////////////////////// #include "referee.h" +#include "rm_referee.h" +///////////////////////// + #include "general_def.h" #include "bsp_dwt.h" - +#include "referee_UI.h" #include "arm_math.h" /* 根据robot_def.h中的macro自动计算的参数 */ @@ -41,7 +47,7 @@ static Subscriber_t *chassis_sub; // 用于订阅底盘的控 static Chassis_Ctrl_Cmd_s chassis_cmd_recv; // 底盘接收到的控制命令 static Chassis_Upload_Data_s chassis_feedback_data; // 底盘回传的反馈数据 -static referee_info_t *referee_data; // 裁判系统的数据 +// static referee_info_t *referee_data; // 裁判系统相关数据 static SuperCapInstance *cap; // 超级电容 static DJIMotorInstance *motor_lf; // left right forward back static DJIMotorInstance *motor_rf; @@ -103,7 +109,11 @@ void ChassisInit() chassis_motor_config.controller_setting_init_config.motor_reverse_flag = MOTOR_DIRECTION_REVERSE; motor_rb = DJIMotorInit(&chassis_motor_config); - referee_data = RefereeInit(&huart6); // 裁判系统初始化 + // referee_data = RefereeInit(&huart6); // 裁判系统初始化 + + // while (referee_data->GameRobotState.robot_id ==0); + // Referee_Interactive_init(referee_data); + SuperCap_Init_Config_s cap_conf = { .can_config = { @@ -240,12 +250,12 @@ void ChassisTask() // 根据电机的反馈速度和IMU(如果有)计算真实速度 EstimateSpeed(); - // 获取裁判系统数据 - // 我方颜色id小于7是红色,大于7是蓝色,注意这里发送的是对方的颜色, 0:blue , 1:red - chassis_feedback_data.enemy_color = referee_data->GameRobotStat.robot_id > 7 ? 1 : 0; - // 当前只做了17mm热量的数据获取,后续根据robot_def中的宏切换双枪管和英雄42mm的情况 - chassis_feedback_data.bullet_speed = referee_data->GameRobotStat.shooter_id1_17mm_speed_limit; - chassis_feedback_data.rest_heat = referee_data->PowerHeatData.shooter_heat0; + // // 获取裁判系统数据 建议将裁判系统与底盘分离,所以此处数据应使用消息中心发送 + // // 我方颜色id小于7是红色,大于7是蓝色,注意这里发送的是对方的颜色, 0:blue , 1:red + // chassis_feedback_data.enemy_color = referee_data->GameRobotState.robot_id > 7 ? 1 : 0; + // // 当前只做了17mm热量的数据获取,后续根据robot_def中的宏切换双枪管和英雄42mm的情况 + // chassis_feedback_data.bullet_speed = referee_data->GameRobotState.shooter_id1_17mm_speed_limit; + // chassis_feedback_data.rest_heat = referee_data->PowerHeatData.shooter_heat0; // 推送反馈消息 #ifdef ONE_BOARD diff --git a/application/cmd/robot_cmd.c b/application/cmd/robot_cmd.c index da3cd06..4883e04 100644 --- a/application/cmd/robot_cmd.c +++ b/application/cmd/robot_cmd.c @@ -1,5 +1,7 @@ +// app #include "robot_def.h" #include "robot_cmd.h" +// module #include "remote_control.h" #include "ins_task.h" #include "master_process.h" diff --git a/application/gimbal/gimbal.c b/application/gimbal/gimbal.c index 99769be..f02ce9c 100644 --- a/application/gimbal/gimbal.c +++ b/application/gimbal/gimbal.c @@ -70,7 +70,7 @@ void GimbalInit() .DeadBand = 0.1, .Improve = PID_Trapezoid_Intergral | PID_Integral_Limit |PID_Derivative_On_Measurement, .IntegralLimit = 100, - + .MaxOut = 500, }, .speed_PID = { @@ -144,6 +144,7 @@ void GimbalTask() // 后续增加未收到数据的处理 SubGetMessage(gimbal_sub, &gimbal_cmd_recv); + // @todo:现在已不再需要电机反馈,实际上可以始终使用IMU的姿态数据来作为云台的反馈,yaw电机的offset只是用来跟随底盘 // 根据控制模式进行电机反馈切换和过渡,视觉模式在robot_cmd模块就已经设置好,gimbal只看yaw_ref和pitch_ref switch (gimbal_cmd_recv.gimbal_mode) { @@ -153,7 +154,7 @@ void GimbalTask() DJIMotorStop(pitch_motor); break; // 使用陀螺仪的反馈,底盘根据yaw电机的offset跟随云台或视觉模式采用 - case GIMBAL_GYRO_MODE: // 后续只保留此模式 + case GIMBAL_GYRO_MODE: // 后续只保留此模式 DJIMotorEnable(yaw_motor); DJIMotorEnable(pitch_motor); DJIMotorChangeFeed(yaw_motor, ANGLE_LOOP, OTHER_FEED); @@ -178,6 +179,10 @@ void GimbalTask() break; } + // 在合适的地方添加pitch重力补偿前馈力矩 + // 根据IMU姿态/pitch电机角度反馈计算出当前配重下的重力矩 + // ... + // 设置反馈数据,主要是imu和yaw的ecd gimbal_feedback_data.gimbal_imu_data = *gimba_IMU_data; gimbal_feedback_data.yaw_motor_single_round_angle = yaw_motor->motor_measure.angle_single_round; diff --git a/application/referee/referee.c b/application/referee/referee.c new file mode 100644 index 0000000..57ab4c6 --- /dev/null +++ b/application/referee/referee.c @@ -0,0 +1,385 @@ +/** + * @file referee.C + * @author kidneygood (you@domain.com) + * @brief + * @version 0.1 + * @date 2022-11-18 + * + * @copyright Copyright (c) 2022 + * + */ +#include "referee.h" +#include "robot_def.h" +#include "rm_referee.h" +#include "referee_UI.h" +#include "referee_communication.h" + +static Referee_Interactive_info_t Interactive_data; // 非裁判系统数据 +static referee_info_t *referee_data; // 裁判系统相关数据 +static robot_interactive_data_t *SendData; + +static void determine_ID(referee_info_t *_referee_info); +static void My_UI_init(referee_info_t *_referee_info); +static void My_UI_Refresh(referee_info_t *_referee_info, Referee_Interactive_info_t *_Interactive_data); +static void Mode_Change_Check(Referee_Interactive_info_t *_Interactive_data); // 模式切换检测 + +//syhtod 正式上车后需删除 +static void robot_mode_change(Referee_Interactive_info_t *_Interactive_data); // 测试用函数,实现模式自动变化 +static void UI_test_init(referee_info_t *_referee_info);//UI测试函数 + +void Referee_Interactive_init() +{ + referee_data = RefereeInit(&huart6); // 裁判系统初始化 + while (referee_data->GameRobotState.robot_id == 0) + ; + determine_ID(referee_data); + My_UI_init(referee_data); + //UI_test_init(referee_data); + + for (int i=0;idata[i]=i+1; + } + referee_data->referee_id.Receiver_Robot_ID = RobotID_BEngineer; // 机器人车间通信时接收者的ID暂时发给蓝色2 + Communicate_SendData(&referee_data->referee_id,SendData); +} + +void Referee_Interactive_task() +{ + robot_mode_change(&Interactive_data); // 测试用函数,实现模式自动变化 + My_UI_Refresh(referee_data, &Interactive_data); +} + +static Graph_Data_t UI_shoot_line[10]; // 射击准线 +static String_Data_t UI_State_sta[6]; // 机器人状态,静态只需画一次 +static String_Data_t UI_State_dyn[6]; // 机器人状态,动态先add才能change +static uint32_t shoot_line_location[10] = {540, 960, 490, 515, 565}; + +static void My_UI_init(referee_info_t *_referee_info) +{ + UI_Delete(&_referee_info->referee_id, UI_Data_Del_ALL, 0); + + // 绘制发射基准线 + Line_Draw(&UI_shoot_line[0], "sl0", UI_Graph_ADD, 7, UI_Color_White, 3, 710, shoot_line_location[0], 1210, shoot_line_location[0]); + Line_Draw(&UI_shoot_line[1], "sl1", UI_Graph_ADD, 7, UI_Color_White, 3, shoot_line_location[1], 340, shoot_line_location[1], 740); + Line_Draw(&UI_shoot_line[2], "sl2", UI_Graph_ADD, 7, UI_Color_Yellow, 2, 810, shoot_line_location[2], 1110, shoot_line_location[2]); + Line_Draw(&UI_shoot_line[3], "sl3", UI_Graph_ADD, 7, UI_Color_Yellow, 2, 810, shoot_line_location[3], 1110, shoot_line_location[3]); + Line_Draw(&UI_shoot_line[4], "sl4", UI_Graph_ADD, 7, UI_Color_Yellow, 2, 810, shoot_line_location[4], 1110, shoot_line_location[4]); + + UI_ReFresh(&_referee_info->referee_id, 5, UI_shoot_line[0], UI_shoot_line[1], UI_shoot_line[2], UI_shoot_line[3], UI_shoot_line[4]); + + // 绘制车辆状态标志,静态 + Char_Draw(&UI_State_sta[0], "ss0", UI_Graph_ADD, 8, UI_Color_Main, 15, 2, 150, 750); + Char_Write(&UI_State_sta[0], "chassis:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[0]); + + Char_Draw(&UI_State_sta[1], "ss1", UI_Graph_ADD, 8, UI_Color_Yellow, 15, 2, 150, 700); + Char_Write(&UI_State_sta[1], "gimbal:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[1]); + + Char_Draw(&UI_State_sta[2], "ss2", UI_Graph_ADD, 8, UI_Color_Orange, 15, 2, 150, 650); + Char_Write(&UI_State_sta[2], "shoot:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[2]); + + Char_Draw(&UI_State_sta[3], "ss3", UI_Graph_ADD, 8, UI_Color_Pink, 15, 2, 150, 600); + Char_Write(&UI_State_sta[3], "frict:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[3]); + + Char_Draw(&UI_State_sta[4], "ss4", UI_Graph_ADD, 8, UI_Color_Pink, 15, 2, 150, 550); + Char_Write(&UI_State_sta[4], "lid:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[4]); + + // 底盘功率显示,静态 + Char_Draw(&UI_State_sta[5], "ss5", UI_Graph_ADD, 8, UI_Color_Green, 18, 2, 720, 210); + Char_Write(&UI_State_sta[5], "Power:"); + Char_ReFresh(&_referee_info->referee_id, UI_State_sta[5]); + + // 绘制车辆状态标志,动态 + // 由于初始化时xxx_last_mode默认为0,所以此处对应UI也应该设为0时对应的UI,防止模式不变的情况下无法置位flag,导致UI无法刷新 + Char_Draw(&UI_State_dyn[0], "sd0", UI_Graph_ADD, 8, UI_Color_Main, 15, 2, 270, 750); + Char_Write(&UI_State_dyn[0], "zeroforce"); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[0]); + + Char_Draw(&UI_State_dyn[1], "sd1", UI_Graph_ADD, 8, UI_Color_Yellow, 15, 2, 270, 700); + Char_Write(&UI_State_dyn[1], "zeroforce"); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[1]); + + Char_Draw(&UI_State_dyn[2], "sd2", UI_Graph_ADD, 8, UI_Color_Orange, 15, 2, 270, 650); + Char_Write(&UI_State_dyn[2], "off"); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[2]); + + Char_Draw(&UI_State_dyn[3], "sd3", UI_Graph_ADD, 8, UI_Color_Pink, 15, 2, 270, 600); + Char_Write(&UI_State_dyn[3], "off"); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[3]); + + Char_Draw(&UI_State_dyn[4], "sd4", UI_Graph_ADD, 8, UI_Color_Pink, 15, 2, 270, 550); + Char_Write(&UI_State_dyn[4], "open "); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[4]); + + // 底盘功率显示,动态 + Char_Draw(&UI_State_dyn[5], "sd5", UI_Graph_ADD, 8, UI_Color_Green, 18, 2, 840, 210); + Char_Write(&UI_State_dyn[5], "0000"); + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[5]); +} + +static uint8_t count = 0; +static uint16_t count1 = 0; +static void robot_mode_change(Referee_Interactive_info_t *_Interactive_data) // 测试用函数,实现模式自动变化 +{ + count++; + if (count >= 50) + { + count = 0; + count1++; + } + switch (count1 % 4) + { + case 0: + { + _Interactive_data->chassis_mode = CHASSIS_ZERO_FORCE; + _Interactive_data->gimbal_mode = GIMBAL_ZERO_FORCE; + _Interactive_data->shoot_mode = SHOOT_ON; + _Interactive_data->friction_mode = FRICTION_ON; + _Interactive_data->lid_mode = LID_OPEN; + break; + } + case 1: + { + ; + _Interactive_data->chassis_mode = CHASSIS_ROTATE; + _Interactive_data->gimbal_mode = GIMBAL_FREE_MODE; + _Interactive_data->shoot_mode = SHOOT_OFF; + _Interactive_data->friction_mode = FRICTION_OFF; + _Interactive_data->lid_mode = LID_CLOSE; + break; + } + case 2: + { + _Interactive_data->chassis_mode = CHASSIS_NO_FOLLOW; + _Interactive_data->gimbal_mode = GIMBAL_GYRO_MODE; + _Interactive_data->shoot_mode = SHOOT_ON; + _Interactive_data->friction_mode = FRICTION_ON; + _Interactive_data->lid_mode = LID_OPEN; + break; + } + case 3: + { + _Interactive_data->chassis_mode = CHASSIS_FOLLOW_GIMBAL_YAW; + _Interactive_data->gimbal_mode = GIMBAL_ZERO_FORCE; + _Interactive_data->shoot_mode = SHOOT_OFF; + _Interactive_data->friction_mode = FRICTION_OFF; + _Interactive_data->lid_mode = LID_CLOSE; + break; + } + default: + break; + } +} + +static void My_UI_Refresh(referee_info_t *_referee_info, Referee_Interactive_info_t *_Interactive_data) +{ + Mode_Change_Check(_Interactive_data); + // chassis + if (_Interactive_data->Referee_Interactive_Flag.chassis_flag == 1) + { + switch (_Interactive_data->chassis_mode) + { + case CHASSIS_ZERO_FORCE: + { + Char_Draw(&UI_State_dyn[0], "sd0", UI_Graph_Change, 8, UI_Color_Main, 15, 2, 270, 750); + Char_Write(&UI_State_dyn[0], "zeroforce"); + break; + } + case CHASSIS_ROTATE: + { + Char_Draw(&UI_State_dyn[0], "sd0", UI_Graph_Change, 8, UI_Color_Main, 15, 2, 270, 750); + Char_Write(&UI_State_dyn[0], "rotate "); // 此处注意字数对齐问题,字数相同才能覆盖掉 + break; + } + case CHASSIS_NO_FOLLOW: + { + Char_Draw(&UI_State_dyn[0], "sd0", UI_Graph_Change, 8, UI_Color_Main, 15, 2, 270, 750); + Char_Write(&UI_State_dyn[0], "nofollow "); + break; + } + case CHASSIS_FOLLOW_GIMBAL_YAW: + { + Char_Draw(&UI_State_dyn[0], "sd0", UI_Graph_Change, 8, UI_Color_Main, 15, 2, 270, 750); + Char_Write(&UI_State_dyn[0], "follow "); + break; + } + } + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[0]); + _Interactive_data->Referee_Interactive_Flag.chassis_flag = 0; + } + // gimbal + if (_Interactive_data->Referee_Interactive_Flag.gimbal_flag == 1) + { + switch (_Interactive_data->gimbal_mode) + { + case GIMBAL_ZERO_FORCE: + { + Char_Draw(&UI_State_dyn[1], "sd1", UI_Graph_Change, 8, UI_Color_Yellow, 15, 2, 270, 700); + Char_Write(&UI_State_dyn[1], "zeroforce"); + break; + } + case GIMBAL_FREE_MODE: + { + Char_Draw(&UI_State_dyn[1], "sd1", UI_Graph_Change, 8, UI_Color_Yellow, 15, 2, 270, 700); + Char_Write(&UI_State_dyn[1], "free "); + break; + } + case GIMBAL_GYRO_MODE: + { + Char_Draw(&UI_State_dyn[1], "sd1", UI_Graph_Change, 8, UI_Color_Yellow, 15, 2, 270, 700); + Char_Write(&UI_State_dyn[1], "gyro "); + break; + } + } + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[1]); + _Interactive_data->Referee_Interactive_Flag.gimbal_flag = 0; + } + // shoot + if (_Interactive_data->Referee_Interactive_Flag.shoot_flag == 1) + { + switch (_Interactive_data->shoot_mode) + { + case SHOOT_OFF: + { + Char_Draw(&UI_State_dyn[2], "sd2", UI_Graph_Change, 8, UI_Color_Orange, 15, 2, 270, 650); + Char_Write(&UI_State_dyn[2], "off"); + break; + } + case SHOOT_ON: + { + Char_Draw(&UI_State_dyn[2], "sd2", UI_Graph_Change, 8, UI_Color_Orange, 15, 2, 270, 650); + Char_Write(&UI_State_dyn[2], "on "); + break; + } + } + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[2]); + _Interactive_data->Referee_Interactive_Flag.shoot_flag = 0; + } + + // friction + if (_Interactive_data->Referee_Interactive_Flag.friction_flag == 1) + { + switch (_Interactive_data->friction_mode) + { + case FRICTION_OFF: + { + Char_Draw(&UI_State_dyn[3], "sd3", UI_Graph_Change, 8, UI_Color_Pink, 15, 2, 270, 600); + Char_Write(&UI_State_dyn[3], "off"); + break; + } + case FRICTION_ON: + { + Char_Draw(&UI_State_dyn[3], "sd3", UI_Graph_Change, 8, UI_Color_Pink, 15, 2, 270, 600); + Char_Write(&UI_State_dyn[3], "on "); + break; + } + } + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[3]); + _Interactive_data->Referee_Interactive_Flag.friction_flag = 0; + } + // lid + if (_Interactive_data->Referee_Interactive_Flag.lid_flag == 1) + { + switch (_Interactive_data->lid_mode) + { + case LID_CLOSE: + { + Char_Draw(&UI_State_dyn[4], "sd4", UI_Graph_Change, 8, UI_Color_Pink, 15, 2, 270, 550); + Char_Write(&UI_State_dyn[4], "close"); + break; + } + case LID_OPEN: + { + Char_Draw(&UI_State_dyn[4], "sd4", UI_Graph_Change, 8, UI_Color_Pink, 15, 2, 270, 550); + Char_Write(&UI_State_dyn[4], "open "); + break; + } + } + Char_ReFresh(&_referee_info->referee_id, UI_State_dyn[4]); + _Interactive_data->Referee_Interactive_Flag.lid_flag = 0; + } +} + + +/** + * @brief 模式切换检测,模式发生切换时,对flag置位 + * @param Referee_Interactive_info_t *_Interactive_data + * @retval none + * @attention + */ +static void Mode_Change_Check(Referee_Interactive_info_t *_Interactive_data) +{ + if (_Interactive_data->chassis_mode != _Interactive_data->chassis_last_mode) + { + _Interactive_data->Referee_Interactive_Flag.chassis_flag = 1; + _Interactive_data->chassis_last_mode = _Interactive_data->chassis_mode; + } + + if (_Interactive_data->gimbal_mode != _Interactive_data->gimbal_last_mode) + { + _Interactive_data->Referee_Interactive_Flag.gimbal_flag = 1; + _Interactive_data->gimbal_last_mode = _Interactive_data->gimbal_mode; + } + + if (_Interactive_data->shoot_mode != _Interactive_data->shoot_last_mode) + { + _Interactive_data->Referee_Interactive_Flag.shoot_flag = 1; + _Interactive_data->shoot_last_mode = _Interactive_data->shoot_mode; + } + + if (_Interactive_data->friction_mode != _Interactive_data->friction_last_mode) + { + _Interactive_data->Referee_Interactive_Flag.friction_flag = 1; + _Interactive_data->friction_last_mode = _Interactive_data->friction_mode; + } + + if (_Interactive_data->lid_mode != _Interactive_data->lid_last_mode) + { + _Interactive_data->Referee_Interactive_Flag.lid_flag = 1; + _Interactive_data->lid_last_mode = _Interactive_data->lid_mode; + } +} + +/** + * @brief 判断各种ID,选择客户端ID + * @param referee_info_t *_referee_info + * @retval none + * @attention + */ +static void determine_ID(referee_info_t *_referee_info) +{ + // id小于7是红色,大于7是蓝色,0为红色,1为蓝色 #define Robot_Red 0 #define Robot_Blue 1 + _referee_info->referee_id.Robot_Color = _referee_info->GameRobotState.robot_id > 7 ? Robot_Blue : Robot_Red; + _referee_info->referee_id.Robot_ID = _referee_info->GameRobotState.robot_id; + _referee_info->referee_id.Cilent_ID = 0x0100 + _referee_info->referee_id.Robot_ID; // 计算客户端ID + _referee_info->referee_id.Receiver_Robot_ID = 0; +} + +/* 测试用函数 */ +static void UI_test_init(referee_info_t *_referee_info) +{ + Graph_Data_t graph[5]; + Graph_Data_t num[2]; + String_Data_t sdata[1]; + UI_Delete(&_referee_info->referee_id, UI_Data_Del_ALL, 0); + + Line_Draw(&graph[0],"s0",UI_Graph_ADD,0,UI_Color_White,3,710,540,1210,540); + Rectangle_Draw(&graph[1],"s1",UI_Graph_ADD,0,UI_Color_Yellow,4,600,200,800,500); + Circle_Draw(&graph[2],"s2",UI_Graph_ADD,0,UI_Color_Green,5,960,540,100); + Elliptical_Draw(&graph[3],"s3",UI_Graph_ADD,0,UI_Color_Orange,3,960,540,100,20); + Arc_Draw(&graph[4],"s4",UI_Graph_ADD,0,UI_Color_Purplish_red,30,160,3,1200,550,50,100); + + Float_Draw(&num[0],"s5",UI_Graph_ADD,0,UI_Color_Pink,50,3,5,1050,660,1245545); + Integer_Draw(&num[1],"s6",UI_Graph_ADD,0,UI_Color_Cyan,50,5,1050,460,12345); + UI_ReFresh(&_referee_info->referee_id,7,graph[0],graph[1],graph[2],graph[3],graph[4],num[0],num[1]); + + + Char_Draw(&sdata[0],"s7",UI_Graph_ADD,0,UI_Color_Green,20,2,620,710); + Char_Write(&sdata[0],"number:%d",123); + Char_ReFresh(&_referee_info->referee_id,sdata[0]); +} diff --git a/application/referee/referee.h b/application/referee/referee.h new file mode 100644 index 0000000..d531d5a --- /dev/null +++ b/application/referee/referee.h @@ -0,0 +1,50 @@ +#ifndef REFEREE_H +#define REFEREE_H + +#include "rm_referee.h" +#include "robot_def.h" +#pragma pack(1) + +//模式是否切换标志位,0为未切换,1为切换,static定义默认为0 +typedef struct +{ + uint32_t chassis_flag : 1; + uint32_t gimbal_flag : 1; + uint32_t shoot_flag : 1; + uint32_t lid_flag : 1; + uint32_t friction_flag : 1; + uint32_t Power_flag : 1; +} Referee_Interactive_Flag_t; + + +// 此结构体包含UI绘制与机器人车间通信的需要的其他非裁判系统数据 +typedef struct +{ + Referee_Interactive_Flag_t Referee_Interactive_Flag; + //为UI绘制以及交互数据所用 + Robot_Status_e Robot_Status;// 机器人状态 + App_Status_e App_Status;// 应用状态 + chassis_mode_e chassis_mode;//底盘模式 + gimbal_mode_e gimbal_mode;//云台模式 + shoot_mode_e shoot_mode;//发射模式设置 + friction_mode_e friction_mode;//摩擦轮关闭 + lid_mode_e lid_mode;//弹舱盖打开 + loader_mode_e loader_mode;//单发...连发 + Chassis_Power_Data_s Chassis_Power_Data;// 功率控制 + + chassis_mode_e chassis_last_mode;//底盘模式 + gimbal_mode_e gimbal_last_mode;//云台模式 + shoot_mode_e shoot_last_mode;//发射模式设置 + friction_mode_e friction_last_mode;//摩擦轮关闭 + lid_mode_e lid_last_mode;//弹舱盖打开 + +} Referee_Interactive_info_t; + +#pragma pack() + +void Referee_Interactive_init(void); +void Referee_Interactive_task(void); +#endif // REFEREE_H + + + diff --git a/application/referee/referee.md b/application/referee/referee.md new file mode 100644 index 0000000..7e97160 --- /dev/null +++ b/application/referee/referee.md @@ -0,0 +1,3 @@ +# referee + +需要将此模块移动到module层,并新建一个rtos任务,以一定频率运行,用于ui刷新和多机通信 diff --git a/application/robot.c b/application/robot.c index fb24f9f..6463b39 100644 --- a/application/robot.c +++ b/application/robot.c @@ -10,6 +10,7 @@ #if defined(ONE_BOARD) || defined(CHASSIS_BOARD) #include "chassis.h" +#include "referee.h" #endif #if defined(ONE_BOARD) || defined(GIMBAL_BOARD) @@ -34,7 +35,8 @@ void RobotInit() #endif #if defined(ONE_BOARD) || defined(CHASSIS_BOARD) - ChassisInit(); + // Referee_Interactive_init(); + ChassisInit(); #endif // 初始化完成,开启中断 __enable_irq(); @@ -50,5 +52,6 @@ void RobotTask() #if defined(ONE_BOARD) || defined(CHASSIS_BOARD) ChassisTask(); + // Referee_Interactive_task(); #endif } diff --git a/application/robot_def.h b/application/robot_def.h index f1532f7..f432ece 100644 --- a/application/robot_def.h +++ b/application/robot_def.h @@ -132,7 +132,6 @@ typedef enum // 功率限制,从裁判系统获取 typedef struct { // 功率控制 - } Chassis_Power_Data_s; /* ----------------CMD应用发布的控制数据,应当由gimbal/chassis/shoot订阅---------------- */ diff --git a/application/shoot/shoot.c b/application/shoot/shoot.c index c9cb2e3..14444be 100644 --- a/application/shoot/shoot.c +++ b/application/shoot/shoot.c @@ -1,5 +1,6 @@ #include "shoot.h" #include "robot_def.h" + #include "dji_motor.h" #include "message_center.h" #include "bsp_dwt.h" diff --git a/bsp/can/bsp_can.c b/bsp/can/bsp_can.c index 5d8c945..36066d1 100644 --- a/bsp/can/bsp_can.c +++ b/bsp/can/bsp_can.c @@ -6,6 +6,7 @@ /* can instance ptrs storage, used for recv callback */ // 在CAN产生接收中断会遍历数组,选出hcan和rxid与发生中断的实例相同的那个,调用其回调函数 +// @todo: 后续为每个CAN总线单独添加一个can_instance指针数组,提高回调查找的性能 static CANInstance *can_instance[CAN_MX_REGISTER_CNT] = {NULL}; static uint8_t idx; // 全局CAN实例索引,每次有新的模块注册会自增 diff --git a/bsp/log/bsp_log.c b/bsp/log/bsp_log.c index 815a59b..da35f0d 100644 --- a/bsp/log/bsp_log.c +++ b/bsp/log/bsp_log.c @@ -4,7 +4,6 @@ #include "SEGGER_RTT_Conf.h" #include -#define BUFFER_INDEX 0 void BSPLogInit() { @@ -15,7 +14,7 @@ int PrintLog(const char *fmt, ...) { va_list args; va_start(args, fmt); - int n = SEGGER_RTT_vprintf(BUFFER_INDEX, fmt, &args); + int n = SEGGER_RTT_vprintf(BUFFER_INDEX, fmt, &args); // 一次可以开启多个buffer(多个终端),我们只用一个 va_end(args); return n; } @@ -31,4 +30,5 @@ void Float2Str(char *str, float va) sprintf(str, "-%d.%d", head, point); else sprintf(str, "%d.%d", head, point); -} \ No newline at end of file +} + diff --git a/bsp/log/bsp_log.h b/bsp/log/bsp_log.h index b3d6e7a..d5f0b2a 100644 --- a/bsp/log/bsp_log.h +++ b/bsp/log/bsp_log.h @@ -1,18 +1,53 @@ #ifndef _BSP_LOG_H #define _BSP_LOG_H +#include "SEGGER_RTT.h" +#include "SEGGER_RTT_Conf.h" +#include + +#define BUFFER_INDEX 0 + + + /** - * @brief 初始化日志功能,在操作系统启动之前调用 + * @brief 日志系统初始化 * */ void BSPLogInit(); /** - * @brief 通过segger RTT打印日志,支持格式化输出,格式化输出的实现参考printf + * @brief 日志功能原型,供下面的LOGI,LOGW,LOGE等使用 * - * @param fmt - * @param ... - * @return int + */ +#define LOG_PROTO(type,color,format,...) \ + SEGGER_RTT_printf(BUFFER_INDEX," %s%s"format"\r\n%s", \ + color, \ + type, \ + ##__VA_ARGS__, \ + RTT_CTRL_RESET) + +/*------下面是日志输出的接口--------*/ + +/* 清屏 */ +#define LOG_CLEAR() SEGGER_RTT_WriteString(0, " "RTT_CTRL_CLEAR) + +/* 无颜色日志输出 */ +#define LOG(format,...) LOG_PROTO("","",format,##__VA_ARGS__) + +/* 有颜色格式日志输出,建议使用这些宏来输出日志 */ +// information level +#define LOGINFO(format,...) LOG_PROTO("I", RTT_CTRL_TEXT_BRIGHT_GREEN , format, ##__VA_ARGS__) +// warning level +#define LOGWARNING(format,...) LOG_PROTO("W", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__) +// error level +#define LOGERROR(format,...) LOG_PROTO("E", RTT_CTRL_TEXT_BRIGHT_RED , format, ##__VA_ARGS__) + +/** + * @brief 通过segger RTT打印日志,支持格式化输出,格式化输出的实现参考printf. + * + * @param fmt 格式字符串 + * @param ... 参数列表 + * @return int 打印的log字符数 */ int PrintLog(const char *fmt, ...); diff --git a/bsp/usart/bsp_usart.c b/bsp/usart/bsp_usart.c index c9edaee..f1c0a97 100644 --- a/bsp/usart/bsp_usart.c +++ b/bsp/usart/bsp_usart.c @@ -25,7 +25,7 @@ static USARTInstance *usart_instance[DEVICE_USART_CNT] = {NULL}; * * @param _instance instance owned by module,模块拥有的串口实例 */ -static void USARTServiceInit(USARTInstance *_instance) +void USARTServiceInit(USARTInstance *_instance) { HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size); // 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback() @@ -67,10 +67,11 @@ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size, break; default: while (1) - ; // illegal mode! check your code context! + ; // illegal mode! check your code context! 检查定义instance的代码上下文,可能出现指针越界 break; } } + /* 串口发送时,gstate会被设为BUSY_TX */ uint8_t USARTIsReady(USARTInstance *_instance) { @@ -83,6 +84,7 @@ uint8_t USARTIsReady(USARTInstance *_instance) return 1; } } + /** * @brief 每次dma/idle中断发生时,都会调用此函数.对于每个uart实例会调用对应的回调进行进一步的处理 * 例如:视觉协议解析/遥控器解析/裁判系统解析 diff --git a/bsp/usart/bsp_usart.h b/bsp/usart/bsp_usart.h index e7e9dcf..6cf0c00 100644 --- a/bsp/usart/bsp_usart.h +++ b/bsp/usart/bsp_usart.h @@ -10,6 +10,7 @@ // 模块回调函数,用于解析协议 typedef void (*usart_module_callback)(); +/* 发送模式枚举 */ typedef enum { USART_TRANSFER_NONE=0, @@ -37,17 +38,25 @@ typedef struct } USART_Init_Config_s; /** - * @brief 注册一个串口实例. + * @brief 注册一个串口实例,返回一个串口实例指针 * * @param init_config 传入串口初始化结构体 */ USARTInstance *USARTRegister(USART_Init_Config_s *init_config); +/** + * @brief 启动串口服务,需要传入一个usart实例.一般用于lost callback的情况(使用串口的模块daemon) + * + * @param _instance + */ +void USARTServiceInit(USARTInstance *_instance); + + /** * @brief 通过调用该函数可以发送一帧数据,需要传入一个usart实例,发送buff以及这一帧的长度 - * 当前默认为DMA发送,后续会增加中断发送和阻塞发送模式的选择 - * @todo 目前只支持DMA发送,后续会增加中断发送和阻塞发送模式的选择 - * 在短时间内连续调用此接口会导致上一次的发送未完成而新的发送取消,后续会增加一个发送状态的判断以及消息队列以解决这个问题 + * @note 在短时间内连续调用此接口,若采用IT/DMA会导致上一次的发送未完成而新的发送取消. + * @note 若希望连续使用DMA/IT进行发送,请配合USARTIsReady()使用,或自行为你的module实现一个发送队列和任务. + * @todo 是否考虑为USARTInstance增加发送队列以进行连续发送? * * @param _instance 串口实例 * @param send_buf 待发送数据的buffer @@ -55,4 +64,12 @@ USARTInstance *USARTRegister(USART_Init_Config_s *init_config); */ void USARTSend(USARTInstance *_instance, uint8_t *send_buf, uint16_t send_size,USART_TRANSFER_MODE mode); +/** + * @brief 判断串口是否准备好,用于连续或异步的IT/DMA发送 + * + * @param _instance 要判断的串口实例 + * @return uint8_t ready 1, busy 0 + */ +uint8_t USARTIsReady(USARTInstance *_instance); + #endif diff --git a/bsp/usb/bsp_usb.c b/bsp/usb/bsp_usb.c index aade217..f19c69b 100644 --- a/bsp/usb/bsp_usb.c +++ b/bsp/usb/bsp_usb.c @@ -11,20 +11,21 @@ #include "bsp_usb.h" -static uint8_t *bsp_usb_rx_buffer; // 接收到的数据会被放在这里,buffer size为2028 +static uint8_t *bsp_usb_rx_buffer; // 接收到的数据会被放在这里,buffer size为2048 // 注意usb单个数据包(Full speed模式下)最大为64byte,超出可能会出现丢包情况 // 这是传输完成的回调函数,在usbd_cdc_if.c中被调用 __weak void USBTransmitCpltCallback(uint32_t len) { - // 本次发送的数据 + // 本次发送的数据长度 UNUSED(len); // 传输完成会调用此函数,to do something } +// 这是接收回调函数 __weak void USBReceiveCpltCallback(uint32_t len) { - // 本次接收的数据 + // 本次接收的数据长度 UNUSED(len); // 传输完成会调用此函数,to do something } diff --git a/modules/master_machine/master_process.c b/modules/master_machine/master_process.c index 68fec8b..9cf70b4 100644 --- a/modules/master_machine/master_process.c +++ b/modules/master_machine/master_process.c @@ -12,12 +12,15 @@ #include "bsp_usart.h" #include "usart.h" #include "seasky_protocol.h" +#include "daemon.h" +#include "bsp_log.h" static Vision_Recv_s recv_data; // @todo:由于后续需要进行IMU-Cam的硬件触发采集控制,因此可能需要将发送设置为定时任务,或由IMU采集完成产生的中断唤醒的任务, // 后者显然更nice,使得时间戳对齐. 因此,在send_data中设定其他的标志位数据,让ins_task填充姿态值. // static Vision_Send_s send_data; static USARTInstance *vision_usart_instance; +static DaemonInstance *vision_daemon_instance; /** * @brief 接收解包回调函数,将在bsp_usart.c中被usart rx callback调用 @@ -27,9 +30,23 @@ static USARTInstance *vision_usart_instance; static void DecodeVision() { static uint16_t flag_register; + DaemonReload(vision_daemon_instance); // 喂狗 get_protocol_info(vision_usart_instance->recv_buff, &flag_register, (uint8_t *)&recv_data.pitch); - // TODO: code to resolve flag_register; + PrintLog("decode vision"); + +} + +/** + * @brief 离线回调函数,将在daemon.c中被daemon task调用 + * @attention 由于HAL库的设计问题,串口开启DMA接收之后同时发送有概率出现__HAL_LOCK()导致的死锁,使得无法 + * 进入接收中断.通过daemon判断数据更新,重新调用服务启动函数以解决此问题. + * + * @param id vision_usart_instance的地址,此处没用. + */ +static void VisionOfflineCallback(void *id) +{ + USARTServiceInit(vision_usart_instance); } /* 视觉通信初始化 */ @@ -39,8 +56,16 @@ Vision_Recv_s *VisionInit(UART_HandleTypeDef *_handle) conf.module_callback = DecodeVision; conf.recv_buff_size = VISION_RECV_SIZE; conf.usart_handle = _handle; - vision_usart_instance = USARTRegister(&conf); + + // 为master process注册daemon,用于判断视觉通信是否离线 + Daemon_Init_Config_s daemon_conf = { + .callback = VisionOfflineCallback, // 离线时调用的回调函数,会重启串口接收 + .owner_id = vision_usart_instance, + .reload_count = 10, + }; + vision_daemon_instance = DaemonRegister(&daemon_conf); + return &recv_data; } @@ -59,13 +84,11 @@ void VisionSend(Vision_Send_s *send) static uint8_t send_buff[VISION_SEND_SIZE]; static uint16_t tx_len; // TODO: code to set flag_register - flag_register = 0x0001; + // 将数据转化为seasky协议的数据包 get_protocol_send_data(0x02, flag_register, &send->yaw, 3, send_buff, &tx_len); - USARTSend(vision_usart_instance, send_buff, tx_len,USART_TRANSFER_IT); - // if(vision_usart_instance->usart_handle->ReceptionType == HAL_UART_RECEPTION_TOIDLE) - // {HAL_UARTEx_ReceiveToIdle_DMA(vision_usart_instance->usart_handle, vision_usart_instance->recv_buff, vision_usart_instance->recv_buff_size); - // __HAL_DMA_DISABLE_IT(vision_usart_instance->usart_handle->hdmarx, DMA_IT_HT); - - // } + USARTSend(vision_usart_instance, send_buff, tx_len, USART_TRANSFER_DMA); // 和视觉通信使用IT,防止和接收使用的DMA冲突 + // 此处为HAL设计的缺陷,DMASTOP会停止发送和接收,导致再也无法进入接收中断. + // 也可在发送完成中断中重新启动DMA接收,但较为复杂.因此,此处使用IT发送. + // 若使用了daemon,则也可以使用DMA发送. } \ No newline at end of file diff --git a/modules/master_machine/seasky_protocol.c b/modules/master_machine/seasky_protocol.c index 18502f7..85304dd 100644 --- a/modules/master_machine/seasky_protocol.c +++ b/modules/master_machine/seasky_protocol.c @@ -56,7 +56,7 @@ static uint8_t protocol_heade_Check(protocol_rm_struct *pro, uint8_t *rx_buf) pro->header.sof = rx_buf[0]; if (CRC8_Check_Sum(&rx_buf[0], 4)) { - pro->header.data_length = ((rx_buf[2] << 8) | rx_buf[1]); + pro->header.data_length = (rx_buf[2] << 8) | rx_buf[1]; pro->header.crc_check = rx_buf[3]; pro->cmd_id = (rx_buf[5] << 8) | rx_buf[4]; return 1; @@ -125,9 +125,9 @@ uint16_t get_protocol_info(uint8_t *rx_buf, // 接收到的原始数据 if (CRC16_Check_Sum(&rx_buf[0], date_length)) { *flags_register = (rx_buf[7] << 8) | rx_buf[6]; - memcpy(rx_data, rx_buf + 8, (pro.header.data_length - 2)); + memcpy(rx_data, rx_buf + 8, pro.header.data_length - 2); return pro.cmd_id; - } + } } return 0; } diff --git a/modules/message_center/message_center.c b/modules/message_center/message_center.c index 387c9fb..cc4e826 100644 --- a/modules/message_center/message_center.c +++ b/modules/message_center/message_center.c @@ -2,78 +2,11 @@ #include "stdlib.h" #include "string.h" -/* 消息初始化用 */ -static char pname[MAX_EVENT_COUNT][MAX_EVENT_NAME_LEN + 1]; -static char sname[MAX_EVENT_COUNT][MAX_EVENT_NAME_LEN + 1]; -static void *p_ptr[MAX_EVENT_COUNT] = {NULL}; -static void **s_pptr[MAX_EVENT_COUNT] = {NULL}; // 因为要修改指针,所以需要二重指针 - -/* ----------------------------------第三方指针传递版的实现,deprecated----------------------------------- */ - -void MessageInit() -{ - // pub必须唯一,即消息名称不能重复,不得有多个pub发布相同消息名称 - // 对每一个subscriber,寻找相同消息名称的publisher,可能有多个sub从相同pub获取消息 - for (size_t i = 0; i < MAX_EVENT_COUNT; ++i) - { - if (s_pptr[i] != NULL) - { - for (size_t j = 0; j < MAX_EVENT_COUNT; ++j) // 遍历publisher - { - if (p_ptr[j] != NULL) // 不为空 - { - if (strcmp(sname[i], pname[j]) == 0) // 比较消息名是否一致 - { - *s_pptr[i] = p_ptr[j]; // 将sub的指针指向pub的数据 - break; - } - } - else // 到结尾,退出 - { - while (1) - ; // 如果你卡在这里,说明没有找到消息发布者!请确认消息名称是否键入错误 - } - } - } - else // 说明已经遍历完所有的subs - { - break; - } - } -} - -/* 传入数据地址 */ -void PublisherRegister(char *name, void *data) -{ - static uint8_t idx; - for (size_t i = 0; i < idx; ++i) - { - if (strcmp(pname[i], name) == 0) - while (1) - ; // 运行至此说明pub的消息发布名称冲突 - } - strcpy(pname[idx], name); - p_ptr[idx++] = data; -} - -/* 注意传入的是指针的地址,传参时使用&对数据指针取地址 */ -void SubscribeEvent(char *name, void **data_ptr) -{ - static uint8_t idx; - strcpy(sname[idx], name); - s_pptr[idx++] = data_ptr; -} - - - -/* ----------------------------------链表-队列版的实现----------------------------------- */ - /* message_center是fake head node,是方便链表编写的技巧,这样就不需要处理链表头的特殊情况 */ static Publisher_t message_center = { .event_name = "Message_Manager", .first_subs = NULL, - .next_event_node = NULL - }; + .next_event_node = NULL}; static void CheckName(char *name) { @@ -112,18 +45,18 @@ Subscriber_t *SubRegister(char *name, uint8_t data_len) { // 给消息队列的每一个元素分配空间,queue里保存的实际上是数据执指针,这样可以兼容不同的数据长度 ret->queue[i] = malloc(sizeof(data_len)); } - //如果是第一个订阅者,特殊处理一下 - if(node->first_subs==NULL) + // 如果是第一个订阅者,特殊处理一下 + if (node->first_subs == NULL) { - node->first_subs=ret; + node->first_subs = ret; return ret; } // 遍历订阅者链表,直到到达尾部 Subscriber_t *sub = node->first_subs; // 作为iterator - while (sub->next_subs_queue) // 遍历订阅了该事件的订阅者链表 + while (sub->next_subs_queue) // 遍历订阅了该事件的订阅者链表 { sub = sub->next_subs_queue; // 移动到下一个订阅者,遇到空指针停下,说明到了链表尾部 - } + } sub->next_subs_queue = ret; // 把刚刚创建的订阅者接上 return ret; } @@ -157,6 +90,7 @@ Publisher_t *PubRegister(char *name, uint8_t data_len) if (strcmp(node->event_name, name) == 0) // 如果已经注册了相同的事件,直接返回结点指针 { CheckLen(data_len, node->data_len); + node->pub_registered_flag = 1; return node; } } // 遍历完发现尚未创建name对应的事件 @@ -165,6 +99,7 @@ Publisher_t *PubRegister(char *name, uint8_t data_len) memset(node->next_event_node, 0, sizeof(Publisher_t)); node->next_event_node->data_len = data_len; strcpy(node->next_event_node->event_name, name); + node->pub_registered_flag = 1; return node->next_event_node; } @@ -189,7 +124,7 @@ uint8_t PubPushMessage(Publisher_t *pub, void *data_ptr) while (iter) { if (iter->temp_size == QUEUE_SIZE) // 如果队列已满,则需要删除最老的数据(头部),再填入 - { + { // 队列头索引前移动,相当于抛弃前一个位置的数据,被抛弃的位置稍后会被写入新的数据 iter->front_idx = (iter->front_idx + 1) % QUEUE_SIZE; iter->temp_size--; // 相当于出队,size-1 diff --git a/modules/message_center/message_center.h b/modules/message_center/message_center.h index 44a3223..8335b4b 100644 --- a/modules/message_center/message_center.h +++ b/modules/message_center/message_center.h @@ -19,30 +19,6 @@ #define MAX_EVENT_COUNT 12 // 最多支持的事件数量 #define QUEUE_SIZE 1 -/** - * @brief 在所有应用初始化结束之后调用,当作app的"回调函数" - * - */ -void MessageInit(); - -/** - * @brief 注册成为消息发布者 - * - * @param name 消息标识名,注意不要超过MAX_EVENT_NAME_LEN - * @param data 发布者的数据地址 - */ -void PublisherRegister(char *name, void *data); - -/** - * @brief 订阅消息,成为消息订阅者 - * - * @param name 消息标识名 - * @param data 保存数据的指针的地址,注意这是一个二级指针,传入的时候对数据指针取地址(&) - */ -void SubscribeEvent(char *name, void **data); - -#endif // !PUBSUB_H - typedef struct mqt { /* 用数组模拟FIFO队列 */ @@ -69,6 +45,7 @@ typedef struct ent Subscriber_t *first_subs; /* 指向下一个Publisher的指针 */ struct ent *next_event_node; + uint8_t pub_registered_flag; // 用于标记该发布者是否已经注册 } Publisher_t; /** @@ -105,3 +82,5 @@ uint8_t SubGetMessage(Subscriber_t *sub, void *data_ptr); * @return uint8_t 新消息成功推送给几个订阅者 */ uint8_t PubPushMessage(Publisher_t *pub, void *data_ptr); + +#endif // !PUBSUB_H \ No newline at end of file diff --git a/modules/motor/DJImotor/dji_motor.c b/modules/motor/DJImotor/dji_motor.c index 06952d8..40da6b5 100644 --- a/modules/motor/DJImotor/dji_motor.c +++ b/modules/motor/DJImotor/dji_motor.c @@ -1,5 +1,6 @@ #include "dji_motor.h" #include "general_def.h" +#include "bsp_log.h" static uint8_t idx = 0; // register idx,是该文件的全局电机索引,在注册时使用 @@ -32,16 +33,6 @@ static CANInstance sender_assignment[6] = { */ static uint8_t sender_enable_flag[6] = {0}; -/** - * @brief 当注册的电机id冲突时,会进入这个函数并提示冲突的ID - * @todo 通过segger jlink 发送日志 - */ -static void IDcrash_Handler(uint8_t conflict_motor_idx, uint8_t temp_motor_idx) -{ - while (1) - ; -} - /** * @brief 根据电调/拨码开关上的ID,根据说明书的默认id分配方式计算发送ID和接收ID, * 并对电机进行分组以便处理多电机控制命令 @@ -79,7 +70,10 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf for (size_t i = 0; i < idx; ++i) { if (dji_motor_instance[i]->motor_can_instance->can_handle == config->can_handle && dji_motor_instance[i]->motor_can_instance->rx_id == config->rx_id) - IDcrash_Handler(i, idx); + { + LOGERROR("[dji_motor] ID crash. Check in debug mode, add dji_motor_instance to watch to get more information."); // 后续可以把id和CAN打印出来 + while (1); // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8) (1!5!,LTC! (((不是) + } } break; @@ -103,7 +97,10 @@ static void MotorSenderGrouping(DJIMotorInstance *motor, CAN_Init_Config_s *conf for (size_t i = 0; i < idx; ++i) { if (dji_motor_instance[i]->motor_can_instance->can_handle == config->can_handle && dji_motor_instance[i]->motor_can_instance->rx_id == config->rx_id) - IDcrash_Handler(i, idx); + { + LOGERROR("[dji_motor] ID crash. Check in debug mode, add dji_motor_instance to watch to get more information."); + while (1); // 6020的id 1-4和2006/3508的id 5-8会发生冲突(若有注册,即1!5,2!6,3!7,4!8) + } } break; @@ -126,6 +123,7 @@ static void DecodeDJIMotor(CANInstance *_instance) static DJI_Motor_Measure_s *measure; rxbuff = _instance->rx_buff; // 这里对can instance的id进行了强制转换,从而获得电机的instance实例地址 + // _instance指针指向的id是对应电机instance的地址,通过强制转换为电机instance的指针,再通过->运算符访问电机的成员motor_measure,最后取地址获得指针 measure = &(((DJIMotorInstance *)_instance->id)->motor_measure); // measure要多次使用,保存指针减小访存开销 // 解析数据并对电流和速度进行滤波,电机的反馈报文具体格式见电机说明手册 @@ -138,7 +136,7 @@ static void DecodeDJIMotor(CANInstance *_instance) CURRENT_SMOOTH_COEF * (float)((int16_t)(rxbuff[4] << 8 | rxbuff[5])); measure->temperate = rxbuff[6]; - // 多圈角度计算,前提是两次采样间电机转过的角度小于180°,高速转动时可能会出现问题,自己画个图就清楚计算过程了 + // 多圈角度计算,前提是假设两次采样间电机转过的角度小于180°,自己画个图就清楚计算过程了 if (measure->ecd - measure->last_ecd > 4096) measure->total_round--; else if (measure->ecd - measure->last_ecd < -4096) @@ -190,8 +188,7 @@ void DJIMotorChangeFeed(DJIMotorInstance *motor, Closeloop_Type_e loop, Feedback } else { - while (1) - ; // LOOP TYPE ERROR!!!检查是否传入了正确的LOOP类型,或发生了指针越界 + LOGERROR("[dji_motor] loop type error, check memory access and func param");// 检查是否传入了正确的LOOP类型,或发生了指针越界 } } diff --git a/modules/motor/DJImotor/dji_motor.md b/modules/motor/DJImotor/dji_motor.md index 7c936e7..6e1dfda 100644 --- a/modules/motor/DJImotor/dji_motor.md +++ b/modules/motor/DJImotor/dji_motor.md @@ -7,6 +7,10 @@ > 1. 给不同的电机设置不同的低通滤波器惯性系数而不是统一使用宏 > 2. 为M2006和M3508增加开环的零位校准函数 +--- + +> 建议将电机的反馈频率通过RoboMaster Assistant统一设置为500Hz。当前默认的`MotorTask()`执行频率为500Hz,若不修改电机反馈频率可能导致单条总线挂载的电机数量有限,且容易出现帧错误和仲裁失败的情况。 + ## 总览和封装说明 > 如果你不需要理解该模块的工作原理,你只需要查看这一小节。 @@ -19,7 +23,7 @@ dji_motor模块对DJI智能电机,包括M2006,M3508以及GM6020进行了详 2. ==速度环为角速度,单位为**度/每秒**(deg/sec)== -3. ==电流环为mA== +3. ==电流环为A== 4. ==GM6020的输入设定为**力矩**,待测量(-30000~30000)== diff --git a/modules/motor/LKmotor/LK9025.c b/modules/motor/LKmotor/LK9025.c index 24e1795..a8b6f43 100644 --- a/modules/motor/LKmotor/LK9025.c +++ b/modules/motor/LKmotor/LK9025.c @@ -16,7 +16,7 @@ static void LKMotorDecode(CANInstance *_instance) static LKMotor_Measure_t *measure; static uint8_t *rx_buff; rx_buff = _instance->rx_buff; - measure = &((LKMotorInstance *)_instance)->measure; // 通过caninstance保存的id获取对应的motorinstance + measure = &(((LKMotorInstance *)_instance->id)->measure); // 通过caninstance保存的id获取对应的motorinstance measure->last_ecd = measure->ecd; measure->ecd = (uint16_t)((rx_buff[7] << 8) | rx_buff[6]); @@ -59,7 +59,10 @@ LKMotorInstance *LKMotorInit(Motor_Init_Config_s *config) motor->motor_can_ins = CANRegister(&config->can_init_config); if (idx == 0) // 用第一个电机的can instance发送数据 + { sender_instance = motor->motor_can_ins; + sender_instance->tx_id = 0x280; // 修改tx_id为0x280,用于多电机发送,不用管其他LKMotorInstance的tx_id,它们仅作初始化用 + } LKMotorEnable(motor); lkmotor_instance[idx++] = motor; diff --git a/modules/motor/LKmotor/LK_motor.md b/modules/motor/LKmotor/LK_motor.md index f815d37..1c9e7f1 100644 --- a/modules/motor/LKmotor/LK_motor.md +++ b/modules/motor/LKmotor/LK_motor.md @@ -2,4 +2,6 @@ 这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值,详见LK电机的说明文档,由于电机实例已经自带三环PID计算,一般来说**我们能用到的只有单电机的力矩指令和多电机指令。** -注意LK电机在使用多电机发送的时候,只支持一条总线上至多4个电机,多电机模式下LK仅支持发送id 0x280为接收ID为0x140+id. \ No newline at end of file +注意LK电机在使用多电机发送的时候,只支持一条总线上至多4个电机,多电机模式下LK仅支持发送id 0x280为接收ID为0x140+id. + +要设置为多电机模式,请通过串口连接电机,并使用该文件夹下的LK motor tool.exe进行配置。 \ No newline at end of file diff --git a/modules/referee/crc_ref.c b/modules/referee/crc_ref.c index edc870b..b22b6ac 100644 --- a/modules/referee/crc_ref.c +++ b/modules/referee/crc_ref.c @@ -136,20 +136,18 @@ void Append_CRC8_Check_Sum( uint8_t *pchMessage, uint16_t dwLength) */ uint16_t Get_CRC16_Check_Sum(uint8_t *pchMessage,uint32_t dwLength,uint16_t wCRC) { - uint8_t chData; - - if (pchMessage == NULL) - { - return 0xFFFF; - } - while(dwLength--) - { - chData = *pchMessage++; - (wCRC) = ((uint16_t)(wCRC) >> 8) ^ - wCRC_Table[((uint16_t)(wCRC) ^ (uint16_t)(chData)) & 0x00ff]; - } - - return wCRC; + uint8_t chData; + if (pchMessage == NULL) + { + return 0xFFFF; + } + while(dwLength--) + { + chData = *pchMessage++; + (wCRC) = ((uint16_t)(wCRC) >> 8) ^ + wCRC_Table[((uint16_t)(wCRC) ^ (uint16_t)(chData)) & 0x00ff]; + } + return wCRC; } diff --git a/modules/referee/referee.h b/modules/referee/referee.h deleted file mode 100644 index b2238c9..0000000 --- a/modules/referee/referee.h +++ /dev/null @@ -1,357 +0,0 @@ -/** - * @file referee.h - * @author kidneygood (you@domain.com) - * @brief - * @version 0.1 - * @date 2022-11-18 - * - * @copyright Copyright (c) 2022 - * - */ - -#ifndef REFEREE_H -#define REFEREE_H - -#include "bsp_usart.h" -#include "usart.h" - -#define FALSE 0 -#define TRUE 1 - -#define JUDGE_DATA_ERROR 0 -#define JUDGE_DATA_CORRECT 1 - -#define LEN_HEADER 5 // 帧头长 -#define LEN_CMDID 2 // 命令码长度 -#define LEN_TAIL 2 // 帧尾CRC16 - -// 起始字节,协议固定为0xA5 -#define JUDGE_FRAME_HEADER (0xA5) - -#pragma pack(1) - -/***************裁判系统数据接收********************/ - -typedef enum -{ - FRAME_HEADER = 0, - CMD_ID = 5, - DATA = 7, -} JudgeFrameOffset; - -// 5字节帧头,偏移位置 -typedef enum -{ - SOF = 0, // 起始位 - DATA_LENGTH = 1, // 帧内数据长度,根据这个来获取数据长度 - SEQ = 3, // 包序号 - CRC8 = 4 // CRC8 - -} FrameHeaderOffset; - -// 命令码ID,用来判断接收的是什么数据 -typedef enum -{ - ID_game_state = 0x0001, // 比赛状态数据 - ID_game_result = 0x0002, // 比赛结果数据 - ID_game_robot_survivors = 0x0003, // 比赛机器人血量数据 - ID_event_data = 0x0101, // 场地事件数据 - ID_supply_projectile_action = 0x0102, // 场地补给站动作标识数据 - ID_supply_projectile_booking = 0x0103, // 场地补给站预约子弹数据 - ID_game_robot_state = 0x0201, // 机器人状态数据 - ID_power_heat_data = 0x0202, // 实时功率热量数据 - ID_game_robot_pos = 0x0203, // 机器人位置数据 - ID_buff_musk = 0x0204, // 机器人增益数据 - ID_aerial_robot_energy = 0x0205, // 空中机器人能量状态数据 - ID_robot_hurt = 0x0206, // 伤害状态数据 - ID_shoot_data = 0x0207, // 实时射击数据 - -} CmdID; - -// 命令码数据段长,根据官方协议来定义长度 -typedef enum -{ - LEN_game_state = 3, // 0x0001 - LEN_game_result = 1, // 0x0002 - LEN_game_robot_HP = 2, // 0x0003 - LEN_event_data = 4, // 0x0101 - LEN_supply_projectile_action = 4, // 0x0102 - // LEN_game_robot_state = 15, // 0x0201 - LEN_game_robot_state = 27, // 0x0201 - LEN_power_heat_data = 14, // 0x0202 - LEN_game_robot_pos = 16, // 0x0203 - LEN_buff_musk = 1, // 0x0204 - LEN_aerial_robot_energy = 1, // 0x0205 - LEN_robot_hurt = 1, // 0x0206 - LEN_shoot_data = 7, // 0x0207 -} JudgeDataLength; - -/* 自定义帧头 */ -typedef struct -{ - uint8_t SOF; - uint16_t DataLength; - uint8_t Seq; - uint8_t CRC8; - -} xFrameHeader; - -/* ID: 0x0001 Byte: 3 比赛状态数据 */ -typedef struct -{ - uint8_t game_type : 4; - uint8_t game_progress : 4; - uint16_t stage_remain_time; -} ext_game_state_t; - -/* ID: 0x0002 Byte: 1 比赛结果数据 */ -typedef struct -{ - uint8_t winner; -} ext_game_result_t; - -/* ID: 0x0003 Byte: 32 比赛机器人血量数据 */ -typedef struct -{ - uint16_t red_1_robot_HP; - uint16_t red_2_robot_HP; - uint16_t red_3_robot_HP; - uint16_t red_4_robot_HP; - uint16_t red_5_robot_HP; - uint16_t red_7_robot_HP; - uint16_t red_outpost_HP; - uint16_t red_base_HP; - uint16_t blue_1_robot_HP; - uint16_t blue_2_robot_HP; - uint16_t blue_3_robot_HP; - uint16_t blue_4_robot_HP; - uint16_t blue_5_robot_HP; - uint16_t blue_7_robot_HP; - uint16_t blue_outpost_HP; - uint16_t blue_base_HP; -} ext_game_robot_HP_t; - -/* ID: 0x0101 Byte: 4 场地事件数据 */ -typedef struct -{ - uint32_t event_type; -} ext_event_data_t; - -/* ID: 0x0102 Byte: 3 场地补给站动作标识数据 */ -typedef struct -{ - uint8_t supply_projectile_id; - uint8_t supply_robot_id; - uint8_t supply_projectile_step; - uint8_t supply_projectile_num; -} ext_supply_projectile_action_t; - -/* ID: 0X0201 Byte: 27 机器人状态数据 */ -typedef struct -{ - uint8_t robot_id; - uint8_t robot_level; - uint16_t remain_HP; - uint16_t max_HP; - uint16_t shooter_id1_17mm_cooling_rate; - uint16_t shooter_id1_17mm_cooling_limit; - uint16_t shooter_id1_17mm_speed_limit; - uint16_t shooter_id2_17mm_cooling_rate; - uint16_t shooter_id2_17mm_cooling_limit; - uint16_t shooter_id2_17mm_speed_limit; - uint16_t shooter_id1_42mm_cooling_rate; - uint16_t shooter_id1_42mm_cooling_limit; - uint16_t shooter_id1_42mm_speed_limit; - uint16_t chassis_power_limit; - uint8_t mains_power_gimbal_output : 1; - uint8_t mains_power_chassis_output : 1; - uint8_t mains_power_shooter_output : 1; -} ext_game_robot_state_t; - -/* ID: 0X0202 Byte: 14 实时功率热量数据 */ -typedef struct -{ - uint16_t chassis_volt; - uint16_t chassis_current; - float chassis_power; // 瞬时功率 - uint16_t chassis_power_buffer; // 60焦耳缓冲能量 - uint16_t shooter_heat0; // 17mm - uint16_t shooter_heat1; -} ext_power_heat_data_t; - -/* ID: 0x0203 Byte: 16 机器人位置数据 */ -typedef struct -{ - float x; - float y; - float z; - float yaw; -} ext_game_robot_pos_t; - -/* ID: 0x0204 Byte: 1 机器人增益数据 */ -typedef struct -{ - uint8_t power_rune_buff; -} ext_buff_musk_t; - -/* ID: 0x0205 Byte: 1 空中机器人能量状态数据 */ -typedef struct -{ - uint8_t attack_time; -} aerial_robot_energy_t; - -/* ID: 0x0206 Byte: 1 伤害状态数据 */ -typedef struct -{ - uint8_t armor_id : 4; - uint8_t hurt_type : 4; -} ext_robot_hurt_t; - -/* ID: 0x0207 Byte: 7 实时射击数据 */ -typedef struct -{ - uint8_t bullet_type; - uint8_t shooter_id; - uint8_t bullet_freq; - float bullet_speed; -} ext_shoot_data_t; - -/***************裁判系统数据********************/ -/* - - 交互数据,包括一个统一的数据段头结构, - 包含了内容 ID,发送者以及接受者的 ID 和内容数据段, - 整个交互数据的包总共长最大为 128 个字节, - 减去 frame_header,cmd_id,frame_tail 以及数据段头结构的 6 个字节, - 故而发送的内容数据段最大为 113。 - 整个交互数据 0x0301 的包上行频率为 10Hz。 - - 机器人 ID: - 1,英雄(红); - 2,工程(红); - 3/4/5,步兵(红); - 6,空中(红); - 7,哨兵(红); - 11,英雄(蓝); - 12,工程(蓝); - 13/14/15,步兵(蓝); - 16,空中(蓝); - 17,哨兵(蓝)。 - 客户端 ID: - 0x0101 为英雄操作手客户端( 红) ; - 0x0102 ,工程操作手客户端 ((红 ); - 0x0103/0x0104/0x0105,步兵操作手客户端(红); - 0x0106,空中操作手客户端((红); - 0x0111,英雄操作手客户端(蓝); - 0x0112,工程操作手客户端(蓝); - 0x0113/0x0114/0x0115,操作手客户端步兵(蓝); - 0x0116,空中操作手客户端(蓝)。 -*/ -/* 交互数据接收信息:0x0301 */ -typedef struct -{ - uint16_t data_cmd_id; - uint16_t send_ID; - uint16_t receiver_ID; -} ext_student_interactive_header_data_t; - -/* - 客户端 客户端自定义数据:cmd_id:0x0301。内容 ID:0xD180 - 发送频率:上限 10Hz - 1. 客户端 客户端自定义数据:cmd_id:0x0301。内容 ID:0xD180。发送频率:上限 10Hz - 字节偏移量 大小 说明 备注 - 0 2 数据的内容 ID 0xD180 - 2 2 送者的 ID 需要校验发送者机器人的 ID 正确性 - 4 2 客户端的 ID 只能为发送者机器人对应的客户端 - 6 4 自定义浮点数据 1 - 10 4 自定义浮点数据 2 - 14 4 自定义浮点数据 3 - 18 1 自定义 8 位数据 4 - -*/ -typedef struct -{ - float data1; - float data2; - float data3; - uint8_t masks; -} client_custom_data_t; - -/* - 学生机器人间通信 cmd_id 0x0301,内容 ID:0x0200~0x02FF - 交互数据 机器人间通信:0x0301。 - 发送频率:上限 10Hz - - 字节偏移量 大小 说明 备注 - 0 2 数据的内容 ID 0x0200~0x02FF - 可以在以上 ID 段选取,具体 ID 含义由参赛队自定义 - - 2 2 发送者的 ID 需要校验发送者的 ID 正确性, - 4 2 接收者的 ID 需要校验接收者的 ID 正确性, - 例如不能发送到敌对机器人的ID - 6 n 数据段 n 需要小于 113 - -*/ -typedef struct -{ - uint8_t data[10]; // 数据段,n需要小于113 -} robot_interactive_data_t; - -// 发送给客户端的信息 -// 帧头 命令码 数据段头结构 数据段 帧尾 -typedef struct -{ - xFrameHeader txFrameHeader; // 帧头 - uint16_t CmdID; // 命令码 - ext_student_interactive_header_data_t dataFrameHeader; // 数据段头结构 - client_custom_data_t clientData; // 数据段 - uint16_t FrameTail; // 帧尾 -} ext_SendClientData_t; - -// 机器人交互信息 -typedef struct -{ - xFrameHeader txFrameHeader; // 帧头 - uint16_t CmdID; // 命令码 - ext_student_interactive_header_data_t dataFrameHeader; // 数据段头结构 - robot_interactive_data_t interactData; // 数据段 - uint16_t FrameTail; // 帧尾 -} ext_CommunatianData_t; - -// 裁判系统接收数据整合进一个结构体 -typedef struct -{ - xFrameHeader FrameHeader; // 接收到的帧头信息 - uint16_t CmdID; - ext_game_state_t GameState; // 0x0001 - ext_game_result_t GameResult; // 0x0002 - ext_game_robot_HP_t GameRobotHP; // 0x0003 - ext_event_data_t EventData; // 0x0101 - ext_supply_projectile_action_t SupplyProjectileAction; // 0x0102 - ext_game_robot_state_t GameRobotStat; // 0x0201 - ext_power_heat_data_t PowerHeatData; // 0x0202 - ext_game_robot_pos_t GameRobotPos; // 0x0203 - ext_buff_musk_t BuffMusk; // 0x0204 - aerial_robot_energy_t AerialRobotEnergy; // 0x0205 - ext_robot_hurt_t RobotHurt; // 0x0206 - ext_shoot_data_t ShootData; // 0x0207 - - ext_SendClientData_t ShowData; // 客户端信息 - ext_CommunatianData_t CommuData; // 队友通信信息 - -} referee_info_t; - -#pragma pack() - -/** - * @brief 初始化裁判系统,返回接收数据指针 - * - * @param referee_usart_handle - * @return referee_info_t* - */ -referee_info_t* RefereeInit(UART_HandleTypeDef *referee_usart_handle); - - - - -#endif // !REFEREE_H diff --git a/modules/referee/referee.md b/modules/referee/referee.md index e69de29..d818026 100644 --- a/modules/referee/referee.md +++ b/modules/referee/referee.md @@ -0,0 +1,3 @@ +# referee + +当前模块组织较为混乱,后续统一为多机通信+裁判系统信息接收+UI绘制。UI绘制和多机通信的发送部分在referee任务中以一定的频率运行,信息的接收通过中断完成。 \ No newline at end of file diff --git a/modules/referee/referee_UI.c b/modules/referee/referee_UI.c index e69de29..2dc3e36 100644 --- a/modules/referee/referee_UI.c +++ b/modules/referee/referee_UI.c @@ -0,0 +1,433 @@ +/** + * @file referee_UI.C + * @author kidneygood (you@domain.com) + * @brief + * @version 0.1 + * @date 2023-1-18 + * + * @copyright Copyright (c) 2022 + * + */ +#include "referee_UI.h" +#include "string.h" +#include "crc.h" +#include "stdio.h" +#include "rm_referee.h" + +// 包序号 +/********************************************删除操作************************************* +**参数:_id 对应的id结构体 + Del_Operate 对应头文件删除操作 + Del_Layer 要删除的层 取值0-9 +*****************************************************************************************/ +void UI_Delete(referee_id_t *_id, uint8_t Del_Operate, uint8_t Del_Layer) +{ + UI_delete_t UI_delete_data; + uint8_t temp_datalength = Interactive_Data_LEN_Head + UI_Operate_LEN_Del; // 计算交互数据长度 + + UI_delete_data.FrameHeader.SOF = REFEREE_SOF; + UI_delete_data.FrameHeader.DataLength = temp_datalength; + UI_delete_data.FrameHeader.Seq = UI_Seq; + UI_delete_data.FrameHeader.CRC8 = Get_CRC8_Check_Sum((uint8_t *)&UI_delete_data, LEN_CRC8, 0xFF); + + UI_delete_data.CmdID = ID_student_interactive; + + UI_delete_data.datahead.data_cmd_id = UI_Data_ID_Del; + UI_delete_data.datahead.receiver_ID = _id->Cilent_ID; + UI_delete_data.datahead.sender_ID = _id->Robot_ID; + + UI_delete_data.Delete_Operate = Del_Operate; // 删除操作 + UI_delete_data.Layer = Del_Layer; + + UI_delete_data.frametail = Get_CRC16_Check_Sum((uint8_t *)&UI_delete_data, LEN_HEADER + LEN_CMDID + temp_datalength, 0xFFFF); + /* 填入0xFFFF,关于crc校验 */ + + RefereeSend((uint8_t *)&UI_delete_data, LEN_HEADER + LEN_CMDID + temp_datalength + LEN_TAIL); // 发送 + + UI_Seq++; // 包序号+1 +} +/************************************************绘制直线************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Width 图形线宽 + Start_x、Start_y 起点xy坐标 + End_x、End_y 终点xy坐标 +**********************************************************************************************************/ + +void Line_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, uint32_t End_x, uint32_t End_y) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) // 填充至‘0’为止 + { + graph->graphic_name[2 - i] = graphname[i]; // 按内存地址增大方向填充,所以会有i与2-i + } + + graph->operate_tpye = Graph_Operate; + graph->graphic_tpye = UI_Graph_Line; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->start_angle = 0; + graph->end_angle = 0; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = 0; + graph->end_x = End_x; + graph->end_y = End_y; +} + +/************************************************绘制矩形************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Width 图形线宽 + Start_x、Start_y 起点xy坐标 + End_x、End_y 对角顶点xy坐标 +**********************************************************************************************************/ +void Rectangle_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, uint32_t End_x, uint32_t End_y) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + + graph->graphic_tpye = UI_Graph_Rectangle; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->start_angle = 0; + graph->end_angle = 0; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = 0; + graph->end_x = End_x; + graph->end_y = End_y; +} + +/************************************************绘制整圆************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Width 图形线宽 + Start_x、Start_y 圆心xy坐标 + Graph_Radius 圆形半径 +**********************************************************************************************************/ + +void Circle_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, uint32_t Graph_Radius) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + + graph->graphic_tpye = UI_Graph_Circle; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->start_angle = 0; + graph->end_angle = 0; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = Graph_Radius; + graph->end_x = 0; + graph->end_y = 0; +} +/************************************************绘制椭圆************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Width 图形线宽 + Start_x、Start_y 圆心xy坐标 + End_x、End_y xy半轴长度 +**********************************************************************************************************/ +void Elliptical_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, uint32_t end_x, uint32_t end_y) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + + graph->graphic_tpye = UI_Graph_Ellipse; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + graph->width = Graph_Width; + + graph->start_angle = 0; + graph->end_angle = 0; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = 0; + graph->end_x = end_x; + graph->end_y = end_y; +} + +/************************************************绘制圆弧************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_StartAngle,Graph_EndAngle 起始终止角度 + Graph_Width 图形线宽 + Start_y,Start_y 圆心xy坐标 + x_Length,y_Length xy半轴长度 +**********************************************************************************************************/ + +void Arc_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_StartAngle, uint32_t Graph_EndAngle, uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, + uint32_t end_x, uint32_t end_y) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + + graph->graphic_tpye = UI_Graph_Arc; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->start_angle = Graph_StartAngle; + graph->end_angle = Graph_EndAngle; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = 0; + graph->end_x = end_x; + graph->end_y = end_y; +} + +/************************************************绘制浮点型数据************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Size 字号 + Graph_Digit 小数位数 + Graph_Width 图形线宽 + Start_x、Start_y 开始坐标 + radius=a&0x3FF; a为浮点数乘以1000后的32位整型数 + end_x=(a>>10)&0x7FF; + end_y=(a>>21)&0x7FF; +**********************************************************************************************************/ + +void Float_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Size, uint32_t Graph_Digit, uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, int32_t Graph_Float) +{ + + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + graph->graphic_tpye = UI_Graph_Float; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->start_angle = Graph_Size; + graph->end_angle = Graph_Digit; + + graph->radius = Graph_Float & 0x3FF; + graph->end_x = (Graph_Float >> 10) & 0x7FF; + graph->end_y = (Graph_Float >> 21) & 0x7FF; +} + +/************************************************绘制整型数据************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Size 字号 + Graph_Width 图形线宽 + Start_x、Start_y 开始坐标 + radius=a&0x3FF; a为32位整型数 + end_x=(a>>10)&0x7FF; + end_y=(a>>21)&0x7FF; +**********************************************************************************************************/ +void Integer_Draw(Graph_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Size, uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y, int32_t Graph_Integer) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->graphic_name[2 - i] = graphname[i]; + } + graph->graphic_tpye = UI_Graph_Int; + graph->operate_tpye = Graph_Operate; + graph->layer = Graph_Layer; + graph->color = Graph_Color; + + graph->start_angle = Graph_Size; + graph->end_angle = 0; + graph->width = Graph_Width; + graph->start_x = Start_x; + graph->start_y = Start_y; + graph->radius = Graph_Integer & 0x3FF; + graph->end_x = (Graph_Integer >> 10) & 0x7FF; + graph->end_y = (Graph_Integer >> 21) & 0x7FF; +} + +/************************************************绘制字符型数据************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + graphname[3] 图片名称,用于标识更改 + Graph_Operate 图片操作,见头文件 + Graph_Layer 图层0-9 + Graph_Color 图形颜色 + Graph_Size 字号 + Graph_Width 图形线宽 + Start_x、Start_y 开始坐标 +**********************************************************************************************************/ +void Char_Draw(String_Data_t *graph, char graphname[3], uint32_t Graph_Operate, uint32_t Graph_Layer, uint32_t Graph_Color, + uint32_t Graph_Size, uint32_t Graph_Width, uint32_t Start_x, uint32_t Start_y) +{ + int i; + for (i = 0; i < 3 && graphname[i] != '\0'; i++) + { + graph->Graph_Control.graphic_name[2 - i] = graphname[i]; + } + + graph->Graph_Control.graphic_tpye = UI_Graph_Char; + graph->Graph_Control.operate_tpye = Graph_Operate; + graph->Graph_Control.layer = Graph_Layer; + graph->Graph_Control.color = Graph_Color; + + graph->Graph_Control.width = Graph_Width; + graph->Graph_Control.start_x = Start_x; + graph->Graph_Control.start_y = Start_y; + graph->Graph_Control.start_angle = Graph_Size; + graph->Graph_Control.radius = 0; + graph->Graph_Control.end_x = 0; + graph->Graph_Control.end_y = 0; +} + +/************************************************绘制字符型数据************************************************* +**参数:*graph Graph_Data类型变量指针,用于存放图形数据 + fmt需要显示的字符串 + 此函数的实现和具体使用类似于printf函数 +**********************************************************************************************************/ +void Char_Write(String_Data_t *graph, char *fmt, ...) +{ + uint16_t i = 0; + va_list ap; + va_start(ap, fmt); + vsprintf((char *)graph->show_Data, fmt, ap); // 使用参数列表进行格式化并输出到字符串 + va_end(ap); + i = strlen((const char *)graph->show_Data); + graph->Graph_Control.end_angle = i; +} + +/* UI推送函数(使更改生效) + 参数: cnt 图形个数 + ... 图形变量参数 + Tips::该函数只能推送1,2,5,7个图形,其他数目协议未涉及 + */ +void UI_ReFresh(referee_id_t *_id, int cnt, ...) +{ + int i; + UI_GraphReFresh_t UI_GraphReFresh_data; + Graph_Data_t graphData; + + va_list ap; // 创建一个 va_list 类型变量 + va_start(ap, cnt); // 初始化 va_list 变量为一个参数列表 + + UI_GraphReFresh_data.FrameHeader.SOF = REFEREE_SOF; + UI_GraphReFresh_data.FrameHeader.DataLength = Interactive_Data_LEN_Head + cnt * UI_Operate_LEN_PerDraw; + UI_GraphReFresh_data.FrameHeader.Seq = UI_Seq; + UI_GraphReFresh_data.FrameHeader.CRC8 = Get_CRC8_Check_Sum((uint8_t *)&UI_GraphReFresh_data, LEN_CRC8, 0xFF); + + UI_GraphReFresh_data.CmdID = ID_student_interactive; + + switch (cnt) + { + case 1: + UI_GraphReFresh_data.datahead.data_cmd_id = UI_Data_ID_Draw1; + break; + case 2: + UI_GraphReFresh_data.datahead.data_cmd_id = UI_Data_ID_Draw2; + break; + case 5: + UI_GraphReFresh_data.datahead.data_cmd_id = UI_Data_ID_Draw5; + break; + case 7: + UI_GraphReFresh_data.datahead.data_cmd_id = UI_Data_ID_Draw7; + break; + } + + UI_GraphReFresh_data.datahead.receiver_ID = _id->Cilent_ID; + UI_GraphReFresh_data.datahead.sender_ID = _id->Robot_ID; + + // 先发送帧头、命令码、交互数据帧头三部分,并计算CRC16校验值 + UI_GraphReFresh_data.frametail = Get_CRC16_Check_Sum((uint8_t *)&UI_GraphReFresh_data, LEN_HEADER + LEN_CMDID + Interactive_Data_LEN_Head, 0xFFFF); + RefereeSend((uint8_t *)&UI_GraphReFresh_data, LEN_HEADER + LEN_CMDID + Interactive_Data_LEN_Head); + + for (i = 0; i < cnt; i++) // 发送交互数据的数据帧,并计算CRC16校验值 + { + graphData = va_arg(ap, Graph_Data_t); // 访问参数列表中的每个项,第二个参数是你要返回的参数的类型,在取值时需要将其强制转化为指定类型的变量 + // 发送并计算CRC16 + RefereeSend((uint8_t *)&graphData, UI_Operate_LEN_PerDraw); + UI_GraphReFresh_data.frametail = Get_CRC16_Check_Sum((uint8_t *)&graphData, UI_Operate_LEN_PerDraw, UI_GraphReFresh_data.frametail); + } + + RefereeSend((uint8_t *)&UI_GraphReFresh_data.frametail, LEN_TAIL); // 发送CRC16校验值 + + va_end(ap); // 结束可变参数的获取 + UI_Seq++; // 包序号+1 +} + +/************************************************UI推送字符(使更改生效)*********************************/ +void Char_ReFresh(referee_id_t *_id, String_Data_t string_Data) +{ + UI_CharReFresh_t UI_CharReFresh_data; + + uint8_t temp_datalength = Interactive_Data_LEN_Head + UI_Operate_LEN_DrawChar; // 计算交互数据长度 + + UI_CharReFresh_data.FrameHeader.SOF = REFEREE_SOF; + UI_CharReFresh_data.FrameHeader.DataLength = temp_datalength; + UI_CharReFresh_data.FrameHeader.Seq = UI_Seq; + UI_CharReFresh_data.FrameHeader.CRC8 = Get_CRC8_Check_Sum((uint8_t *)&UI_CharReFresh_data, LEN_CRC8, 0xFF); + + UI_CharReFresh_data.CmdID = ID_student_interactive; + + UI_CharReFresh_data.datahead.data_cmd_id = UI_Data_ID_DrawChar; + + UI_CharReFresh_data.datahead.receiver_ID = _id->Cilent_ID; + UI_CharReFresh_data.datahead.sender_ID = _id->Robot_ID; + + UI_CharReFresh_data.String_Data = string_Data; + + UI_CharReFresh_data.frametail = Get_CRC16_Check_Sum((uint8_t *)&UI_CharReFresh_data, LEN_HEADER + LEN_CMDID + temp_datalength, 0xFFFF); + + RefereeSend((uint8_t *)&UI_CharReFresh_data, LEN_HEADER + LEN_CMDID + temp_datalength + LEN_TAIL); // 发送 + + UI_Seq++; // 包序号+1 +} diff --git a/modules/referee/referee_UI.h b/modules/referee/referee_UI.h new file mode 100644 index 0000000..474b966 --- /dev/null +++ b/modules/referee/referee_UI.h @@ -0,0 +1,77 @@ +#ifndef REFEREE_UI_H +#define REFEREE_UI_H + +#include "stdarg.h" +#include "stdint.h" +#include "referee_def.h" +#include "rm_referee.h" + +#pragma pack(1) //按1字节对齐 + +/* 此处的定义只与UI绘制有关 */ +typedef struct +{ + xFrameHeader FrameHeader; + uint16_t CmdID; + ext_student_interactive_header_data_t datahead; + uint8_t Delete_Operate; //删除操作 + uint8_t Layer; + uint16_t frametail; +} UI_delete_t; + +typedef struct +{ + xFrameHeader FrameHeader; + uint16_t CmdID; + ext_student_interactive_header_data_t datahead; + uint16_t frametail; +} UI_GraphReFresh_t; + +typedef struct +{ + xFrameHeader FrameHeader; + uint16_t CmdID; + ext_student_interactive_header_data_t datahead; + String_Data_t String_Data; + uint16_t frametail; +} UI_CharReFresh_t; //打印字符串数据 + +#pragma pack() + + + + +void UI_Delete(referee_id_t *_id,uint8_t Del_Operate,uint8_t Del_Layer); + +void Line_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,uint32_t End_x,uint32_t End_y); + +void Rectangle_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,uint32_t End_x,uint32_t End_y); + +void Circle_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,uint32_t Graph_Radius); + +void Elliptical_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,uint32_t end_x,uint32_t end_y); + +void Arc_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_StartAngle,uint32_t Graph_EndAngle,uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y, + uint32_t end_x,uint32_t end_y); + +void Float_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Size,uint32_t Graph_Digit,uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,int32_t Graph_Float); + +void Integer_Draw(Graph_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Size,uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y,int32_t Graph_Integer); + +void Char_Draw(String_Data_t *graph,char graphname[3],uint32_t Graph_Operate,uint32_t Graph_Layer,uint32_t Graph_Color, + uint32_t Graph_Size,uint32_t Graph_Width,uint32_t Start_x,uint32_t Start_y); + +void Char_Write(String_Data_t *graph,char* fmt, ...); + +void UI_ReFresh(referee_id_t *_id,int cnt,...); + +void Char_ReFresh(referee_id_t *_id,String_Data_t string_Data); + +#endif diff --git a/modules/referee/referee_communication.c b/modules/referee/referee_communication.c index e69de29..d714605 100644 --- a/modules/referee/referee_communication.c +++ b/modules/referee/referee_communication.c @@ -0,0 +1,45 @@ +/** + * @file referee_communication.h + * @author kidneygood (you@domain.com) + * @version 0.1 + * @date 2022-12-02 + * + * @copyright Copyright (c) HNU YueLu EC 2022 all rights reserved + * + */ + +#include "referee_communication.h" +#include "crc.h" +#include "stdio.h" +#include "rm_referee.h" + +/** + * @brief 发送机器人间的交互数据 + * @param referee_id_t *_id sender为本机器人,receiver为接收方机器人,发送前设置Receiver_Robot_ID再调用该函数 + * robot_interactive_data_t *data 数据段 + * @retval none + * @attention + */ +void Communicate_SendData(referee_id_t *_id,robot_interactive_data_t *_data) +{ + Communicate_SendData_t SendData; + uint8_t temp_datalength = Interactive_Data_LEN_Head + Communicate_Data_LEN; // 计算交互数据长度 6+n,n为交互数据长度 + + SendData.FrameHeader.SOF = REFEREE_SOF; + SendData.FrameHeader.DataLength = temp_datalength; + SendData.FrameHeader.Seq = UI_Seq; + SendData.FrameHeader.CRC8 = Get_CRC8_Check_Sum((uint8_t *)&SendData, LEN_CRC8, 0xFF); + + SendData.CmdID = ID_student_interactive; + + SendData.datahead.data_cmd_id = Communicate_Data_ID; + SendData.datahead.sender_ID = _id->Robot_ID; + SendData.datahead.receiver_ID = _id->Receiver_Robot_ID; + + SendData.Data = *_data; + + SendData.frametail = Get_CRC16_Check_Sum((uint8_t *)&SendData,LEN_HEADER+LEN_CMDID+temp_datalength,0xFFFF); + + RefereeSend((uint8_t *)&SendData,LEN_HEADER+LEN_CMDID+temp_datalength+LEN_TAIL); //发送 + UI_Seq++; // 包序号+1 +} diff --git a/modules/referee/referee_communication.h b/modules/referee/referee_communication.h new file mode 100644 index 0000000..cba2324 --- /dev/null +++ b/modules/referee/referee_communication.h @@ -0,0 +1,10 @@ +#ifndef REFEREE_COMMUNICATION_H +#define REFEREE_COMMUNICATION_H + +#include "stdint.h" +#include "referee_def.h" +#include "rm_referee.h" + +void Communicate_SendData(referee_id_t *_id,robot_interactive_data_t *_data); + +#endif \ No newline at end of file diff --git a/modules/referee/referee_def.h b/modules/referee/referee_def.h new file mode 100644 index 0000000..4c2312f --- /dev/null +++ b/modules/referee/referee_def.h @@ -0,0 +1,397 @@ +/** + * @file referee_def.h + * @author kidneygood (you@domain.com) + * @version 0.1 + * @date 2022-12-02 + * + * @copyright Copyright (c) HNU YueLu EC 2022 all rights reserved + * + */ + +#ifndef REFEREE_DEF_H +#define REFEREE_DEF_H + +#include "stdint.h" + +/****************************宏定义部分****************************/ +/****************************宏定义部分****************************/ + +#define REFEREE_SOF 0xA5 // 起始字节,协议固定为0xA5 +#define Robot_Red 0 +#define Robot_Blue 1 +#define Communicate_Data_LEN 5 //自定义交互数据长度,该长度决定了我方发送和他方接收,自定义交互数据协议更改时只需要更改此宏定义即可 + +#pragma pack(1) + +/****************************通信协议格式****************************/ +/****************************通信协议格式****************************/ + +/* 通信协议格式偏移,枚举类型,代替#define声明 */ +typedef enum +{ + FRAME_HEADER_Offset = 0, + CMD_ID_Offset = 5, + DATA_Offset = 7, +} JudgeFrameOffset_e; + +/* 通信协议长度 */ +typedef enum +{ + LEN_HEADER = 5, // 帧头长 + LEN_CMDID = 2, // 命令码长度 + LEN_TAIL = 2, // 帧尾CRC16 + + LEN_CRC8 = 4, // 帧头CRC8校验长度=帧头+数据长+包序号 +} JudgeFrameLength_e; + +/****************************帧头****************************/ +/****************************帧头****************************/ + +/* 帧头偏移 */ +typedef enum +{ + SOF = 0, // 起始位 + DATA_LENGTH = 1, // 帧内数据长度,根据这个来获取数据长度 + SEQ = 3, // 包序号 + CRC8 = 4 // CRC8 +} FrameHeaderOffset_e; + +/* 帧头定义 */ +typedef struct +{ + uint8_t SOF; + uint16_t DataLength; + uint8_t Seq; + uint8_t CRC8; +} xFrameHeader; + +/****************************cmd_id命令码说明****************************/ +/****************************cmd_id命令码说明****************************/ + +/* 命令码ID,用来判断接收的是什么数据 */ +typedef enum +{ + ID_game_state = 0x0001, // 比赛状态数据 + ID_game_result = 0x0002, // 比赛结果数据 + ID_game_robot_survivors = 0x0003, // 比赛机器人血量数据 + ID_event_data = 0x0101, // 场地事件数据 + ID_supply_projectile_action = 0x0102, // 场地补给站动作标识数据 + ID_supply_projectile_booking = 0x0103, // 场地补给站预约子弹数据 + ID_game_robot_state = 0x0201, // 机器人状态数据 + ID_power_heat_data = 0x0202, // 实时功率热量数据 + ID_game_robot_pos = 0x0203, // 机器人位置数据 + ID_buff_musk = 0x0204, // 机器人增益数据 + ID_aerial_robot_energy = 0x0205, // 空中机器人能量状态数据 + ID_robot_hurt = 0x0206, // 伤害状态数据 + ID_shoot_data = 0x0207, // 实时射击数据 + ID_student_interactive = 0x0301, // 机器人间交互数据 +} CmdID_e; + +/* 命令码数据段长,根据官方协议来定义长度,还有自定义数据长度 */ +typedef enum +{ + LEN_game_state = 3, // 0x0001 + LEN_game_result = 1, // 0x0002 + LEN_game_robot_HP = 2, // 0x0003 + LEN_event_data = 4, // 0x0101 + LEN_supply_projectile_action = 4, // 0x0102 + LEN_game_robot_state = 27, // 0x0201 + LEN_power_heat_data = 14, // 0x0202 + LEN_game_robot_pos = 16, // 0x0203 + LEN_buff_musk = 1, // 0x0204 + LEN_aerial_robot_energy = 1, // 0x0205 + LEN_robot_hurt = 1, // 0x0206 + LEN_shoot_data = 7, // 0x0207 + LEN_receive_data = 6+Communicate_Data_LEN, //0x0301 + +} JudgeDataLength_e; + +/****************************接收数据的详细说明****************************/ +/****************************接收数据的详细说明****************************/ + +/* ID: 0x0001 Byte: 3 比赛状态数据 */ +typedef struct +{ + uint8_t game_type : 4; + uint8_t game_progress : 4; + uint16_t stage_remain_time; +} ext_game_state_t; + +/* ID: 0x0002 Byte: 1 比赛结果数据 */ +typedef struct +{ + uint8_t winner; +} ext_game_result_t; + +/* ID: 0x0003 Byte: 32 比赛机器人血量数据 */ +typedef struct +{ + uint16_t red_1_robot_HP; + uint16_t red_2_robot_HP; + uint16_t red_3_robot_HP; + uint16_t red_4_robot_HP; + uint16_t red_5_robot_HP; + uint16_t red_7_robot_HP; + uint16_t red_outpost_HP; + uint16_t red_base_HP; + uint16_t blue_1_robot_HP; + uint16_t blue_2_robot_HP; + uint16_t blue_3_robot_HP; + uint16_t blue_4_robot_HP; + uint16_t blue_5_robot_HP; + uint16_t blue_7_robot_HP; + uint16_t blue_outpost_HP; + uint16_t blue_base_HP; +} ext_game_robot_HP_t; + +/* ID: 0x0101 Byte: 4 场地事件数据 */ +typedef struct +{ + uint32_t event_type; +} ext_event_data_t; + +/* ID: 0x0102 Byte: 3 场地补给站动作标识数据 */ +typedef struct +{ + uint8_t supply_projectile_id; + uint8_t supply_robot_id; + uint8_t supply_projectile_step; + uint8_t supply_projectile_num; +} ext_supply_projectile_action_t; + +/* ID: 0X0201 Byte: 27 机器人状态数据 */ +typedef struct +{ + uint8_t robot_id; + uint8_t robot_level; + uint16_t remain_HP; + uint16_t max_HP; + uint16_t shooter_id1_17mm_cooling_rate; + uint16_t shooter_id1_17mm_cooling_limit; + uint16_t shooter_id1_17mm_speed_limit; + uint16_t shooter_id2_17mm_cooling_rate; + uint16_t shooter_id2_17mm_cooling_limit; + uint16_t shooter_id2_17mm_speed_limit; + uint16_t shooter_id1_42mm_cooling_rate; + uint16_t shooter_id1_42mm_cooling_limit; + uint16_t shooter_id1_42mm_speed_limit; + uint16_t chassis_power_limit; + uint8_t mains_power_gimbal_output : 1; + uint8_t mains_power_chassis_output : 1; + uint8_t mains_power_shooter_output : 1; +} ext_game_robot_state_t; + +/* ID: 0X0202 Byte: 14 实时功率热量数据 */ +typedef struct +{ + uint16_t chassis_volt; + uint16_t chassis_current; + float chassis_power; // 瞬时功率 + uint16_t chassis_power_buffer; // 60焦耳缓冲能量 + uint16_t shooter_heat0; // 17mm + uint16_t shooter_heat1; +} ext_power_heat_data_t; + +/* ID: 0x0203 Byte: 16 机器人位置数据 */ +typedef struct +{ + float x; + float y; + float z; + float yaw; +} ext_game_robot_pos_t; + +/* ID: 0x0204 Byte: 1 机器人增益数据 */ +typedef struct +{ + uint8_t power_rune_buff; +} ext_buff_musk_t; + +/* ID: 0x0205 Byte: 1 空中机器人能量状态数据 */ +typedef struct +{ + uint8_t attack_time; +} aerial_robot_energy_t; + +/* ID: 0x0206 Byte: 1 伤害状态数据 */ +typedef struct +{ + uint8_t armor_id : 4; + uint8_t hurt_type : 4; +} ext_robot_hurt_t; + +/* ID: 0x0207 Byte: 7 实时射击数据 */ +typedef struct +{ + uint8_t bullet_type; + uint8_t shooter_id; + uint8_t bullet_freq; + float bullet_speed; +} ext_shoot_data_t; + +/****************************机器人交互数据****************************/ +/****************************机器人交互数据****************************/ +/* 发送的内容数据段最大为 113 检测是否超出大小限制?实际上图形段不会超,数据段最多30个,也不会超*/ +/* 交互数据头结构 */ +typedef struct +{ + uint16_t data_cmd_id; //由于存在多个内容 ID,但整个cmd_id 上行频率最大为 10Hz,请合理安排带宽。注意交互部分的上行频率 + uint16_t sender_ID; + uint16_t receiver_ID; +} ext_student_interactive_header_data_t; + +/* 机器人id */ +typedef enum +{ + // 红方机器人ID + RobotID_RHero = 1, + RobotID_REngineer = 2, + RobotID_RStandard1 = 3, + RobotID_RStandard2 = 4, + RobotID_RStandard3 = 5, + RobotID_RAerial = 6, + RobotID_RSentry = 7, + RobotID_RRadar = 9, + // 蓝方机器人ID + RobotID_BHero = 101, + RobotID_BEngineer = 102, + RobotID_BStandard1 = 103, + RobotID_BStandard2 = 104, + RobotID_BStandard3 = 105, + RobotID_BAerial = 106, + RobotID_BSentry = 107, + RobotID_BRadar = 109, +} Robot_ID_e; + +/* 交互数据ID */ +typedef enum +{ + UI_Data_ID_Del = 0x100, + UI_Data_ID_Draw1 = 0x101, + UI_Data_ID_Draw2 = 0x102, + UI_Data_ID_Draw5 = 0x103, + UI_Data_ID_Draw7 = 0x104, + UI_Data_ID_DrawChar = 0x110, + + /* 自定义交互数据部分 */ + Communicate_Data_ID = 0x0200, + +} Interactive_Data_ID_e; +/* 交互数据长度 */ +typedef enum +{ + Interactive_Data_LEN_Head = 6, + UI_Operate_LEN_Del = 2, + UI_Operate_LEN_PerDraw = 15, + UI_Operate_LEN_DrawChar = 15 + 30, + + /* 自定义交互数据部分 */ + // Communicate_Data_LEN = 5, + +} Interactive_Data_Length_e; + +/****************************自定义交互数据****************************/ +/****************************自定义交互数据****************************/ +/* + 学生机器人间通信 cmd_id 0x0301,内容 ID:0x0200~0x02FF + 自定义交互数据 机器人间通信:0x0301。 + 发送频率:上限 10Hz +*/ +//自定义交互数据协议,可更改,更改后需要修改最上方宏定义数据长度的值 +typedef struct +{ + uint8_t data[Communicate_Data_LEN]; // 数据段,n需要小于113 +} robot_interactive_data_t; + +// 机器人交互信息_发送 +typedef struct +{ + xFrameHeader FrameHeader; + uint16_t CmdID; + ext_student_interactive_header_data_t datahead; + robot_interactive_data_t Data; // 数据段 + uint16_t frametail; +} Communicate_SendData_t; +// 机器人交互信息_接收 +typedef struct +{ + ext_student_interactive_header_data_t datahead; + robot_interactive_data_t Data; // 数据段 +} Communicate_ReceiveData_t; + + +/****************************UI交互数据****************************/ +/****************************UI交互数据****************************/ + +/* 图形数据 */ +typedef struct +{ + uint8_t graphic_name[3]; + uint32_t operate_tpye : 3; + uint32_t graphic_tpye : 3; + uint32_t layer : 4; + uint32_t color : 4; + uint32_t start_angle : 9; + uint32_t end_angle : 9; + uint32_t width : 10; + uint32_t start_x : 11; + uint32_t start_y : 11; + uint32_t radius : 10; + uint32_t end_x : 11; + uint32_t end_y : 11; +} Graph_Data_t; + +typedef struct +{ + Graph_Data_t Graph_Control; + uint8_t show_Data[30]; +} String_Data_t; // 打印字符串数据 + +/* 删除操作 */ +typedef enum +{ + UI_Data_Del_NoOperate = 0, + UI_Data_Del_Layer = 1, + UI_Data_Del_ALL = 2, // 删除全部图层,后面的参数已经不重要了。 +} UI_Delete_Operate_e; + +/* 图形配置参数__图形操作 */ +typedef enum +{ + UI_Graph_ADD = 1, + UI_Graph_Change = 2, + UI_Graph_Del = 3, +} UI_Graph_Operate_e; + +/* 图形配置参数__图形类型 */ +typedef enum +{ + UI_Graph_Line = 0, // 直线 + UI_Graph_Rectangle = 1, // 矩形 + UI_Graph_Circle = 2, // 整圆 + UI_Graph_Ellipse = 3, // 椭圆 + UI_Graph_Arc = 4, // 圆弧 + UI_Graph_Float = 5, // 浮点型 + UI_Graph_Int = 6, // 整形 + UI_Graph_Char = 7, // 字符型 + +} UI_Graph_Type_e; + +/* 图形配置参数__图形颜色 */ +typedef enum +{ + UI_Color_Main = 0, // 红蓝主色 + UI_Color_Yellow = 1, + UI_Color_Green = 2, + UI_Color_Orange = 3, + UI_Color_Purplish_red = 4, // 紫红色 + UI_Color_Pink = 5, + UI_Color_Cyan = 6, // 青色 + UI_Color_Black = 7, + UI_Color_White = 8, + +} UI_Graph_Color_e; + +#pragma pack() + +#endif diff --git a/modules/referee/referee.c b/modules/referee/rm_referee.c similarity index 57% rename from modules/referee/referee.c rename to modules/referee/rm_referee.c index 3abb972..7d5e329 100644 --- a/modules/referee/referee.c +++ b/modules/referee/rm_referee.c @@ -1,18 +1,56 @@ -#include "referee.h" +/** + * @file rm_referee.C + * @author kidneygood (you@domain.com) + * @brief + * @version 0.1 + * @date 2022-11-18 + * + * @copyright Copyright (c) 2022 + * + */ + +#include "rm_referee.h" #include "string.h" #include "crc_ref.h" #include "bsp_usart.h" -#include "dma.h" -// 参考深圳大学 Infantry_X-master #define RE_RX_BUFFER_SIZE 200 -static USARTInstance *referee_usart_instance; +static USARTInstance *referee_usart_instance; // 暂时改为非静态变量 -/**************裁判系统数据******************/ static referee_info_t referee_info; -static uint8_t Judge_Self_ID; // 当前机器人的ID -static uint16_t Judge_SelfClient_ID; // 发送者机器人对应的客户端ID +static void JudgeReadData(uint8_t *ReadFromUsart); +static void RefereeRxCallback(); + +uint8_t UI_Seq = 0; // 包序号,供整个referee文件使用 + +/* 裁判系统通信初始化 */ +referee_info_t *RefereeInit(UART_HandleTypeDef *referee_usart_handle) +{ + USART_Init_Config_s conf; + conf.module_callback = RefereeRxCallback; + conf.usart_handle = referee_usart_handle; + conf.recv_buff_size = RE_RX_BUFFER_SIZE; + referee_usart_instance = USARTRegister(&conf); + return &referee_info; +} + +/** + * @brief 发送函数 + * @param send 待发送数据 + */ +void RefereeSend(uint8_t *send, uint16_t tx_len) +{ + USARTSend(referee_usart_instance, send, tx_len,USART_TRANSFER_IT); + /* syhtodo DMA请求过快会导致数据发送丢失,考虑数据尽可能打成一个整包以及队列发送,并且发送函数添加缓冲区 */ + HAL_Delay(5); +} + +/*裁判系统串口接收回调函数,解析数据 */ +static void RefereeRxCallback() +{ + JudgeReadData(referee_usart_instance->recv_buff); +} /** * @brief 读取裁判数据,中断中读取保证速度 @@ -22,17 +60,15 @@ static uint16_t Judge_SelfClient_ID; // 发送者机器人对应的客户端ID */ static void JudgeReadData(uint8_t *ReadFromUsart) { - uint16_t judge_length; // 统计一帧数据长度 - // referee_info.CmdID = 0; //数据命令码解析 - // 空数据包,则不作任何处理 - if (ReadFromUsart == NULL) + uint16_t judge_length; // 统计一帧数据长度 + if (ReadFromUsart == NULL) // 空数据包,则不作任何处理 return; // 写入帧头数据(5-byte),用于判断是否开始存储裁判数据 memcpy(&referee_info.FrameHeader, ReadFromUsart, LEN_HEADER); // 判断帧头数据(0)是否为0xA5 - if (ReadFromUsart[SOF] == JUDGE_FRAME_HEADER) + if (ReadFromUsart[SOF] == REFEREE_SOF) { // 帧头CRC8校验 if (Verify_CRC8_Check_Sum(ReadFromUsart, LEN_HEADER) == TRUE) @@ -49,51 +85,43 @@ static void JudgeReadData(uint8_t *ReadFromUsart) switch (referee_info.CmdID) { case ID_game_state: // 0x0001 - memcpy(&referee_info.GameState, (ReadFromUsart + DATA), LEN_game_state); + memcpy(&referee_info.GameState, (ReadFromUsart + DATA_Offset), LEN_game_state); break; - case ID_game_result: // 0x0002 - memcpy(&referee_info.GameResult, (ReadFromUsart + DATA), LEN_game_result); + memcpy(&referee_info.GameResult, (ReadFromUsart + DATA_Offset), LEN_game_result); break; - case ID_game_robot_survivors: // 0x0003 - memcpy(&referee_info.GameRobotHP, (ReadFromUsart + DATA), LEN_game_robot_HP); + memcpy(&referee_info.GameRobotHP, (ReadFromUsart + DATA_Offset), LEN_game_robot_HP); break; - case ID_event_data: // 0x0101 - memcpy(&referee_info.EventData, (ReadFromUsart + DATA), LEN_event_data); + memcpy(&referee_info.EventData, (ReadFromUsart + DATA_Offset), LEN_event_data); break; - case ID_supply_projectile_action: // 0x0102 - memcpy(&referee_info.SupplyProjectileAction, (ReadFromUsart + DATA), LEN_supply_projectile_action); + memcpy(&referee_info.SupplyProjectileAction, (ReadFromUsart + DATA_Offset), LEN_supply_projectile_action); break; - case ID_game_robot_state: // 0x0201 - memcpy(&referee_info.GameRobotStat, (ReadFromUsart + DATA), LEN_game_robot_state); + memcpy(&referee_info.GameRobotState, (ReadFromUsart + DATA_Offset), LEN_game_robot_state); break; case ID_power_heat_data: // 0x0202 - memcpy(&referee_info.PowerHeatData, (ReadFromUsart + DATA), LEN_power_heat_data); + memcpy(&referee_info.PowerHeatData, (ReadFromUsart + DATA_Offset), LEN_power_heat_data); break; - case ID_game_robot_pos: // 0x0203 - memcpy(&referee_info.GameRobotPos, (ReadFromUsart + DATA), LEN_game_robot_pos); + memcpy(&referee_info.GameRobotPos, (ReadFromUsart + DATA_Offset), LEN_game_robot_pos); break; - case ID_buff_musk: // 0x0204 - memcpy(&referee_info.BuffMusk, (ReadFromUsart + DATA), LEN_buff_musk); + memcpy(&referee_info.BuffMusk, (ReadFromUsart + DATA_Offset), LEN_buff_musk); break; - case ID_aerial_robot_energy: // 0x0205 - memcpy(&referee_info.AerialRobotEnergy, (ReadFromUsart + DATA), LEN_aerial_robot_energy); + memcpy(&referee_info.AerialRobotEnergy, (ReadFromUsart + DATA_Offset), LEN_aerial_robot_energy); break; - case ID_robot_hurt: // 0x0206 - memcpy(&referee_info.RobotHurt, (ReadFromUsart + DATA), LEN_robot_hurt); + memcpy(&referee_info.RobotHurt, (ReadFromUsart + DATA_Offset), LEN_robot_hurt); break; - case ID_shoot_data: // 0x0207 - memcpy(&referee_info.ShootData, (ReadFromUsart + DATA), LEN_shoot_data); - // JUDGE_ShootNumCount();//发弹量统计 + memcpy(&referee_info.ShootData, (ReadFromUsart + DATA_Offset), LEN_shoot_data); + break; + case ID_student_interactive: // 0x0301 syhtodo接收代码未测试 + memcpy(&referee_info.ReceiveData, (ReadFromUsart + DATA_Offset), LEN_receive_data); break; } } @@ -106,22 +134,3 @@ static void JudgeReadData(uint8_t *ReadFromUsart) } } } - -/** - * @brief 裁判系统串口接收回调函数,解析数据 - * - */ -static void RefereeRxCallback() -{ - JudgeReadData(referee_usart_instance->recv_buff); -} - -referee_info_t *RefereeInit(UART_HandleTypeDef *referee_usart_handle) -{ - USART_Init_Config_s conf; - conf.module_callback = RefereeRxCallback; - conf.usart_handle = referee_usart_handle; - conf.recv_buff_size = RE_RX_BUFFER_SIZE; - referee_usart_instance = USARTRegister(&conf); - return &referee_info; -} \ No newline at end of file diff --git a/modules/referee/rm_referee.h b/modules/referee/rm_referee.h new file mode 100644 index 0000000..16fb381 --- /dev/null +++ b/modules/referee/rm_referee.h @@ -0,0 +1,60 @@ +#ifndef RM_REFEREE_H +#define RM_REFEREE_H + +#include "usart.h" +#include "referee_def.h" +#include "bsp_usart.h" +extern uint8_t UI_Seq; + +#pragma pack(1) +typedef struct +{ + uint8_t Robot_Color; //机器人颜色 + uint16_t Robot_ID; //本机器人ID + uint16_t Cilent_ID; //本机器人对应的客户端ID + uint16_t Receiver_Robot_ID; //机器人车间通信时接收者的ID,必须和本机器人同颜色 +} referee_id_t; + +// 此结构体包含裁判系统接收数据以及UI绘制与机器人车间通信的相关信息 +typedef struct +{ + referee_id_t referee_id; + + xFrameHeader FrameHeader; // 接收到的帧头信息 + uint16_t CmdID; + ext_game_state_t GameState; // 0x0001 + ext_game_result_t GameResult; // 0x0002 + ext_game_robot_HP_t GameRobotHP; // 0x0003 + ext_event_data_t EventData; // 0x0101 + ext_supply_projectile_action_t SupplyProjectileAction; // 0x0102 + ext_game_robot_state_t GameRobotState; // 0x0201 + ext_power_heat_data_t PowerHeatData; // 0x0202 + ext_game_robot_pos_t GameRobotPos; // 0x0203 + ext_buff_musk_t BuffMusk; // 0x0204 + aerial_robot_energy_t AerialRobotEnergy; // 0x0205 + ext_robot_hurt_t RobotHurt; // 0x0206 + ext_shoot_data_t ShootData; // 0x0207 + + //自定义交互数据的接收 + Communicate_ReceiveData_t ReceiveData; + +} referee_info_t; + +#pragma pack() + +/** + * @brief 初始化裁判系统,返回接收数据指针 + * + * @param referee_usart_handle + * @return referee_info_t* + */ +referee_info_t *RefereeInit(UART_HandleTypeDef *referee_usart_handle); + +/** + * @brief 发送函数 + * @todo + * @param send 待发送数据 + */ +void RefereeSend(uint8_t *send, uint16_t tx_len); + +#endif // !REFEREE_H diff --git a/如何定位bug.md b/如何定位bug.md index 3138e9a..a2e521f 100644 --- a/如何定位bug.md +++ b/如何定位bug.md @@ -1,5 +1,7 @@ # how to locate bug in your code +[TOC] + 只讨论运行中的bug(指程序的运行结果不符合你的期望)和异常(直接异常终止)。编译期出现的warning和error不在此范畴,他们都可以通过直接阅读报错信息解决。 ## Debug方法论 @@ -81,4 +83,79 @@ long long的范围比float小。无符号和有符号数直接转换可能变成 // 宏通过空格来解析替换字段,YOUR_DEF空格后的第一个字段当作替换文本 ``` -**宏只在当前文件生效**,如果宏放在.c那么对其他的文件是不可见的,这也一般称作私有宏。 \ No newline at end of file +**宏只在当前文件生效**,如果宏放在.c那么对其他的文件是不可见的,这也一般称作私有宏。 + + + +## 典型debug案例 + +这是一个结合了软件和硬件且有多模块耦合的异常。该bug发生在调试平衡步兵的底盘过程当中。 + +### 引发bug的原因 + +1. 指针在强制类型转换中变成了错误的类型,使得指向的内存地址被错误地修改 +2. CAN总线负载过大导致电机反馈消息丢失 + +这里是发生bug的代码片段: + +```c +static void LKMotorDecode(CANInstance *_instance) +{ + static LKMotor_Measure_t *measure; + static uint8_t *rx_buff; + rx_buff = _instance->rx_buff; + measure = &((LKMotorInstance *)_instance)->measure; // 通过caninstance保存的id获取对应的motorinstance + // 上面一行应为: measure = &(((LKMotorInstance *)_instance->id)->measure); + measure->last_ecd = measure->ecd; + measure->ecd = ... + + // .... + +} +``` + +这是问题1的出处。can instance中保存了父指针,即拥有该instance的LKMotorInstance。这里想通过强制类型转换将`void*`类型的`id`转换成电机的instance指针类型并访问其measure成员变量以从CAN反馈的报文中更新量测值。然而却直接将can instance转换成motor instance。 + +随后,更新之后的数据被覆写到can instance内部,使得其成员变量改变,包括hcan、txbuf、rxbuf、tx/rxlen等。hcan是HAL定义的can句柄类型,里面保存了指向can状态和控制寄存器的指针以及其他HAL状态信息,然而其值被电机反馈回来的值覆写,之后HAL的接口访问hcan时将引起异常。 + +第二个问题则不是显式存在的: + +```c +void MotorControlTask() +{ + DJIMotorControl(); + + HTMotorControl(); + + LKMotorControl(); + + ServeoMotorControl(); + + StepMotorControl(); +} +``` + +这是motortask的内容,此任务将以500hz的频率运行。在发生bug时,我们将4个HT04电机和2个LK MF9025电机全部连接到CAN1上。注意,HT04不支持多电机指令,因此占用的带宽较大。在`LKMotorControl()`完成参考值计算和CAN发送之后立刻会调用`HTMotorControl()`,后者需要连续发送4条报文。而HT和LK电机都会在接收到控制指令之后发送反馈信息报文。由于HT电机的控制在LK电机控制之后立刻执行,导致总线被占据,LK电机发送的反馈数据仲裁失败无法获得总线占有权,使得主机收不到反馈数据。 + + + +### bug的发现和定位的尝试 + +程序的大体情况如下,当时进行轮足式倒立摆机器人的测试,启用了balance.c,在其中注册了4个HT04电机(can1)和2个LK9025电机(can2)。控制报文的发送频率均为500Hz。 + +测试时发现,9025电机可以接收到mcu发送的控制指令并响应,但是mcu始终无法获得反馈值,`LKMotorInstance->measure`的所有成员变量一直是零。由于CAN是总线架构,电机能接收到数据说明通信正常。HT04电机也可以正常控制并收到反馈信息。在`LKMotorDecode()`函数中添加断点发现能够成功进入1~2次,随后便引发HardFault。 + +此时内心有些动摇,开始检查硬件连线。我们尝试把LK电机也挂载到CAN1总线上。开始单步调试,发现LK电机可以正常接收一次反馈报文,之后就进入`Hardfault_handler()`。HT和DJI电机均无此问题。进一步进行每条指令的调试,发现在成功接收到一次报文之后(接收报文指的是can发生中断并在处理函数中调用LK电机的解码函数,我们并没有查看measure值是否刷新,实际上这时候反馈值仍然为零),进入该电机的控制报文发送时,通过在`Hardfault_handler()`中添加汇编语句`asm("bx lr")`,即跳转到最后一次执行的指令,发现访问`hcan->state`会引起硬件错误。遇到这种情况,说明发生了越界访问或使用了野指针。检查hcan的值,发现是一个非常大的地址。因此怀疑hcan指针被其他的内存访问语句修改。 + +有了方向之后,进一步对每一个函数都进行单步进入调试,同时时刻监测hcan1的值。然而,这时候出现即使一开始就单步调试也无法进入LK电机解码函数的问题。于是,怀疑是CAN过滤器的配置问题,使得LK电机反馈报文被过滤,检查LK的接收id无误后,认为可能由于LK电机的发送和接收ID都比较大(0x140和0x280),CAN标准ID放不下。但是查阅CAN specification后发现standar ID可以容纳11位的值,应该不会有问题。于是把过滤器配置为mask模式,让bxCAN控制器接收所有报文(即不进行过滤)。然而还是不奏效,仍然无法收到数据。 + +这时候想起HT电机是不支持多电机控制指令的,因此500Hz的控制频率似乎有些过高,相当于2ms内要完成2x4+1+2=11次CAN报文的发送。计算1M波特率下最大通信速率,果然超出了负载。于是降低`MotorTask()`的频率为200Hz,果然能重新接收到数据了。 + +继续单步调试,终于发现在`LKMotorDecode()`中,通过强制类型转换获取LKMotorInstance的时候,用错了变量,使得反馈值被写入电机的`CANInstance`内,导致hcan指向随机的地址,最终造成访问时引发hardfault。 + +修改之后,将LK电机挂载到CAN2上,控制频率回到500Hz,程序正常运行。 + +### 解决方案 + +均衡总线负载,调节任务运行时间。 + diff --git a/必须做&禁止做.md b/必须做&禁止做.md index 60c2629..e3a9d0b 100644 --- a/必须做&禁止做.md +++ b/必须做&禁止做.md @@ -14,5 +14,7 @@ ## 请给你编写的bsp和module提供详细的文档和使用示例,并为接口增加安全检查 -“treat your user as idot!” +用于调试的条件编译和log输出也是必须的。 + +另外,“treat your user as idot!”