Compare commits

...

7 Commits

Author SHA1 Message Date
1697a51555 修改版本号 2025-08-18 03:31:26 +08:00
474ea3ded3 添加依赖 2025-08-18 03:27:39 +08:00
41de38a146 添加odrive和vesc 2025-08-18 03:20:20 +08:00
7e92f32642 修改can生成逻辑 2025-08-18 03:19:30 +08:00
68394c616e 修复完路径问题了 2025-08-18 03:18:50 +08:00
266868ad71 合并前的最终提交 2025-08-18 02:08:55 +08:00
898c5dfb2b 修复了exe路径问题 2025-08-18 01:57:03 +08:00
12 changed files with 1140 additions and 117 deletions

View File

@ -8,7 +8,7 @@ from qfluentwidgets import InfoBar, InfoBarPosition, SubtitleLabel
from .function_fit_interface import FunctionFitInterface
from app.tools.check_update import check_update
__version__ = "1.0.3"
__version__ = "1.0.4"
class AboutInterface(QWidget):
def __init__(self, parent=None):

View File

@ -4,6 +4,7 @@ from qfluentwidgets import TitleLabel, BodyLabel, PushButton, TreeWidget, Fluent
from app.tools.analyzing_ioc import analyzing_ioc
from app.code_page.bsp_interface import bsp
from app.data_interface import DataInterface
from app.tools.code_generator import CodeGenerator
import os
import csv
@ -159,9 +160,7 @@ class CodeGenerateInterface(QWidget):
"""生成所有代码,包括未加载页面"""
try:
# 先收集所有页面名从CSV配置文件读取
script_dir = os.path.dirname(os.path.abspath(__file__))
csv_path = os.path.join(script_dir, "../assets/User_code/config.csv")
csv_path = os.path.abspath(csv_path)
csv_path = os.path.join(CodeGenerator.get_assets_dir("User_code"), "config.csv")
all_class_names = []
if os.path.exists(csv_path):
with open(csv_path, newline='', encoding='utf-8') as f:
@ -238,9 +237,7 @@ class CodeGenerateInterface(QWidget):
return "未找到.ioc文件"
def _load_csv_and_build_tree(self):
script_dir = os.path.dirname(os.path.abspath(__file__))
csv_path = os.path.join(script_dir, "../assets/User_code/config.csv")
csv_path = os.path.abspath(csv_path)
csv_path = os.path.join(CodeGenerator.get_assets_dir("User_code"), "config.csv")
print(f"加载CSV路径: {csv_path}")
if not os.path.exists(csv_path):
print(f"配置文件未找到: {csv_path}")

View File

@ -37,6 +37,8 @@ def save_with_preserve(path, new_code):
with open(path, "r", encoding="utf-8") as f:
old_code = f.read()
new_code = preserve_all_user_regions(new_code, old_code)
# 确保目录存在
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(new_code)
@ -47,7 +49,7 @@ class BspSimplePeripheral(QWidget):
self.peripheral_name = peripheral_name
self.template_names = template_names
# 加载描述
describe_path = os.path.join(os.path.dirname(__file__), "../../assets/User_code/bsp/describe.csv")
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path)
self._init_ui()
self._load_config()
@ -123,7 +125,7 @@ class BspPeripheralBase(QWidget):
self.get_available_func = get_available_func
self.available_list = []
# 新增:加载描述
describe_path = os.path.join(os.path.dirname(__file__), "../../assets/User_code/bsp/describe.csv")
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path)
self._init_ui()
self._load_config()
@ -388,6 +390,7 @@ class bsp_i2c(BspPeripheralBase):
get_available_i2c
)
class bsp_can(BspPeripheralBase):
def __init__(self, project_path):
super().__init__(
@ -400,7 +403,6 @@ class bsp_can(BspPeripheralBase):
get_available_can
)
def _generate_source_file(self, configs, template_dir):
template_path = os.path.join(template_dir, self.template_names['source'])
template_content = CodeGenerator.load_template(template_path)
@ -436,90 +438,20 @@ class bsp_can(BspPeripheralBase):
init_lines.append(" inited = true;")
init_lines.append("")
# 检查是否同时有CAN1和CAN2
has_can1 = any(instance == 'CAN1' for _, instance in configs)
has_can2 = any(instance == 'CAN2' for _, instance in configs)
# 按照新的FIFO分配策略
can_instances = [instance for _, instance in configs]
can_count = len(can_instances)
if has_can1 and has_can2:
# 同时配置CAN1和CAN2的情况 - 统一使用FIFO0
init_lines.extend([
" // 初始化 CAN1 - 使用 FIFO0",
" CAN_FilterTypeDef can1_filter = {0};",
" can1_filter.FilterBank = 0;",
" can1_filter.FilterIdHigh = 0;",
" can1_filter.FilterIdLow = 0;",
" can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
" can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
" can1_filter.FilterMaskIdHigh = 0;",
" can1_filter.FilterMaskIdLow = 0;",
" can1_filter.FilterActivation = ENABLE;",
" can1_filter.SlaveStartFilterBank = 14; // 重要:设置从过滤器起始组",
" can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
" HAL_CAN_ConfigFilter(&hcan1, &can1_filter);",
" HAL_CAN_Start(&hcan1);",
" HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
" // 初始化 CAN2 - 使用 FIFO0注意通过 CAN1 配置 CAN2 的过滤器)",
" CAN_FilterTypeDef can2_filter = {0};",
" can2_filter.FilterBank = 14; // CAN2 使用过滤器组 14",
" can2_filter.FilterIdHigh = 0;",
" can2_filter.FilterIdLow = 0;",
" can2_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
" can2_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
" can2_filter.FilterMaskIdHigh = 0;",
" can2_filter.FilterMaskIdLow = 0;",
" can2_filter.FilterActivation = ENABLE;",
" can2_filter.FilterFIFOAssignment = CAN_RX_FIFO0; // 改为 FIFO0",
" HAL_CAN_ConfigFilter(&hcan1, &can2_filter); // 通过 CAN1 配置",
" HAL_CAN_Start(&hcan2);",
" HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING); // 激活 FIFO0 中断",
"",
" // 注册回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
])
else:
# 只有单个CAN的情况
for idx, (name, instance) in enumerate(configs):
can_num = instance[-1] # CAN1 -> 1, CAN2 -> 2
init_lines.append(f" // 初始化 {instance}")
init_lines.append(f" CAN_FilterTypeDef can{can_num}_filter = {{0}};")
if instance == 'CAN1':
init_lines.extend([
f" can{can_num}_filter.FilterBank = 0;",
f" can{can_num}_filter.SlaveStartFilterBank = 14;",
f" can{can_num}_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
])
else: # CAN2
init_lines.extend([
f" can{can_num}_filter.FilterBank = 14;",
f" can{can_num}_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
])
init_lines.extend([
f" can{can_num}_filter.FilterIdHigh = 0;",
f" can{can_num}_filter.FilterIdLow = 0;",
f" can{can_num}_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can{can_num}_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can{can_num}_filter.FilterMaskIdHigh = 0;",
f" can{can_num}_filter.FilterMaskIdLow = 0;",
f" can{can_num}_filter.FilterActivation = ENABLE;",
])
if instance == 'CAN2':
init_lines.append(f" HAL_CAN_ConfigFilter(&hcan1, &can{can_num}_filter); // 通过 CAN1 配置")
else:
init_lines.append(f" HAL_CAN_ConfigFilter(&hcan{can_num}, &can{can_num}_filter);")
init_lines.extend([
f" HAL_CAN_Start(&hcan{can_num});",
f" HAL_CAN_ActivateNotification(&hcan{can_num}, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
f" // 注册回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
# 根据CAN数量分配FIFO
if can_count == 1:
# 只有CAN1 -> 用FIFO0
self._generate_single_can_init(init_lines, configs, "CAN_RX_FIFO0")
elif can_count == 2:
# CAN1和CAN2 -> CAN1用FIFO0CAN2用FIFO1
self._generate_dual_can_init(init_lines, configs)
elif can_count >= 3:
# CAN1,2,3+ -> CAN1和CAN2用FIFO0CAN3用FIFO1
self._generate_multi_can_init(init_lines, configs)
content = CodeGenerator.replace_auto_generated(
content, "AUTO GENERATED CAN_INIT", "\n".join(init_lines)
@ -529,7 +461,167 @@ class bsp_can(BspPeripheralBase):
save_with_preserve(output_path, content)
return True
def _generate_single_can_init(self, init_lines, configs, fifo_assignment):
"""单个CAN的初始化使用FIFO0"""
name, instance = configs[0]
can_num = instance[-1] # CAN1 -> 1
init_lines.extend([
f" // 初始化 {instance} - 使用 FIFO0",
f" CAN_FilterTypeDef can{can_num}_filter = {{0}};",
f" can{can_num}_filter.FilterBank = 0;",
f" can{can_num}_filter.FilterIdHigh = 0;",
f" can{can_num}_filter.FilterIdLow = 0;",
f" can{can_num}_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can{can_num}_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can{can_num}_filter.FilterMaskIdHigh = 0;",
f" can{can_num}_filter.FilterMaskIdLow = 0;",
f" can{can_num}_filter.FilterActivation = ENABLE;",
f" can{can_num}_filter.SlaveStartFilterBank = 14;",
f" can{can_num}_filter.FilterFIFOAssignment = {fifo_assignment};",
f" HAL_CAN_ConfigFilter(&hcan{can_num}, &can{can_num}_filter);",
f" HAL_CAN_Start(&hcan{can_num});",
f" HAL_CAN_ActivateNotification(&hcan{can_num}, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
f" // 注册回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
def _generate_dual_can_init(self, init_lines, configs):
"""双CAN初始化CAN1用FIFO0CAN2用FIFO1"""
# 找到CAN1和CAN2
can1_config = next((cfg for cfg in configs if cfg[1] == 'CAN1'), None)
can2_config = next((cfg for cfg in configs if cfg[1] == 'CAN2'), None)
if can1_config:
name, instance = can1_config
init_lines.extend([
f" // 初始化 CAN1 - 使用 FIFO0",
f" CAN_FilterTypeDef can1_filter = {{0}};",
f" can1_filter.FilterBank = 0;",
f" can1_filter.FilterIdHigh = 0;",
f" can1_filter.FilterIdLow = 0;",
f" can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can1_filter.FilterMaskIdHigh = 0;",
f" can1_filter.FilterMaskIdLow = 0;",
f" can1_filter.FilterActivation = ENABLE;",
f" can1_filter.SlaveStartFilterBank = 14;",
f" can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
f" HAL_CAN_ConfigFilter(&hcan1, &can1_filter);",
f" HAL_CAN_Start(&hcan1);",
f" HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
f" // 注册CAN1回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
if can2_config:
name, instance = can2_config
init_lines.extend([
f" // 初始化 CAN2 - 使用 FIFO1",
f" CAN_FilterTypeDef can2_filter = {{0}};",
f" can2_filter.FilterBank = 14;",
f" can2_filter.FilterIdHigh = 0;",
f" can2_filter.FilterIdLow = 0;",
f" can2_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can2_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can2_filter.FilterMaskIdHigh = 0;",
f" can2_filter.FilterMaskIdLow = 0;",
f" can2_filter.FilterActivation = ENABLE;",
f" can2_filter.FilterFIFOAssignment = CAN_RX_FIFO1;",
f" HAL_CAN_ConfigFilter(&hcan1, &can2_filter); // 通过 CAN1 配置",
f" HAL_CAN_Start(&hcan2);",
f" HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO1_MSG_PENDING);",
"",
f" // 注册CAN2回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO1_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
def _generate_multi_can_init(self, init_lines, configs):
"""多CAN初始化CAN1和CAN2用FIFO0CAN3+用FIFO1"""
can1_config = next((cfg for cfg in configs if cfg[1] == 'CAN1'), None)
can2_config = next((cfg for cfg in configs if cfg[1] == 'CAN2'), None)
other_configs = [cfg for cfg in configs if cfg[1] not in ['CAN1', 'CAN2']]
# CAN1 - FIFO0
if can1_config:
name, instance = can1_config
init_lines.extend([
f" // 初始化 CAN1 - 使用 FIFO0",
f" CAN_FilterTypeDef can1_filter = {{0}};",
f" can1_filter.FilterBank = 0;",
f" can1_filter.FilterIdHigh = 0;",
f" can1_filter.FilterIdLow = 0;",
f" can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can1_filter.FilterMaskIdHigh = 0;",
f" can1_filter.FilterMaskIdLow = 0;",
f" can1_filter.FilterActivation = ENABLE;",
f" can1_filter.SlaveStartFilterBank = 14;",
f" can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
f" HAL_CAN_ConfigFilter(&hcan1, &can1_filter);",
f" HAL_CAN_Start(&hcan1);",
f" HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
f" // 注册CAN1回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
# CAN2 - FIFO0
if can2_config:
name, instance = can2_config
init_lines.extend([
f" // 初始化 CAN2 - 使用 FIFO0",
f" CAN_FilterTypeDef can2_filter = {{0}};",
f" can2_filter.FilterBank = 14;",
f" can2_filter.FilterIdHigh = 0;",
f" can2_filter.FilterIdLow = 0;",
f" can2_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can2_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can2_filter.FilterMaskIdHigh = 0;",
f" can2_filter.FilterMaskIdLow = 0;",
f" can2_filter.FilterActivation = ENABLE;",
f" can2_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
f" HAL_CAN_ConfigFilter(&hcan1, &can2_filter); // 通过 CAN1 配置",
f" HAL_CAN_Start(&hcan2);",
f" HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);",
"",
f" // 注册CAN2回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
# CAN3+ - FIFO1
filter_bank = 20 # 从过滤器组20开始分配给CAN3+
for config in other_configs:
name, instance = config
can_num = ''.join(filter(str.isdigit, instance))
init_lines.extend([
f" // 初始化 {instance} - 使用 FIFO1",
f" CAN_FilterTypeDef can{can_num}_filter = {{0}};",
f" can{can_num}_filter.FilterBank = {filter_bank};",
f" can{can_num}_filter.FilterIdHigh = 0;",
f" can{can_num}_filter.FilterIdLow = 0;",
f" can{can_num}_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
f" can{can_num}_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
f" can{can_num}_filter.FilterMaskIdHigh = 0;",
f" can{can_num}_filter.FilterMaskIdLow = 0;",
f" can{can_num}_filter.FilterActivation = ENABLE;",
f" can{can_num}_filter.FilterFIFOAssignment = CAN_RX_FIFO1;",
f" HAL_CAN_ConfigFilter(&hcan1, &can{can_num}_filter); // 通过 CAN1 配置",
f" HAL_CAN_Start(&hcan{can_num});",
f" HAL_CAN_ActivateNotification(&hcan{can_num}, CAN_IT_RX_FIFO1_MSG_PENDING);",
"",
f" // 注册{instance}回调函数",
f" BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO1_MSG_PENDING_CB, BSP_CAN_RxFifoCallback);",
""
])
filter_bank += 1 # 为下一个CAN分配不同的过滤器组
class bsp_spi(BspPeripheralBase):
def __init__(self, project_path):

View File

@ -105,7 +105,7 @@ class ComponentSimple(QWidget):
self.component_manager = component_manager
# 加载描述和依赖信息
component_dir = os.path.dirname(__file__) + "/../../assets/User_code/component"
component_dir = CodeGenerator.get_assets_dir("User_code/component")
describe_path = os.path.join(component_dir, "describe.csv")
dependencies_path = os.path.join(component_dir, "dependencies.csv")
self.descriptions = load_descriptions(describe_path)
@ -172,13 +172,7 @@ class ComponentSimple(QWidget):
return True
def _get_component_template_dir(self):
current_dir = os.path.dirname(os.path.abspath(__file__))
while os.path.basename(current_dir) != 'MRobot' and current_dir != '/':
current_dir = os.path.dirname(current_dir)
if os.path.basename(current_dir) == 'MRobot':
return os.path.join(current_dir, "assets/User_code/component")
else:
return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "assets/User_code/component")
return CodeGenerator.get_assets_dir("User_code/component")
def _save_config(self):
config_path = os.path.join(self.project_path, "User/component/component_config.yaml")
@ -286,7 +280,7 @@ class component(QWidget):
def generate_component(project_path, pages):
"""生成所有组件代码,处理依赖关系"""
# 自动添加 component.h
src_component_h = os.path.join(os.path.dirname(__file__), "../../assets/User_code/component/component.h")
src_component_h = os.path.join(CodeGenerator.get_assets_dir("User_code/component"), "component.h")
dst_component_h = os.path.join(project_path, "User/component/component.h")
os.makedirs(os.path.dirname(dst_component_h), exist_ok=True)
if os.path.exists(src_component_h):

View File

@ -37,7 +37,7 @@ def get_available_bsp_devices(project_path, bsp_type, gpio_type=None):
def generate_device_header(project_path, enabled_devices):
"""生成device.h文件"""
device_dir = os.path.join(os.path.dirname(__file__), "../../assets/User_code/device")
device_dir = CodeGenerator.get_assets_dir("User_code/device")
template_path = os.path.join(device_dir, "device.h")
# 读取模板文件
@ -274,16 +274,7 @@ class DeviceSimple(QWidget):
def _get_device_template_dir(self):
"""获取设备模板目录"""
current_dir = os.path.dirname(os.path.abspath(__file__))
# 向上找到 MRobot 根目录
while os.path.basename(current_dir) != 'MRobot' and current_dir != '/':
current_dir = os.path.dirname(current_dir)
if os.path.basename(current_dir) == 'MRobot':
return os.path.join(current_dir, "assets/User_code/device")
else:
# 如果找不到,使用相对路径作为备选
return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "assets/User_code/device")
return CodeGenerator.get_assets_dir("User_code/device")
def _save_config(self):
"""保存配置"""
@ -316,7 +307,7 @@ class DeviceSimple(QWidget):
def get_device_page(device_name, project_path):
"""根据设备名返回对应的页面类"""
# 加载设备配置
device_dir = os.path.join(os.path.dirname(__file__), "../../assets/User_code/device")
device_dir = CodeGenerator.get_assets_dir("User_code/device")
config_path = os.path.join(device_dir, "config.yaml")
device_configs = load_device_config(config_path)

View File

@ -78,4 +78,33 @@ class CodeGenerator:
if not os.path.exists(template_dir):
print(f"警告:模板目录不存在: {template_dir}")
return template_dir
return template_dir
@staticmethod
def get_assets_dir(sub_path=""):
"""获取assets目录路径兼容打包环境
Args:
sub_path: 子路径 "User_code/component" "User_code/device"
Returns:
str: 完整的assets路径
"""
if getattr(sys, 'frozen', False):
# 打包后的环境
base_path = sys._MEIPASS
assets_dir = os.path.join(base_path, "assets")
else:
# 开发环境
current_dir = os.path.dirname(os.path.abspath(__file__))
while os.path.basename(current_dir) != 'MRobot' and current_dir != '/':
current_dir = os.path.dirname(current_dir)
assets_dir = os.path.join(current_dir, "assets")
if sub_path:
full_path = os.path.join(assets_dir, sub_path)
else:
full_path = assets_dir
if not os.path.exists(full_path):
print(f"警告:资源目录不存在: {full_path}")
return full_path

View File

@ -1,4 +1,4 @@
bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
component,ahrs,ballistics,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
device,dr16,bmi088,ist8310,motor,motor_rm
device,dr16,bmi088,ist8310,motor,motor_rm,motor_vesc,motor_odrive
module,
1 bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
2 component,ahrs,ballistics,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
3 device,dr16,bmi088,ist8310,motor,motor_rm device,dr16,bmi088,ist8310,motor,motor_rm,motor_vesc,motor_odrive
4 module,

View File

@ -73,4 +73,38 @@ devices:
- name: "SIGNAL_IST8310_MAGN_NEW_DATA"
files:
header: "ist8310.h"
source: "ist8310.c"
source: "ist8310.c"
motor_vesc:
name: "VESC 电调"
description: "VESC 电调驱动"
dependencies:
bsp: ["can", "time"]
files:
header: "motor_vesc.h"
source: "motor_vesc.c"
motor_odrive:
name: "ODrive 电机"
description: "ODrive 电机驱动"
dependencies:
bsp: ["can", "time"]
files:
header: "motor_odrive.h"
source: "motor_odrive.c"
motor_rm:
name: "RM 电机"
description: "RM 电机驱动"
dependencies:
bsp: ["can", "time"]
files:
header: "motor_rm.h"
source: "motor_rm.c"
motor:
name: "通用电机"
description: "通用电机驱动"
files:
header: "motor.h"
source: "motor.c"

View File

@ -0,0 +1,328 @@
/*
CAN总线上的设备
CAN总线上挂载的设备抽象成一个设备进行配置和控制
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_odrive.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static ODrive_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
// 获取指定CAN总线的电机管理器指针
static ODrive_CANManager_t* MOTOR_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
// 为指定CAN总线创建电机管理器
static int8_t MOTOR_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (ODrive_CANManager_t*)BSP_Malloc(sizeof(ODrive_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(ODrive_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
// 解析CAN报文更新电机反馈信息
static void Motor_Decode(ODrive_t *motor, BSP_CAN_Message_t *msg)
{
uint8_t axis_id = (msg->original_id >> 5) & 0x3F; // 提取电机号0~63
uint8_t cmd_id = msg->original_id & 0x1F; // 提取命令 ID低 5 位)
motor->param.id = axis_id; // 保存电机 ID
// 解析帧类型(数据帧或远程帧)
if (msg->frame_type == BSP_CAN_FRAME_STD_DATA) {
// 数据帧处理
switch (cmd_id)
{
case ODRIVE_HEARTBEAT_MESSAGE: // 0x001 ODrive心跳消息
// motor->motor.feedback.axis_error = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
// motor->motor.feedback.axis_state = msg->data[4];
// motor->motor.feedback.controller_status = msg->data[5];
break;
case ENCODER_ESTIMATES: // 0x009
{
uint32_t raw_pos = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
uint32_t raw_vel = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
memcpy(&motor->motor.feedback.rotor_abs_angle, &raw_pos, sizeof(float));
memcpy(&motor->motor.feedback.rotor_speed, &raw_vel, sizeof(float));
}
break;
case GET_ENCODER_COUNT: // 0x014
// motor->motor.feedback.encoder_shadow = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
// motor->motor.feedback.encoder_cpr = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
break;
case GET_BUS_VOLTAGE_CURRENT: // 0x017
{
uint32_t raw_vbus, raw_ibus;
raw_vbus = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
raw_ibus = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
// memcpy(&motor->motor.feedback.bus_voltage, &raw_vbus, sizeof(float));
memcpy(&motor->motor.feedback.torque_current, &raw_ibus, sizeof(float));
}
break;
case GET_IQ: // 0x018
{
uint32_t raw_iq_set, raw_iq_meas;
raw_iq_set = (msg->data[0] | msg->data[1]<<8 | msg->data[2]<<16 | msg->data[3]<<24);
raw_iq_meas = (msg->data[4] | msg->data[5]<<8 | msg->data[6]<<16 | msg->data[7]<<24);
// memcpy(&motor->motor.feedback.iq_setpoint, &raw_iq_set, sizeof(float));
// memcpy(&motor->motor.feedback.iq_measured, &raw_iq_meas, sizeof(float));
}
break;
default:
break;
}
}
}
/* Exported functions ------------------------------------------------------- */
// 注册一个新的电机实例到管理器
int8_t ODrive_Register(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= ODRIVE_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
ODrive_t *new_motor = (ODrive_t*)BSP_Malloc(sizeof(ODrive_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(ODrive_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, param->id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
// 更新指定电机的反馈数据
int8_t ODrive_Update(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, param->id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
Motor_Decode(motor, &rx_msg);
return DEVICE_OK;
}
}
return DEVICE_ERR_NO_DEV;
}
// 更新所有CAN总线下所有电机的反馈数据
int8_t ODrive_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
ODrive_CANManager_t *manager = MOTOR_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor != NULL) {
if (ODrive_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
// 获取指定参数对应的电机实例指针
ODrive_t* ODrive_GetMotor(ODrive_Param_t *param) {
if (param == NULL) return NULL;
ODrive_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
ODrive_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
// 设置指定电机的输出值
int8_t ODrive_SetOutput(ODrive_Param_t *param, float value) {
if (param == NULL) return DEVICE_ERR_NULL;
// 如果电机反转标志为 true则反向值
if (param->reverse) {
value = -value;
}
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id;
uint8_t *pVal = (uint8_t *)&value;
// 选择命令 ID 和数据打包方式
switch (param->mode) {
case POSITION_CONTROL: {
command_id = SET_INPUT_POS;
float pos = value;
int16_t vel_ff = 0; // 可扩展为参数传入 0就行
int16_t torque_ff = 0; // 可扩展为参数传入 0就行
uint8_t *pPos = (uint8_t *)&pos;
uint8_t *pVel = (uint8_t *)&vel_ff;
uint8_t *pTor = (uint8_t *)&torque_ff;
memcpy(&tx_frame.data[0], pPos, 4);
memcpy(&tx_frame.data[4], pVel, 2);
memcpy(&tx_frame.data[6], pTor, 2);
tx_frame.dlc = 8;
break;
}
case VELOCITY_CONTROL: {
command_id = SET_INPUT_VEL;
float vel = value;
float torque_ff = 0.0f; // 可扩展为参数传入
uint8_t *pVel = (uint8_t *)&vel;
uint8_t *pTor = (uint8_t *)&torque_ff;
memcpy(&tx_frame.data[0], pVel, 4);
memcpy(&tx_frame.data[4], pTor, 4);
tx_frame.dlc = 8;
break;
}
case TORQUE_CONTROL: {
command_id = SET_INPUT_TORQUE;
memcpy(&tx_frame.data[0], pVal, 4);
tx_frame.dlc = 4;
break;
}
case VOLTAGE_CONTROL:
default:
return DEVICE_ERR; // 暂不支持电压模式
}
// 组装 CAN ID标准帧
tx_frame.id = (param->id << 5) | command_id;
// 标准数据帧
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 设置加速度和减速度
int8_t ODrive_SetAccel(ODrive_Param_t *param, float accel, float decel) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = SET_TRAJ_ACCEL_LIMITS;
uint8_t *pAccel = (uint8_t *)&accel;
uint8_t *pDecel = (uint8_t *)&decel;
memcpy(&tx_frame.data[0], pAccel, 4);
memcpy(&tx_frame.data[4], pDecel, 4);
tx_frame.dlc = 8;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 获取位置和速度反馈
int8_t ODrive_RequestEncoderEstimates(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = ENCODER_ESTIMATES; // 请求编码器估计值命令
uint8_t zero_data[8] = {0}; // 发送全 0 数据ODrive 协议要求)
memcpy(tx_frame.data, zero_data, 8);
tx_frame.dlc = 8;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 设置轴请求状态(一般用来重启 ODrive 的某个轴)
// ODrive_SetAxisRequestedState(odrive_axis[0], CLOSED_LOOP_CONTROL);
int8_t ODrive_SetAxisRequestedState(ODrive_Param_t *param, Axis_State state) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = SET_AXIS_REQUESTED_STATE;
// 将 state 转为 4 字节
memcpy(tx_frame.data, &state, 4);
memset(&tx_frame.data[4], 0, 4);
tx_frame.dlc = 4;
// 组装 CAN ID
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 清除错误
int8_t ODrive_ClearErrors(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = CLEAR_ERRORS;
memset(tx_frame.data, 0, 8);
tx_frame.dlc = 0;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
// 重启 ODrive
int8_t ODrive_Reboot(ODrive_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id = REBOOT_ODRIVE;
memset(tx_frame.data, 0, 8);
tx_frame.dlc = 0;
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}

View File

@ -0,0 +1,162 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Private define ----------------------------------------------------------- */
//ODrive型号根据实际情况调整
#define ODRIVE_MAX_MOTORS 2
//COMMAND ID
#define ODRIVE_HEARTBEAT_MESSAGE 0x001 // ODrive心跳消息
#define SET_AXIS_NODE_ID 0x006 // 设置电机节点ID
#define GET_ENCODER_ESTIMATES 0x008 // 获取编码器估计值
#define GET_ENCODER_COUNT 0x00A // 获取编码器计数
#define SET_AXIS_REQUESTED_STATE 0x007 // 设置电机请求状态
#define ENCODER_ESTIMATES 0x009 // 编码器估计值
#define GET_ENCODER_COUNT 0x00A // 获取编码器计数
#define SET_CONTROLLER_MODES 0x00B // 设置控制器模式
#define SET_INPUT_POS 0x00C // 设置输入位置
#define SET_INPUT_VEL 0x00D // 设置输入速度
#define SET_INPUT_TORQUE 0x00E // 设置输入转矩
#define SET_LIMITS 0x00F // 设置限制
#define GET_IQ 0x014 // 获取电流
#define REBOOT_ODRIVE 0x016 // 重启ODrive
#define GET_BUS_VOLTAGE_CURRENT 0x017 // 获取总线电压和电流
#define CLEAR_ERRORS 0x018 // 清除错误
#define SET_POSITION_GAIN 0x01A // 设置位置增益
#define SET_VEL_GAINS 0x01B // 设置速度增益
#define SET_TRAJ_ACCEL_LIMITS 0x012 // 设置轨迹加速度限制
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
//Axis States
typedef enum {
UNDEFINED = 0x0,
IDLE = 0x1,
STARTUP_SEQUENCE = 0x2,
FULL_CALIBRATION_SEQUENCE = 0x3,
MOTOR_CALIBRATION = 0x4,
ENCODER_INDEX_SEARCH = 0x6,
ENCODER_OFFSET_CALIBRATION = 0x7,
CLOSED_LOOP_CONTROL = 0x8,
LOCKIN_SPIN = 0x9,
ENCODER_DIR_FIND = 0xA,
HOMING = 0xB,
ENCODER_HALL_POLARITY_CALIBRATION = 0xC,
ENCODER_HALL_PHASE_CALIBRATION = 0xD
} Axis_State;
//Control Modes
typedef enum{
VOLTAGE_CONTROL = 0x0,
TORQUE_CONTROL = 0x1,
VELOCITY_CONTROL = 0x2,
POSITION_CONTROL = 0x3
} Control_Mode;
/*每个电机需要的参数*/
typedef struct {
BSP_CAN_t can;
uint16_t id;
uint16_t mode;
bool reverse;
} ODrive_Param_t;
/*电机实例*/
typedef struct ODrive_t {
ODrive_Param_t param;
MOTOR_t motor;
} ODrive_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct {
BSP_CAN_t can;
ODrive_t *motors[ODRIVE_MAX_MOTORS];
uint8_t motor_count;
} ODrive_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief odrive电机
* @param param
* @return
*/
int8_t ODrive_Register(ODrive_Param_t *param);
/**
* @brief
* @param param
* @return
*/
int8_t ODrive_Update(ODrive_Param_t *param);
/** * @brief 更新所有ODrive电机状态
* @return
*/
int8_t ODrive_UpdateAll(void);
/**
* @brief
* @param param
* @param value
* @return
*/
int8_t ODrive_SetOutput(ODrive_Param_t *param, float value);
/** * @brief 设置电机加速度和减速度限制
* @param param
* @param accel
* @param decel
* @return
*/
int8_t ODrive_SetAccel(ODrive_Param_t *param, float accel, float decel);
/**
* @brief
* @param param
* @return
*/
ODrive_t* ODrive_GetMotor(ODrive_Param_t *param);
/** * @brief 获取指定电机的编码器估计值
* @param param
* @return
*/
int8_t ODrive_RequestEncoderEstimates(ODrive_Param_t *param);
/** * @brief 设置轴请求状态(一般用来重启 ODrive 的某个轴)
* @param param
* @return
*/
int8_t ODrive_SetAxisRequestedState(ODrive_Param_t *param, Axis_State state);
/** * @brief 清除错误
* @param param
* @return
*/
int8_t ODrive_ClearErrors(ODrive_Param_t *param);
/** * @brief 重启 ODrive
* @param param
* @return
*/
int8_t ODrive_Reboot(ODrive_Param_t *param);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,250 @@
/*
CAN总线上的设备
CAN总线上挂载的设备抽象成一个设备进行配置和控制
*/
/* Includes ----------------------------------------------------------------- */
#include "motor_vesc.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/can.h"
#include "bsp/mm.h"
#include "bsp/time.h"
#include "component/user_math.h"
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
/**************************************
*
**************************************/
void assert_param_duty(float *duty){
// 如果 duty 是 -1.0 ~ 1.0,则最大值用 wtrcfg_VESC_COMMAND_DUTY_MAX / 100
float max_duty = wtrcfg_VESC_COMMAND_DUTY_MAX / 100.0f;
if (fabsf(*duty) > max_duty) {
*duty = (*duty > 0) ? max_duty : -max_duty;
}
}
void assert_param_current(float *current){
if( fabsf(*current) > wtrcfg_VESC_COMMAND_CURRENT_MAX )
*current = *current > 0 ? wtrcfg_VESC_COMMAND_CURRENT_MAX : - wtrcfg_VESC_COMMAND_CURRENT_MAX ;
}
void assert_param_rpm(float *rpm){
if( fabsf(*rpm) > wtrcfg_VESC_COMMAND_ERPM_MAX )
*rpm = *rpm > 0 ? wtrcfg_VESC_COMMAND_ERPM_MAX : - wtrcfg_VESC_COMMAND_ERPM_MAX ;
}
void assert_param_pos(float *pos){
if( fabsf(*pos) > wtrcfg_VESC_COMMAND_POS_MAX )
*pos = *pos > 0 ? wtrcfg_VESC_COMMAND_POS_MAX : - wtrcfg_VESC_COMMAND_POS_MAX ;
}
static VESC_CANManager_t *can_managers[BSP_CAN_NUM] = {NULL};
// 获取指定CAN总线的电机管理器指针
static VESC_CANManager_t* MOTOR_GetCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return NULL;
return can_managers[can];
}
// 为指定CAN总线创建电机管理器
static int8_t MOTOR_CreateCANManager(BSP_CAN_t can) {
if (can >= BSP_CAN_NUM) return DEVICE_ERR;
if (can_managers[can] != NULL) return DEVICE_OK;
can_managers[can] = (VESC_CANManager_t*)BSP_Malloc(sizeof(VESC_CANManager_t));
if (can_managers[can] == NULL) return DEVICE_ERR;
memset(can_managers[can], 0, sizeof(VESC_CANManager_t));
can_managers[can]->can = can;
return DEVICE_OK;
}
// 解析CAN报文更新电机反馈信息
static void Motor_VESC_Decode(VESC_t *motor, BSP_CAN_Message_t *msg)
{
if (motor == NULL || msg == NULL) return;
motor->motor.feedback.rotor_speed =
((int32_t)msg->data[0] << 24) |
((int32_t)msg->data[1] << 16) |
((int32_t)msg->data[2] << 8) |
((int32_t)msg->data[3]);
// torque_current: 低 2 字节 (data[4], data[5])
int16_t raw_current = (int16_t)((msg->data[5] << 8) | msg->data[4]);
motor->motor.feedback.torque_current = raw_current / 1000.0f; // 从 0.1A -> A
// duty_cycle: 低 2 字节 (data[6], data[7])
int16_t raw_duty = (int16_t)((msg->data[7] << 8) | msg->data[6]);
//motor->motor.feedback.duty_cycle = raw_duty / 1000.0f; // 从千分之一 -> (-1.0 ~ 1.0)
}
/* Exported functions ------------------------------------------------------- */
// 注册一个新的电机实例到管理器
int8_t VESC_Register(VESC_Param_t *param) {
if (param == NULL) return DEVICE_ERR_NULL;
if (MOTOR_CreateCANManager(param->can) != DEVICE_OK) return DEVICE_ERR;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR;
// 检查是否已注册
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
return DEVICE_ERR_INITED;
}
}
// 检查数量
if (manager->motor_count >= VESC_MAX_MOTORS) return DEVICE_ERR;
// 创建新电机实例
VESC_t *new_motor = (VESC_t*)BSP_Malloc(sizeof(VESC_t));
if (new_motor == NULL) return DEVICE_ERR;
memcpy(&new_motor->param, param, sizeof(VESC_Param_t));
memset(&new_motor->motor, 0, sizeof(MOTOR_t));
new_motor->motor.reverse = param->reverse;
// 注册CAN接收ID
if (BSP_CAN_RegisterId(param->can, param->id, 3) != BSP_OK) {
BSP_Free(new_motor);
return DEVICE_ERR;
}
manager->motors[manager->motor_count] = new_motor;
manager->motor_count++;
return DEVICE_OK;
}
// 更新指定电机的反馈数据(扩展帧方式)
int8_t VESC_Update(VESC_Param_t *param)
{
if (param == NULL) return DEVICE_ERR_NULL;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return DEVICE_ERR_NO_DEV;
VESC_t *motor = NULL;
for (int i = 0; i < manager->motor_count; i++) {
if (manager->motors[i] && manager->motors[i]->param.id == param->id) {
motor = manager->motors[i];
break;
}
}
if (motor == NULL) return DEVICE_ERR_NO_DEV;
// 根据电机 ID 获取对应扩展帧 ID
uint32_t ext_id = 0;
switch (param->id) {
case VESC_1: ext_id = CAN_VESC5065_M1_MSG1; break;
case VESC_2: ext_id = CAN_VESC5065_M2_MSG1; break;
case VESC_4: ext_id = CAN_VESC5065_M3_MSG1; break;
default: return DEVICE_ERR_NO_DEV;
}
BSP_CAN_Message_t rx_msg;
if (BSP_CAN_GetMessage(param->can, ext_id, &rx_msg, BSP_CAN_TIMEOUT_IMMEDIATE) != BSP_OK) {
uint64_t now_time = BSP_TIME_Get();
if (now_time - motor->motor.header.last_online_time > 1000) {
motor->motor.header.online = false;
return DEVICE_ERR_NO_DEV;
}
return DEVICE_ERR;
}
motor->motor.header.online = true;
motor->motor.header.last_online_time = BSP_TIME_Get();
Motor_VESC_Decode(motor, &rx_msg);
return DEVICE_OK;
}
// 更新所有CAN总线下所有电机的反馈数据
int8_t VESC_UpdateAll(void) {
int8_t ret = DEVICE_OK;
for (int can = 0; can < BSP_CAN_NUM; can++) {
VESC_CANManager_t *manager = MOTOR_GetCANManager((BSP_CAN_t)can);
if (manager == NULL) continue;
for (int i = 0; i < manager->motor_count; i++) {
VESC_t *motor = manager->motors[i];
if (motor != NULL) {
if (VESC_Update(&motor->param) != DEVICE_OK) {
ret = DEVICE_ERR;
}
}
}
}
return ret;
}
// 获取指定参数对应的电机实例指针
VESC_t* VESC_GetMotor(VESC_Param_t *param) {
if (param == NULL) return NULL;
VESC_CANManager_t *manager = MOTOR_GetCANManager(param->can);
if (manager == NULL) return NULL;
for (int i = 0; i < manager->motor_count; i++) {
VESC_t *motor = manager->motors[i];
if (motor && motor->param.id == param->id) {
return motor;
}
}
return NULL;
}
// 设置指定电机的输出值
int8_t VESC_SetOutput(VESC_Param_t *param, float value)
{
if (param == NULL) return DEVICE_ERR_NULL;
BSP_CAN_StdDataFrame_t tx_frame;
uint16_t command_id;
if (param->reverse) {
value = -value;
}
switch (param->mode)
{
case DUTY_CONTROL: {
assert_param_duty(&value); // 调用你现有的限幅函数
command_id = CAN_PACKET_SET_DUTY;
int32_t duty_val = (int32_t)(value * 1e5f); // duty 放大 1e5
memcpy(&tx_frame.data[0], &duty_val, 4);
tx_frame.dlc = 4;
break;
}
case RPM_CONTROL: {
assert_param_rpm(&value);
command_id = CAN_PACKET_SET_RPM;
int32_t rpm_val = (int32_t)value;
memcpy(&tx_frame.data[0], &rpm_val, 4);
tx_frame.dlc = 4;
break;
}
case CURRENT_CONTROL: {
assert_param_current(&value);
command_id = CAN_PACKET_SET_CURRENT;
int32_t cur_val = (int32_t)(value * 1e3f); // A -> mA 0-50A
memcpy(&tx_frame.data[0], &cur_val, 4);
tx_frame.dlc = 4;
break;
}
case POSITION_CONTROL: {
assert_param_pos(&value);
command_id = CAN_PACKET_SET_POS;
memcpy(&tx_frame.data[0], &value, 4);
tx_frame.dlc = 4;
break;
}
default:
return DEVICE_ERR;
}
tx_frame.id = (param->id << 5) | command_id;
return BSP_CAN_TransmitStdDataFrame(param->can, &tx_frame) == BSP_OK ? DEVICE_OK : DEVICE_ERR;
}
int8_t VESC_Relax(VESC_Param_t *param) {
return VESC_SetOutput(param, 0.0f);
}
int8_t VESC_Offine(VESC_Param_t *param) {
VESC_t *motor = VESC_GetMotor(param);
if (motor) {
motor->motor.header.online = false;
return DEVICE_OK;
}
return DEVICE_ERR_NO_DEV;
}

View File

@ -0,0 +1,146 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
/* Includes ----------------------------------------------------------------- */
#include "device/device.h"
#include "device/motor.h"
#include "bsp/can.h"
/* Private define ----------------------------------------------------------- */
#define wtrcfg_VESC_COMMAND_DUTY_MAX 100
#define wtrcfg_VESC_COMMAND_CURRENT_MAX 10
#define wtrcfg_VESC_COMMAND_POS_MAX 360
#define wtrcfg_VESC_COMMAND_ERPM_MAX 35000
#define wtrcfg_VESC_UART_TIMEOUT 0xff
// VESC数量根据实际情况调整
#define VESC_MAX_MOTORS 4
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
typedef enum
{
VESC_1 = 1,
VESC_2 = 2,
VESC_3 = 3,
VESC_4 = 4,
CAN_VESC5065_M1_MSG1 = 0x901, // vesc的数据回传使用了扩展id[0:7]为驱动器id[8:15]为帧类型
CAN_VESC5065_M2_MSG1 = 0x902,
CAN_VESC5065_M3_MSG1 = 0x903,
CAN_VESC5065_M4_MSG1 = 0x904,
}VESC_ID;
typedef enum
{
CAN_PACKET_SET_DUTY = 0,
CAN_PACKET_SET_CURRENT = 1,
CAN_PACKET_SET_CURRENT_BRAKE = 2,
CAN_PACKET_SET_RPM = 3,
CAN_PACKET_SET_POS = 4,
CAN_PACKET_FILL_RX_BUFFER = 5,
CAN_PACKET_FILL_RX_BUFFER_LONG = 6,
CAN_PACKET_PROCESS_RX_BUFFER = 7,
CAN_PACKET_PROCESS_SHORT_BUFFER = 8,
CAN_PACKET_STATUS = 9,
CAN_PACKET_SET_CURRENT_REL = 10,
CAN_PACKET_SET_CURRENT_BRAKE_REL = 11,
CAN_PACKET_SET_CURRENT_HANDBRAKE = 12,
CAN_PACKET_SET_CURRENT_HANDBRAKE_REL = 13
} CAN_PACKET_ID;
// Control Modes
typedef enum
{
DUTY_CONTROL = 0x0,
RPM_CONTROL = 0x1,
CURRENT_CONTROL = 0x2,
POSITION_CONTROL = 0x3
} Control_Mode;
/*每个电机需要的参数*/
typedef struct
{
BSP_CAN_t can;
uint16_t id;
uint16_t mode;
bool reverse;
} VESC_Param_t;
/*电机实例*/
typedef struct ODrive_t
{
VESC_Param_t param;
MOTOR_t motor;
} VESC_t;
/*CAN管理器管理一个CAN总线上所有的电机*/
typedef struct
{
BSP_CAN_t can;
VESC_t *motors[VESC_MAX_MOTORS];
uint8_t motor_count;
} VESC_CANManager_t;
/* Exported functions prototypes -------------------------------------------- */
/**
* @brief vesc电机
* @param param
* @return
*/
int8_t VESC_Register(VESC_Param_t *param);
/**
* @brief
* @param param
* @return
*/
int8_t VESC_Update(VESC_Param_t *param);
/**
* @brief
* @return
*/
int8_t VESC_UpdateAll(void);
/**
* @brief
* @param param
* @param value
* @return
*/
int8_t VESC_SetOutput(VESC_Param_t *param, float value);
/**
* @brief
* @param param
* @return
*/
VESC_t* VESC_GetMotor(VESC_Param_t *param);
/**
* @brief 使0
* @param param
* @return
*/
int8_t VESC_Relax(VESC_Param_t *param);
/**
* @brief 使线线false
* @param param
* @return
*/
int8_t VESC_Offine(VESC_Param_t *param);
#ifdef __cplusplus
}
#endif