diff --git a/.DS_Store b/.DS_Store index 765ded4..cdded1e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.vscode/settings.json b/.vscode/settings.json index ec48fa4..3778a0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "string": "c", "string_view": "c", "vector": "c", - "can.h": "c" + "can.h": "c", + "device.h": "c" } } \ No newline at end of file diff --git a/app/code_page/bsp_interface.py b/app/code_page/bsp_interface.py index e3a059c..cea567f 100644 --- a/app/code_page/bsp_interface.py +++ b/app/code_page/bsp_interface.py @@ -752,16 +752,23 @@ class bsp_gpio(QWidget): def _save_config(self, configs): config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml") 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'] = { 'enabled': True, - 'configs': [ - { - 'custom_name': config['custom_name'], - 'ioc_label': config['ioc_label'], - 'pin': config['pin'], - 'has_exti': config['has_exti'] - } for config in configs - ] + 'configs': gpio_configs } CodeGenerator.save_config(config_data, config_path) diff --git a/app/code_page/component_interface.py b/app/code_page/component_interface.py index 1d05449..d7a2753 100644 --- a/app/code_page/component_interface.py +++ b/app/code_page/component_interface.py @@ -94,12 +94,9 @@ def get_all_dependency_components(dependencies): dependent_components.add(dep_name.lower()) return dependent_components + class ComponentSimple(QWidget): """简单组件界面 - 只有开启/关闭功能""" - - # 添加信号,用于通知其他组件状态变化 - dependency_changed = pyqtSignal(str, bool) # 组件名, 是否启用 - def __init__(self, project_path, component_name, template_names, component_manager=None): super().__init__() self.project_path = project_path @@ -108,259 +105,106 @@ class ComponentSimple(QWidget): 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") dependencies_path = os.path.join(component_dir, "dependencies.csv") - self.descriptions = load_descriptions(describe_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._load_config() def _init_ui(self): layout = QVBoxLayout(self) - - # 顶部横向布局:左侧复选框,居中标题 top_layout = QHBoxLayout() top_layout.setAlignment(Qt.AlignVCenter) - - # 所有组件都有复选框 self.generate_checkbox = CheckBox(f"启用 {self.component_name}") self.generate_checkbox.stateChanged.connect(self._on_checkbox_changed) 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() - title = SubtitleLabel(f"{self.component_name} 配置 ") title.setAlignment(Qt.AlignHCenter) top_layout.addWidget(title, alignment=Qt.AlignHCenter) - - # 再加一个弹性空间,保证标题居中 top_layout.addStretch() - layout.addLayout(top_layout) - # 功能说明 desc = self.descriptions.get(self.component_name.lower(), "") if desc: desc_label = BodyLabel(f"功能说明:{desc}") desc_label.setWordWrap(True) layout.addWidget(desc_label) - # 依赖信息 deps = self.dependencies.get(self.component_name.lower(), []) if deps: deps_text = f"依赖组件:{', '.join([os.path.basename(dep) for dep in deps])}" - self.deps_label = BodyLabel(deps_text) - self.deps_label.setWordWrap(True) - self.deps_label.setStyleSheet("color: #888888;") - layout.addWidget(self.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) + deps_label = BodyLabel(deps_text) + deps_label.setWordWrap(True) + deps_label.setStyleSheet("color: #888888;") + layout.addWidget(deps_label) + # 不再自动启用依赖,只做提示 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): - """处理复选框状态变化,自动管理依赖""" - # 如果是被强制启用的,不允许用户取消 - 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) + pass # 不再自动启用依赖 - 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): - """检查是否需要生成代码""" 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): - """获取已启用的依赖项列表""" if not self.is_need_generate(): return [] return self.dependencies.get(self.component_name.lower(), []) def _generate_component_code_internal(self): - """生成组件代码""" if not self.is_need_generate(): return False - template_dir = self._get_component_template_dir() - - # 生成头文件和源文件 for key, filename in self.template_names.items(): template_path = os.path.join(template_dir, filename) template_content = CodeGenerator.load_template(template_path) if not template_content: print(f"模板文件不存在或为空: {template_path}") continue - output_path = os.path.join(self.project_path, f"User/component/{filename}") save_with_preserve(output_path, template_content) - self._save_config() return True def _get_component_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/component") else: - # 如果找不到,使用相对路径作为备选 return os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "assets/User_code/component") def _save_config(self): - """保存配置""" config_path = os.path.join(self.project_path, "User/component/component_config.yaml") config_data = CodeGenerator.load_config(config_path) config_data[self.component_name.lower()] = { 'enabled': self.is_need_generate(), - 'dependencies': self.dependencies.get(self.component_name.lower(), []), - 'is_dependency': self.is_dependency, - 'dependency_count': self._dependency_count, - 'forced_enabled': self._forced_enabled + 'dependencies': self.dependencies.get(self.component_name.lower(), []) } CodeGenerator.save_config(config_data, config_path) def _load_config(self): - """加载配置""" config_path = os.path.join(self.project_path, "User/component/component_config.yaml") config_data = CodeGenerator.load_config(config_path) 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): self.generate_checkbox.setChecked(True) - - # 更新UI状态 - self._update_ui_state() class ComponentManager: """组件依赖管理器""" def __init__(self): self.component_pages = {} # 组件名 -> 页面对象 - self.dependency_count = {} # 被依赖组件 -> 依赖计数 def register_component(self, component_name, page): """注册组件页面""" self.component_pages[component_name.lower()] = page - - # 注册后立即同步状态 - self._sync_dependency_states() def _sync_dependency_states(self): """同步所有依赖状态""" @@ -474,7 +318,7 @@ class component(QWidget): # 创建临时组件页面 template_names = {'header': f'{comp_name}.h', 'source': f'{comp_name}.c'} 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 # 如果没有组件需要生成,返回提示信息 diff --git a/app/code_page/device_interface.py b/app/code_page/device_interface.py index 759861f..358352c 100644 --- a/app/code_page/device_interface.py +++ b/app/code_page/device_interface.py @@ -14,22 +14,25 @@ def load_device_config(config_path): return yaml.safe_load(f) return {} -def get_available_bsp_devices(project_path, bsp_type): - """获取可用的BSP设备""" +def get_available_bsp_devices(project_path, bsp_type, gpio_type=None): + """获取可用的BSP设备,GPIO可选类型过滤""" bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml") if not os.path.exists(bsp_config_path): return [] - try: with open(bsp_config_path, 'r', encoding='utf-8') as f: bsp_config = yaml.safe_load(f) - - if bsp_type in bsp_config and bsp_config[bsp_type].get('enabled', False): + if bsp_type == "gpio" and bsp_config.get("gpio", {}).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', []) return [f"BSP_{bsp_type.upper()}_{device['name']}" for device in devices] except Exception as e: print(f"读取BSP配置失败: {e}") - return [] def generate_device_header(project_path, enabled_devices): @@ -156,49 +159,40 @@ class DeviceSimple(QWidget): layout.addWidget(deps_label) def _add_bsp_config(self, layout): - """添加BSP配置区域""" bsp_requirements = self.device_config.get('bsp_requirements', []) self.bsp_combos = {} - if bsp_requirements: layout.addWidget(BodyLabel("BSP设备配置:")) - for req in bsp_requirements: bsp_type = req['type'] var_name = req['var_name'] description = req.get('description', '') - - # 创建选择组合框 + gpio_type = req.get('gpio_type', None) # 新增 req_layout = QHBoxLayout() - label = BodyLabel(f"{bsp_type.upper()}:") label.setMinimumWidth(80) req_layout.addWidget(label) - combo = ComboBox() - self._update_bsp_combo(combo, bsp_type) - + # 传递gpio_type参数 + self._update_bsp_combo(combo, bsp_type, gpio_type) req_layout.addWidget(combo) - if description: desc_label = BodyLabel(f"({description})") desc_label.setStyleSheet("color: #666666; font-size: 12px;") req_layout.addWidget(desc_label) - req_layout.addStretch() layout.addLayout(req_layout) - self.bsp_combos[var_name] = combo - def _update_bsp_combo(self, combo, bsp_type): - """更新BSP组合框选项""" + def _update_bsp_combo(self, combo, bsp_type, gpio_type=None): 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: combo.addItems(available_devices) else: combo.addItem(f"未找到可用的{bsp_type.upper()}设备") combo.setEnabled(False) + def refresh_bsp_combos(self): """刷新所有BSP组合框""" @@ -245,8 +239,17 @@ class DeviceSimple(QWidget): for file_type, filename in files.items(): src_path = os.path.join(template_dir, filename) dst_path = os.path.join(self.project_path, f"User/device/{filename}") - + 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': # 头文件直接复制,不做修改 os.makedirs(os.path.dirname(dst_path), exist_ok=True) diff --git a/assets/User_code/bsp/time.c b/assets/User_code/bsp/time.c index 3476482..5817cea 100644 --- a/assets/User_code/bsp/time.c +++ b/assets/User_code/bsp/time.c @@ -16,14 +16,15 @@ uint32_t BSP_TIME_Get_ms() { return xTaskGetTickCount(); } 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 ms_new = xTaskGetTickCount(); + uint32_t ticks_new = xTaskGetTickCount()*(1000/tick_freq); uint32_t tick_value_new = SysTick->VAL; - if (ms_old == ms_new) { - return ms_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1); + if (ticks_old == ticks_new) { + return ticks_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1); } 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); } } diff --git a/assets/User_code/config.csv b/assets/User_code/config.csv index 047265b..8240ccd 100644 --- a/assets/User_code/config.csv +++ b/assets/User_code/config.csv @@ -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,ai,nuc +device,dr16,bmi088 module,chassis,gimbal,arm,shoot \ No newline at end of file diff --git a/assets/User_code/device/bmi088.c b/assets/User_code/device/bmi088.c new file mode 100644 index 0000000..e9c083d --- /dev/null +++ b/assets/User_code/device/bmi088.c @@ -0,0 +1,372 @@ +/* + BMI088 陀螺仪+加速度计传感器。 + +*/ + +/* Includes ----------------------------------------------------------------- */ +#include "bmi088.h" + +#include +#include +#include +#include + +#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; +} diff --git a/assets/User_code/device/bmi088.h b/assets/User_code/device/bmi088.h new file mode 100644 index 0000000..8847cf1 --- /dev/null +++ b/assets/User_code/device/bmi088.h @@ -0,0 +1,64 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ----------------------------------------------------------------- */ +#include +#include + +#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 diff --git a/assets/User_code/device/config.yaml b/assets/User_code/device/config.yaml index cf27c0e..0b218cb 100644 --- a/assets/User_code/device/config.yaml +++ b/assets/User_code/device/config.yaml @@ -13,4 +13,39 @@ devices: - name: "SIGNAL_DR16_RAW_REDY" files: header: "dr16.h" - source: "dr16.c" \ No newline at end of file + 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" \ No newline at end of file diff --git a/bsp_config.yaml b/bsp_config.yaml index 337fa09..17ce7d6 100644 --- a/bsp_config.yaml +++ b/bsp_config.yaml @@ -123,4 +123,6 @@ uart: devices: - instance: USART3 name: DR16 + - instance: UART5 + name: AI enabled: true diff --git a/gpio.c b/gpio.c deleted file mode 100644 index 2c00ec4..0000000 --- a/gpio.c +++ /dev/null @@ -1,93 +0,0 @@ -/* Includes ----------------------------------------------------------------- */ -#include "bsp/gpio.h" - -#include -#include - -/* 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; -} diff --git a/gpio.h b/gpio.h deleted file mode 100644 index 9ed26d4..0000000 --- a/gpio.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ----------------------------------------------------------------- */ -#include -#include - -#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 diff --git a/test.py b/test.py deleted file mode 100644 index 3571b79..0000000 --- a/test.py +++ /dev/null @@ -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_()) \ No newline at end of file