Compare commits

...

2 Commits

Author SHA1 Message Date
RB
52acfaf20c 添加gpio中断bsp 2025-05-05 00:02:24 +08:00
RB
af69d030fe 添加图标和delay 2025-05-04 23:49:39 +08:00
19 changed files with 242 additions and 110 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,5 +1,6 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from PIL import Image, ImageTk
import sys import sys
import os import os
import threading import threading
@ -13,7 +14,11 @@ import xml.etree.ElementTree as ET
# 配置常量 # 配置常量
REPO_DIR = "MRobot_repo" REPO_DIR = "MRobot_repo"
REPO_URL = "http://gitea.qutrobot.top/robofish/MRobot.git" REPO_URL = "http://gitea.qutrobot.top/robofish/MRobot.git"
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) if getattr(sys, 'frozen', False): # 检查是否为打包后的环境
CURRENT_DIR = os.path.dirname(sys.executable) # 使用可执行文件所在目录
else:
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) # 使用脚本所在目录
MDK_ARM_DIR = os.path.join(CURRENT_DIR, "MDK-ARM") MDK_ARM_DIR = os.path.join(CURRENT_DIR, "MDK-ARM")
USER_DIR = os.path.join(CURRENT_DIR, "User") USER_DIR = os.path.join(CURRENT_DIR, "User")
@ -83,55 +88,60 @@ class MRobotApp:
self.log(f"找到项目文件:{project_file}") self.log(f"找到项目文件:{project_file}")
return project_file return project_file
def add_groups_and_files(self): def add_groups_and_files(self):
"""添加 User 文件夹中的组和文件到 Keil 项目""" """添加 User 文件夹中的组和 .c 文件到 Keil 项目"""
project_file = self.find_uvprojx_file() project_file = self.find_uvprojx_file()
if not project_file: if not project_file:
return return
tree = ET.parse(project_file) tree = ET.parse(project_file)
root = tree.getroot() root = tree.getroot()
groups_node = root.find(".//Groups") groups_node = root.find(".//Groups")
if groups_node is None: if groups_node is None:
self.log("未找到 Groups 节点!") self.log("未找到 Groups 节点!")
return return
existing_groups = {group.find("GroupName").text for group in groups_node.findall("Group")} # 获取现有组和文件信息
existing_files = { existing_groups = {group.find("GroupName").text: group for group in groups_node.findall("Group")}
file.text
for group in groups_node.findall("Group")
for file in group.findall(".//FileName")
}
for folder_name in os.listdir(USER_DIR): for folder_name in os.listdir(USER_DIR):
folder_path = os.path.join(USER_DIR, folder_name) folder_path = os.path.join(USER_DIR, folder_name)
if not os.path.isdir(folder_path): if not os.path.isdir(folder_path):
continue continue
group_name = f"User/{folder_name}" group_name = f"User/{folder_name}"
if group_name in existing_groups: if group_name in existing_groups:
self.log(f"{group_name} 已存在,跳过...") group_node = existing_groups[group_name]
continue self.log(f"{group_name} 已存在,检查文件...")
else:
group_node = ET.SubElement(groups_node, "Group") group_node = ET.SubElement(groups_node, "Group")
ET.SubElement(group_node, "GroupName").text = group_name ET.SubElement(group_node, "GroupName").text = group_name
files_node = ET.SubElement(group_node, "Files") ET.SubElement(group_node, "Files")
self.log(f"创建新组: {group_name}")
files_node = group_node.find("Files")
existing_files_in_group = {file.find("FileName").text for file in files_node.findall("File")}
for file_name in os.listdir(folder_path): for file_name in os.listdir(folder_path):
file_path = os.path.join(folder_path, file_name) file_path = os.path.join(folder_path, file_name)
if not os.path.isfile(file_path) or file_name in existing_files: if not os.path.isfile(file_path) or not file_name.lower().endswith(".c"):
self.log(f"文件 {file_name} 已存在或无效,跳过...") self.log(f"文件 {file_name} 无效,跳过...")
continue continue
if file_name in existing_files_in_group:
self.log(f"文件 {file_name} 已存在于组 {group_name},跳过...")
continue
file_node = ET.SubElement(files_node, "File") file_node = ET.SubElement(files_node, "File")
ET.SubElement(file_node, "FileName").text = file_name ET.SubElement(file_node, "FileName").text = file_name
ET.SubElement(file_node, "FileType").text = "1" ET.SubElement(file_node, "FileType").text = "1"
relative_path = os.path.relpath(file_path, os.path.dirname(project_file)).replace("\\", "/") relative_path = os.path.relpath(file_path, os.path.dirname(project_file)).replace("\\", "/")
ET.SubElement(file_node, "FilePath").text = relative_path ET.SubElement(file_node, "FilePath").text = relative_path
self.log(f"已添加文件: {file_name} 到组 {group_name}")
tree.write(project_file, encoding="utf-8", xml_declaration=True) tree.write(project_file, encoding="utf-8", xml_declaration=True)
self.log("Keil 项目文件已更新!") self.log("Keil 项目文件已更新,仅添加 .c 文件!")
def add_include_path(self, new_path): def add_include_path(self, new_path):
"""添加新的 IncludePath 到 Keil 项目""" """添加新的 IncludePath 到 Keil 项目"""
@ -301,6 +311,7 @@ class MRobotApp:
root.title("MRobot 自动生成脚本") root.title("MRobot 自动生成脚本")
root.geometry("800x600") # 调整窗口大小以适应布局 root.geometry("800x600") # 调整窗口大小以适应布局
# 在窗口关闭时调用 on_closing 方法 # 在窗口关闭时调用 on_closing 方法
root.protocol("WM_DELETE_WINDOW", lambda: self.on_closing(root)) root.protocol("WM_DELETE_WINDOW", lambda: self.on_closing(root))

View File

@ -3,7 +3,7 @@
更加高效快捷的 STM32 开发架构,诞生于 Robocon 和 Robomaster但绝不仅限于此。 更加高效快捷的 STM32 开发架构,诞生于 Robocon 和 Robomaster但绝不仅限于此。
<div align="center"> <div align="center">
<img src="./image/MRobot.jpeg" height="100" alt="MRobot Logo"> <img src="./img/MRobot.png" height="100" alt="MRobot Logo">
<p>是时候使用更简洁的方式开发单片机了</p> <p>是时候使用更简洁的方式开发单片机了</p>
<p> <p>
<!-- <img src="https://img.shields.io/github/license/xrobot-org/XRobot.svg" alt="License"> <!-- <img src="https://img.shields.io/github/license/xrobot-org/XRobot.svg" alt="License">

BIN
User/.DS_Store vendored

Binary file not shown.

BIN
User/bsp/.DS_Store vendored Normal file

Binary file not shown.

34
User/bsp/delay.c Normal file
View File

@ -0,0 +1,34 @@
/* Includes ----------------------------------------------------------------- */
#include "bsp\delay.h"
#include <cmsis_os2.h>
#include <main.h>
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
/* Private function -------------------------------------------------------- */
/* Exported functions ------------------------------------------------------- */
int8_t BSP_Delay(uint32_t ms) {
uint32_t tick_period = 1000u / osKernelGetTickFreq();
uint32_t ticks = ms / tick_period;
switch (osKernelGetState()) {
case osKernelError:
case osKernelReserved:
case osKernelLocked:
case osKernelSuspended:
return BSP_ERR;
case osKernelRunning:
osDelay(ticks ? ticks : 1);
break;
case osKernelInactive:
case osKernelReady:
HAL_Delay(ms);
break;
}
return BSP_OK;
}

20
User/bsp/delay.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <stdint.h>
#include "bsp/bsp.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* Exported functions prototypes -------------------------------------------- */
int8_t BSP_Delay(uint32_t ms);
#ifdef __cplusplus
}
#endif

72
User/bsp/gpio_exti.c Normal file
View File

@ -0,0 +1,72 @@
/* Includes ----------------------------------------------------------------- */
#include "bsp\gpio.h"
#include <gpio.h>
#include <main.h>
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static void (*GPIO_Callback[BSP_GPIO_NUM][BSP_GPIO_CB_NUM])(void);
/* Private function -------------------------------------------------------- */
static BSP_GPIO_t GPIO_Get(uint16_t pin) {
switch (pin) {
case USER_KEY_Pin:
return BSP_GPIO_USER_KEY;
/* case XXX_Pin:
return BSP_GPIO_XXX; */
default:
return BSP_GPIO_ERR;
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
BSP_GPIO_t gpio = GPIO_Get(GPIO_Pin);
if (gpio != BSP_GPIO_ERR) {
if (GPIO_Callback[gpio][BSP_GPIO_EXTI_CB]) {
GPIO_Callback[gpio][BSP_GPIO_EXTI_CB]();
}
}
}
/* Exported functions ------------------------------------------------------- */
int8_t BSP_GPIO_RegisterCallback(BSP_GPIO_t gpio, BSP_GPIO_Callback_t type, void (*callback)(void)) {
if (callback == NULL || gpio >= BSP_GPIO_NUM || type >= BSP_GPIO_CB_NUM) return BSP_ERR_NULL;
GPIO_Callback[gpio][type] = callback;
return BSP_OK;
}
int8_t BSP_GPIO_EnableIRQ(BSP_GPIO_t gpio) {
switch (gpio) {
case BSP_GPIO_USER_KEY:
HAL_NVIC_EnableIRQ(USER_KEY_EXTI_IRQn);
break;
/* case BSP_GPIO_XXX:
HAL_NVIC_EnableIRQ(XXX_IRQn);
break; */
default:
return BSP_ERR;
}
return BSP_OK;
}
int8_t BSP_GPIO_DisableIRQ(BSP_GPIO_t gpio) {
switch (gpio) {
case BSP_GPIO_USER_KEY:
HAL_NVIC_DisableIRQ(USER_KEY_EXTI_IRQn);
break;
/* case BSP_GPIO_XXX:
HAL_NVIC_DisableIRQ(XXX_IRQn);
break; */
default:
return BSP_ERR;
}
return BSP_OK;
}

37
User/bsp/gpio_exti.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include <stdint.h>
#include "bsp/bsp.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* GPIO设备枚举与设备对应 */
typedef enum {
BSP_GPIO_USER_KEY,
/* BSP_GPIO_XXX, */
BSP_GPIO_NUM,
BSP_GPIO_ERR,
} BSP_GPIO_t;
/* GPIO支持的中断回调函数类型 */
typedef enum {
BSP_GPIO_EXTI_CB,
BSP_GPIO_CB_NUM,
} BSP_GPIO_Callback_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t BSP_GPIO_RegisterCallback(BSP_GPIO_t gpio, BSP_GPIO_Callback_t type, void (*callback)(void));
int8_t BSP_GPIO_EnableIRQ(BSP_GPIO_t gpio);
int8_t BSP_GPIO_DisableIRQ(BSP_GPIO_t gpio);
#ifdef __cplusplus
}
#endif

View File

@ -1,48 +0,0 @@
/* Includes ----------------------------------------------------------------- */
#include "key_gpio.h"
#include "bsp.h"
#include <gpio.h>
/* Private define ----------------------------------------------------------- */
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static uint32_t key_stats; // 使用位掩码记录每个通道的状态最多支持32LED
/* 按键配置表(根据实际硬件修改) */
static const BSP_Key_Config_t KEY_CONFIGS[] = {
{GPIOA, GPIO_PIN_7, GPIO_PIN_SET}, // KEY1按下时电平为高
{GPIOA, GPIO_PIN_9, GPIO_PIN_SET}, // KEY2按下时电平为低
// 添加更多按键...
};
#define KEY_COUNT (sizeof(KEY_CONFIGS)/sizeof(KEY_CONFIGS[0])
//读取按键状态(带消抖)
int8_t BSP_Key_Read(BSP_Key_Channel_t ch) {
static uint32_t last_press_time[BSP_KEY_COUNT] = {0}; //上次按下时间
const uint32_t debounce_ms = 20; //按键消抖时间
const uint32_t long_press_ms = 2000; //按键长按时间
if(ch >= BSP_KEY_COUNT) return BSP_KEY_RELEASED ;
const BSP_Key_Config_t *cfg = &KEY_CONFIGS[ch];
GPIO_PinState state = HAL_GPIO_ReadPin(cfg->port, cfg->pin);
if(state == cfg->active_level) {
uint32_t now = HAL_GetTick(); //用于记录按键按下时间这里比较state是为了方便适应不同有效电平做出修改的也可以改成直接检测电平高低
//消抖检测(只有按下超过20ms才被认为按下
if((now - last_press_time[ch]) > debounce_ms) {
//长按检测只有被按下超过2000ms才被认为是长按根据实际情况可做出修改
if((now - last_press_time[ch]) > long_press_ms) {
return BSP_KEY_LONG_PRESS;
}
return BSP_KEY_PRESSED;
}
} else {
last_press_time[ch] = HAL_GetTick();
}
return BSP_KEY_RELEASED;
}

View File

@ -1,36 +0,0 @@
#pragma once
/* Includes ----------------------------------------------------------------- */
#include <stdint.h>
#include "main.h"
//#include "key_gpio.h"
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
/* Exported types ----------------------------------------------------------- */
/* KEY按键状态设置用 */
typedef enum
{
BSP_KEY_RELEASED, //按键释放
BSP_KEY_PRESSED, //按键按下
BSP_KEY_LONG_PRESS, //按键长按
} BSP_KEY_Status_t;
/* 按键通道定义 */
typedef enum {
BSP_KEY_1,
BSP_KEY_2,
/* 可根据需要扩展 */
BSP_KEY_COUNT
} BSP_Key_Channel_t;
/* 按键硬件配置结构体 */
typedef struct {
GPIO_TypeDef *port; // GPIO端口
uint16_t pin; // 引脚编号
uint8_t active_level; // 有效电平GPIO_PIN_SET/RESET
} BSP_Key_Config_t;
int8_t BSP_Key_Read(BSP_Key_Channel_t ch);

BIN
img/.DS_Store vendored Normal file

Binary file not shown.

BIN
img/M.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
img/M.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
img/MR.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
img/MR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
img/MRobot.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
img/MRobot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

42
pngico.py Normal file
View File

@ -0,0 +1,42 @@
from PIL import Image
import os
def crop_transparent_background(input_path, output_path):
"""
裁切 PNG 图片的透明背景并保存
:param input_path: 输入图片路径
:param output_path: 输出图片路径
"""
try:
# 打开图片
img = Image.open(input_path)
# 确保图片是 RGBA 模式
if img.mode != "RGBA":
img = img.convert("RGBA")
# 获取图片的 alpha 通道
bbox = img.getbbox()
if bbox:
# 裁切图片
cropped_img = img.crop(bbox)
# 保存裁切后的图片
cropped_img.save(output_path, format="PNG")
print(f"图片已保存到: {output_path}")
else:
print("图片没有透明背景或为空。")
except Exception as e:
print(f"处理图片时出错: {e}")
if __name__ == "__main__":
# 示例:输入和输出路径
input_file = "C:\Mac\Home\Desktop\MRobot\img\M.png" # 替换为你的输入图片路径
output_file = "C:\Mac\Home\Desktop\MRobot\img\M.png" # 替换为你的输出图片路径
# 检查文件是否存在
if os.path.exists(input_file):
crop_transparent_background(input_file, output_file)
else:
print(f"输入文件不存在: {input_file}")