Merge remote-tracking branch 'origin/master'

# Conflicts:
#	.vscode/launch.json
#	application/gimbal/gimbal.c
#	application/robot.c
#	bsp/usart/bsp_usart.c
#	bsp/usart/bsp_usart.h
#	modules/master_machine/master_process.c
#	modules/master_machine/seasky_protocol.c
This commit is contained in:
chenfu 2023-03-22 16:49:56 +08:00
commit cce87bcccd
44 changed files with 1877 additions and 602 deletions

5
.vscode/launch.json vendored
View File

@ -19,7 +19,8 @@
"configFiles": [
"openocd_dap.cfg", // ,,openocdshare/scripts
],
"runToEntryPoint": "main" // main
"runToEntryPoint": "main", // main
//"preLaunchTask": "build task",//Build,使
//"preLaunchTask": "log", // RTT viewer
// log,使logprelaunch tasklogdepends on
},
@ -36,6 +37,8 @@
"servertype": "jlink",
"interface": "swd",
"svdFile": "STM32F407.svd",
"rtos": "FreeRTOS",
// "preLaunchTask": "build task",//Build,使
//"preLaunchTask": "log", // RTT viewer
// log,使logprelaunch tasklogdepends on
},

10
.vscode/tasks.json vendored
View File

@ -30,5 +30,15 @@
"isDefault": false,
}
},
{
"label": "log",
"type": "shell",
"command":"JlinkRTTClient",
"args": [],
"problemMatcher": [],
// "dependsOn":[
// "build task", // .
// ]
}
]
}

View File

@ -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
### 尝试解决的方案

View File

@ -197,7 +197,7 @@ void StartROBOTTASK(void const * argument)
{
// 200Hz
RobotTask();
osDelay(5);
osDelay(10);//syh此处暂时将时间改为10ms原因在于未使用缓冲区发送发送时延时5ms
}
}

View File

@ -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 \

View File

@ -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的安装目录添加到环境变量中。
### 更好的编辑体验
建议安装以下插件:

View File

@ -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" // 需要用到较多三角函数
/* 底盘拥有的模块实例 */
@ -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()
{
}

View File

@ -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 关闭插值

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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)
{
@ -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;

View File

@ -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;i<Communicate_Data_LEN;i++)
{
SendData->data[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 IDID
* @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]);
}

View File

@ -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

View File

@ -0,0 +1,3 @@
# referee
需要将此模块移动到module层并新建一个rtos任务以一定频率运行用于ui刷新和多机通信

View File

@ -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,6 +35,7 @@ void RobotInit()
#endif
#if defined(ONE_BOARD) || defined(CHASSIS_BOARD)
// Referee_Interactive_init();
ChassisInit();
#endif
// 初始化完成,开启中断
@ -50,5 +52,6 @@ void RobotTask()
#if defined(ONE_BOARD) || defined(CHASSIS_BOARD)
ChassisTask();
// Referee_Interactive_task();
#endif
}

View File

@ -132,7 +132,6 @@ typedef enum
// 功率限制,从裁判系统获取
typedef struct
{ // 功率控制
} Chassis_Power_Data_s;
/* ----------------CMD应用发布的控制数据,应当由gimbal/chassis/shoot订阅---------------- */

View File

@ -1,5 +1,6 @@
#include "shoot.h"
#include "robot_def.h"
#include "dji_motor.h"
#include "message_center.h"
#include "bsp_dwt.h"

View File

@ -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实例索引,每次有新的模块注册会自增

View File

@ -4,7 +4,6 @@
#include "SEGGER_RTT_Conf.h"
#include <stdio.h>
#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;
}
@ -32,3 +31,4 @@ void Float2Str(char *str, float va)
else
sprintf(str, "%d.%d", head, point);
}

View File

@ -1,18 +1,53 @@
#ifndef _BSP_LOG_H
#define _BSP_LOG_H
#include "SEGGER_RTT.h"
#include "SEGGER_RTT_Conf.h"
#include <stdio.h>
#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, ...);

View File

@ -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实例会调用对应的回调进行进一步的处理
* ://

View File

@ -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

View File

@ -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
}

View File

@ -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发送.
}

View File

@ -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,7 +125,7 @@ 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;
}
}

View File

@ -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)
{
@ -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;
}

View File

@ -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

View File

@ -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类型,或发生了指针越界
}
}

View File

@ -7,6 +7,10 @@
> 1. 给不同的电机设置不同的低通滤波器惯性系数而不是统一使用宏
> 2. 为M2006和M3508增加开环的零位校准函数
---
> 建议将电机的反馈频率通过RoboMaster Assistant统一设置为500Hz。当前默认的`MotorTask()`执行频率为500Hz若不修改电机反馈频率可能导致单条总线挂载的电机数量有限且容易出现帧错误和仲裁失败的情况。
## 总览和封装说明
> 如果你不需要理解该模块的工作原理,你只需要查看这一小节。
@ -19,7 +23,7 @@ dji_motor模块对DJI智能电机包括M2006M3508以及GM6020进行了详
2. ==速度环为角速度,单位为**度/每秒**deg/sec==
3. ==电流环为mA==
3. ==电流环为A==
4. ==GM6020的输入设定为**力矩**,待测量(-30000~30000==

View File

@ -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;

View File

@ -3,3 +3,5 @@
这是瓴控电机的模块封装说明文档。关于LK电机的控制报文和反馈报文值详见LK电机的说明文档由于电机实例已经自带三环PID计算一般来说**我们能用到的只有单电机的力矩指令和多电机指令。**
注意LK电机在使用多电机发送的时候只支持一条总线上至多4个电机多电机模式下LK仅支持发送id 0x280为接收ID为0x140+id.
要设置为多电机模式请通过串口连接电机并使用该文件夹下的LK motor tool.exe进行配置。

View File

@ -137,7 +137,6 @@ 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;
@ -148,7 +147,6 @@ uint16_t Get_CRC16_Check_Sum(uint8_t *pchMessage,uint32_t dwLength,uint16_t wCRC
(wCRC) = ((uint16_t)(wCRC) >> 8) ^
wCRC_Table[((uint16_t)(wCRC) ^ (uint16_t)(chData)) & 0x00ff];
}
return wCRC;
}

View File

@ -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

View File

@ -0,0 +1,3 @@
# referee
当前模块组织较为混乱,后续统一为多机通信+裁判系统信息接收+UI绘制。UI绘制和多机通信的发送部分在referee任务中以一定的频率运行信息的接收通过中断完成。

View File

@ -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_xStart_y xy坐标
End_xEnd_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_xStart_y xy坐标
End_xEnd_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_xStart_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_xStart_y xy坐标
End_xEnd_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_xStart_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_xStart_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_xStart_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
...
Tips1257
*/
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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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 ,
@ -23,16 +61,14 @@ static uint16_t Judge_SelfClient_ID; // 发送者机器人对应的客户端ID
static void JudgeReadData(uint8_t *ReadFromUsart)
{
uint16_t judge_length; // 统计一帧数据长度
// referee_info.CmdID = 0; //数据命令码解析
// 空数据包,则不作任何处理
if (ReadFromUsart == NULL)
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;
}

View File

@ -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

View File

@ -1,5 +1,7 @@
# how to locate bug in your code
[TOC]
只讨论运行中的bug指程序的运行结果不符合你的期望和异常直接异常终止。编译期出现的warning和error不在此范畴他们都可以通过直接阅读报错信息解决。
## Debug方法论
@ -82,3 +84,78 @@ long long的范围比float小。无符号和有符号数直接转换可能变成
```
**宏只在当前文件生效**,如果宏放在.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和0x280CAN标准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程序正常运行。
### 解决方案
均衡总线负载,调节任务运行时间。

View File

@ -14,5 +14,7 @@
## 请给你编写的bsp和module提供详细的文档和使用示例并为接口增加安全检查
“treat your user as idot
用于调试的条件编译和log输出也是必须的。
另外“treat your user as idot