mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-09-14 12:54:33 +08:00
修改time和gpio的保存类型
This commit is contained in:
parent
c8ca5e1031
commit
43749e0391
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -7,6 +7,7 @@
|
|||||||
"string": "c",
|
"string": "c",
|
||||||
"string_view": "c",
|
"string_view": "c",
|
||||||
"vector": "c",
|
"vector": "c",
|
||||||
"can.h": "c"
|
"can.h": "c",
|
||||||
|
"device.h": "c"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -752,16 +752,23 @@ class bsp_gpio(QWidget):
|
|||||||
def _save_config(self, configs):
|
def _save_config(self, configs):
|
||||||
config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
|
config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
|
||||||
config_data = CodeGenerator.load_config(config_path)
|
config_data = CodeGenerator.load_config(config_path)
|
||||||
|
gpio_configs = []
|
||||||
|
for config in configs:
|
||||||
|
# 根据 pin 查找原始 available_list 项
|
||||||
|
match = next((item for item in self.available_list if item['pin'] == config['pin']), None)
|
||||||
|
gpio_type = "EXTI" if config['has_exti'] else (
|
||||||
|
"OUTPUT" if match and match.get('is_output') else "INPUT"
|
||||||
|
)
|
||||||
|
gpio_configs.append({
|
||||||
|
'custom_name': config['custom_name'],
|
||||||
|
'ioc_label': config['ioc_label'],
|
||||||
|
'pin': config['pin'],
|
||||||
|
'has_exti': config['has_exti'],
|
||||||
|
'type': gpio_type
|
||||||
|
})
|
||||||
config_data['gpio'] = {
|
config_data['gpio'] = {
|
||||||
'enabled': True,
|
'enabled': True,
|
||||||
'configs': [
|
'configs': gpio_configs
|
||||||
{
|
|
||||||
'custom_name': config['custom_name'],
|
|
||||||
'ioc_label': config['ioc_label'],
|
|
||||||
'pin': config['pin'],
|
|
||||||
'has_exti': config['has_exti']
|
|
||||||
} for config in configs
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
CodeGenerator.save_config(config_data, config_path)
|
CodeGenerator.save_config(config_data, config_path)
|
||||||
|
|
||||||
|
@ -94,12 +94,9 @@ def get_all_dependency_components(dependencies):
|
|||||||
dependent_components.add(dep_name.lower())
|
dependent_components.add(dep_name.lower())
|
||||||
return dependent_components
|
return dependent_components
|
||||||
|
|
||||||
|
|
||||||
class ComponentSimple(QWidget):
|
class ComponentSimple(QWidget):
|
||||||
"""简单组件界面 - 只有开启/关闭功能"""
|
"""简单组件界面 - 只有开启/关闭功能"""
|
||||||
|
|
||||||
# 添加信号,用于通知其他组件状态变化
|
|
||||||
dependency_changed = pyqtSignal(str, bool) # 组件名, 是否启用
|
|
||||||
|
|
||||||
def __init__(self, project_path, component_name, template_names, component_manager=None):
|
def __init__(self, project_path, component_name, template_names, component_manager=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.project_path = project_path
|
self.project_path = project_path
|
||||||
@ -108,260 +105,107 @@ class ComponentSimple(QWidget):
|
|||||||
self.component_manager = component_manager
|
self.component_manager = component_manager
|
||||||
|
|
||||||
# 加载描述和依赖信息
|
# 加载描述和依赖信息
|
||||||
component_dir = os.path.join(os.path.dirname(__file__), "../../assets/User_code/component")
|
component_dir = os.path.dirname(__file__) + "/../../assets/User_code/component"
|
||||||
describe_path = os.path.join(component_dir, "describe.csv")
|
describe_path = os.path.join(component_dir, "describe.csv")
|
||||||
dependencies_path = os.path.join(component_dir, "dependencies.csv")
|
dependencies_path = os.path.join(component_dir, "dependencies.csv")
|
||||||
|
|
||||||
self.descriptions = load_descriptions(describe_path)
|
self.descriptions = load_descriptions(describe_path)
|
||||||
self.dependencies = load_dependencies(dependencies_path)
|
self.dependencies = load_dependencies(dependencies_path)
|
||||||
self.all_dependent_components = get_all_dependency_components(self.dependencies)
|
|
||||||
|
|
||||||
# 判断当前组件是否被其他组件依赖
|
|
||||||
self.is_dependency = self.component_name.lower() in self.all_dependent_components
|
|
||||||
|
|
||||||
# 强制启用状态相关
|
|
||||||
self._forced_enabled = False
|
|
||||||
self._dependency_count = 0 # 有多少个组件依赖此组件
|
|
||||||
|
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
self._load_config()
|
self._load_config()
|
||||||
|
|
||||||
def _init_ui(self):
|
def _init_ui(self):
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
# 顶部横向布局:左侧复选框,居中标题
|
|
||||||
top_layout = QHBoxLayout()
|
top_layout = QHBoxLayout()
|
||||||
top_layout.setAlignment(Qt.AlignVCenter)
|
top_layout.setAlignment(Qt.AlignVCenter)
|
||||||
|
|
||||||
# 所有组件都有复选框
|
|
||||||
self.generate_checkbox = CheckBox(f"启用 {self.component_name}")
|
self.generate_checkbox = CheckBox(f"启用 {self.component_name}")
|
||||||
self.generate_checkbox.stateChanged.connect(self._on_checkbox_changed)
|
self.generate_checkbox.stateChanged.connect(self._on_checkbox_changed)
|
||||||
top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)
|
top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)
|
||||||
|
|
||||||
# 如果是被依赖的组件,添加状态标签
|
|
||||||
if self.is_dependency:
|
|
||||||
self.dependency_status_label = BodyLabel("")
|
|
||||||
self.dependency_status_label.setStyleSheet("color: #888888; font-style: italic; margin-left: 10px;")
|
|
||||||
top_layout.addWidget(self.dependency_status_label, alignment=Qt.AlignLeft)
|
|
||||||
|
|
||||||
# 弹性空间
|
|
||||||
top_layout.addStretch()
|
top_layout.addStretch()
|
||||||
|
|
||||||
title = SubtitleLabel(f"{self.component_name} 配置 ")
|
title = SubtitleLabel(f"{self.component_name} 配置 ")
|
||||||
title.setAlignment(Qt.AlignHCenter)
|
title.setAlignment(Qt.AlignHCenter)
|
||||||
top_layout.addWidget(title, alignment=Qt.AlignHCenter)
|
top_layout.addWidget(title, alignment=Qt.AlignHCenter)
|
||||||
|
|
||||||
# 再加一个弹性空间,保证标题居中
|
|
||||||
top_layout.addStretch()
|
top_layout.addStretch()
|
||||||
|
|
||||||
layout.addLayout(top_layout)
|
layout.addLayout(top_layout)
|
||||||
|
|
||||||
# 功能说明
|
|
||||||
desc = self.descriptions.get(self.component_name.lower(), "")
|
desc = self.descriptions.get(self.component_name.lower(), "")
|
||||||
if desc:
|
if desc:
|
||||||
desc_label = BodyLabel(f"功能说明:{desc}")
|
desc_label = BodyLabel(f"功能说明:{desc}")
|
||||||
desc_label.setWordWrap(True)
|
desc_label.setWordWrap(True)
|
||||||
layout.addWidget(desc_label)
|
layout.addWidget(desc_label)
|
||||||
|
|
||||||
# 依赖信息
|
|
||||||
deps = self.dependencies.get(self.component_name.lower(), [])
|
deps = self.dependencies.get(self.component_name.lower(), [])
|
||||||
if deps:
|
if deps:
|
||||||
deps_text = f"依赖组件:{', '.join([os.path.basename(dep) for dep in deps])}"
|
deps_text = f"依赖组件:{', '.join([os.path.basename(dep) for dep in deps])}"
|
||||||
self.deps_label = BodyLabel(deps_text)
|
deps_label = BodyLabel(deps_text)
|
||||||
self.deps_label.setWordWrap(True)
|
deps_label.setWordWrap(True)
|
||||||
self.deps_label.setStyleSheet("color: #888888;")
|
deps_label.setStyleSheet("color: #888888;")
|
||||||
layout.addWidget(self.deps_label)
|
layout.addWidget(deps_label)
|
||||||
|
# 不再自动启用依赖,只做提示
|
||||||
# 依赖状态显示
|
|
||||||
self.deps_status_widget = QWidget()
|
|
||||||
deps_status_layout = QVBoxLayout(self.deps_status_widget)
|
|
||||||
deps_status_layout.setContentsMargins(20, 10, 20, 10)
|
|
||||||
|
|
||||||
self.deps_checkboxes = {}
|
|
||||||
for dep in deps:
|
|
||||||
# 从路径中提取组件名
|
|
||||||
dep_name = os.path.basename(dep)
|
|
||||||
dep_checkbox = CheckBox(f"自动启用 {dep_name}")
|
|
||||||
dep_checkbox.setEnabled(False) # 依赖项自动管理,不允许手动取消
|
|
||||||
deps_status_layout.addWidget(dep_checkbox)
|
|
||||||
self.deps_checkboxes[dep] = dep_checkbox
|
|
||||||
|
|
||||||
layout.addWidget(self.deps_status_widget)
|
|
||||||
|
|
||||||
# 如果是被依赖的组件,显示被哪些组件依赖
|
|
||||||
if self.is_dependency:
|
|
||||||
dependent_by = []
|
|
||||||
for component, deps in self.dependencies.items():
|
|
||||||
for dep_path in deps:
|
|
||||||
if os.path.basename(dep_path).lower() == self.component_name.lower():
|
|
||||||
dependent_by.append(component)
|
|
||||||
|
|
||||||
if dependent_by:
|
|
||||||
dependent_text = f"被以下组件依赖:{', '.join(dependent_by)}"
|
|
||||||
dependent_label = BodyLabel(dependent_text)
|
|
||||||
dependent_label.setWordWrap(True)
|
|
||||||
dependent_label.setStyleSheet("color: #0078d4;")
|
|
||||||
layout.addWidget(dependent_label)
|
|
||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
|
|
||||||
# 初始化界面状态
|
|
||||||
self._update_ui_state()
|
|
||||||
|
|
||||||
def _update_ui_state(self):
|
|
||||||
"""更新界面状态"""
|
|
||||||
if self.is_dependency:
|
|
||||||
if self._dependency_count > 0:
|
|
||||||
# 有组件依赖此组件,设置为强制启用状态
|
|
||||||
self.generate_checkbox.setEnabled(False)
|
|
||||||
self.generate_checkbox.setChecked(True)
|
|
||||||
self.dependency_status_label.setText(f"(被 {self._dependency_count} 个组件自动启用)")
|
|
||||||
self.dependency_status_label.setStyleSheet("color: #0078d4; font-style: italic; margin-left: 10px;")
|
|
||||||
else:
|
|
||||||
# 没有组件依赖此组件,恢复正常状态
|
|
||||||
self.generate_checkbox.setEnabled(True)
|
|
||||||
self.dependency_status_label.setText("(可选组件)")
|
|
||||||
self.dependency_status_label.setStyleSheet("color: #888888; font-style: italic; margin-left: 10px;")
|
|
||||||
|
|
||||||
def _on_checkbox_changed(self, state):
|
def _on_checkbox_changed(self, state):
|
||||||
"""处理复选框状态变化,自动管理依赖"""
|
pass # 不再自动启用依赖
|
||||||
# 如果是被强制启用的,不允许用户取消
|
|
||||||
if self.is_dependency and self._dependency_count > 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if state == 2: # 选中状态
|
|
||||||
# 自动选中所有依赖项
|
|
||||||
deps = self.dependencies.get(self.component_name.lower(), [])
|
|
||||||
for dep in deps:
|
|
||||||
if dep in self.deps_checkboxes:
|
|
||||||
self.deps_checkboxes[dep].setChecked(True)
|
|
||||||
|
|
||||||
# 通知组件管理器启用依赖项
|
|
||||||
if self.component_manager:
|
|
||||||
self.component_manager.enable_dependencies(self.component_name, deps)
|
|
||||||
else: # 未选中状态
|
|
||||||
# 取消选中所有依赖项
|
|
||||||
if hasattr(self, 'deps_checkboxes'):
|
|
||||||
for checkbox in self.deps_checkboxes.values():
|
|
||||||
checkbox.setChecked(False)
|
|
||||||
|
|
||||||
# 通知组件管理器禁用依赖项
|
|
||||||
if self.component_manager:
|
|
||||||
deps = self.dependencies.get(self.component_name.lower(), [])
|
|
||||||
self.component_manager.disable_dependencies(self.component_name, deps)
|
|
||||||
|
|
||||||
def set_forced_enabled(self, enabled: bool):
|
|
||||||
"""设置强制启用状态(用于依赖自动启用)"""
|
|
||||||
self._forced_enabled = enabled
|
|
||||||
if enabled:
|
|
||||||
self.set_dependency_count(max(1, self._dependency_count))
|
|
||||||
else:
|
|
||||||
self.set_dependency_count(0)
|
|
||||||
|
|
||||||
def is_need_generate(self):
|
def is_need_generate(self):
|
||||||
"""检查是否需要生成代码"""
|
|
||||||
return self.generate_checkbox.isChecked()
|
return self.generate_checkbox.isChecked()
|
||||||
|
|
||||||
def set_dependency_count(self, count):
|
|
||||||
"""设置依赖计数并更新UI状态"""
|
|
||||||
self._dependency_count = count
|
|
||||||
if count > 0:
|
|
||||||
self._forced_enabled = True
|
|
||||||
if not self.generate_checkbox.isChecked():
|
|
||||||
# 阻止信号触发,直接设置状态
|
|
||||||
self.generate_checkbox.blockSignals(True)
|
|
||||||
self.generate_checkbox.setChecked(True)
|
|
||||||
self.generate_checkbox.blockSignals(False)
|
|
||||||
else:
|
|
||||||
self._forced_enabled = False
|
|
||||||
|
|
||||||
self._update_ui_state()
|
|
||||||
self._save_config() # 保存状态变化
|
|
||||||
|
|
||||||
def get_enabled_dependencies(self):
|
def get_enabled_dependencies(self):
|
||||||
"""获取已启用的依赖项列表"""
|
|
||||||
if not self.is_need_generate():
|
if not self.is_need_generate():
|
||||||
return []
|
return []
|
||||||
return self.dependencies.get(self.component_name.lower(), [])
|
return self.dependencies.get(self.component_name.lower(), [])
|
||||||
|
|
||||||
def _generate_component_code_internal(self):
|
def _generate_component_code_internal(self):
|
||||||
"""生成组件代码"""
|
|
||||||
if not self.is_need_generate():
|
if not self.is_need_generate():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
template_dir = self._get_component_template_dir()
|
template_dir = self._get_component_template_dir()
|
||||||
|
|
||||||
# 生成头文件和源文件
|
|
||||||
for key, filename in self.template_names.items():
|
for key, filename in self.template_names.items():
|
||||||
template_path = os.path.join(template_dir, filename)
|
template_path = os.path.join(template_dir, filename)
|
||||||
template_content = CodeGenerator.load_template(template_path)
|
template_content = CodeGenerator.load_template(template_path)
|
||||||
if not template_content:
|
if not template_content:
|
||||||
print(f"模板文件不存在或为空: {template_path}")
|
print(f"模板文件不存在或为空: {template_path}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
output_path = os.path.join(self.project_path, f"User/component/{filename}")
|
output_path = os.path.join(self.project_path, f"User/component/{filename}")
|
||||||
save_with_preserve(output_path, template_content)
|
save_with_preserve(output_path, template_content)
|
||||||
|
|
||||||
self._save_config()
|
self._save_config()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_component_template_dir(self):
|
def _get_component_template_dir(self):
|
||||||
"""获取组件模板目录"""
|
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
# 向上找到 MRobot 根目录
|
|
||||||
while os.path.basename(current_dir) != 'MRobot' and current_dir != '/':
|
while os.path.basename(current_dir) != 'MRobot' and current_dir != '/':
|
||||||
current_dir = os.path.dirname(current_dir)
|
current_dir = os.path.dirname(current_dir)
|
||||||
|
|
||||||
if os.path.basename(current_dir) == 'MRobot':
|
if os.path.basename(current_dir) == 'MRobot':
|
||||||
return os.path.join(current_dir, "assets/User_code/component")
|
return os.path.join(current_dir, "assets/User_code/component")
|
||||||
else:
|
else:
|
||||||
# 如果找不到,使用相对路径作为备选
|
|
||||||
return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "assets/User_code/component")
|
return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "assets/User_code/component")
|
||||||
|
|
||||||
def _save_config(self):
|
def _save_config(self):
|
||||||
"""保存配置"""
|
|
||||||
config_path = os.path.join(self.project_path, "User/component/component_config.yaml")
|
config_path = os.path.join(self.project_path, "User/component/component_config.yaml")
|
||||||
config_data = CodeGenerator.load_config(config_path)
|
config_data = CodeGenerator.load_config(config_path)
|
||||||
config_data[self.component_name.lower()] = {
|
config_data[self.component_name.lower()] = {
|
||||||
'enabled': self.is_need_generate(),
|
'enabled': self.is_need_generate(),
|
||||||
'dependencies': self.dependencies.get(self.component_name.lower(), []),
|
'dependencies': self.dependencies.get(self.component_name.lower(), [])
|
||||||
'is_dependency': self.is_dependency,
|
|
||||||
'dependency_count': self._dependency_count,
|
|
||||||
'forced_enabled': self._forced_enabled
|
|
||||||
}
|
}
|
||||||
CodeGenerator.save_config(config_data, config_path)
|
CodeGenerator.save_config(config_data, config_path)
|
||||||
|
|
||||||
def _load_config(self):
|
def _load_config(self):
|
||||||
"""加载配置"""
|
|
||||||
config_path = os.path.join(self.project_path, "User/component/component_config.yaml")
|
config_path = os.path.join(self.project_path, "User/component/component_config.yaml")
|
||||||
config_data = CodeGenerator.load_config(config_path)
|
config_data = CodeGenerator.load_config(config_path)
|
||||||
conf = config_data.get(self.component_name.lower(), {})
|
conf = config_data.get(self.component_name.lower(), {})
|
||||||
|
|
||||||
# 加载依赖计数
|
|
||||||
self._dependency_count = conf.get('dependency_count', 0)
|
|
||||||
self._forced_enabled = conf.get('forced_enabled', False)
|
|
||||||
|
|
||||||
# 设置复选框状态
|
|
||||||
if conf.get('enabled', False):
|
if conf.get('enabled', False):
|
||||||
self.generate_checkbox.setChecked(True)
|
self.generate_checkbox.setChecked(True)
|
||||||
|
|
||||||
# 更新UI状态
|
|
||||||
self._update_ui_state()
|
|
||||||
|
|
||||||
class ComponentManager:
|
class ComponentManager:
|
||||||
"""组件依赖管理器"""
|
"""组件依赖管理器"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.component_pages = {} # 组件名 -> 页面对象
|
self.component_pages = {} # 组件名 -> 页面对象
|
||||||
self.dependency_count = {} # 被依赖组件 -> 依赖计数
|
|
||||||
|
|
||||||
def register_component(self, component_name, page):
|
def register_component(self, component_name, page):
|
||||||
"""注册组件页面"""
|
"""注册组件页面"""
|
||||||
self.component_pages[component_name.lower()] = page
|
self.component_pages[component_name.lower()] = page
|
||||||
|
|
||||||
# 注册后立即同步状态
|
|
||||||
self._sync_dependency_states()
|
|
||||||
|
|
||||||
def _sync_dependency_states(self):
|
def _sync_dependency_states(self):
|
||||||
"""同步所有依赖状态"""
|
"""同步所有依赖状态"""
|
||||||
# 重新计算所有依赖计数
|
# 重新计算所有依赖计数
|
||||||
@ -474,7 +318,7 @@ class component(QWidget):
|
|||||||
# 创建临时组件页面
|
# 创建临时组件页面
|
||||||
template_names = {'header': f'{comp_name}.h', 'source': f'{comp_name}.c'}
|
template_names = {'header': f'{comp_name}.h', 'source': f'{comp_name}.c'}
|
||||||
temp_page = ComponentSimple(project_path, comp_name.upper(), template_names)
|
temp_page = ComponentSimple(project_path, comp_name.upper(), template_names)
|
||||||
temp_page.set_forced_enabled(True) # 自动启用依赖组件
|
# temp_page.set_forced_enabled(True) # 自动启用依赖组件
|
||||||
component_pages[comp_name] = temp_page
|
component_pages[comp_name] = temp_page
|
||||||
|
|
||||||
# 如果没有组件需要生成,返回提示信息
|
# 如果没有组件需要生成,返回提示信息
|
||||||
|
@ -14,22 +14,25 @@ def load_device_config(config_path):
|
|||||||
return yaml.safe_load(f)
|
return yaml.safe_load(f)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_available_bsp_devices(project_path, bsp_type):
|
def get_available_bsp_devices(project_path, bsp_type, gpio_type=None):
|
||||||
"""获取可用的BSP设备"""
|
"""获取可用的BSP设备,GPIO可选类型过滤"""
|
||||||
bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml")
|
bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml")
|
||||||
if not os.path.exists(bsp_config_path):
|
if not os.path.exists(bsp_config_path):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(bsp_config_path, 'r', encoding='utf-8') as f:
|
with open(bsp_config_path, 'r', encoding='utf-8') as f:
|
||||||
bsp_config = yaml.safe_load(f)
|
bsp_config = yaml.safe_load(f)
|
||||||
|
if bsp_type == "gpio" and bsp_config.get("gpio", {}).get("enabled", False):
|
||||||
if bsp_type in bsp_config and bsp_config[bsp_type].get('enabled', False):
|
configs = bsp_config["gpio"].get("configs", [])
|
||||||
|
# 增加类型过滤
|
||||||
|
if gpio_type:
|
||||||
|
configs = [cfg for cfg in configs if cfg.get('type', '').lower() == gpio_type.lower()]
|
||||||
|
return [f"BSP_GPIO_{cfg['custom_name']}" for cfg in configs]
|
||||||
|
elif bsp_type in bsp_config and bsp_config[bsp_type].get('enabled', False):
|
||||||
devices = bsp_config[bsp_type].get('devices', [])
|
devices = bsp_config[bsp_type].get('devices', [])
|
||||||
return [f"BSP_{bsp_type.upper()}_{device['name']}" for device in devices]
|
return [f"BSP_{bsp_type.upper()}_{device['name']}" for device in devices]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"读取BSP配置失败: {e}")
|
print(f"读取BSP配置失败: {e}")
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def generate_device_header(project_path, enabled_devices):
|
def generate_device_header(project_path, enabled_devices):
|
||||||
@ -156,50 +159,41 @@ class DeviceSimple(QWidget):
|
|||||||
layout.addWidget(deps_label)
|
layout.addWidget(deps_label)
|
||||||
|
|
||||||
def _add_bsp_config(self, layout):
|
def _add_bsp_config(self, layout):
|
||||||
"""添加BSP配置区域"""
|
|
||||||
bsp_requirements = self.device_config.get('bsp_requirements', [])
|
bsp_requirements = self.device_config.get('bsp_requirements', [])
|
||||||
self.bsp_combos = {}
|
self.bsp_combos = {}
|
||||||
|
|
||||||
if bsp_requirements:
|
if bsp_requirements:
|
||||||
layout.addWidget(BodyLabel("BSP设备配置:"))
|
layout.addWidget(BodyLabel("BSP设备配置:"))
|
||||||
|
|
||||||
for req in bsp_requirements:
|
for req in bsp_requirements:
|
||||||
bsp_type = req['type']
|
bsp_type = req['type']
|
||||||
var_name = req['var_name']
|
var_name = req['var_name']
|
||||||
description = req.get('description', '')
|
description = req.get('description', '')
|
||||||
|
gpio_type = req.get('gpio_type', None) # 新增
|
||||||
# 创建选择组合框
|
|
||||||
req_layout = QHBoxLayout()
|
req_layout = QHBoxLayout()
|
||||||
|
|
||||||
label = BodyLabel(f"{bsp_type.upper()}:")
|
label = BodyLabel(f"{bsp_type.upper()}:")
|
||||||
label.setMinimumWidth(80)
|
label.setMinimumWidth(80)
|
||||||
req_layout.addWidget(label)
|
req_layout.addWidget(label)
|
||||||
|
|
||||||
combo = ComboBox()
|
combo = ComboBox()
|
||||||
self._update_bsp_combo(combo, bsp_type)
|
# 传递gpio_type参数
|
||||||
|
self._update_bsp_combo(combo, bsp_type, gpio_type)
|
||||||
req_layout.addWidget(combo)
|
req_layout.addWidget(combo)
|
||||||
|
|
||||||
if description:
|
if description:
|
||||||
desc_label = BodyLabel(f"({description})")
|
desc_label = BodyLabel(f"({description})")
|
||||||
desc_label.setStyleSheet("color: #666666; font-size: 12px;")
|
desc_label.setStyleSheet("color: #666666; font-size: 12px;")
|
||||||
req_layout.addWidget(desc_label)
|
req_layout.addWidget(desc_label)
|
||||||
|
|
||||||
req_layout.addStretch()
|
req_layout.addStretch()
|
||||||
layout.addLayout(req_layout)
|
layout.addLayout(req_layout)
|
||||||
|
|
||||||
self.bsp_combos[var_name] = combo
|
self.bsp_combos[var_name] = combo
|
||||||
|
|
||||||
def _update_bsp_combo(self, combo, bsp_type):
|
def _update_bsp_combo(self, combo, bsp_type, gpio_type=None):
|
||||||
"""更新BSP组合框选项"""
|
|
||||||
combo.clear()
|
combo.clear()
|
||||||
available_devices = get_available_bsp_devices(self.project_path, bsp_type)
|
available_devices = get_available_bsp_devices(self.project_path, bsp_type, gpio_type)
|
||||||
if available_devices:
|
if available_devices:
|
||||||
combo.addItems(available_devices)
|
combo.addItems(available_devices)
|
||||||
else:
|
else:
|
||||||
combo.addItem(f"未找到可用的{bsp_type.upper()}设备")
|
combo.addItem(f"未找到可用的{bsp_type.upper()}设备")
|
||||||
combo.setEnabled(False)
|
combo.setEnabled(False)
|
||||||
|
|
||||||
|
|
||||||
def refresh_bsp_combos(self):
|
def refresh_bsp_combos(self):
|
||||||
"""刷新所有BSP组合框"""
|
"""刷新所有BSP组合框"""
|
||||||
bsp_requirements = self.device_config.get('bsp_requirements', [])
|
bsp_requirements = self.device_config.get('bsp_requirements', [])
|
||||||
@ -247,6 +241,15 @@ class DeviceSimple(QWidget):
|
|||||||
dst_path = os.path.join(self.project_path, f"User/device/{filename}")
|
dst_path = os.path.join(self.project_path, f"User/device/{filename}")
|
||||||
|
|
||||||
if os.path.exists(src_path):
|
if os.path.exists(src_path):
|
||||||
|
# 头文件和源文件都做变量替换
|
||||||
|
with open(src_path, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
for var_name, device_name in bsp_config.items():
|
||||||
|
content = content.replace(var_name, device_name)
|
||||||
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||||
|
with open(dst_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
if file_type == 'header':
|
if file_type == 'header':
|
||||||
# 头文件直接复制,不做修改
|
# 头文件直接复制,不做修改
|
||||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||||
|
@ -16,14 +16,15 @@
|
|||||||
uint32_t BSP_TIME_Get_ms() { return xTaskGetTickCount(); }
|
uint32_t BSP_TIME_Get_ms() { return xTaskGetTickCount(); }
|
||||||
|
|
||||||
uint64_t BSP_TIME_Get_us() {
|
uint64_t BSP_TIME_Get_us() {
|
||||||
uint32_t ms_old = xTaskGetTickCount();
|
uint32_t tick_freq = osKernelGetTickFreq();
|
||||||
|
uint32_t ticks_old = xTaskGetTickCount()*(1000/tick_freq);
|
||||||
uint32_t tick_value_old = SysTick->VAL;
|
uint32_t tick_value_old = SysTick->VAL;
|
||||||
uint32_t ms_new = xTaskGetTickCount();
|
uint32_t ticks_new = xTaskGetTickCount()*(1000/tick_freq);
|
||||||
uint32_t tick_value_new = SysTick->VAL;
|
uint32_t tick_value_new = SysTick->VAL;
|
||||||
if (ms_old == ms_new) {
|
if (ticks_old == ticks_new) {
|
||||||
return ms_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1);
|
return ticks_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1);
|
||||||
} else {
|
} else {
|
||||||
return ms_new * 1000 + 1000 - tick_value_new * 1000 / (SysTick->LOAD + 1);
|
return ticks_new * 1000 + 1000 - tick_value_new * 1000 / (SysTick->LOAD + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
|
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
|
component,ahrs,ballistics,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
|
||||||
device,dr16,ai,nuc
|
device,dr16,bmi088
|
||||||
module,chassis,gimbal,arm,shoot
|
module,chassis,gimbal,arm,shoot
|
|
372
assets/User_code/device/bmi088.c
Normal file
372
assets/User_code/device/bmi088.c
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
/*
|
||||||
|
BMI088 陀螺仪+加速度计传感器。
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Includes ----------------------------------------------------------------- */
|
||||||
|
#include "bmi088.h"
|
||||||
|
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <gpio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "bsp/time.h"
|
||||||
|
#include "bsp/gpio.h"
|
||||||
|
#include "bsp/spi.h"
|
||||||
|
#include "component/user_math.h"
|
||||||
|
|
||||||
|
/* Private define ----------------------------------------------------------- */
|
||||||
|
#define BMI088_REG_ACCL_CHIP_ID (0x00)
|
||||||
|
#define BMI088_REG_ACCL_ERR (0x02)
|
||||||
|
#define BMI088_REG_ACCL_STATUS (0x03)
|
||||||
|
#define BMI088_REG_ACCL_X_LSB (0x12)
|
||||||
|
#define BMI088_REG_ACCL_X_MSB (0x13)
|
||||||
|
#define BMI088_REG_ACCL_Y_LSB (0x14)
|
||||||
|
#define BMI088_REG_ACCL_Y_MSB (0x15)
|
||||||
|
#define BMI088_REG_ACCL_Z_LSB (0x16)
|
||||||
|
#define BMI088_REG_ACCL_Z_MSB (0x17)
|
||||||
|
#define BMI088_REG_ACCL_SENSORTIME_0 (0x18)
|
||||||
|
#define BMI088_REG_ACCL_SENSORTIME_1 (0x19)
|
||||||
|
#define BMI088_REG_ACCL_SENSORTIME_2 (0x1A)
|
||||||
|
#define BMI088_REG_ACCL_INT_STAT_1 (0x1D)
|
||||||
|
#define BMI088_REG_ACCL_TEMP_MSB (0x22)
|
||||||
|
#define BMI088_REG_ACCL_TEMP_LSB (0x23)
|
||||||
|
#define BMI088_REG_ACCL_CONF (0x40)
|
||||||
|
#define BMI088_REG_ACCL_RANGE (0x41)
|
||||||
|
#define BMI088_REG_ACCL_INT1_IO_CONF (0x53)
|
||||||
|
#define BMI088_REG_ACCL_INT2_IO_CONF (0x54)
|
||||||
|
#define BMI088_REG_ACCL_INT1_INT2_MAP_DATA (0x58)
|
||||||
|
#define BMI088_REG_ACCL_SELF_TEST (0x6D)
|
||||||
|
#define BMI088_REG_ACCL_PWR_CONF (0x7C)
|
||||||
|
#define BMI088_REG_ACCL_PWR_CTRL (0x7D)
|
||||||
|
#define BMI088_REG_ACCL_SOFTRESET (0x7E)
|
||||||
|
|
||||||
|
#define BMI088_REG_GYRO_CHIP_ID (0x00)
|
||||||
|
#define BMI088_REG_GYRO_X_LSB (0x02)
|
||||||
|
#define BMI088_REG_GYRO_X_MSB (0x03)
|
||||||
|
#define BMI088_REG_GYRO_Y_LSB (0x04)
|
||||||
|
#define BMI088_REG_GYRO_Y_MSB (0x05)
|
||||||
|
#define BMI088_REG_GYRO_Z_LSB (0x06)
|
||||||
|
#define BMI088_REG_GYRO_Z_MSB (0x07)
|
||||||
|
#define BMI088_REG_GYRO_INT_STAT_1 (0x0A)
|
||||||
|
#define BMI088_REG_GYRO_RANGE (0x0F)
|
||||||
|
#define BMI088_REG_GYRO_BANDWIDTH (0x10)
|
||||||
|
#define BMI088_REG_GYRO_LPM1 (0x11)
|
||||||
|
#define BMI088_REG_GYRO_SOFTRESET (0x14)
|
||||||
|
#define BMI088_REG_GYRO_INT_CTRL (0x15)
|
||||||
|
#define BMI088_REG_GYRO_INT3_INT4_IO_CONF (0x16)
|
||||||
|
#define BMI088_REG_GYRO_INT3_INT4_IO_MAP (0x18)
|
||||||
|
#define BMI088_REG_GYRO_SELF_TEST (0x3C)
|
||||||
|
|
||||||
|
#define BMI088_CHIP_ID_ACCL (0x1E)
|
||||||
|
#define BMI088_CHIP_ID_GYRO (0x0F)
|
||||||
|
|
||||||
|
#define BMI088_LEN_RX_BUFF (19)
|
||||||
|
/* Private macro ------------------------------------------------------------ */
|
||||||
|
#define BMI088_ACCL_NSS_SET() \
|
||||||
|
BSP_GPIO_WritePin(BSP_GPIO_ACCL_CS, GPIO_PIN_SET)
|
||||||
|
#define BMI088_ACCL_NSS_RESET() \
|
||||||
|
BSP_GPIO_WritePin(BSP_GPIO_ACCL_CS, GPIO_PIN_RESET)
|
||||||
|
|
||||||
|
#define BMI088_GYRO_NSS_SET() \
|
||||||
|
BSP_GPIO_WritePin(BSP_GPIO_GYRO_CS, GPIO_PIN_SET)
|
||||||
|
#define BMI088_GYRO_NSS_RESET() \
|
||||||
|
BSP_GPIO_WritePin(BSP_GPIO_GYRO_CS, GPIO_PIN_RESET)
|
||||||
|
|
||||||
|
/* Private typedef ---------------------------------------------------------- */
|
||||||
|
typedef enum {
|
||||||
|
BMI_ACCL,
|
||||||
|
BMI_GYRO,
|
||||||
|
} BMI_Device_t;
|
||||||
|
|
||||||
|
/* Private variables -------------------------------------------------------- */
|
||||||
|
static uint8_t buffer[2];
|
||||||
|
static uint8_t bmi088_rxbuf[BMI088_LEN_RX_BUFF];
|
||||||
|
|
||||||
|
static osThreadId_t thread_alert;
|
||||||
|
static bool inited = false;
|
||||||
|
|
||||||
|
/* Private function -------------------------------------------------------- */
|
||||||
|
static void BMI_WriteSingle(BMI_Device_t dv, uint8_t reg, uint8_t data) {
|
||||||
|
buffer[0] = (reg & 0x7f);
|
||||||
|
buffer[1] = data;
|
||||||
|
|
||||||
|
BSP_TIME_Delay(1);
|
||||||
|
switch (dv) {
|
||||||
|
case BMI_ACCL:
|
||||||
|
BMI088_ACCL_NSS_RESET();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMI_GYRO:
|
||||||
|
BMI088_GYRO_NSS_RESET();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_SPI_Transmit(BSP_SPI_GetHandle(BSP_SPI_BMI088), buffer, 2u, 20u);
|
||||||
|
|
||||||
|
switch (dv) {
|
||||||
|
case BMI_ACCL:
|
||||||
|
BMI088_ACCL_NSS_SET();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMI_GYRO:
|
||||||
|
BMI088_GYRO_NSS_SET();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t BMI_ReadSingle(BMI_Device_t dv, uint8_t reg) {
|
||||||
|
BSP_TIME_Delay(1);
|
||||||
|
switch (dv) {
|
||||||
|
case BMI_ACCL:
|
||||||
|
BMI088_ACCL_NSS_RESET();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMI_GYRO:
|
||||||
|
BMI088_GYRO_NSS_RESET();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[0] = (uint8_t)(reg | 0x80);
|
||||||
|
HAL_SPI_Transmit(BSP_SPI_GetHandle(BSP_SPI_BMI088), buffer, 1u, 20u);
|
||||||
|
HAL_SPI_Receive(BSP_SPI_GetHandle(BSP_SPI_BMI088), buffer, 2u, 20u);
|
||||||
|
|
||||||
|
switch (dv) {
|
||||||
|
case BMI_ACCL:
|
||||||
|
BMI088_ACCL_NSS_SET();
|
||||||
|
return buffer[1];
|
||||||
|
|
||||||
|
case BMI_GYRO:
|
||||||
|
BMI088_GYRO_NSS_SET();
|
||||||
|
return buffer[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMI_Read(BMI_Device_t dv, uint8_t reg, uint8_t *data, uint8_t len) {
|
||||||
|
if (data == NULL) return;
|
||||||
|
|
||||||
|
switch (dv) {
|
||||||
|
case BMI_ACCL:
|
||||||
|
BMI088_ACCL_NSS_RESET();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMI_GYRO:
|
||||||
|
BMI088_GYRO_NSS_RESET();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[0] = (uint8_t)(reg | 0x80);
|
||||||
|
HAL_SPI_Transmit(BSP_SPI_GetHandle(BSP_SPI_BMI088), buffer, 1u, 20u);
|
||||||
|
HAL_SPI_Receive_DMA(BSP_SPI_GetHandle(BSP_SPI_BMI088), data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMI088_RxCpltCallback(void) {
|
||||||
|
if (HAL_GPIO_ReadPin(ACCL_CS_GPIO_Port, ACCL_CS_Pin) == GPIO_PIN_RESET) {
|
||||||
|
BMI088_ACCL_NSS_SET();
|
||||||
|
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_ACCL_RAW_REDY);
|
||||||
|
}
|
||||||
|
if (HAL_GPIO_ReadPin(GYRO_CS_GPIO_Port, GYRO_CS_Pin) == GPIO_PIN_RESET) {
|
||||||
|
BMI088_GYRO_NSS_SET();
|
||||||
|
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_GYRO_RAW_REDY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMI088_AcclIntCallback(void) {
|
||||||
|
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_ACCL_NEW_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BMI088_GyroIntCallback(void) {
|
||||||
|
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_GYRO_NEW_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exported functions ------------------------------------------------------- */
|
||||||
|
int8_t BMI088_Init(BMI088_t *bmi088, const BMI088_Cali_t *cali) {
|
||||||
|
if (bmi088 == NULL) return DEVICE_ERR_NULL;
|
||||||
|
if (cali == NULL) return DEVICE_ERR_NULL;
|
||||||
|
if (inited) return DEVICE_ERR_INITED;
|
||||||
|
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
|
||||||
|
|
||||||
|
bmi088->cali = cali;
|
||||||
|
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_SOFTRESET, 0xB6);
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_SOFTRESET, 0xB6);
|
||||||
|
BSP_TIME_Delay(30);
|
||||||
|
|
||||||
|
/* Switch accl to SPI mode. */
|
||||||
|
BMI_ReadSingle(BMI_ACCL, BMI088_CHIP_ID_ACCL);
|
||||||
|
|
||||||
|
if (BMI_ReadSingle(BMI_ACCL, BMI088_REG_ACCL_CHIP_ID) != BMI088_CHIP_ID_ACCL)
|
||||||
|
return DEVICE_ERR_NO_DEV;
|
||||||
|
|
||||||
|
if (BMI_ReadSingle(BMI_GYRO, BMI088_REG_GYRO_CHIP_ID) != BMI088_CHIP_ID_GYRO)
|
||||||
|
return DEVICE_ERR_NO_DEV;
|
||||||
|
|
||||||
|
BSP_GPIO_DisableIRQ(BSP_GPIO_ACCL_INT);
|
||||||
|
BSP_GPIO_DisableIRQ(BSP_GPIO_GYRO_INT);
|
||||||
|
|
||||||
|
BSP_SPI_RegisterCallback(BSP_SPI_BMI088, BSP_SPI_RX_CPLT_CB,
|
||||||
|
BMI088_RxCpltCallback);
|
||||||
|
// BSP_GPIO_RegisterCallback(ACCL_INT_Pin, BMI088_AcclIntCallback);
|
||||||
|
// BSP_GPIO_RegisterCallback(GYRO_INT_Pin, BMI088_GyroIntCallback);
|
||||||
|
BSP_GPIO_RegisterCallback(BSP_GPIO_ACCL_INT, BMI088_AcclIntCallback);
|
||||||
|
BSP_GPIO_RegisterCallback(BSP_GPIO_GYRO_INT, BMI088_GyroIntCallback);
|
||||||
|
/* Accl init. */
|
||||||
|
/* Filter setting: Normal. */
|
||||||
|
/* ODR: 0xAB: 800Hz. 0xAA: 400Hz. 0xA9: 200Hz. 0xA8: 100Hz. 0xA6: 25Hz. */
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_CONF, 0xAA);
|
||||||
|
|
||||||
|
/* 0x00: +-3G. 0x01: +-6G. 0x02: +-12G. 0x03: +-24G. */
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_RANGE, 0x01);
|
||||||
|
|
||||||
|
/* INT1 as output. Push-pull. Active low. Output. */
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_INT1_IO_CONF, 0x08);
|
||||||
|
|
||||||
|
/* Map data ready interrupt to INT1. */
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_INT1_INT2_MAP_DATA, 0x04);
|
||||||
|
|
||||||
|
/* Turn on accl. Now we can read data. */
|
||||||
|
BMI_WriteSingle(BMI_ACCL, BMI088_REG_ACCL_PWR_CTRL, 0x04);
|
||||||
|
BSP_TIME_Delay(50);
|
||||||
|
|
||||||
|
/* Gyro init. */
|
||||||
|
/* 0x00: +-2000. 0x01: +-1000. 0x02: +-500. 0x03: +-250. 0x04: +-125. */
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_RANGE, 0x01);
|
||||||
|
|
||||||
|
/* Filter bw: 47Hz. */
|
||||||
|
/* ODR: 0x02: 1000Hz. 0x03: 400Hz. 0x06: 200Hz. 0x07: 100Hz. */
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_BANDWIDTH, 0x03);
|
||||||
|
|
||||||
|
/* INT3 and INT4 as output. Push-pull. Active low. */
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT3_INT4_IO_CONF, 0x00);
|
||||||
|
|
||||||
|
/* Map data ready interrupt to INT3. */
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT3_INT4_IO_MAP, 0x01);
|
||||||
|
|
||||||
|
/* Enable new data interrupt. */
|
||||||
|
BMI_WriteSingle(BMI_GYRO, BMI088_REG_GYRO_INT_CTRL, 0x80);
|
||||||
|
|
||||||
|
BSP_TIME_Delay(10);
|
||||||
|
|
||||||
|
inited = true;
|
||||||
|
|
||||||
|
// BSP_GPIO_EnableIRQ(ACCL_INT_Pin);
|
||||||
|
// BSP_GPIO_EnableIRQ(GYRO_INT_Pin);
|
||||||
|
BSP_GPIO_EnableIRQ(BSP_GPIO_ACCL_INT);
|
||||||
|
BSP_GPIO_EnableIRQ(BSP_GPIO_GYRO_INT);
|
||||||
|
return DEVICE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BMI088_GyroStable(AHRS_Gyro_t *gyro) {
|
||||||
|
return ((gyro->x < 0.03f) && (gyro->y < 0.03f) && (gyro->z < 0.03f));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BMI088_WaitNew() {
|
||||||
|
return osThreadFlagsWait(
|
||||||
|
SIGNAL_BMI088_ACCL_NEW_DATA | SIGNAL_BMI088_GYRO_NEW_DATA, osFlagsWaitAll,
|
||||||
|
osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t BMI088_AcclStartDmaRecv() {
|
||||||
|
BMI_Read(BMI_ACCL, BMI088_REG_ACCL_X_LSB, bmi088_rxbuf, BMI088_LEN_RX_BUFF);
|
||||||
|
return DEVICE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BMI088_AcclWaitDmaCplt() {
|
||||||
|
return osThreadFlagsWait(SIGNAL_BMI088_ACCL_RAW_REDY, osFlagsWaitAll,
|
||||||
|
osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t BMI088_GyroStartDmaRecv() {
|
||||||
|
BMI_Read(BMI_GYRO, BMI088_REG_GYRO_X_LSB, bmi088_rxbuf + 7, 6u);
|
||||||
|
return DEVICE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BMI088_GyroWaitDmaCplt() {
|
||||||
|
return osThreadFlagsWait(SIGNAL_BMI088_GYRO_RAW_REDY, osFlagsWaitAll,
|
||||||
|
osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t BMI088_ParseAccl(BMI088_t *bmi088) {
|
||||||
|
if (bmi088 == NULL) return DEVICE_ERR_NULL;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
int16_t raw_x, raw_y, raw_z;
|
||||||
|
memcpy(&raw_x, bmi088_rxbuf + 1, sizeof(raw_x));
|
||||||
|
memcpy(&raw_y, bmi088_rxbuf + 3, sizeof(raw_y));
|
||||||
|
memcpy(&raw_z, bmi088_rxbuf + 5, sizeof(raw_z));
|
||||||
|
|
||||||
|
bmi088->accl.x = (float)raw_x;
|
||||||
|
bmi088->accl.y = (float)raw_y;
|
||||||
|
bmi088->accl.z = (float)raw_z;
|
||||||
|
|
||||||
|
#else
|
||||||
|
const int16_t *praw_x = (int16_t *)(bmi088_rxbuf + 1);
|
||||||
|
const int16_t *praw_y = (int16_t *)(bmi088_rxbuf + 3);
|
||||||
|
const int16_t *praw_z = (int16_t *)(bmi088_rxbuf + 5);
|
||||||
|
|
||||||
|
bmi088->accl.x = (float)*praw_x;
|
||||||
|
bmi088->accl.y = (float)*praw_y;
|
||||||
|
bmi088->accl.z = (float)*praw_z;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 3G: 10920. 6G: 5460. 12G: 2730. 24G: 1365. */
|
||||||
|
bmi088->accl.x /= 5460.0f;
|
||||||
|
bmi088->accl.y /= 5460.0f;
|
||||||
|
bmi088->accl.z /= 5460.0f;
|
||||||
|
|
||||||
|
int16_t raw_temp =
|
||||||
|
(uint16_t)((bmi088_rxbuf[17] << 3) | (bmi088_rxbuf[18] >> 5));
|
||||||
|
|
||||||
|
if (raw_temp > 1023) raw_temp -= 2048;
|
||||||
|
|
||||||
|
bmi088->temp = (float)raw_temp * 0.125f + 23.0f;
|
||||||
|
|
||||||
|
return DEVICE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t BMI088_ParseGyro(BMI088_t *bmi088) {
|
||||||
|
if (bmi088 == NULL) return DEVICE_ERR_NULL;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
/* Gyroscope imu_raw -> degrees/sec -> radians/sec */
|
||||||
|
int16_t raw_x, raw_y, raw_z;
|
||||||
|
memcpy(&raw_x, bmi088_rxbuf + 7, sizeof(raw_x));
|
||||||
|
memcpy(&raw_y, bmi088_rxbuf + 9, sizeof(raw_y));
|
||||||
|
memcpy(&raw_z, bmi088_rxbuf + 11, sizeof(raw_z));
|
||||||
|
|
||||||
|
bmi088->gyro.x = (float)raw_x;
|
||||||
|
bmi088->gyro.y = (float)raw_y;
|
||||||
|
bmi088->gyro.z = (float)raw_z;
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Gyroscope imu_raw -> degrees/sec -> radians/sec */
|
||||||
|
const int16_t *raw_x = (int16_t *)(bmi088_rxbuf + 7);
|
||||||
|
const int16_t *raw_y = (int16_t *)(bmi088_rxbuf + 9);
|
||||||
|
const int16_t *raw_z = (int16_t *)(bmi088_rxbuf + 11);
|
||||||
|
|
||||||
|
bmi088->gyro.x = (float)*raw_x;
|
||||||
|
bmi088->gyro.y = (float)*raw_y;
|
||||||
|
bmi088->gyro.z = (float)*raw_z;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* FS125: 262.144. FS250: 131.072. FS500: 65.536. FS1000: 32.768.
|
||||||
|
* FS2000: 16.384.*/
|
||||||
|
bmi088->gyro.x /= 32.768f;
|
||||||
|
bmi088->gyro.y /= 32.768f;
|
||||||
|
bmi088->gyro.z /= 32.768f;
|
||||||
|
|
||||||
|
bmi088->gyro.x *= M_DEG2RAD_MULT;
|
||||||
|
bmi088->gyro.y *= M_DEG2RAD_MULT;
|
||||||
|
bmi088->gyro.z *= M_DEG2RAD_MULT;
|
||||||
|
|
||||||
|
bmi088->gyro.x -= bmi088->cali->gyro_offset.x;
|
||||||
|
bmi088->gyro.y -= bmi088->cali->gyro_offset.y;
|
||||||
|
bmi088->gyro.z -= bmi088->cali->gyro_offset.z;
|
||||||
|
|
||||||
|
return DEVICE_ERR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float BMI088_GetUpdateFreq(BMI088_t *bmi088) {
|
||||||
|
(void)bmi088;
|
||||||
|
return 400.0f;
|
||||||
|
}
|
64
assets/User_code/device/bmi088.h
Normal file
64
assets/User_code/device/bmi088.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Includes ----------------------------------------------------------------- */
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "component/ahrs.h"
|
||||||
|
#include "device/device.h"
|
||||||
|
|
||||||
|
/* Exported constants ------------------------------------------------------- */
|
||||||
|
/* Exported macro ----------------------------------------------------------- */
|
||||||
|
/* Exported types ----------------------------------------------------------- */
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
} gyro_offset; /* 陀螺仪偏置 */
|
||||||
|
} BMI088_Cali_t; /* BMI088校准数据 */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
AHRS_Accl_t accl;
|
||||||
|
AHRS_Gyro_t gyro;
|
||||||
|
|
||||||
|
float temp; /* 温度 */
|
||||||
|
|
||||||
|
const BMI088_Cali_t *cali;
|
||||||
|
} BMI088_t;
|
||||||
|
|
||||||
|
/* Exported functions prototypes -------------------------------------------- */
|
||||||
|
int8_t BMI088_Init(BMI088_t *bmi088, const BMI088_Cali_t *cali);
|
||||||
|
int8_t BMI088_Restart(void);
|
||||||
|
|
||||||
|
bool BMI088_GyroStable(AHRS_Gyro_t *gyro);
|
||||||
|
|
||||||
|
/* Sensor use right-handed coordinate system. */
|
||||||
|
/*
|
||||||
|
x < R(logo)
|
||||||
|
y
|
||||||
|
UP is z
|
||||||
|
All implementation should follow this rule.
|
||||||
|
*/
|
||||||
|
uint32_t BMI088_WaitNew();
|
||||||
|
|
||||||
|
/*
|
||||||
|
BMI088的Accl和Gyro共用同一个DMA通道,所以一次只能读一个传感器。
|
||||||
|
即BMI088_AcclStartDmaRecv() 和 BMI088_AcclWaitDmaCplt() 中间不能
|
||||||
|
出现 BMI088_GyroStartDmaRecv()。
|
||||||
|
*/
|
||||||
|
int8_t BMI088_AcclStartDmaRecv();
|
||||||
|
uint32_t BMI088_AcclWaitDmaCplt();
|
||||||
|
int8_t BMI088_GyroStartDmaRecv();
|
||||||
|
uint32_t BMI088_GyroWaitDmaCplt();
|
||||||
|
int8_t BMI088_ParseAccl(BMI088_t *bmi088);
|
||||||
|
int8_t BMI088_ParseGyro(BMI088_t *bmi088);
|
||||||
|
float BMI088_GetUpdateFreq(BMI088_t *bmi088);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -14,3 +14,38 @@ devices:
|
|||||||
files:
|
files:
|
||||||
header: "dr16.h"
|
header: "dr16.h"
|
||||||
source: "dr16.c"
|
source: "dr16.c"
|
||||||
|
|
||||||
|
bmi088:
|
||||||
|
name: "BMI088"
|
||||||
|
description: "BMI088 陀螺仪+加速度计传感器"
|
||||||
|
dependencies:
|
||||||
|
bsp: ["spi", "gpio"]
|
||||||
|
component: ["user_math"]
|
||||||
|
bsp_requirements:
|
||||||
|
- type: "spi"
|
||||||
|
var_name: "BSP_SPI_BMI088"
|
||||||
|
description: "用于与 BMI088 通信的 SPI 总线"
|
||||||
|
- type: "gpio"
|
||||||
|
var_name: "BSP_GPIO_ACCL_CS"
|
||||||
|
description: "加速度计片选输出引脚"
|
||||||
|
gpio_type: "output"
|
||||||
|
- type: "gpio"
|
||||||
|
var_name: "BSP_GPIO_GYRO_CS"
|
||||||
|
description: "陀螺仪片选输出引脚"
|
||||||
|
gpio_type: "output"
|
||||||
|
- type: "gpio"
|
||||||
|
var_name: "BSP_GPIO_ACCL_INT"
|
||||||
|
description: "加速度计中断输入引脚"
|
||||||
|
gpio_type: "EXTI"
|
||||||
|
- type: "gpio"
|
||||||
|
var_name: "BSP_GPIO_GYRO_INT"
|
||||||
|
description: "陀螺仪中断输入引脚"
|
||||||
|
gpio_type: "EXTI"
|
||||||
|
thread_signals:
|
||||||
|
- name: "SIGNAL_BMI088_ACCL_RAW_REDY"
|
||||||
|
- name: "SIGNAL_BMI088_GYRO_RAW_REDY"
|
||||||
|
- name: "SIGNAL_BMI088_ACCL_NEW_DATA"
|
||||||
|
- name: "SIGNAL_BMI088_GYRO_NEW_DATA"
|
||||||
|
files:
|
||||||
|
header: "bmi088.h"
|
||||||
|
source: "bmi088.c"
|
@ -123,4 +123,6 @@ uart:
|
|||||||
devices:
|
devices:
|
||||||
- instance: USART3
|
- instance: USART3
|
||||||
name: DR16
|
name: DR16
|
||||||
|
- instance: UART5
|
||||||
|
name: AI
|
||||||
enabled: true
|
enabled: true
|
||||||
|
93
gpio.c
93
gpio.c
@ -1,93 +0,0 @@
|
|||||||
/* Includes ----------------------------------------------------------------- */
|
|
||||||
#include "bsp/gpio.h"
|
|
||||||
|
|
||||||
#include <gpio.h>
|
|
||||||
#include <main.h>
|
|
||||||
|
|
||||||
/* Private define ----------------------------------------------------------- */
|
|
||||||
/* Private macro ------------------------------------------------------------ */
|
|
||||||
/* Private typedef ---------------------------------------------------------- */
|
|
||||||
typedef struct {
|
|
||||||
uint16_t pin;
|
|
||||||
GPIO_TypeDef *gpio;
|
|
||||||
} BSP_GPIO_MAP_t;
|
|
||||||
|
|
||||||
/* Private variables -------------------------------------------------------- */
|
|
||||||
static const BSP_GPIO_MAP_t GPIO_Map[BSP_GPIO_NUM] = {
|
|
||||||
{ACCL_CS_Pin, ACCL_CS_GPIO_Port},
|
|
||||||
{GYRO_CS_Pin, GYRO_CS_GPIO_Port},
|
|
||||||
{ACCL_INT_Pin, ACCL_INT_GPIO_Port},
|
|
||||||
{GYRO_INT_Pin, GYRO_INT_GPIO_Port},
|
|
||||||
{USER_KEY_Pin, USER_KEY_GPIO_Port},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void (*GPIO_Callback[16])(void);
|
|
||||||
|
|
||||||
/* Private function -------------------------------------------------------- */
|
|
||||||
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
|
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
|
||||||
if (GPIO_Pin & (1 << i)) {
|
|
||||||
if (GPIO_Callback[i]) {
|
|
||||||
GPIO_Callback[i]();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exported functions ------------------------------------------------------- */
|
|
||||||
int8_t BSP_GPIO_RegisterCallback(uint16_t pin, void (*callback)(void)) {
|
|
||||||
if (callback == NULL) return BSP_ERR_NULL;
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
|
||||||
if (pin & (1 << i)) {
|
|
||||||
GPIO_Callback[i] = callback;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BSP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_EnableIRQ(uint16_t pin) {
|
|
||||||
switch (pin) {
|
|
||||||
case BSP_GPIO_IMU_ACCL_CS:
|
|
||||||
HAL_NVIC_EnableIRQ(ACCL_INT_EXTI_IRQn);
|
|
||||||
break;
|
|
||||||
case BSP_GPIO_IMU_GYRO_CS:
|
|
||||||
HAL_NVIC_EnableIRQ(GYRO_INT_EXTI_IRQn);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return BSP_ERR;
|
|
||||||
}
|
|
||||||
return BSP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_DisableIRQ(uint16_t pin) {
|
|
||||||
switch (pin) {
|
|
||||||
case BSP_GPIO_IMU_ACCL_INT:
|
|
||||||
HAL_NVIC_DisableIRQ(ACCL_INT_EXTI_IRQn);
|
|
||||||
break;
|
|
||||||
case BSP_GPIO_IMU_GYRO_INT:
|
|
||||||
HAL_NVIC_DisableIRQ(GYRO_INT_EXTI_IRQn);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return BSP_ERR;
|
|
||||||
}
|
|
||||||
return BSP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_WritePin(BSP_GPIO_t gpio, bool value){
|
|
||||||
if (gpio >= BSP_GPIO_NUM) return BSP_ERR;
|
|
||||||
HAL_GPIO_WritePin(GPIO_Map[gpio].gpio, GPIO_Map[gpio].pin, value);
|
|
||||||
return BSP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_TogglePin(BSP_GPIO_t gpio){
|
|
||||||
if (gpio >= BSP_GPIO_NUM) return BSP_ERR;
|
|
||||||
HAL_GPIO_TogglePin(GPIO_Map[gpio].gpio, GPIO_Map[gpio].pin);
|
|
||||||
return BSP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BSP_GPIO_ReadPin(BSP_GPIO_t gpio){
|
|
||||||
if (gpio >= BSP_GPIO_NUM) return false;
|
|
||||||
return HAL_GPIO_ReadPin(GPIO_Map[gpio].gpio, GPIO_Map[gpio].pin) == GPIO_PIN_SET;
|
|
||||||
}
|
|
40
gpio.h
40
gpio.h
@ -1,40 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Includes ----------------------------------------------------------------- */
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "bsp/bsp.h"
|
|
||||||
|
|
||||||
/* Exported constants ------------------------------------------------------- */
|
|
||||||
/* Exported macro ----------------------------------------------------------- */
|
|
||||||
/* Exported types ----------------------------------------------------------- */
|
|
||||||
typedef enum {
|
|
||||||
BSP_GPIO_IMU_ACCL_CS,
|
|
||||||
BSP_GPIO_IMU_GYRO_CS,
|
|
||||||
BSP_GPIO_IMU_ACCL_INT,
|
|
||||||
BSP_GPIO_IMU_GYRO_INT,
|
|
||||||
BSP_GPIO_USER_KEY,
|
|
||||||
BSP_GPIO_NUM,
|
|
||||||
BSP_GPIO_ERR,
|
|
||||||
} BSP_GPIO_t;
|
|
||||||
|
|
||||||
/* Exported functions prototypes -------------------------------------------- */
|
|
||||||
int8_t BSP_GPIO_RegisterCallback(uint16_t pin, void (*callback)(void));
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_EnableIRQ(BSP_GPIO_t gpio);
|
|
||||||
int8_t BSP_GPIO_DisableIRQ(BSP_GPIO_t gpio);
|
|
||||||
|
|
||||||
|
|
||||||
int8_t BSP_GPIO_WritePin(BSP_GPIO_t gpio, bool value);
|
|
||||||
int8_t BSP_GPIO_TogglePin(BSP_GPIO_t gpio);
|
|
||||||
|
|
||||||
bool BSP_GPIO_ReadPin(BSP_GPIO_t gpio);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
17
test.py
17
test.py
@ -1,17 +0,0 @@
|
|||||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QStackedLayout, QFileDialog, QHeaderView
|
|
||||||
from qfluentwidgets import TitleLabel, BodyLabel, SubtitleLabel, StrongBodyLabel, HorizontalSeparator, PushButton, TreeWidget, InfoBar, FluentIcon, Dialog, FlowLayout, FluentWindow, MSFluentWindow, SplitFluentWindow
|
|
||||||
|
|
||||||
class Demo(SplitFluentWindow):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
layout = FlowLayout(self, needAni=True) # 启用动画
|
|
||||||
self.resize(250, 300)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from PyQt5.QtWidgets import QApplication
|
|
||||||
import sys
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
w = Demo()
|
|
||||||
w.show()
|
|
||||||
sys.exit(app.exec_())
|
|
Loading…
Reference in New Issue
Block a user