Merge fdcan-feature into main

This commit is contained in:
Robofish 2026-01-01 16:59:45 +08:00
commit daf0a28517
26 changed files with 199 additions and 83 deletions

22
.vscode/settings.json vendored
View File

@ -1,22 +0,0 @@
{
"files.associations": {
"user_math.h": "c",
"bsp.h": "c",
"stdint.h": "c",
"array": "c",
"string": "c",
"string_view": "c",
"vector": "c",
"can.h": "c",
"device.h": "c",
"gpio.h": "c",
"uart.h": "c",
"motor_rm.h": "c",
"mm.h": "c",
"capacity.h": "c",
"error_detect.h": "c",
"bmi088.h": "c",
"time.h": "c",
"motor.h": "c"
}
}

View File

@ -11,8 +11,6 @@ OutputBaseFilename=MRobotInstaller
Source: "dist\MRobot\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs Source: "dist\MRobot\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
; 复制 assets 资源文件到安装目录(支持后续更新) ; 复制 assets 资源文件到安装目录(支持后续更新)
Source: "assets\logo\*"; DestDir: "{app}\assets\logo"; Flags: ignoreversion recursesubdirs Source: "assets\logo\*"; DestDir: "{app}\assets\logo"; Flags: ignoreversion recursesubdirs
Source: "assets\User_code\*"; DestDir: "{app}\assets\User_code"; Flags: ignoreversion recursesubdirs
Source: "assets\mech_lib\*"; DestDir: "{app}\assets\mech_lib"; Flags: ignoreversion recursesubdirs
[Icons] [Icons]
Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico" Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico"

View File

@ -86,5 +86,5 @@
使用以下命令构建可执行文件: 使用以下命令构建可执行文件:
```bash ```bash
pyinstaller MRobot.py --onefile --windowed --add-data "assets;assets" --add-data "app;app" --add-data "app/tools;app/tools" pyinstaller MRobot.py --onefile --windowed --add-data "assets/logo;assets/logo" --add-data "app;app" --add-data "app/tools;app/tools"
``` ```

View File

@ -803,6 +803,92 @@ class bsp_spi(BspPeripheralBase):
) )
class bsp_fdcan(BspPeripheralBase):
def __init__(self, project_path):
super().__init__(
project_path,
"FDCAN",
{'header': 'fdcan.h', 'source': 'fdcan.c'},
"BSP_FDCAN",
"hfdcan",
"fdcan",
get_available_fdcan
)
def _generate_header_file(self, configs, template_dir):
"""生成FDCAN头文件包含动态宏定义"""
template_path = os.path.join(template_dir, self.template_names['header'])
template_content = CodeGenerator.load_template(template_path)
if not template_content:
return False
# 生成枚举
enum_lines = [f" {self.enum_prefix}_{name}," for name, _ in configs]
content = CodeGenerator.replace_auto_generated(
template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines)
)
# 生成FDCAN实例使能宏定义
macro_lines = []
for name, instance in configs:
fdcan_num = ''.join(filter(str.isdigit, instance)) # 提取数字如FDCAN1 -> 1
macro_lines.append(f"#define {instance}_EN")
# 替换宏定义区域
content = CodeGenerator.replace_auto_generated(
content, "AUTO GENERATED FDCAN_EN", "\n".join(macro_lines)
)
# 生成FIFO配置宏定义
fifo_lines = []
fdcan_count = len(configs)
for idx, (name, instance) in enumerate(configs):
fdcan_num = ''.join(filter(str.isdigit, instance))
# FDCAN1使用FIFO0其他使用FIFO1
if instance == 'FDCAN1':
fifo_lines.append(f"#define {instance}_RX_FIFO 0")
else:
fifo_lines.append(f"#define {instance}_RX_FIFO 1")
content = CodeGenerator.replace_auto_generated(
content, "AUTO GENERATED FDCAN_RX_FIFO", "\n".join(fifo_lines)
)
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}")
CodeGenerator.save_with_preserve(output_path, content)
return True
def _generate_source_file(self, configs, template_dir):
"""生成FDCAN源文件"""
template_path = os.path.join(template_dir, self.template_names['source'])
template_content = CodeGenerator.load_template(template_path)
if not template_content:
return False
# Get函数
get_lines = []
for idx, (name, instance) in enumerate(configs):
get_lines.append(f" case {idx}: return {self.enum_prefix}_{name};")
content = CodeGenerator.replace_auto_generated(
template_content, "AUTO GENERATED FDCAN_GET", "\n".join(get_lines)
)
# Handle函数
handle_lines = []
for name, instance in configs:
fdcan_num = ''.join(filter(str.isdigit, instance))
handle_lines.append(f"#ifdef {instance}_EN")
handle_lines.append(f" case {self.enum_prefix}_{name}: return &{self.handle_prefix}{fdcan_num};")
handle_lines.append(f"#endif")
content = CodeGenerator.replace_auto_generated(
content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
)
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
CodeGenerator.save_with_preserve(output_path, content)
return True
def patch_uart_interrupts(project_path, uart_instances): def patch_uart_interrupts(project_path, uart_instances):
"""自动修改 stm32f4xx_it.c插入 UART BSP 相关代码""" """自动修改 stm32f4xx_it.c插入 UART BSP 相关代码"""
it_path = os.path.join(project_path, "Core/Src/stm32f4xx_it.c") it_path = os.path.join(project_path, "Core/Src/stm32f4xx_it.c")

View File

@ -5,10 +5,20 @@ import sys
import os import os
def resource_path(relative_path): def resource_path(relative_path):
"""获取资源文件的绝对路径,兼容打包和开发环境""" """获取资源文件的绝对路径,兼容打包和开发环境
对于 logo 文件使用打包的临时目录只有 logo 被打包
对于其他资源使用可执行文件所在目录
"""
if getattr(sys, 'frozen', False):
# 打包环境
if 'logo' in relative_path:
# logo 文件使用打包的临时目录
if hasattr(sys, '_MEIPASS'): if hasattr(sys, '_MEIPASS'):
# PyInstaller 打包后的临时目录
return os.path.join(sys._MEIPASS, relative_path) return os.path.join(sys._MEIPASS, relative_path)
# 其他资源使用可执行文件所在目录
exe_dir = os.path.dirname(sys.executable)
return os.path.join(exe_dir, relative_path)
# 开发环境
return os.path.join(os.path.abspath("."), relative_path) return os.path.join(os.path.abspath("."), relative_path)
class HomeInterface(QWidget): class HomeInterface(QWidget):

View File

@ -71,7 +71,7 @@ class analyzing_ioc:
@staticmethod @staticmethod
def get_enabled_can_from_ioc(ioc_path): def get_enabled_can_from_ioc(ioc_path):
""" """
获取已启用的CAN列表 获取已启用的CAN列表不包括FDCAN
返回格式: ['CAN1', 'CAN2'] 返回格式: ['CAN1', 'CAN2']
""" """
enabled_can = [] enabled_can = []
@ -84,6 +84,7 @@ class analyzing_ioc:
key, value = line.split('=', 1) key, value = line.split('=', 1)
key = key.strip() key = key.strip()
value = value.strip() value = value.strip()
# 只匹配CAN不包括FDCAN
if key.startswith('Mcu.IP') and value.startswith('CAN') and not value.startswith('FDCAN'): if key.startswith('Mcu.IP') and value.startswith('CAN') and not value.startswith('FDCAN'):
can_name = value.split('.')[0] if '.' in value else value can_name = value.split('.')[0] if '.' in value else value
if can_name not in enabled_can: if can_name not in enabled_can:

View File

@ -97,44 +97,19 @@ class CodeGenerator:
assets_dir = "" assets_dir = ""
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
# 打包后的环境 # 打包后的环境 - 始终使用可执行文件所在目录
print("检测到打包环境") # 这样可以使用安装目录下的文件,而不是打包进去的文件
# 优先使用可执行文件所在目录(支持更新后的文件)
exe_dir = os.path.dirname(sys.executable) exe_dir = os.path.dirname(sys.executable)
exe_assets = os.path.join(exe_dir, "assets") assets_dir = os.path.join(exe_dir, "assets")
print(f"打包环境:使用可执行文件目录: {assets_dir}")
# 如果exe目录下不存在assets但_MEIPASS中有则首次复制过去 # 如果assets目录不存在创建它
if not os.path.exists(exe_assets) and hasattr(sys, '_MEIPASS'): if not os.path.exists(assets_dir):
base_path = getattr(sys, '_MEIPASS')
meipass_assets = os.path.join(base_path, "assets")
if os.path.exists(meipass_assets):
try: try:
import shutil os.makedirs(assets_dir, exist_ok=True)
print(f"首次运行:从 {meipass_assets} 复制到 {exe_assets}") print(f"创建assets目录: {assets_dir}")
shutil.copytree(meipass_assets, exe_assets)
print("初始资源复制成功")
except Exception as e: except Exception as e:
print(f"复制初始资源失败: {e}") print(f"创建assets目录失败: {e}")
# 优先使用exe目录下的assets这样可以读取更新后的文件
if os.path.exists(exe_assets):
assets_dir = exe_assets
print(f"使用可执行文件目录: {assets_dir}")
# 后备方案使用PyInstaller的临时解包目录
elif hasattr(sys, '_MEIPASS'):
base_path = getattr(sys, '_MEIPASS')
assets_dir = os.path.join(base_path, "assets")
print(f"后备使用PyInstaller临时目录: {assets_dir}")
# 最后尝试工作目录
else:
cwd_assets = os.path.join(os.getcwd(), "assets")
if os.path.exists(cwd_assets):
assets_dir = cwd_assets
print(f"从工作目录找到assets: {assets_dir}")
else:
assets_dir = exe_assets # 即使不存在也使用exe目录后续会创建
print(f"使用默认路径(将创建): {assets_dir}")
else: else:
# 开发环境 # 开发环境
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))

View File

@ -0,0 +1,8 @@
{
"id": "7751771f-1363-4606-b292-2db511004bae",
"name": "admin",
"description": "默认管理账户",
"categories": [],
"created_at": "2025-12-29T20:48:32.536465",
"updated_at": "2025-12-29T20:48:32.536472"
}

Binary file not shown.

View File

@ -1,5 +1,6 @@
uart,请开启uart的dma和中断 uart,请开启uart的dma和中断
can,请开启can中断使用函数前请确保can已经初始化。一定要开启can发送中断 can,请开启can中断使用函数前请确保can已经初始化。一定要开启can发送中断
fdcan,请开启fdcan中断支持经典CAN和FDCAN模式。会自动根据IOC配置生成对应的宏定义。
gpio,会自动读取cubemx中配置为gpio的引脚并自动区分输入输出和中断。 gpio,会自动读取cubemx中配置为gpio的引脚并自动区分输入输出和中断。
spi,请开启spi的dma和中断 spi,请开启spi的dma和中断
i2c,要求开始spi中断 i2c,要求开始spi中断

1 uart 请开启uart的dma和中断
2 can 请开启can中断,使用函数前请确保can已经初始化。一定要开启can发送中断!!!
3 fdcan 请开启fdcan中断,支持经典CAN和FDCAN模式。会自动根据IOC配置生成对应的宏定义。
4 gpio 会自动读取cubemx中配置为gpio的引脚,并自动区分输入输出和中断。
5 spi 请开启spi的dma和中断
6 i2c 要求开始spi中断

View File

@ -90,7 +90,9 @@ static bool BSP_FDCAN_TxQueueIsEmpty(BSP_FDCAN_t fdcan);
/* Private functions -------------------------------------------------------- */ /* Private functions -------------------------------------------------------- */
static BSP_FDCAN_t FDCAN_Get(FDCAN_HandleTypeDef *hfdcan) { static BSP_FDCAN_t FDCAN_Get(FDCAN_HandleTypeDef *hfdcan) {
if (hfdcan == NULL) return BSP_FDCAN_ERR; if (hfdcan == NULL) return BSP_FDCAN_ERR;
/* AUTO GENERATED FDCAN_GET */ if (hfdcan->Instance == FDCAN1) return BSP_FDCAN_1;
else if (hfdcan->Instance == FDCAN2) return BSP_FDCAN_2;
else if (hfdcan->Instance == FDCAN3) return BSP_FDCAN_3;
else return BSP_FDCAN_ERR; else return BSP_FDCAN_ERR;
} }
@ -326,11 +328,10 @@ int8_t BSP_FDCAN_Init(void) {
queue_mutex = osMutexNew(NULL); queue_mutex = osMutexNew(NULL);
if (queue_mutex == NULL) return BSP_ERR; if (queue_mutex == NULL) return BSP_ERR;
/* 配置并启动 FDCAN 实例,绑定中断/回调 */
/* AUTO GENERATED FDCAN_INIT */
inited = true; inited = true;
/* 配置并启动 FDCAN 实例,绑定中断/回调 */
//========== 过滤器配置说明:========================== //========== 过滤器配置说明:==========================
// 过滤器编号相对于每个相当于经典can过滤器的bank // 过滤器编号相对于每个相当于经典can过滤器的bank
// sFilterConfig.FilterIndex = 0 to 127(标准ID) or 0 to 63(扩展ID); // sFilterConfig.FilterIndex = 0 to 127(标准ID) or 0 to 63(扩展ID);
@ -370,10 +371,47 @@ int8_t BSP_FDCAN_Init(void) {
// IsCalibrationMsg = 0 or 1; // IsCalibrationMsg = 0 or 1;
// fdcan_filter_table.h // fdcan_filter_table.h
//================================================================================= //=================================================================================
/* 配置并启动 FDCAN 实例,绑定中断/回调 */ /* 依据上述说明,配置过滤器并启动FDCAN */
/* AUTO GENERATED FDCAN_INIT */ FDCAN_FilterTypeDef sFilterConfig;
inited = true; #ifdef FDCAN1_EN
#define hfdcan hfdcan1
#define FDCANX_RX_FIFO FDCAN1_RX_FIFO
FDCAN1_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)
#undef hfdcan
#undef FDCANX_RX_FIFO
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN1_GLOBAL_FILTER);
HAL_FDCAN_ActivateNotification(&hfdcan1, FDCANx_NOTIFY_FLAGS(FDCAN1_RX_FIFO), 0);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_1, FDCANX_MSG_PENDING_CB(FDCAN1_RX_FIFO), BSP_FDCAN_RxFifo0Callback);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_1, HAL_FDCAN_TX_EVENT_FIFO_CB, BSP_FDCAN_TxCompleteCallback);
HAL_FDCAN_Start(&hfdcan1);
#endif
#ifdef FDCAN2_EN
#define hfdcan hfdcan2
#define FDCANX_RX_FIFO FDCAN2_RX_FIFO
FDCAN2_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)
#undef hfdcan
#undef FDCANX_RX_FIFO
HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN2_GLOBAL_FILTER);
HAL_FDCAN_ActivateNotification(&hfdcan2, FDCANx_NOTIFY_FLAGS(FDCAN2_RX_FIFO), 0);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_2, FDCANX_MSG_PENDING_CB(FDCAN2_RX_FIFO), BSP_FDCAN_RxFifo1Callback);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_2, HAL_FDCAN_TX_EVENT_FIFO_CB, BSP_FDCAN_TxCompleteCallback);
HAL_FDCAN_Start(&hfdcan2);
#endif
#ifdef FDCAN3_EN
#define hfdcan hfdcan3
#define FDCANX_RX_FIFO FDCAN3_RX_FIFO
FDCAN3_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)
#undef hfdcan
#undef FDCANX_RX_FIFO
HAL_FDCAN_ConfigGlobalFilter(&hfdcan3, FDCAN3_GLOBAL_FILTER);
HAL_FDCAN_ActivateNotification(&hfdcan3, FDCANx_NOTIFY_FLAGS(FDCAN3_RX_FIFO), 0);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_3, FDCANX_MSG_PENDING_CB(FDCAN3_RX_FIFO), BSP_FDCAN_RxFifo1Callback);
BSP_FDCAN_RegisterCallback(BSP_FDCAN_3, HAL_FDCAN_TX_EVENT_FIFO_CB, BSP_FDCAN_TxCompleteCallback);
HAL_FDCAN_Start(&hfdcan3);
#endif
#undef FDCAN_FILTER_TO_RXFIFO_ENUM_INNER #undef FDCAN_FILTER_TO_RXFIFO_ENUM_INNER
#undef FDCAN_FILTER_TO_RXFIFO_ENUM #undef FDCAN_FILTER_TO_RXFIFO_ENUM
@ -389,11 +427,13 @@ int8_t BSP_FDCAN_Init(void) {
FDCAN_HandleTypeDef *BSP_FDCAN_GetHandle(BSP_FDCAN_t fdcan) { FDCAN_HandleTypeDef *BSP_FDCAN_GetHandle(BSP_FDCAN_t fdcan) {
if (fdcan >= BSP_FDCAN_NUM) return NULL; if (fdcan >= BSP_FDCAN_NUM) return NULL;
switch (fdcan) { switch (fdcan) {
/* AUTO GENERATED BSP_FDCAN_GET_HANDLE */ /* AUTO GENERATED BSP_FDCAN_GET_HANDLE BEGIN */
default: case BSP_FDCAN_1: return &hfdcan1;
return NULL; case BSP_FDCAN_2: return &hfdcan2;
case BSP_FDCAN_3: return &hfdcan3;
/* AUTO GENERATED BSP_FDCAN_GET_HANDLE END */
default: return NULL;
} }
} }

View File

@ -22,13 +22,32 @@ extern "C" {
#define BSP_FDCAN_TIMEOUT_FOREVER osWaitForever #define BSP_FDCAN_TIMEOUT_FOREVER osWaitForever
#define BSP_FDCAN_TX_QUEUE_SIZE 32 #define BSP_FDCAN_TX_QUEUE_SIZE 32
/* Exported macro ----------------------------------------------------------- */ /* Exported macro ----------------------------------------------------------- */
/* AUTO GENERATED FDCAN_ENABLE */ //FDCANX实例使能
/* AUTO GENERATED FDCAN_EN BEGIN */
#define FDCAN1_EN
#define FDCAN2_EN
#define FDCAN3_EN
/* AUTO GENERATED FDCAN_EN END */
// FDCANX接收FIFO选择0=FIFO0, 1=FIFO1
/* AUTO GENERATED FDCAN_RX_FIFO BEGIN */
#ifdef FDCAN1_EN
#define FDCAN1_RX_FIFO 0
#endif
#ifdef FDCAN2_EN
#define FDCAN2_RX_FIFO 1
#endif
#ifdef FDCAN3_EN
#define FDCAN3_RX_FIFO 1
#endif
/* AUTO GENERATED FDCAN_RX_FIFO END */
/* Exported types ----------------------------------------------------------- */ /* Exported types ----------------------------------------------------------- */
typedef enum { typedef enum {
/* AUTO GENERATED BSP_FDCAN_NAME BEGIN */
BSP_FDCAN_1, BSP_FDCAN_1,
BSP_FDCAN_2, BSP_FDCAN_2,
BSP_FDCAN_3, BSP_FDCAN_3,
/* AUTO GENERATED BSP_FDCAN_NAME END */
BSP_FDCAN_NUM, BSP_FDCAN_NUM,
BSP_FDCAN_ERR, BSP_FDCAN_ERR,
} BSP_FDCAN_t; } BSP_FDCAN_t;