Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2517d8b9ec | |||
| 4cf349cf14 | |||
| b7f2539321 | |||
| b03ce04217 | |||
| 17847459b4 | |||
| c3faf353e9 | |||
| 12db6ce44a | |||
| 20b6af6c34 | |||
| 05ad2226de | |||
| 9d6a10135d | |||
| 8d741c6d61 | |||
| 83aff179ee | |||
| b56d7bf8e0 | |||
| e22ed7f393 | |||
| ab3445ef6d | |||
| 09eabb804a | |||
| ab0a95b0af | |||
| b36738ecac | |||
| a91f175e9e | |||
| 5369861c88 | |||
| c13d3a5e44 | |||
| 345743a1c6 | |||
| 91eeda0e07 | |||
| d467318505 | |||
| 69c200d4c9 | |||
| 22bddbcda7 | |||
| fc94a3fa33 | |||
| 5cabd8c3b6 | |||
| 94841a02dd | |||
| 78104b724b |
34
.gitignore
vendored
34
.gitignore
vendored
@ -1,34 +0,0 @@
|
||||
*.rar
|
||||
*.o
|
||||
*.d
|
||||
*.crf
|
||||
*.htm
|
||||
*.dep
|
||||
*.map
|
||||
*.bak
|
||||
*.lnp
|
||||
*.lst
|
||||
*.ini
|
||||
*.iex
|
||||
*.sct
|
||||
*.scvd
|
||||
*.uvguix
|
||||
*.dbg*
|
||||
*.uvguix.*
|
||||
.mxproject
|
||||
|
||||
RTE/
|
||||
Templates/
|
||||
Examples/
|
||||
|
||||
!*.uvprojx
|
||||
!*.h
|
||||
!*.c
|
||||
!*.ioc
|
||||
!*.axf
|
||||
!*.bin
|
||||
!*.hex
|
||||
|
||||
build/
|
||||
dist/
|
||||
*.spec
|
||||
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 zucheng Lv
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2339
MR_Tool.py
2339
MR_Tool.py
File diff suppressed because it is too large
Load Diff
673
MRobot.py
673
MRobot.py
@ -1,673 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QThread
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QGroupBox,
|
||||
QComboBox, QPushButton, QTextEdit, QLineEdit, QLabel, QSizePolicy,
|
||||
QFileDialog, QMessageBox, QStackedLayout
|
||||
)
|
||||
|
||||
from qfluentwidgets import (
|
||||
Theme, setTheme, FluentIcon, SwitchButton, BodyLabel, SubtitleLabel,
|
||||
StrongBodyLabel, HorizontalSeparator, InfoBar, MessageDialog, Dialog,
|
||||
AvatarWidget, NavigationItemPosition, FluentWindow, NavigationAvatarWidget,
|
||||
PushButton, TextEdit, LineEdit, ComboBox, ImageLabel
|
||||
)
|
||||
from qfluentwidgets import FluentIcon as FIF
|
||||
import requests
|
||||
import shutil
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, QHeaderView
|
||||
|
||||
from qfluentwidgets import (
|
||||
TreeWidget, InfoBar, InfoBarPosition, MessageDialog, TreeItemDelegate
|
||||
)
|
||||
from qfluentwidgets import CheckBox
|
||||
from qfluentwidgets import TreeWidget
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
from qfluentwidgets import ProgressBar
|
||||
|
||||
# ===================== 页面基类 =====================
|
||||
class BaseInterface(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# ===================== 首页界面 =====================
|
||||
class HomeInterface(BaseInterface):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("homeInterface")
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(60, 60, 60, 60)
|
||||
layout.setSpacing(32)
|
||||
self.setLayout(layout)
|
||||
|
||||
# 顶部logo和欢迎区
|
||||
top_layout = QHBoxLayout()
|
||||
logo = ImageLabel('img/MRobot.png')
|
||||
logo.setFixedSize(260, 80)
|
||||
top_layout.addWidget(logo, alignment=Qt.AlignmentFlag.AlignTop)
|
||||
title_layout = QVBoxLayout()
|
||||
title_layout.addWidget(StrongBodyLabel("欢迎使用 MRobot Toolbox"))
|
||||
title_layout.addWidget(SubtitleLabel("让你的机器人开发更高效、更智能"))
|
||||
top_layout.addLayout(title_layout)
|
||||
top_layout.addStretch()
|
||||
layout.addLayout(top_layout)
|
||||
|
||||
layout.addWidget(HorizontalSeparator())
|
||||
|
||||
# 项目简介
|
||||
layout.addWidget(BodyLabel(
|
||||
"MRobot Toolbox 是一款集成化的机器人开发辅助工具,"
|
||||
"支持代码生成、串口终端、主题切换等多种实用功能。\n"
|
||||
"点击左侧导航栏可快速切换各功能页面。"
|
||||
))
|
||||
|
||||
# 开发者与项目目标
|
||||
layout.addWidget(HorizontalSeparator())
|
||||
layout.addWidget(SubtitleLabel("开发者与项目目标"))
|
||||
layout.addWidget(BodyLabel("开发团队:QUT 青岛理工大学 MOVE 战队"))
|
||||
layout.addWidget(BodyLabel("项目目标:为所有 rmer 和 rcer 提供现代化、简单、高效的机器人开发方式,"
|
||||
"让机器人开发变得更轻松、更智能。"))
|
||||
layout.addWidget(BodyLabel("适用于 RM、RC、各类嵌入式机器人项目。"))
|
||||
|
||||
# layout.addStretch()
|
||||
|
||||
# ===================== 代码生成页面 =====================
|
||||
class DataInterface(BaseInterface):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("dataInterface")
|
||||
self.stacked_layout = QStackedLayout()
|
||||
self.setLayout(self.stacked_layout)
|
||||
|
||||
# --- 页面1:工程路径选择 ---
|
||||
self.select_widget = QWidget()
|
||||
select_layout = QVBoxLayout(self.select_widget)
|
||||
select_layout.addSpacing(40)
|
||||
select_layout.addWidget(SubtitleLabel("MRobot 代码生成"))
|
||||
select_layout.addWidget(HorizontalSeparator())
|
||||
select_layout.addSpacing(10)
|
||||
select_layout.addWidget(BodyLabel("请选择包含 .ioc 文件的工程文件夹,点击下方按钮进行选择。"))
|
||||
select_layout.addSpacing(20)
|
||||
self.choose_btn = PushButton("选择工程路径")
|
||||
self.choose_btn.clicked.connect(self.choose_project_folder)
|
||||
select_layout.addWidget(self.choose_btn)
|
||||
select_layout.addStretch()
|
||||
self.stacked_layout.addWidget(self.select_widget)
|
||||
|
||||
# --- 页面2:代码配置 ---
|
||||
self.config_widget = QWidget()
|
||||
self.config_layout = QVBoxLayout(self.config_widget)
|
||||
# 左上角小返回按钮
|
||||
top_bar = QHBoxLayout()
|
||||
self.back_btn = PushButton('返回', icon=FluentIcon.SKIP_BACK)
|
||||
# self.back_btn.setFixedSize(32, 32)
|
||||
self.back_btn.clicked.connect(self.back_to_select)
|
||||
self.back_btn.setToolTip("返回")
|
||||
top_bar.addWidget(self.back_btn, alignment=Qt.AlignmentFlag.AlignLeft)
|
||||
top_bar.addStretch()
|
||||
self.config_layout.addLayout(top_bar)
|
||||
self.config_layout.addWidget(SubtitleLabel("工程配置信息"))
|
||||
self.config_layout.addWidget(HorizontalSeparator())
|
||||
self.project_info_labels = []
|
||||
self.config_layout.addStretch()
|
||||
self.stacked_layout.addWidget(self.config_widget)
|
||||
|
||||
# 默认显示选择页面
|
||||
self.stacked_layout.setCurrentWidget(self.select_widget)
|
||||
|
||||
def choose_project_folder(self):
|
||||
folder = QFileDialog.getExistingDirectory(self, "请选择代码项目文件夹")
|
||||
if not folder:
|
||||
return
|
||||
ioc_files = [f for f in os.listdir(folder) if f.endswith('.ioc')]
|
||||
if not ioc_files:
|
||||
QMessageBox.warning(self, "提示", "未找到.ioc文件,请确认项目文件夹。")
|
||||
return
|
||||
self.project_path = folder
|
||||
self.project_name = os.path.basename(folder)
|
||||
self.ioc_file = os.path.join(folder, ioc_files[0])
|
||||
self.show_config_page()
|
||||
|
||||
def show_config_page(self):
|
||||
# 清理旧内容
|
||||
for label in self.project_info_labels:
|
||||
self.config_layout.removeWidget(label)
|
||||
label.deleteLater()
|
||||
self.project_info_labels.clear()
|
||||
# 显示项目信息
|
||||
l1 = BodyLabel(f"项目名称: {self.project_name}")
|
||||
l2 = BodyLabel(f"项目路径: {self.project_path}")
|
||||
l3 = BodyLabel(f"IOC 文件: {self.ioc_file}")
|
||||
self.config_layout.insertWidget(2, l1)
|
||||
self.config_layout.insertWidget(3, l2)
|
||||
self.config_layout.insertWidget(4, l3)
|
||||
self.project_info_labels.extend([l1, l2, l3])
|
||||
self.stacked_layout.setCurrentWidget(self.config_widget)
|
||||
|
||||
def back_to_select(self):
|
||||
self.stacked_layout.setCurrentWidget(self.select_widget)
|
||||
|
||||
# ===================== 串口终端界面 =====================
|
||||
class SerialReadThread(QThread):
|
||||
data_received = pyqtSignal(str)
|
||||
|
||||
def __init__(self, ser):
|
||||
super().__init__()
|
||||
self.ser = ser
|
||||
self._running = True
|
||||
|
||||
def run(self):
|
||||
while self._running:
|
||||
if self.ser and self.ser.is_open and self.ser.in_waiting:
|
||||
try:
|
||||
data = self.ser.readline().decode(errors='ignore')
|
||||
self.data_received.emit(data)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
self.wait()
|
||||
|
||||
class SerialTerminalInterface(BaseInterface):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("serialTerminalInterface")
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setSpacing(12)
|
||||
|
||||
# 顶部:串口设置区
|
||||
top_hbox = QHBoxLayout()
|
||||
top_hbox.addWidget(BodyLabel("串口:"))
|
||||
self.port_combo = ComboBox()
|
||||
self.refresh_ports()
|
||||
top_hbox.addWidget(self.port_combo)
|
||||
top_hbox.addWidget(BodyLabel("波特率:"))
|
||||
self.baud_combo = ComboBox()
|
||||
self.baud_combo.addItems(['9600', '115200', '57600', '38400', '19200', '4800'])
|
||||
top_hbox.addWidget(self.baud_combo)
|
||||
self.connect_btn = PushButton("连接")
|
||||
self.connect_btn.clicked.connect(self.toggle_connection)
|
||||
top_hbox.addWidget(self.connect_btn)
|
||||
self.refresh_btn = PushButton(FluentIcon.SYNC, "刷新")
|
||||
self.refresh_btn.clicked.connect(self.refresh_ports)
|
||||
top_hbox.addWidget(self.refresh_btn)
|
||||
top_hbox.addStretch()
|
||||
main_layout.addLayout(top_hbox)
|
||||
|
||||
main_layout.addWidget(HorizontalSeparator())
|
||||
|
||||
# 中部:左侧预设命令,右侧显示区
|
||||
center_hbox = QHBoxLayout()
|
||||
# 左侧:预设命令竖排
|
||||
preset_vbox = QVBoxLayout()
|
||||
preset_vbox.addWidget(SubtitleLabel("快捷指令"))
|
||||
#快捷指令居中
|
||||
preset_vbox.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.preset_commands = [
|
||||
("线程监视器", "RESET"),
|
||||
("陀螺仪校准", "GET_VERSION"),
|
||||
("性能监视", "START"),
|
||||
("重启", "STOP"),
|
||||
("显示所有设备", "SELF_TEST"),
|
||||
("查询id", "STATUS"),
|
||||
]
|
||||
for label, cmd in self.preset_commands:
|
||||
btn = PushButton(label)
|
||||
btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
btn.clicked.connect(lambda _, c=cmd: self.send_preset_command(c))
|
||||
preset_vbox.addWidget(btn)
|
||||
preset_vbox.addStretch()
|
||||
main_layout.addLayout(center_hbox, stretch=1)
|
||||
|
||||
|
||||
# 右侧:串口数据显示区
|
||||
self.text_edit = TextEdit()
|
||||
self.text_edit.setReadOnly(True)
|
||||
self.text_edit.setMinimumWidth(400)
|
||||
center_hbox.addWidget(self.text_edit, 3)
|
||||
center_hbox.addLayout(preset_vbox, 1)
|
||||
|
||||
main_layout.addWidget(HorizontalSeparator())
|
||||
|
||||
# 底部:输入区
|
||||
bottom_hbox = QHBoxLayout()
|
||||
self.input_line = LineEdit()
|
||||
self.input_line.setPlaceholderText("输入内容,回车发送")
|
||||
self.input_line.returnPressed.connect(self.send_data)
|
||||
bottom_hbox.addWidget(self.input_line, 4)
|
||||
send_btn = PushButton("发送")
|
||||
send_btn.clicked.connect(self.send_data)
|
||||
bottom_hbox.addWidget(send_btn, 1)
|
||||
self.auto_enter_checkbox = CheckBox("自动回车")
|
||||
self.auto_enter_checkbox.setChecked(True)
|
||||
bottom_hbox.addWidget(self.auto_enter_checkbox)
|
||||
bottom_hbox.addStretch()
|
||||
main_layout.addLayout(bottom_hbox)
|
||||
|
||||
self.ser = None
|
||||
self.read_thread = None
|
||||
|
||||
def send_preset_command(self, cmd):
|
||||
self.input_line.setText(cmd)
|
||||
self.send_data()
|
||||
|
||||
def refresh_ports(self):
|
||||
self.port_combo.clear()
|
||||
ports = serial.tools.list_ports.comports()
|
||||
for port in ports:
|
||||
self.port_combo.addItem(port.device)
|
||||
|
||||
def toggle_connection(self):
|
||||
if self.ser and self.ser.is_open:
|
||||
self.disconnect_serial()
|
||||
else:
|
||||
self.connect_serial()
|
||||
|
||||
def connect_serial(self):
|
||||
port = self.port_combo.currentText()
|
||||
baud = int(self.baud_combo.currentText())
|
||||
try:
|
||||
self.ser = serial.Serial(port, baud, timeout=0.1)
|
||||
self.connect_btn.setText("断开")
|
||||
self.text_edit.append(f"已连接到 {port} @ {baud}")
|
||||
self.read_thread = SerialReadThread(self.ser)
|
||||
self.read_thread.data_received.connect(self.display_data)
|
||||
self.read_thread.start()
|
||||
except Exception as e:
|
||||
self.text_edit.append(f"连接失败: {e}")
|
||||
|
||||
def disconnect_serial(self):
|
||||
if self.read_thread:
|
||||
self.read_thread.stop()
|
||||
self.read_thread = None
|
||||
if self.ser:
|
||||
self.ser.close()
|
||||
self.ser = None
|
||||
self.connect_btn.setText("连接")
|
||||
self.text_edit.append("已断开连接")
|
||||
|
||||
def display_data(self, data):
|
||||
self.text_edit.moveCursor(QTextCursor.End)
|
||||
self.text_edit.insertPlainText(data)
|
||||
self.text_edit.moveCursor(QTextCursor.End)
|
||||
|
||||
def send_data(self):
|
||||
if self.ser and self.ser.is_open:
|
||||
text = self.input_line.text()
|
||||
try:
|
||||
if not text:
|
||||
self.ser.write('\n'.encode())
|
||||
else:
|
||||
for char in text:
|
||||
self.ser.write(char.encode())
|
||||
# 判断是否自动回车
|
||||
if self.auto_enter_checkbox.isChecked():
|
||||
self.ser.write('\n'.encode())
|
||||
except Exception as e:
|
||||
self.text_edit.append(f"发送失败: {e}")
|
||||
self.input_line.clear()
|
||||
|
||||
|
||||
# ===================== 零件库页面 =====================
|
||||
|
||||
# ...existing code...
|
||||
class DownloadThread(QThread):
|
||||
progressChanged = pyqtSignal(int)
|
||||
finished = pyqtSignal(list, list) # success, fail
|
||||
|
||||
def __init__(self, files, server_url, secret_key, local_dir, parent=None):
|
||||
super().__init__(parent)
|
||||
self.files = files
|
||||
self.server_url = server_url
|
||||
self.secret_key = secret_key
|
||||
self.local_dir = local_dir
|
||||
|
||||
def run(self):
|
||||
success, fail = [], []
|
||||
total = len(self.files)
|
||||
max_retry = 3 # 最大重试次数
|
||||
for idx, rel_path in enumerate(self.files):
|
||||
retry = 0
|
||||
while retry < max_retry:
|
||||
try:
|
||||
url = f"{self.server_url}/download/{rel_path}"
|
||||
params = {"key": self.secret_key}
|
||||
resp = requests.get(url, params=params, stream=True, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
local_path = os.path.join(self.local_dir, rel_path)
|
||||
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
||||
with open(local_path, "wb") as f:
|
||||
shutil.copyfileobj(resp.raw, f)
|
||||
success.append(rel_path)
|
||||
break # 下载成功,跳出重试循环
|
||||
else:
|
||||
print(f"下载失败({resp.status_code}): {rel_path},第{retry+1}次尝试")
|
||||
retry += 1
|
||||
except Exception as e:
|
||||
print(f"下载异常: {rel_path},第{retry+1}次尝试,错误: {e}")
|
||||
retry += 1
|
||||
else:
|
||||
fail.append(rel_path)
|
||||
self.progressChanged.emit(int((idx + 1) / total * 100))
|
||||
self.finished.emit(success, fail)
|
||||
|
||||
|
||||
class PartLibraryInterface(BaseInterface):
|
||||
SERVER_URL = "http://154.37.215.220:5000"
|
||||
SECRET_KEY = "MRobot_Download"
|
||||
LOCAL_LIB_DIR = "mech_lib"
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("partLibraryInterface")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(16)
|
||||
|
||||
layout.addWidget(SubtitleLabel("零件库(在线bate版)"))
|
||||
layout.addWidget(HorizontalSeparator())
|
||||
layout.addWidget(BodyLabel("可浏览服务器零件库,选择需要的文件下载到本地。(如无法使用或者下载失败,请尝试重新下载或检查网络连接)"))
|
||||
|
||||
btn_layout = QHBoxLayout()
|
||||
refresh_btn = PushButton(FluentIcon.SYNC, "刷新列表")
|
||||
refresh_btn.clicked.connect(self.refresh_list)
|
||||
btn_layout.addWidget(refresh_btn)
|
||||
|
||||
# 新增:打开本地零件库按钮
|
||||
open_local_btn = PushButton(FluentIcon.FOLDER, "打开本地零件库")
|
||||
open_local_btn.clicked.connect(self.open_local_lib)
|
||||
btn_layout.addWidget(open_local_btn)
|
||||
btn_layout.addStretch()
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
self.tree = TreeWidget(self)
|
||||
|
||||
self.tree.setHeaderLabels(["名称", "类型"])
|
||||
self.tree.setSelectionMode(self.tree.ExtendedSelection)
|
||||
self.tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
self.tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
|
||||
self.tree.setCheckedColor("#0078d4", "#2d7d9a")
|
||||
self.tree.setBorderRadius(8)
|
||||
self.tree.setBorderVisible(True)
|
||||
layout.addWidget(self.tree, stretch=1)
|
||||
|
||||
download_btn = PushButton(FluentIcon.DOWNLOAD, "下载选中文件")
|
||||
download_btn.clicked.connect(self.download_selected_files)
|
||||
layout.addWidget(download_btn)
|
||||
|
||||
self.refresh_list(first=True)
|
||||
|
||||
def refresh_list(self, first=False):
|
||||
self.tree.clear()
|
||||
try:
|
||||
resp = requests.get(
|
||||
f"{self.SERVER_URL}/list",
|
||||
params={"key": self.SECRET_KEY},
|
||||
timeout=5
|
||||
)
|
||||
resp.raise_for_status()
|
||||
tree = resp.json()
|
||||
self.populate_tree(self.tree, tree, "")
|
||||
if not first:
|
||||
InfoBar.success(
|
||||
title="刷新成功",
|
||||
content="零件库已经是最新的!",
|
||||
parent=self,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=2000
|
||||
)
|
||||
except Exception as e:
|
||||
InfoBar.error(
|
||||
title="刷新失败",
|
||||
content=f"获取零件库失败: {e}",
|
||||
parent=self,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=3000
|
||||
)
|
||||
|
||||
def populate_tree(self, parent, node, path_prefix):
|
||||
from PyQt5.QtWidgets import QTreeWidgetItem
|
||||
for dname, dnode in node.get("dirs", {}).items():
|
||||
item = QTreeWidgetItem([dname, "文件夹"])
|
||||
if isinstance(parent, TreeWidget):
|
||||
parent.addTopLevelItem(item)
|
||||
else:
|
||||
parent.addChild(item)
|
||||
self.populate_tree(item, dnode, os.path.join(path_prefix, dname))
|
||||
for fname in node.get("files", []):
|
||||
item = QTreeWidgetItem([fname, "文件"])
|
||||
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||
item.setCheckState(0, Qt.Unchecked)
|
||||
item.setData(0, Qt.UserRole, os.path.join(path_prefix, fname))
|
||||
if isinstance(parent, TreeWidget):
|
||||
parent.addTopLevelItem(item)
|
||||
else:
|
||||
parent.addChild(item)
|
||||
|
||||
def get_checked_files(self):
|
||||
files = []
|
||||
def _traverse(item):
|
||||
for i in range(item.childCount()):
|
||||
child = item.child(i)
|
||||
if child.text(1) == "文件" and child.checkState(0) == Qt.Checked:
|
||||
files.append(child.data(0, Qt.UserRole))
|
||||
_traverse(child)
|
||||
root = self.tree.invisibleRootItem()
|
||||
for i in range(root.childCount()):
|
||||
_traverse(root.child(i))
|
||||
return files
|
||||
|
||||
def download_selected_files(self):
|
||||
files = self.get_checked_files()
|
||||
if not files:
|
||||
InfoBar.info(
|
||||
title="提示",
|
||||
content="请先勾选要下载的文件。",
|
||||
parent=self,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=2000
|
||||
)
|
||||
return
|
||||
|
||||
# 进度条对话框
|
||||
self.progress_dialog = Dialog(
|
||||
title="正在下载",
|
||||
content="正在下载选中文件,请稍候...",
|
||||
parent=self
|
||||
)
|
||||
self.progress_bar = ProgressBar()
|
||||
self.progress_bar.setValue(0)
|
||||
# 插入进度条到内容布局
|
||||
self.progress_dialog.textLayout.addWidget(self.progress_bar)
|
||||
self.progress_dialog.show()
|
||||
|
||||
# 启动下载线程
|
||||
self.download_thread = DownloadThread(
|
||||
files, self.SERVER_URL, self.SECRET_KEY, self.LOCAL_LIB_DIR
|
||||
)
|
||||
self.download_thread.progressChanged.connect(self.progress_bar.setValue)
|
||||
self.download_thread.finished.connect(self.on_download_finished)
|
||||
self.download_thread.finished.connect(self.download_thread.deleteLater)
|
||||
self.download_thread.start()
|
||||
|
||||
def on_download_finished(self, success, fail):
|
||||
self.progress_dialog.close()
|
||||
msg = f"成功下载: {len(success)} 个文件\n失败: {len(fail)} 个文件"
|
||||
dialog = Dialog(
|
||||
title="下载结果",
|
||||
content=msg,
|
||||
parent=self
|
||||
)
|
||||
# 添加“打开文件夹”按钮
|
||||
open_btn = PushButton("打开文件夹")
|
||||
def open_folder():
|
||||
folder = os.path.abspath(self.LOCAL_LIB_DIR)
|
||||
# 打开文件夹(macOS用open,Windows用explorer,Linux用xdg-open)
|
||||
import platform, subprocess
|
||||
if platform.system() == "Darwin":
|
||||
subprocess.call(["open", folder])
|
||||
elif platform.system() == "Windows":
|
||||
subprocess.call(["explorer", folder])
|
||||
else:
|
||||
subprocess.call(["xdg-open", folder])
|
||||
dialog.close()
|
||||
open_btn.clicked.connect(open_folder)
|
||||
# 添加按钮到Dialog布局
|
||||
dialog.textLayout.addWidget(open_btn)
|
||||
dialog.exec()
|
||||
|
||||
def open_local_lib(self):
|
||||
folder = os.path.abspath(self.LOCAL_LIB_DIR)
|
||||
import platform, subprocess
|
||||
if platform.system() == "Darwin":
|
||||
subprocess.call(["open", folder])
|
||||
elif platform.system() == "Windows":
|
||||
subprocess.call(["explorer", folder])
|
||||
else:
|
||||
subprocess.call(["xdg-open", folder])
|
||||
|
||||
# ===================== 设置界面 =====================
|
||||
class SettingInterface(BaseInterface):
|
||||
themeSwitchRequested = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("settingInterface")
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
# 标题
|
||||
layout.addSpacing(10)
|
||||
layout.addWidget(SubtitleLabel("设置中心"))
|
||||
layout.addSpacing(10)
|
||||
layout.addWidget(HorizontalSeparator())
|
||||
|
||||
# 主题切换区域
|
||||
theme_title = StrongBodyLabel("外观设置")
|
||||
theme_desc = BodyLabel("切换夜间/白天模式,适应不同环境。")
|
||||
theme_desc.setWordWrap(True)
|
||||
layout.addSpacing(10)
|
||||
layout.addWidget(theme_title)
|
||||
layout.addWidget(theme_desc)
|
||||
|
||||
theme_box = QHBoxLayout()
|
||||
self.theme_label = BodyLabel("夜间模式")
|
||||
self.theme_switch = SwitchButton()
|
||||
self.theme_switch.setChecked(Theme.DARK == Theme.DARK)
|
||||
self.theme_switch.checkedChanged.connect(self.on_theme_switch)
|
||||
theme_box.addWidget(self.theme_label)
|
||||
theme_box.addWidget(self.theme_switch)
|
||||
theme_box.addStretch()
|
||||
layout.addLayout(theme_box)
|
||||
|
||||
layout.addSpacing(15)
|
||||
layout.addWidget(HorizontalSeparator())
|
||||
|
||||
# 其它设置区域(示例)
|
||||
other_title = StrongBodyLabel("其它设置")
|
||||
other_desc = BodyLabel("更多功能正在开发中,敬请期待。")
|
||||
other_desc.setWordWrap(True)
|
||||
layout.addSpacing(10)
|
||||
layout.addWidget(other_title)
|
||||
layout.addWidget(other_desc)
|
||||
|
||||
# 版权信息
|
||||
layout.addStretch()
|
||||
copyright_label = BodyLabel("© 2025 MRobot Toolbox")
|
||||
copyright_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
layout.addWidget(copyright_label)
|
||||
layout.addSpacing(10)
|
||||
|
||||
def on_theme_switch(self, checked):
|
||||
self.themeSwitchRequested.emit()
|
||||
|
||||
# ===================== 帮助与关于界面 =====================
|
||||
class HelpInterface(BaseInterface):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("helpInterface")
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
class AboutInterface(BaseInterface):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("aboutInterface")
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
# ===================== 主窗口与导航 =====================
|
||||
class MainWindow(FluentWindow):
|
||||
themeChanged = pyqtSignal(Theme)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("MR_ToolBox")
|
||||
self.resize(1000, 700)
|
||||
self.setMinimumSize(800, 600)
|
||||
|
||||
# 记录当前主题
|
||||
self.current_theme = Theme.DARK
|
||||
|
||||
# 创建页面实例
|
||||
self.setting_page = SettingInterface(self)
|
||||
self.setting_page.themeSwitchRequested.connect(self.toggle_theme)
|
||||
|
||||
self.page_registry = [
|
||||
(HomeInterface(self), FIF.HOME, "首页", NavigationItemPosition.TOP),
|
||||
(DataInterface(self), FIF.LIBRARY, "MRobot代码生成", NavigationItemPosition.SCROLL),
|
||||
(SerialTerminalInterface(self), FIF.COMMAND_PROMPT, "Mini_Shell", NavigationItemPosition.SCROLL),
|
||||
(PartLibraryInterface(self), FIF.DOWNLOAD, "零件库", NavigationItemPosition.SCROLL), # ← 加上这一行
|
||||
(self.setting_page, FIF.SETTING, "设置", NavigationItemPosition.BOTTOM),
|
||||
(HelpInterface(self), FIF.HELP, "帮助", NavigationItemPosition.BOTTOM),
|
||||
(AboutInterface(self), FIF.INFO, "关于", NavigationItemPosition.BOTTOM),
|
||||
]
|
||||
self.initNavigation()
|
||||
|
||||
def initNavigation(self):
|
||||
for page, icon, name, position in self.page_registry:
|
||||
self.addSubInterface(page, icon, name, position)
|
||||
self.navigationInterface.addSeparator()
|
||||
avatar = NavigationAvatarWidget('用户', ':/qfluentwidgets/images/avatar.png')
|
||||
self.navigationInterface.addWidget(
|
||||
routeKey='avatar',
|
||||
widget=avatar,
|
||||
onClick=self.show_user_info, # 这里改为 self.show_user_info
|
||||
position=NavigationItemPosition.BOTTOM
|
||||
)
|
||||
|
||||
def toggle_theme(self):
|
||||
# 切换主题
|
||||
if self.current_theme == Theme.DARK:
|
||||
self.current_theme = Theme.LIGHT
|
||||
else:
|
||||
self.current_theme = Theme.DARK
|
||||
setTheme(self.current_theme)
|
||||
# 同步设置界面按钮状态
|
||||
self.setting_page.theme_switch.setChecked(self.current_theme == Theme.DARK)
|
||||
|
||||
def show_user_info(self):
|
||||
dialog = Dialog(
|
||||
title="用户信息",
|
||||
content="用户:MRobot至尊VIP用户",
|
||||
parent=self
|
||||
)
|
||||
dialog.exec()
|
||||
|
||||
# ===================== 程序入口 =====================
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
setTheme(Theme.DARK)
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
719
MRobot_old.py
719
MRobot_old.py
@ -1,719 +0,0 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from PIL import Image, ImageTk
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
import shutil
|
||||
import re
|
||||
from git import Repo
|
||||
from collections import defaultdict
|
||||
import csv
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# 配置常量
|
||||
REPO_DIR = "MRobot_repo"
|
||||
REPO_URL = "http://gitea.qutrobot.top/robofish/MRobot.git"
|
||||
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")
|
||||
USER_DIR = os.path.join(CURRENT_DIR, "User")
|
||||
|
||||
class MRobotApp:
|
||||
def __init__(self):
|
||||
self.ioc_data = None
|
||||
self.add_gitignore_var = None # 延迟初始化
|
||||
self.header_file_vars = {}
|
||||
self.task_vars = [] # 用于存储任务的变量
|
||||
|
||||
# 初始化
|
||||
def initialize(self):
|
||||
print("初始化中,正在克隆仓库...")
|
||||
self.clone_repo()
|
||||
self.ioc_data = self.find_and_read_ioc_file()
|
||||
print("初始化完成,启动主窗口...")
|
||||
self.show_main_window()
|
||||
|
||||
# 克隆仓库
|
||||
def clone_repo(self):
|
||||
try:
|
||||
if os.path.exists(REPO_DIR):
|
||||
shutil.rmtree(REPO_DIR)
|
||||
print(f"正在克隆仓库到 {REPO_DIR}(仅克隆当前文件内容)...")
|
||||
Repo.clone_from(REPO_URL, REPO_DIR, multi_options=["--depth=1"])
|
||||
print("仓库克隆成功!")
|
||||
except Exception as e:
|
||||
print(f"克隆仓库时出错: {e}")
|
||||
|
||||
# 删除克隆的仓库
|
||||
def delete_repo(self):
|
||||
try:
|
||||
if os.path.exists(REPO_DIR):
|
||||
shutil.rmtree(REPO_DIR)
|
||||
print(f"已删除克隆的仓库目录: {REPO_DIR}")
|
||||
except Exception as e:
|
||||
print(f"删除仓库目录时出错: {e}")
|
||||
|
||||
|
||||
# 复制文件
|
||||
def copy_file_from_repo(self, src_path, dest_path):
|
||||
try:
|
||||
# 修复路径拼接问题,确保 src_path 不重复包含 REPO_DIR
|
||||
if src_path.startswith(REPO_DIR):
|
||||
full_src_path = src_path
|
||||
else:
|
||||
full_src_path = os.path.join(REPO_DIR, src_path.lstrip(os.sep))
|
||||
|
||||
# 检查源文件是否存在
|
||||
if not os.path.exists(full_src_path):
|
||||
print(f"文件 {full_src_path} 不存在!(检查路径或仓库内容)")
|
||||
return
|
||||
|
||||
# 检查目标路径是否有效
|
||||
if not dest_path or not dest_path.strip():
|
||||
print("目标路径为空或无效,无法复制文件!")
|
||||
return
|
||||
|
||||
# 创建目标目录(如果不存在)
|
||||
dest_dir = os.path.dirname(dest_path)
|
||||
if dest_dir and not os.path.exists(dest_dir):
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
|
||||
# 执行文件复制
|
||||
shutil.copy(full_src_path, dest_path)
|
||||
print(f"文件已从 {full_src_path} 复制到 {dest_path}")
|
||||
except Exception as e:
|
||||
print(f"复制文件时出错: {e}")
|
||||
|
||||
# 查找并读取 .ioc 文件
|
||||
def find_and_read_ioc_file(self):
|
||||
try:
|
||||
for file in os.listdir("."):
|
||||
if file.endswith(".ioc"):
|
||||
print(f"找到 .ioc 文件: {file}")
|
||||
with open(file, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
print("未找到 .ioc 文件!")
|
||||
except Exception as e:
|
||||
print(f"读取 .ioc 文件时出错: {e}")
|
||||
return None
|
||||
|
||||
# 检查是否启用了 FreeRTOS
|
||||
def check_freertos_enabled(self, ioc_data):
|
||||
try:
|
||||
return bool(re.search(r"Mcu\.IP\d+=FREERTOS", ioc_data))
|
||||
except Exception as e:
|
||||
print(f"检查 FreeRTOS 配置时出错: {e}")
|
||||
return False
|
||||
|
||||
# 生成操作
|
||||
def generate_action(self):
|
||||
def task():
|
||||
# 检查并创建目录
|
||||
self.create_directories()
|
||||
|
||||
if self.add_gitignore_var.get():
|
||||
self.copy_file_from_repo(".gitignore", ".gitignore")
|
||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
self.copy_file_from_repo("src/freertos.c", os.path.join("Core", "Src", "freertos.c"))
|
||||
|
||||
# 定义需要处理的文件夹
|
||||
folders = ["bsp", "component", "device", "module"]
|
||||
|
||||
# 遍历每个文件夹,复制选中的 .h 和 .c 文件
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
||||
if not os.path.exists(folder_dir):
|
||||
continue # 如果文件夹不存在,跳过
|
||||
|
||||
for file_name in os.listdir(folder_dir):
|
||||
file_base, file_ext = os.path.splitext(file_name)
|
||||
if file_ext not in [".h", ".c"]:
|
||||
continue # 只处理 .h 和 .c 文件
|
||||
|
||||
# 强制复制与文件夹同名的文件
|
||||
if file_base == folder:
|
||||
src_path = os.path.join(folder_dir, file_name)
|
||||
dest_path = os.path.join("User", folder, file_name)
|
||||
self.copy_file_from_repo(src_path, dest_path)
|
||||
continue # 跳过后续检查,直接复制
|
||||
|
||||
# 检查是否选中了对应的文件
|
||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
||||
src_path = os.path.join(folder_dir, file_name)
|
||||
dest_path = os.path.join("User", folder, file_name)
|
||||
self.copy_file_from_repo(src_path, dest_path)
|
||||
|
||||
threading.Thread(target=task).start()
|
||||
|
||||
|
||||
|
||||
# 创建必要的目录
|
||||
def create_directories(self):
|
||||
try:
|
||||
directories = [
|
||||
"User/bsp",
|
||||
"User/component",
|
||||
"User/device",
|
||||
"User/module",
|
||||
]
|
||||
# 根据是否启用 FreeRTOS 决定是否创建 User/task
|
||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
directories.append("User/task")
|
||||
|
||||
for directory in directories:
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
print(f"已创建目录: {directory}")
|
||||
else:
|
||||
print(f"目录已存在: {directory}")
|
||||
except Exception as e:
|
||||
print(f"创建目录时出错: {e}")
|
||||
|
||||
|
||||
# 更新 FreeRTOS 状态标签
|
||||
def update_freertos_status(self, label):
|
||||
if self.ioc_data:
|
||||
status = "已启用" if self.check_freertos_enabled(self.ioc_data) else "未启用"
|
||||
else:
|
||||
status = "未检测到 .ioc 文件"
|
||||
label.config(text=f"FreeRTOS 状态: {status}")
|
||||
|
||||
|
||||
|
||||
# 显示主窗口
|
||||
# ...existing code...
|
||||
# ...existing code...
|
||||
|
||||
# 显示主窗口
|
||||
def show_main_window(self):
|
||||
root = tk.Tk()
|
||||
root.title("MRobot 自动生成脚本")
|
||||
root.geometry("1000x650") # 调整窗口大小以适应布局
|
||||
|
||||
# 在窗口关闭时调用 on_closing 方法
|
||||
root.protocol("WM_DELETE_WINDOW", lambda: self.on_closing(root))
|
||||
|
||||
# 初始化 BooleanVar
|
||||
self.add_gitignore_var = tk.BooleanVar(value=False)
|
||||
self.auto_configure_var = tk.BooleanVar(value=False) # 新增复选框变量
|
||||
|
||||
# 创建主框架
|
||||
main_frame = ttk.Frame(root)
|
||||
main_frame.pack(fill="both", expand=True)
|
||||
|
||||
# 添加标题
|
||||
title_label = ttk.Label(main_frame, text="MRobot 自动生成脚本", font=("Arial", 16, "bold"))
|
||||
title_label.pack(pady=10)
|
||||
|
||||
# 添加 FreeRTOS 状态标签
|
||||
freertos_status_label = ttk.Label(main_frame, text="FreeRTOS 状态: 检测中...", font=("Arial", 12))
|
||||
freertos_status_label.pack(pady=10)
|
||||
self.update_freertos_status(freertos_status_label)
|
||||
|
||||
# 模块文件选择和任务管理框架(添加滚动功能)
|
||||
module_task_frame = ttk.Frame(main_frame)
|
||||
module_task_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# 创建 Canvas 和 Scrollbar
|
||||
canvas = tk.Canvas(module_task_frame)
|
||||
scrollbar = ttk.Scrollbar(module_task_frame, orient="vertical", command=canvas.yview)
|
||||
scrollable_frame = ttk.Frame(canvas)
|
||||
|
||||
# 配置滚动区域
|
||||
scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
)
|
||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# 绑定鼠标滚轮事件
|
||||
def on_mouse_wheel(event):
|
||||
canvas.yview_scroll(-1 * int(event.delta / 120), "units")
|
||||
|
||||
canvas.bind_all("<MouseWheel>", on_mouse_wheel)
|
||||
|
||||
# 布局 Canvas 和 Scrollbar
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# 左右布局:模块文件选择框和任务管理框
|
||||
left_frame = ttk.Frame(scrollable_frame)
|
||||
left_frame.pack(side="left", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
right_frame = ttk.Frame(scrollable_frame)
|
||||
right_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
||||
|
||||
# 模块文件选择框
|
||||
header_files_frame = ttk.LabelFrame(left_frame, text="模块文件选择", padding=(10, 10))
|
||||
header_files_frame.pack(fill="both", expand=True, padx=5)
|
||||
self.header_files_frame = header_files_frame
|
||||
self.update_header_files()
|
||||
|
||||
# 任务管理框
|
||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
task_frame = ttk.LabelFrame(right_frame, text="任务管理", padding=(10, 10))
|
||||
task_frame.pack(fill="both", expand=True, padx=5)
|
||||
self.task_frame = task_frame
|
||||
self.update_task_ui()
|
||||
|
||||
# 添加消息框和生成按钮在同一行
|
||||
bottom_frame = ttk.Frame(main_frame)
|
||||
bottom_frame.pack(fill="x", pady=10, side="bottom")
|
||||
|
||||
# 消息框
|
||||
self.message_box = tk.Text(bottom_frame, wrap="word", state="disabled", height=5, width=60)
|
||||
self.message_box.pack(side="left", fill="x", expand=True, padx=5, pady=5)
|
||||
|
||||
# 生成按钮和复选框选项
|
||||
button_frame = ttk.Frame(bottom_frame)
|
||||
button_frame.pack(side="right", padx=10)
|
||||
|
||||
# 添加复选框容器(横向排列复选框)
|
||||
checkbox_frame = ttk.Frame(button_frame)
|
||||
checkbox_frame.pack(side="top", pady=5)
|
||||
|
||||
# 添加 .gitignore 复选框(左侧)
|
||||
ttk.Checkbutton(checkbox_frame, text=".gitignore", variable=self.add_gitignore_var).pack(side="left", padx=5)
|
||||
|
||||
# 添加自动配置环境复选框(右侧)
|
||||
ttk.Checkbutton(checkbox_frame, text="自动环境", variable=self.auto_configure_var).pack(side="left", padx=5)
|
||||
|
||||
# 添加生成按钮(竖向排列在复选框下方)
|
||||
generate_button = ttk.Button(button_frame, text="一键生成MRobot代码", command=self.generate_action)
|
||||
generate_button.pack(side="top", pady=10)
|
||||
generate_button.config(width=25) # 设置按钮宽度
|
||||
|
||||
# 重定向输出到消息框
|
||||
self.redirect_output()
|
||||
|
||||
# 打印欢迎信息
|
||||
print("欢迎使用 MRobot 自动生成脚本!")
|
||||
print("请根据需要选择模块文件和任务。")
|
||||
print("点击“一键生成MRobot代码”按钮开始生成。")
|
||||
|
||||
# 启动 Tkinter 主事件循环
|
||||
root.mainloop()
|
||||
|
||||
# ...existing code...
|
||||
# ...existing code...
|
||||
|
||||
def redirect_output(self):
|
||||
"""
|
||||
重定向标准输出到消息框
|
||||
"""
|
||||
class TextRedirector:
|
||||
def __init__(self, text_widget):
|
||||
self.text_widget = text_widget
|
||||
|
||||
def write(self, message):
|
||||
self.text_widget.config(state="normal")
|
||||
self.text_widget.insert("end", message)
|
||||
self.text_widget.see("end")
|
||||
self.text_widget.config(state="disabled")
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
sys.stdout = TextRedirector(self.message_box)
|
||||
sys.stderr = TextRedirector(self.message_box)
|
||||
|
||||
# 修改 update_task_ui 方法
|
||||
def update_task_ui(self):
|
||||
# 检查是否有已存在的任务文件
|
||||
task_dir = os.path.join("User", "task")
|
||||
if os.path.exists(task_dir):
|
||||
for file_name in os.listdir(task_dir):
|
||||
file_base, file_ext = os.path.splitext(file_name)
|
||||
if file_ext == ".c" and file_base not in ["init", "user_task"] and file_base not in [task_var.get() for task_var, _ in self.task_vars]:
|
||||
frequency = 100 # 默认频率
|
||||
user_task_header_path = os.path.join("User", "task", "user_task.h")
|
||||
if os.path.exists(user_task_header_path):
|
||||
try:
|
||||
with open(user_task_header_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
pattern = rf"#define\s+TASK_FREQ_{file_base.upper()}\s*\((\d+)[uU]?\)"
|
||||
match = re.search(pattern, content)
|
||||
if match:
|
||||
frequency = int(match.group(1))
|
||||
print(f"从 user_task.h 文件中读取到任务 {file_base} 的频率: {frequency}")
|
||||
except Exception as e:
|
||||
print(f"读取 user_task.h 文件时出错: {e}")
|
||||
|
||||
new_task_var = tk.StringVar(value=file_base)
|
||||
self.task_vars.append((new_task_var, tk.IntVar(value=frequency)))
|
||||
|
||||
# 清空任务框架中的所有子组件
|
||||
for widget in self.task_frame.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
|
||||
# 设置任务管理框的固定宽度
|
||||
self.task_frame.config(width=400)
|
||||
|
||||
# 显示任务列表
|
||||
for i, (task_var, freq_var) in enumerate(self.task_vars):
|
||||
task_row = ttk.Frame(self.task_frame, width=400)
|
||||
task_row.pack(fill="x", pady=5)
|
||||
|
||||
ttk.Entry(task_row, textvariable=task_var, width=20).pack(side="left", padx=5)
|
||||
ttk.Label(task_row, text="频率:").pack(side="left", padx=5)
|
||||
ttk.Spinbox(task_row, from_=1, to=1000, textvariable=freq_var, width=5).pack(side="left", padx=5)
|
||||
ttk.Button(task_row, text="删除", command=lambda idx=i: self.remove_task(idx)).pack(side="left", padx=5)
|
||||
|
||||
# 添加新任务按钮
|
||||
add_task_button = ttk.Button(self.task_frame, text="添加任务", command=self.add_task)
|
||||
add_task_button.pack(pady=10)
|
||||
|
||||
|
||||
# 修改 add_task 方法
|
||||
def add_task(self):
|
||||
new_task_var = tk.StringVar(value=f"Task_{len(self.task_vars) + 1}")
|
||||
new_freq_var = tk.IntVar(value=100) # 默认频率为 100
|
||||
self.task_vars.append((new_task_var, new_freq_var))
|
||||
self.update_task_ui()
|
||||
|
||||
# 修改 remove_task 方法
|
||||
def remove_task(self, idx):
|
||||
del self.task_vars[idx]
|
||||
self.update_task_ui()
|
||||
|
||||
# 更新文件夹显示
|
||||
def update_folder_display(self):
|
||||
for widget in self.folder_frame.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
folders = ["User/bsp", "User/component", "User/device", "User/module"]
|
||||
# if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
# folders.append("User/task")
|
||||
|
||||
for folder in folders:
|
||||
# 去掉 "User/" 前缀
|
||||
display_name = folder.replace("User/", "")
|
||||
tk.Label(self.folder_frame, text=display_name).pack()
|
||||
|
||||
# 更新 .h 文件复选框
|
||||
def update_header_files(self):
|
||||
for widget in self.header_files_frame.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
folders = ["bsp", "component", "device", "module"]
|
||||
dependencies = defaultdict(list)
|
||||
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
||||
if os.path.exists(folder_dir):
|
||||
dependencies_file = os.path.join(folder_dir, "dependencies.csv")
|
||||
if os.path.exists(dependencies_file):
|
||||
with open(dependencies_file, "r", encoding="utf-8") as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) == 2:
|
||||
dependencies[row[0]].append(row[1])
|
||||
|
||||
# 创建复选框
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
||||
if os.path.exists(folder_dir):
|
||||
module_frame = ttk.LabelFrame(self.header_files_frame, text=folder.capitalize(), padding=(10, 10))
|
||||
module_frame.pack(fill="x", pady=5)
|
||||
|
||||
row, col = 0, 0
|
||||
for file in os.listdir(folder_dir):
|
||||
file_base, file_ext = os.path.splitext(file)
|
||||
if file_ext == ".h" and file_base != folder:
|
||||
var = tk.BooleanVar(value=False)
|
||||
self.header_file_vars[file_base] = var
|
||||
|
||||
checkbox = ttk.Checkbutton(
|
||||
module_frame,
|
||||
text=file_base,
|
||||
variable=var,
|
||||
command=lambda fb=file_base: self.handle_dependencies(fb, dependencies)
|
||||
)
|
||||
checkbox.grid(row=row, column=col, padx=5, pady=5, sticky="w")
|
||||
col += 1
|
||||
if col >= 6:
|
||||
col = 0
|
||||
row += 1
|
||||
|
||||
|
||||
|
||||
def handle_dependencies(self, file_base, dependencies):
|
||||
"""
|
||||
根据依赖关系自动勾选相关模块
|
||||
"""
|
||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
||||
# 如果当前模块被选中,自动勾选其依赖项
|
||||
for dependency in dependencies.get(file_base, []):
|
||||
dep_base = os.path.basename(dependency)
|
||||
if dep_base in self.header_file_vars:
|
||||
self.header_file_vars[dep_base].set(True)
|
||||
|
||||
# 在 MRobotApp 类中添加以下方法
|
||||
def generate_task_files(self):
|
||||
try:
|
||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "task.c.template")
|
||||
task_dir = os.path.join("User", "task")
|
||||
|
||||
if not os.path.exists(template_file_path):
|
||||
print(f"模板文件 {template_file_path} 不存在,无法生成 task.c 文件!")
|
||||
return
|
||||
|
||||
os.makedirs(task_dir, exist_ok=True)
|
||||
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 为每个任务生成对应的 task.c 文件
|
||||
for task_var, _ in self.task_vars: # 解包元组
|
||||
task_name = f"Task_{task_var.get()}" # 添加前缀 Task_
|
||||
task_file_path = os.path.join(task_dir, f"{task_var.get().lower()}.c") # 文件名保持原始小写
|
||||
|
||||
# 替换模板中的占位符
|
||||
task_content = template_content.replace("{{task_name}}", task_name)
|
||||
task_content = task_content.replace("{{task_function}}", task_name)
|
||||
task_content = task_content.replace(
|
||||
"{{task_frequency}}", f"TASK_FREQ_{task_var.get().upper()}"
|
||||
) # 替换为 user_task.h 中的宏定义
|
||||
task_content = task_content.replace("{{task_delay}}", f"TASK_INIT_DELAY_{task_var.get().upper()}")
|
||||
|
||||
with open(task_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(task_content)
|
||||
|
||||
print(f"已成功生成 {task_file_path} 文件!")
|
||||
except Exception as e:
|
||||
print(f"生成 task.c 文件时出错: {e}")
|
||||
# 修改 user_task.c 文件
|
||||
def modify_user_task_file(self):
|
||||
try:
|
||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "user_task.c.template")
|
||||
generated_task_file_path = os.path.join("User", "task", "user_task.c")
|
||||
|
||||
if not os.path.exists(template_file_path):
|
||||
print(f"模板文件 {template_file_path} 不存在,无法生成 user_task.c 文件!")
|
||||
return
|
||||
|
||||
os.makedirs(os.path.dirname(generated_task_file_path), exist_ok=True)
|
||||
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 生成任务属性定义
|
||||
task_attr_definitions = "\n".join([
|
||||
f"""const osThreadAttr_t attr_{task_var.get().lower()} = {{
|
||||
.name = "{task_var.get()}",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 128 * 4,
|
||||
}};"""
|
||||
for task_var, _ in self.task_vars # 解包元组
|
||||
])
|
||||
|
||||
# 替换模板中的占位符
|
||||
task_content = template_content.replace("{{task_attr_definitions}}", task_attr_definitions)
|
||||
|
||||
with open(generated_task_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(task_content)
|
||||
|
||||
print(f"已成功生成 {generated_task_file_path} 文件!")
|
||||
except Exception as e:
|
||||
print(f"修改 user_task.c 文件时出错: {e}")
|
||||
# ...existing code...
|
||||
|
||||
def generate_user_task_header(self):
|
||||
try:
|
||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "user_task.h.template")
|
||||
header_file_path = os.path.join("User", "task", "user_task.h")
|
||||
|
||||
if not os.path.exists(template_file_path):
|
||||
print(f"模板文件 {template_file_path} 不存在,无法生成 user_task.h 文件!")
|
||||
return
|
||||
|
||||
os.makedirs(os.path.dirname(header_file_path), exist_ok=True)
|
||||
|
||||
# 如果 user_task.h 已存在,提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
||||
existing_msgq_content = ""
|
||||
if os.path.exists(header_file_path):
|
||||
with open(header_file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
# 提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
||||
if match:
|
||||
existing_msgq_content = match.group(1).strip()
|
||||
print("已存在的 msgq 区域内容:")
|
||||
print(existing_msgq_content)
|
||||
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 定义占位符内容
|
||||
thread_definitions = "\n".join([f" osThreadId_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
||||
msgq_definitions = existing_msgq_content if existing_msgq_content else " osMessageQueueId_t default_msgq;"
|
||||
freq_definitions = "\n".join([f" float {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
||||
last_up_time_definitions = "\n".join([f" uint32_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
||||
task_attr_declarations = "\n".join([f"extern const osThreadAttr_t attr_{task_var.get().lower()};" for task_var, _ in self.task_vars])
|
||||
task_function_declarations = "\n".join([f"void Task_{task_var.get()}(void *argument);" for task_var, _ in self.task_vars])
|
||||
task_frequency_definitions = "\n".join([
|
||||
f"#define TASK_FREQ_{task_var.get().upper()} ({freq_var.get()}u)"
|
||||
for task_var, freq_var in self.task_vars
|
||||
])
|
||||
task_init_delay_definitions = "\n".join([f"#define TASK_INIT_DELAY_{task_var.get().upper()} (0u)" for task_var, _ in self.task_vars])
|
||||
task_handle_definitions = "\n".join([f" osThreadId_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
||||
|
||||
# 替换模板中的占位符
|
||||
header_content = template_content.replace("{{thread_definitions}}", thread_definitions)
|
||||
header_content = header_content.replace("{{msgq_definitions}}", msgq_definitions)
|
||||
header_content = header_content.replace("{{freq_definitions}}", freq_definitions)
|
||||
header_content = header_content.replace("{{last_up_time_definitions}}", last_up_time_definitions)
|
||||
header_content = header_content.replace("{{task_attr_declarations}}", task_attr_declarations)
|
||||
header_content = header_content.replace("{{task_function_declarations}}", task_function_declarations)
|
||||
header_content = header_content.replace("{{task_frequency_definitions}}", task_frequency_definitions)
|
||||
header_content = header_content.replace("{{task_init_delay_definitions}}", task_init_delay_definitions)
|
||||
header_content = header_content.replace("{{task_handle_definitions}}", task_handle_definitions)
|
||||
|
||||
# 如果存在 /* USER MESSAGE BEGIN */ 区域内容,则保留
|
||||
if existing_msgq_content:
|
||||
header_content = re.sub(
|
||||
r"/\* USER MESSAGE BEGIN \*/\s*.*?\s*/\* USER MESSAGE END \*/",
|
||||
f"/* USER MESSAGE BEGIN */\n\n {existing_msgq_content}\n\n /* USER MESSAGE END */",
|
||||
header_content,
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
with open(header_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(header_content)
|
||||
|
||||
print(f"已成功生成 {header_file_path} 文件!")
|
||||
except Exception as e:
|
||||
print(f"生成 user_task.h 文件时出错: {e}")
|
||||
|
||||
def generate_init_file(self):
|
||||
try:
|
||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "init.c.template")
|
||||
generated_file_path = os.path.join("User", "task", "init.c")
|
||||
|
||||
if not os.path.exists(template_file_path):
|
||||
print(f"模板文件 {template_file_path} 不存在,无法生成 init.c 文件!")
|
||||
return
|
||||
|
||||
os.makedirs(os.path.dirname(generated_file_path), exist_ok=True)
|
||||
|
||||
# 如果 init.c 已存在,提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
||||
existing_msgq_content = ""
|
||||
if os.path.exists(generated_file_path):
|
||||
with open(generated_file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
# 提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
||||
if match:
|
||||
existing_msgq_content = match.group(1).strip()
|
||||
print("已存在的消息队列区域内容:")
|
||||
print(existing_msgq_content)
|
||||
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 生成任务创建代码
|
||||
thread_creation_code = "\n".join([
|
||||
f" task_runtime.thread.{task_var.get().lower()} = osThreadNew(Task_{task_var.get()}, NULL, &attr_{task_var.get().lower()});"
|
||||
for task_var, _ in self.task_vars # 解包元组
|
||||
])
|
||||
|
||||
# 替换模板中的占位符
|
||||
init_content = template_content.replace("{{thread_creation_code}}", thread_creation_code)
|
||||
|
||||
# 如果存在 /* USER MESSAGE BEGIN */ 区域内容,则保留
|
||||
if existing_msgq_content:
|
||||
init_content = re.sub(
|
||||
r"/\* USER MESSAGE BEGIN \*/\s*.*?\s*/\* USER MESSAGE END \*/",
|
||||
f"/* USER MESSAGE BEGIN */\n {existing_msgq_content}\n /* USER MESSAGE END */",
|
||||
init_content,
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
with open(generated_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(init_content)
|
||||
|
||||
print(f"已成功生成 {generated_file_path} 文件!")
|
||||
except Exception as e:
|
||||
print(f"生成 init.c 文件时出错: {e}")
|
||||
|
||||
# 修改 generate_action 方法
|
||||
|
||||
def generate_action(self):
|
||||
def task():
|
||||
# 检查并创建目录(与 FreeRTOS 状态无关的模块始终创建)
|
||||
self.create_directories()
|
||||
|
||||
# 复制 .gitignore 文件
|
||||
if self.add_gitignore_var.get():
|
||||
self.copy_file_from_repo(".gitignore", ".gitignore")
|
||||
|
||||
# 如果启用了 FreeRTOS,复制相关文件
|
||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
self.copy_file_from_repo("src/freertos.c", os.path.join("Core", "Src", "freertos.c"))
|
||||
|
||||
# 定义需要处理的文件夹(与 FreeRTOS 状态无关)
|
||||
folders = ["bsp", "component", "device", "module"]
|
||||
|
||||
# 遍历每个文件夹,复制选中的 .h 和 .c 文件
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
||||
if not os.path.exists(folder_dir):
|
||||
continue # 如果文件夹不存在,跳过
|
||||
|
||||
for file_name in os.listdir(folder_dir):
|
||||
file_base, file_ext = os.path.splitext(file_name)
|
||||
if file_ext not in [".h", ".c"]:
|
||||
continue # 只处理 .h 和 .c 文件
|
||||
|
||||
# 强制复制与文件夹同名的文件
|
||||
if file_base == folder:
|
||||
src_path = os.path.join(folder_dir, file_name)
|
||||
dest_path = os.path.join("User", folder, file_name)
|
||||
self.copy_file_from_repo(src_path, dest_path)
|
||||
print(f"强制复制与文件夹同名的文件: {file_name}")
|
||||
continue # 跳过后续检查,直接复制
|
||||
|
||||
# 检查是否选中了对应的文件
|
||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
||||
src_path = os.path.join(folder_dir, file_name)
|
||||
dest_path = os.path.join("User", folder, file_name)
|
||||
self.copy_file_from_repo(src_path, dest_path)
|
||||
|
||||
# 如果启用了 FreeRTOS,执行任务相关的生成逻辑
|
||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
||||
# 修改 user_task.c 文件
|
||||
self.modify_user_task_file()
|
||||
|
||||
# 生成 user_task.h 文件
|
||||
self.generate_user_task_header()
|
||||
|
||||
# 生成 init.c 文件
|
||||
self.generate_init_file()
|
||||
|
||||
# 生成 task.c 文件
|
||||
self.generate_task_files()
|
||||
|
||||
# # 自动配置环境
|
||||
# if self.auto_configure_var.get():
|
||||
|
||||
# self.auto_configure_environment()
|
||||
|
||||
|
||||
threading.Thread(target=task).start()
|
||||
|
||||
# 程序关闭时清理
|
||||
def on_closing(self, root):
|
||||
self.delete_repo()
|
||||
root.destroy()
|
||||
|
||||
|
||||
# 程序入口
|
||||
if __name__ == "__main__":
|
||||
app = MRobotApp()
|
||||
app.initialize()
|
||||
93
README.md
93
README.md
@ -1,93 +0,0 @@
|
||||
# MRobot
|
||||
|
||||
更加高效快捷的 STM32 开发架构,诞生于 Robocon 和 Robomaster,但绝不仅限于此。
|
||||
|
||||
<div align="center">
|
||||
<img src="./img/MRobot.png" height="100" alt="MRobot Logo">
|
||||
<p>是时候使用更简洁的方式开发单片机了</p>
|
||||
<p>
|
||||
<!-- <img src="https://img.shields.io/github/license/xrobot-org/XRobot.svg" alt="License">
|
||||
<img src="https://img.shields.io/github/repo-size/xrobot-org/XRobot.svg" alt="Repo Size">
|
||||
<img src="https://img.shields.io/github/last-commit/xrobot-org/XRobot.svg" alt="Last Commit">
|
||||
<img src="https://img.shields.io/badge/language-c/c++-F34B7D.svg" alt="Language"> -->
|
||||
</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 引言
|
||||
|
||||
提起嵌入式开发,绝大多数人对每次繁琐的配置,以及查阅各种文档来写东西感到非常枯燥和浪费使时间,对于小形形目创建优雅的架构又比较费事,那么我们哟u没有办法快速完成基础环境的搭建后直接开始写逻辑代码呢?
|
||||
|
||||
这就是**MRobot**。
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 获取源代码
|
||||
|
||||
(此处可补充获取代码的具体方法)
|
||||
|
||||
---
|
||||
|
||||
## 主要特色
|
||||
|
||||
(此处可补充项目的主要特色)
|
||||
|
||||
---
|
||||
|
||||
## 组成
|
||||
|
||||
<div align="center">
|
||||
<img src="./image/嵌入式程序层次图.png" alt="嵌入式程序层次图">
|
||||
</div>
|
||||
|
||||
- `src/bsp`
|
||||
- `src/component`
|
||||
- `src/device`
|
||||
- `src/module`
|
||||
- `src/task`
|
||||
|
||||
---
|
||||
|
||||
## 应用案例
|
||||
|
||||
> **Robomaster**
|
||||
|
||||
- 全向轮步兵
|
||||
- 英雄
|
||||
- 哨兵
|
||||
|
||||
---
|
||||
|
||||
## 机器人展示
|
||||
|
||||
`以上机器人均使用 MRobot 搭建`
|
||||
|
||||
---
|
||||
|
||||
## 硬件支持
|
||||
|
||||
(此处可补充支持的硬件列表)
|
||||
|
||||
---
|
||||
|
||||
## 图片展示
|
||||
|
||||
|
||||
## 相关依赖
|
||||
|
||||
(此处可补充项目依赖的具体内容)
|
||||
|
||||
---
|
||||
|
||||
## 构建 exe
|
||||
|
||||
使用以下命令构建可执行文件:
|
||||
|
||||
```bash
|
||||
pyinstaller --onefile --windowed
|
||||
pyinstaller MR_Toolbox.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img"
|
||||
|
||||
pyinstaller MR_Tool.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img" --add-data "src;src" --add-data "User;User"
|
||||
BIN
User/.DS_Store
vendored
BIN
User/.DS_Store
vendored
Binary file not shown.
BIN
User/bsp/.DS_Store
vendored
BIN
User/bsp/.DS_Store
vendored
Binary file not shown.
@ -1,32 +0,0 @@
|
||||
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <gpio.h>
|
||||
#include "bsp/bsp.h"
|
||||
#include "bsp/buzzer_gpio.h"
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define BSP_BUZZER_GPIO GPIOA
|
||||
#define BSP_BUZZER_PIN GPIO_PIN_1
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
int8_t BSP_Buzzer_Set(BSP_Buzzer_Status_t s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case BSP_BUZZER_ON:
|
||||
HAL_GPIO_WritePin(BSP_BUZZER_GPIO, BSP_BUZZER_PIN, GPIO_PIN_SET); // 打开蜂鸣器
|
||||
break;
|
||||
case BSP_BUZZER_OFF:
|
||||
HAL_GPIO_WritePin(BSP_BUZZER_GPIO, BSP_BUZZER_PIN, GPIO_PIN_RESET); // 关闭蜂鸣器
|
||||
break;
|
||||
case BSP_BUZZER_TAGGLE:
|
||||
HAL_GPIO_TogglePin(BSP_BUZZER_GPIO, BSP_BUZZER_PIN); // 切换蜂鸣器状态
|
||||
break;
|
||||
default:
|
||||
return BSP_ERR;
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
141
User/bsp/can.c
141
User/bsp/can.c
@ -1,141 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp\can.h"
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static void (*CAN_Callback[BSP_CAN_NUM][BSP_CAN_CB_NUM])(void);
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static BSP_CAN_t CAN_Get(CAN_HandleTypeDef *hcan) {
|
||||
if (hcan->Instance == CAN2)
|
||||
return BSP_CAN_2;
|
||||
else if (hcan->Instance == CAN1)
|
||||
return BSP_CAN_1;
|
||||
else
|
||||
return BSP_CAN_ERR;
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_ERROR_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_ERROR_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can) {
|
||||
switch (can) {
|
||||
case BSP_CAN_2:
|
||||
return &hcan2;
|
||||
case BSP_CAN_1:
|
||||
return &hcan1;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void)) {
|
||||
if (callback == NULL) return BSP_ERR_NULL;
|
||||
CAN_Callback[can][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <can.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
BSP_CAN_1,
|
||||
BSP_CAN_2,
|
||||
BSP_CAN_NUM,
|
||||
BSP_CAN_ERR,
|
||||
} BSP_CAN_t;
|
||||
|
||||
typedef enum {
|
||||
HAL_CAN_TX_MAILBOX0_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX0_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_ABORT_CB,
|
||||
HAL_CAN_RX_FIFO0_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO0_FULL_CB,
|
||||
HAL_CAN_RX_FIFO1_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO1_FULL_CB,
|
||||
HAL_CAN_SLEEP_CB,
|
||||
HAL_CAN_WAKEUP_FROM_RX_MSG_CB,
|
||||
HAL_CAN_ERROR_CB,
|
||||
BSP_CAN_CB_NUM
|
||||
} BSP_CAN_Callback_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can);
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
118
User/bsp/delay.c
118
User/bsp/delay.c
@ -1,118 +0,0 @@
|
||||
#include "bsp_delay.h"
|
||||
|
||||
#include "cmsis_os.h"
|
||||
#include "main.h"
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static uint8_t fac_us = 0;
|
||||
static uint32_t fac_ms = 0;
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static void delay_ticks(uint32_t ticks)
|
||||
{
|
||||
uint32_t told = SysTick->VAL;
|
||||
uint32_t tnow = 0;
|
||||
uint32_t tcnt = 0;
|
||||
uint32_t reload = SysTick->LOAD;
|
||||
while (1)
|
||||
{
|
||||
tnow = SysTick->VAL;
|
||||
if (tnow != told)
|
||||
{
|
||||
if (tnow < told)
|
||||
{
|
||||
tcnt += told - tnow;
|
||||
}
|
||||
else
|
||||
{
|
||||
tcnt += reload - tnow + told;
|
||||
}
|
||||
told = tnow;
|
||||
if (tcnt >= ticks)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 毫秒延时函数
|
||||
* @param ms
|
||||
* @return
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 延时初始化
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
int8_t BSP_Delay_Init(void)
|
||||
{
|
||||
if (SystemCoreClock == 0)
|
||||
{
|
||||
return BSP_ERR;
|
||||
}
|
||||
fac_us = SystemCoreClock / 1000000;
|
||||
fac_ms = SystemCoreClock / 1000;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 微秒延时,注意:此函数会阻塞当前线程,直到延时结束,并且会被中断打断
|
||||
* @param us
|
||||
* @return
|
||||
*/
|
||||
int8_t BSP_Delay_us(uint32_t us)
|
||||
{
|
||||
if (fac_us == 0)
|
||||
{
|
||||
return BSP_ERR;
|
||||
}
|
||||
uint32_t ticks = us * fac_us;
|
||||
delay_ticks(ticks);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 毫秒延时,注意:此函数会阻塞当前线程,直到延时结束,并且会被中断打断
|
||||
* @param ms
|
||||
* @return
|
||||
*/
|
||||
int8_t BSP_Delay_ms(uint32_t ms)
|
||||
{
|
||||
if (fac_ms == 0)
|
||||
{
|
||||
return BSP_ERR;
|
||||
}
|
||||
uint32_t ticks = ms * fac_ms;
|
||||
delay_ticks(ticks);
|
||||
return BSP_OK;
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
i2c,I2C
|
||||
uart,USART,UART
|
||||
|
@ -1,72 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/led_gpio.h"
|
||||
#include "bsp/bsp.h"
|
||||
#include <gpio.h>
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static uint32_t led_stats; // 使用位掩码记录每个通道的状态,最多支持32LED
|
||||
|
||||
// 定义 LED 引脚和端口映射表:需要根据自己的修改,添加,或删减。
|
||||
static const BSP_LED_Config_t LED_CONFIGS[] = {
|
||||
{GPIOA, GPIO_PIN_2}, // BSP_LED_1
|
||||
{GPIOA, GPIO_PIN_3}, // BSP_LED_2
|
||||
{GPIOA, GPIO_PIN_4}, // BSP_LED_3
|
||||
};
|
||||
|
||||
#define LED_CHANNEL_COUNT (sizeof(LED_CONFIGS) / sizeof(LED_CONFIGS[0])) // 通道数量
|
||||
|
||||
/* Private function --------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
int8_t BSP_LED_Set(BSP_LED_Channel_t ch, BSP_LED_Status_t s)
|
||||
{
|
||||
if (ch < LED_CHANNEL_COUNT)
|
||||
{
|
||||
GPIO_TypeDef *port = LED_CONFIGS[ch].port;
|
||||
uint16_t pin = LED_CONFIGS[ch].pin;
|
||||
switch (s)
|
||||
{
|
||||
case BSP_LED_ON:
|
||||
led_stats |= (1 << ch);
|
||||
HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); // 点亮LED
|
||||
break;
|
||||
case BSP_LED_OFF:
|
||||
led_stats &= ~(1 << ch);
|
||||
HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); // 熄灭LED
|
||||
break;
|
||||
case BSP_LED_TAGGLE:
|
||||
led_stats ^= (1 << ch);
|
||||
HAL_GPIO_TogglePin(port, pin); // 切换LED状态
|
||||
break;
|
||||
default:
|
||||
return BSP_ERR;
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
return BSP_ERR;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include "gpio.h"
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* LED灯状态,设置用 */
|
||||
typedef enum
|
||||
{
|
||||
BSP_LED_ON,
|
||||
BSP_LED_OFF,
|
||||
BSP_LED_TAGGLE,
|
||||
} BSP_LED_Status_t;
|
||||
|
||||
/* LED通道 */
|
||||
typedef enum
|
||||
{
|
||||
BSP_LED_1,
|
||||
BSP_LED_2,
|
||||
BSP_LED_3,
|
||||
/*BSP_LED_XXX*/
|
||||
} BSP_LED_Channel_t;
|
||||
|
||||
/* LED GPIO 配置 */
|
||||
typedef struct
|
||||
{
|
||||
GPIO_TypeDef *port; // GPIO 端口 (如 GPIOA, GPIOB)
|
||||
uint16_t pin; // GPIO 引脚
|
||||
} BSP_LED_Config_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
int8_t BSP_LED_Set(BSP_LED_Channel_t ch, BSP_LED_Status_t s);
|
||||
@ -1,48 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "servo_pwm.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
int8_t BSP_PWM_Start(BSP_PWM_Channel_t ch) {
|
||||
|
||||
TIM_HandleTypeDef* htim = pwm_channel_config[ch].htim;
|
||||
uint32_t channel = pwm_channel_config[ch].channel;
|
||||
|
||||
if(HAL_TIM_PWM_Start(htim, channel)!=HAL_OK){
|
||||
return -1;
|
||||
}else return 0;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_Set(BSP_PWM_Channel_t ch, float duty_cycle) {
|
||||
if (duty_cycle > 1.0f) return -1;
|
||||
|
||||
uint16_t pulse = duty_cycle/CYCLE * PWM_RESOLUTION;
|
||||
|
||||
if(__HAL_TIM_SET_COMPARE(pwm_channel_config[ch].htim, pwm_channel_config[ch].channel, pulse)!=HAL_OK){
|
||||
return -1;
|
||||
}else return 0;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_Stop(BSP_PWM_Channel_t ch){
|
||||
|
||||
TIM_HandleTypeDef* htim = pwm_channel_config[ch].htim;
|
||||
uint32_t channel = pwm_channel_config[ch].channel;
|
||||
|
||||
if(HAL_TIM_PWM_Stop(htim, channel)!=HAL_OK){
|
||||
return -1;
|
||||
}else return 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include "tim.h"
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct {
|
||||
TIM_HandleTypeDef* htim; // 定时器句柄
|
||||
uint32_t channel; // 定时器通道
|
||||
} PWM_Channel_Config_t;
|
||||
|
||||
#define PWM_RESOLUTION 1000 // ARR change begin
|
||||
#define CYCLE 20 //ms
|
||||
|
||||
typedef enum {
|
||||
BSP_PWM_SERVO = 0,
|
||||
BSP_PWM_IMU_HEAT = 1,
|
||||
} BSP_PWM_Channel_t;
|
||||
|
||||
const PWM_Channel_Config_t pwm_channel_config[] = {
|
||||
[BSP_PWM_SERVO] = { &htim1, TIM_CHANNEL_1 }, // xxx 对应 TIMx 通道x
|
||||
[BSP_PWM_IMU_HEAT] = { &htim1, TIM_CHANNEL_2 }
|
||||
}; //change end
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
int8_t BSP_PWM_Start(BSP_PWM_Channel_t ch);
|
||||
int8_t BSP_PWM_Set(BSP_PWM_Channel_t ch, float duty_cycle);
|
||||
int8_t BSP_PWM_Stop(BSP_PWM_Channel_t ch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
pid,component/filter
|
||||
pid,component/user_math
|
||||
filter,component/user_math
|
||||
|
@ -1,301 +0,0 @@
|
||||
#include "bmp280_i2c.h"
|
||||
#include "bsp/i2c.h"
|
||||
|
||||
#define I2C_Handle BSP_I2C_GetHandle(BSP_I2C_BMP280)
|
||||
|
||||
/**
|
||||
* @brief 读寄存器
|
||||
* @param regAdd 寄存器开始地址
|
||||
* @param pdata 存储数据的指针
|
||||
* @param size 寄存器个数
|
||||
* @retval 无
|
||||
*/
|
||||
void bmp280_readReg(uint8_t regAdd, uint8_t *pdata, uint8_t size) {
|
||||
HAL_I2C_Mem_Read(I2C_Handle, BMP280_I2C_ADDR << 1, regAdd, I2C_MEMADD_SIZE_8BIT, pdata, size, 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 写1个寄存器
|
||||
* @param regAdd 寄存器开始地址
|
||||
* @param pdata 存储数据的指针
|
||||
* @retval 0 写入成功
|
||||
* 1 写入失败
|
||||
*/
|
||||
uint8_t bmp280_writeReg(uint8_t regAdd, uint8_t *pdata) {
|
||||
if (HAL_I2C_Mem_Write(I2C_Handle, BMP280_I2C_ADDR << 1, regAdd, I2C_MEMADD_SIZE_8BIT, pdata, 1, 1000) == HAL_OK) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 读取设备物理id,用于调试
|
||||
* @param 无
|
||||
* @retval 设备id
|
||||
*/
|
||||
uint8_t bmp280_get_id(void) {
|
||||
uint8_t temp = 0;
|
||||
bmp280_readReg(BMP280_ID, &temp, 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重启设备
|
||||
* @param 无
|
||||
* @retval 0 重启成功
|
||||
* 1 重启失败
|
||||
*/
|
||||
uint8_t bmp280_reset(void) {
|
||||
uint8_t temp = 0xB6;
|
||||
return bmp280_writeReg(BMP280_RESET, &temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取设备状态
|
||||
* @param 无
|
||||
* @retval 0 空闲
|
||||
* 1 正在测量或者正在复制
|
||||
*/
|
||||
uint8_t bmp280_getStatus(void) {
|
||||
uint8_t temp = 0;
|
||||
bmp280_readReg(BMP280_STATUS, &temp, 1);
|
||||
return (temp & 0x09) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 工作模式设置推荐
|
||||
* @param mode 0 睡眠模式
|
||||
* 1 单次测量模式,测量完成后回到休眠模式
|
||||
* 2 连续测量模式
|
||||
* @retval 0 设置成功
|
||||
* 1 设置失败
|
||||
*/
|
||||
uint8_t bmp280_setMode(uint8_t mode)
|
||||
{
|
||||
uint8_t temp=0;
|
||||
bmp280_readReg(BMP280_CTRL_MEAS,&temp,1);
|
||||
switch(mode)
|
||||
{
|
||||
case 0:
|
||||
temp&=0xFC;
|
||||
break;
|
||||
case 1:
|
||||
temp&=0xFC;
|
||||
temp|=0x01;
|
||||
break;
|
||||
case 2:
|
||||
temp&=0xFC;
|
||||
temp|=0x03;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
return bmp280_writeReg(BMP280_CTRL_MEAS,&temp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 过采样设置
|
||||
* @param mode temp&press 0 禁用
|
||||
* 1 过采样×1
|
||||
* 2 过采样×2
|
||||
* 3 过采样×4
|
||||
* .. .....
|
||||
* 5 过采样×16
|
||||
* @retval 0 设置成功
|
||||
* 1 设置失败
|
||||
*/
|
||||
uint8_t bmp280_setOversampling(uint8_t osrs_p,uint8_t osrs_t)
|
||||
{
|
||||
uint8_t temp=0;
|
||||
bmp280_readReg(BMP280_CTRL_MEAS,&temp,1);
|
||||
temp&=0xE3;
|
||||
osrs_p = osrs_p<<2;
|
||||
osrs_p&= 0x1C;
|
||||
temp|=osrs_p;
|
||||
|
||||
temp&=0x1F;
|
||||
osrs_t = osrs_t<<5;
|
||||
osrs_t&= 0xE0;
|
||||
temp|=osrs_t;
|
||||
return bmp280_writeReg(BMP280_CTRL_MEAS,&temp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 滤波器系数和采样间隔时间设置
|
||||
* @param Standbyt 0 0.5ms filter 0 关闭滤波器
|
||||
* 1 62.5ms 1 2
|
||||
* 2 125ms 2 4
|
||||
* 3 250ms 3 8
|
||||
* 4 500ms 4 16
|
||||
* 5 1000ms
|
||||
* 6 2000ms
|
||||
* 7 4000ms
|
||||
* @retval 0 设置成功
|
||||
* 1 设置失败
|
||||
*/
|
||||
uint8_t bmp280_setConfig(uint8_t Standbyt,uint8_t filter)
|
||||
{
|
||||
uint8_t temp=0;
|
||||
temp = Standbyt<<5;
|
||||
filter&=0x07;
|
||||
filter=filter<<2;
|
||||
temp|=filter;
|
||||
return bmp280_writeReg(BMP280_CONFIG,&temp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取校准系数
|
||||
* @param calib 储存系数的结构体
|
||||
* @retval 无
|
||||
*/
|
||||
void bmp280_getCalibration(bmp280_calib *calib)
|
||||
{
|
||||
uint8_t buf[20];
|
||||
bmp280_readReg(BMP280_DIGT,buf,6);
|
||||
calib->dig_t1 =(uint16_t)(bmp280_msblsb_to_u16(buf[1], buf[0]));
|
||||
calib->dig_t2 =(int16_t)(bmp280_msblsb_to_u16(buf[3], buf[2]));
|
||||
calib->dig_t3 =(int16_t)(bmp280_msblsb_to_u16(buf[5], buf[4]));
|
||||
bmp280_readReg(BMP280_DIGP,buf,18);
|
||||
calib->dig_p1 = (uint16_t)(bmp280_msblsb_to_u16(buf[1], buf[0]));
|
||||
calib->dig_p2 =(int16_t)(bmp280_msblsb_to_u16(buf[3], buf[2]));
|
||||
calib->dig_p3 =(int16_t)(bmp280_msblsb_to_u16(buf[5], buf[4]));
|
||||
calib->dig_p4 =(int16_t)(bmp280_msblsb_to_u16(buf[7], buf[6]));
|
||||
calib->dig_p5 =(int16_t)(bmp280_msblsb_to_u16(buf[9], buf[8]));
|
||||
calib->dig_p6 =(int16_t)(bmp280_msblsb_to_u16(buf[11], buf[10]));
|
||||
calib->dig_p7 =(int16_t)(bmp280_msblsb_to_u16(buf[13], buf[12]));
|
||||
calib->dig_p8 =(int16_t)(bmp280_msblsb_to_u16(buf[15], buf[14]));
|
||||
calib->dig_p9 =(int16_t)(bmp280_msblsb_to_u16(buf[17], buf[16]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取温度
|
||||
* @param calib 系数的结构体
|
||||
* *temperature 温度值指针
|
||||
* *t_fine 精细分辨率温度值指针
|
||||
* @retval 无
|
||||
*/
|
||||
void bmp280_getTemperature(bmp280_calib *calib,double *temperature,int32_t *t_fine)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
uint32_t data_xlsb;
|
||||
uint32_t data_lsb;
|
||||
uint32_t data_msb;
|
||||
int32_t uncomp_temperature;
|
||||
double var1, var2;
|
||||
|
||||
bmp280_readReg(BMP280_TEMP,buf,3);
|
||||
data_msb = (int32_t)buf[0] << 12;
|
||||
data_lsb = (int32_t)buf[1] << 4;
|
||||
data_xlsb = (int32_t)buf[2] >> 4;
|
||||
uncomp_temperature = (int32_t)(data_msb | data_lsb | data_xlsb);
|
||||
|
||||
|
||||
var1 = (((double) uncomp_temperature) / 16384.0 - ((double) calib->dig_t1) / 1024.0) *
|
||||
((double) calib->dig_t2);
|
||||
var2 =
|
||||
((((double) uncomp_temperature) / 131072.0 - ((double) calib->dig_t1) / 8192.0) *
|
||||
(((double) uncomp_temperature) / 131072.0 - ((double) calib->dig_t1) / 8192.0)) *
|
||||
((double) calib->dig_t3);
|
||||
*t_fine = (int32_t) (var1 + var2);
|
||||
*temperature = (var1 + var2) / 5120.0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取气压
|
||||
* @param calib 系数的结构体
|
||||
* *pressure 气压值指针
|
||||
* *t_fine 精细分辨率温度值指针
|
||||
* @retval 无
|
||||
*/
|
||||
void bmp280_getPressure(bmp280_calib *calib,double *pressure,int32_t *t_fine)
|
||||
{
|
||||
uint8_t buf[3];
|
||||
uint32_t data_xlsb;
|
||||
uint32_t data_lsb;
|
||||
uint32_t data_msb;
|
||||
int32_t uncomp_pressure;
|
||||
double var1, var2;
|
||||
|
||||
bmp280_readReg(BMP280_PRES,buf,3);
|
||||
data_msb = (uint32_t)buf[0] << 12;
|
||||
data_lsb = (uint32_t)buf[1] << 4;
|
||||
data_xlsb = (uint32_t)buf[2] >> 4;
|
||||
uncomp_pressure = (data_msb | data_lsb | data_xlsb);
|
||||
|
||||
var1 = ((double) *t_fine / 2.0) - 64000.0;
|
||||
var2 = var1 * var1 * ((double) calib->dig_p6) / 32768.0;
|
||||
var2 = var2 + var1 * ((double) calib->dig_p5) * 2.0;
|
||||
var2 = (var2 / 4.0) + (((double) calib->dig_p4) * 65536.0);
|
||||
var1 = (((double)calib->dig_p3) * var1 * var1 / 524288.0 + ((double)calib->dig_p2) * var1) /
|
||||
524288.0;
|
||||
var1 = (1.0 + var1 / 32768.0) * ((double) calib->dig_p1);
|
||||
*pressure = 1048576.0 - (double)uncomp_pressure;
|
||||
*pressure = (*pressure - (var2 / 4096.0)) * 6250.0 / var1;
|
||||
var1 = ((double)calib->dig_p9) * *pressure * *pressure / 2147483648.0;
|
||||
var2 = *pressure * ((double)calib->dig_p8) / 32768.0;
|
||||
|
||||
*pressure = *pressure + (var1 + var2 + ((double)calib->dig_p7)) / 16.0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 初始化
|
||||
* @param *calib 系数的结构体指针
|
||||
* @retval 0 设置成功
|
||||
* 1 设置失败
|
||||
*/
|
||||
uint8_t bmp280_init(bmp280_calib *calib)
|
||||
{
|
||||
uint8_t rslt;
|
||||
rslt = bmp280_get_id();
|
||||
if(rslt == BMP2_CHIP_ID)
|
||||
{
|
||||
bmp280_getCalibration(calib);
|
||||
rslt = bmp280_setOversampling(5,2);
|
||||
if(rslt)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
rslt = bmp280_setConfig(0,4);
|
||||
if(rslt)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
rslt = bmp280_setMode(2);
|
||||
if(rslt)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取最终数据
|
||||
* @param *calib 系数的结构体指针
|
||||
* @retval 无
|
||||
*/
|
||||
void bmp280_getdata(bmp280_calib *calib,float *temperature,float *pressure)
|
||||
{
|
||||
double temp_T,temp_P;
|
||||
int32_t t_fine;
|
||||
bmp280_getTemperature(calib,&temp_T,&t_fine);
|
||||
bmp280_getPressure(calib,&temp_P,&t_fine);
|
||||
*temperature = (float)temp_T;
|
||||
*pressure = (float)temp_P;
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*底层接口定义*/
|
||||
#include "bsp/i2c.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#define BMP280_I2C_ADDR 0x76 // BMP280 默认 I2C 地址
|
||||
|
||||
/*寄存器地址*/
|
||||
#define BMP280_ID 0xD0 // 设备ID地址
|
||||
#define BMP280_RESET 0xE0 // 设备重启
|
||||
#define BMP280_STATUS 0xF3 // 设备状态
|
||||
#define BMP280_CTRL_MEAS 0xF4 // 数据采集和模式设置
|
||||
#define BMP280_CONFIG 0xF5 // 采样速率,滤波器和接口设置
|
||||
#define BMP280_DIGT 0x88 // 温度校准系数起始位置
|
||||
#define BMP280_DIGP 0x8E // 气压校准系数起始位置
|
||||
#define BMP280_TEMP 0xFA // 温度储存起始位置
|
||||
#define BMP280_PRES 0xF7 // 气压储存起始位置
|
||||
|
||||
#define BMP2_CHIP_ID 0x58 // 设备ID地址
|
||||
|
||||
#define bmp280_msblsb_to_u16(msb, lsb) (((uint16_t)msb << 8) | ((uint16_t)lsb))
|
||||
|
||||
typedef struct {
|
||||
unsigned short dig_t1;
|
||||
signed short dig_t2;
|
||||
signed short dig_t3;
|
||||
unsigned short dig_p1;
|
||||
signed short dig_p2;
|
||||
signed short dig_p3;
|
||||
signed short dig_p4;
|
||||
signed short dig_p5;
|
||||
signed short dig_p6;
|
||||
signed short dig_p7;
|
||||
signed short dig_p8;
|
||||
signed short dig_p9;
|
||||
} bmp280_calib;
|
||||
|
||||
uint8_t bmp280_get_id(void);
|
||||
uint8_t bmp280_reset(void);
|
||||
uint8_t bmp280_getStatus(void);
|
||||
uint8_t bmp280_setMode(uint8_t mode);
|
||||
uint8_t bmp280_setOversampling(uint8_t osrs_p, uint8_t osrs_t);
|
||||
uint8_t bmp280_setConfig(uint8_t Standbyt, uint8_t filter);
|
||||
void bmp280_getCalibration(bmp280_calib *calib);
|
||||
void bmp280_getTemperature(bmp280_calib *calib, double *temperature, int32_t *t_fine);
|
||||
void bmp280_getPressure(bmp280_calib *calib, double *pressure, int32_t *t_fine);
|
||||
uint8_t bmp280_init(bmp280_calib *calib);
|
||||
void bmp280_getdata(bmp280_calib *calib, float *temperature, float *pressure);
|
||||
@ -1,5 +0,0 @@
|
||||
oled_i2c,bsp/i2c
|
||||
bmp280_i2c,bsp/i2c
|
||||
pc_uart,bsp/uart
|
||||
key_gpio,bsp/gpio_exti
|
||||
servo,bsp/servo_pwm
|
||||
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEVICE_OK (0)
|
||||
#define DEVICE_ERR (-1)
|
||||
#define DEVICE_ERR_NULL (-2)
|
||||
#define DEVICE_ERR_INITED (-3)
|
||||
#define DEVICE_ERR_NO_DEV (-4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,65 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "key_gpio.h"
|
||||
#include "device.h"
|
||||
#include "bsp/gpio_exti.h"
|
||||
#include "gpio.h"
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define DEBOUNCE_TIME_MS 20
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* 外部声明标志位(标志位) */
|
||||
volatile uint8_t key_flag = 0; // 1=按下,0=松开
|
||||
volatile uint8_t key_exti = 0;
|
||||
volatile uint8_t key_pressed = 0; // 全局标志位
|
||||
static uint32_t last_debounce_time = 0; // 消抖
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static void KEY_Interrupt_Callback(void) {
|
||||
// 切换标志位状态
|
||||
|
||||
key_flag = !key_flag;
|
||||
key_exti = 1;
|
||||
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
void KEY_Process(void)
|
||||
{
|
||||
BSP_GPIO_RegisterCallback(BSP_GPIO_USER_KEY, BSP_GPIO_EXTI_CB, KEY_Interrupt_Callback);
|
||||
|
||||
if(key_exti == 1)
|
||||
{
|
||||
uint32_t now = HAL_GetTick();
|
||||
// 检查是否超过消抖时间
|
||||
if ((now - last_debounce_time) > DEBOUNCE_TIME_MS) {
|
||||
// 更新有效状态(假设按下为低电平)
|
||||
if(key_flag == 0)
|
||||
{
|
||||
key_pressed = DEVICE_KEY_RELEASED;
|
||||
}
|
||||
if(key_flag == 1)
|
||||
{
|
||||
key_pressed = DEVICE_KEY_PRESSED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6);
|
||||
|
||||
}
|
||||
last_debounce_time = now; // 重置消抖计时器
|
||||
key_exti = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t KEY_Get_State(void) {
|
||||
return key_pressed;
|
||||
}
|
||||
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
#ifndef KEY_GPIO_H
|
||||
#define KEY_GPIO_H
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include "main.h"
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
///* KEY按键状态,设置用 */
|
||||
typedef enum
|
||||
{
|
||||
DEVICE_KEY_RELEASED, //按键释放
|
||||
DEVICE_KEY_PRESSED, //按键按下
|
||||
} DEVICE_KEY_Status_t;
|
||||
|
||||
void KEY_Process(void);
|
||||
uint8_t KEY_Get_State(void);
|
||||
|
||||
#endif
|
||||
@ -1,267 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/oled_i2c.h"
|
||||
#include "bsp/i2c.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define OLED_I2C_ADDR 0x78 // OLED I2C 地址
|
||||
#define OLED_WIDTH 128
|
||||
#define OLED_HEIGHT 64
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static uint8_t oled_buffer[OLED_WIDTH * OLED_HEIGHT / 8];
|
||||
static struct {
|
||||
uint8_t x_min;
|
||||
uint8_t x_max;
|
||||
uint8_t y_min;
|
||||
uint8_t y_max;
|
||||
uint8_t dirty; // 标志是否有脏区域
|
||||
} dirty_rect = {0, 0, 0, 0, 0};
|
||||
|
||||
/* Private function prototypes ---------------------------------------------- */
|
||||
static void OLED_WriteCommand(uint8_t cmd) {
|
||||
uint8_t data[2] = {0x00, cmd};
|
||||
HAL_I2C_Master_Transmit(BSP_I2C_GetHandle(BSP_I2C_OLED), OLED_I2C_ADDR, data, 2, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
static void OLED_WriteData(uint8_t *data, uint16_t size) {
|
||||
uint8_t buffer[size + 1];
|
||||
buffer[0] = 0x40;
|
||||
memcpy(&buffer[1], data, size);
|
||||
HAL_I2C_Master_Transmit(BSP_I2C_GetHandle(BSP_I2C_OLED), OLED_I2C_ADDR, buffer, size + 1, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
static void OLED_MarkDirty(uint8_t x, uint8_t y);
|
||||
static void OLED_UpdateDirtyScreen(void);
|
||||
|
||||
static const uint8_t oled_font[95][8] = {
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}, /* " ", 0 */
|
||||
{0x00,0x00,0x00,0xcf,0xcf,0x00,0x00,0x00,}, /* "!", 1 */
|
||||
{0x00,0x0c,0x06,0x00,0x0c,0x06,0x00,0x00,}, /* """, 2 */
|
||||
{0x24,0xe4,0x3c,0x27,0xe4,0x3c,0x27,0x24,}, /* "#", 3 */
|
||||
{0x00,0x20,0x46,0xf9,0x9f,0x62,0x04,0x00,}, /* "$", 4 */
|
||||
{0x06,0x09,0xc6,0x30,0x0c,0x63,0x90,0x60,}, /* "%", 5 */
|
||||
{0x00,0x00,0x6e,0x91,0xa9,0x46,0xa0,0x00,}, /* "&", 6 */
|
||||
{0x00,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,}, /* "'", 7 */
|
||||
{0x00,0x00,0x3c,0x42,0x81,0x00,0x00,0x00,}, /* "(", 8 */
|
||||
{0x00,0x00,0x00,0x81,0x42,0x3c,0x00,0x00,}, /* ")", 9 */
|
||||
{0x00,0x10,0x54,0x38,0x38,0x54,0x10,0x00,}, /* "*", 10 */
|
||||
{0x00,0x10,0x10,0xfc,0x10,0x10,0x00,0x00,}, /* "+", 11 */
|
||||
{0x00,0x00,0x00,0xc0,0x60,0x00,0x00,0x00,}, /* ",", 12 */
|
||||
{0x00,0x00,0x10,0x10,0x10,0x10,0x00,0x00,}, /* "-", 13 */
|
||||
{0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,0x00,}, /* ".", 14 */
|
||||
{0x00,0x00,0x00,0xc0,0x38,0x07,0x00,0x00,}, /* "/", 15 */
|
||||
{0x00,0x00,0x7c,0x92,0x8a,0x7c,0x00,0x00,}, /* "0", 16 */
|
||||
{0x00,0x00,0x00,0x84,0xfe,0x80,0x00,0x00,}, /* "1", 17 */
|
||||
{0x00,0x00,0x8c,0xc2,0xa2,0x9c,0x00,0x00,}, /* "2", 18 */
|
||||
{0x00,0x00,0x44,0x92,0x92,0x6c,0x00,0x00,}, /* "3", 19 */
|
||||
{0x00,0x20,0x38,0x24,0xfe,0x20,0x00,0x00,}, /* "4", 20 */
|
||||
{0x00,0x00,0x5e,0x92,0x92,0x62,0x00,0x00,}, /* "5", 21 */
|
||||
{0x00,0x00,0x78,0x94,0x92,0x62,0x00,0x00,}, /* "6", 22 */
|
||||
{0x00,0x00,0x82,0x62,0x1a,0x06,0x00,0x00,}, /* "7", 23 */
|
||||
{0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,}, /* "8", 24 */
|
||||
{0x00,0x00,0x8c,0x52,0x32,0x1c,0x00,0x00,}, /* "9", 25 */
|
||||
{0x00,0x00,0x00,0x6c,0x6c,0x00,0x00,0x00,}, /* ":", 26 */
|
||||
{0x00,0x00,0x80,0xec,0x6c,0x00,0x00,0x00,}, /* ";", 27 */
|
||||
{0x00,0x00,0x10,0x28,0x44,0x00,0x00,0x00,}, /* "<", 28 */
|
||||
{0x00,0x00,0x24,0x24,0x24,0x24,0x00,0x00,}, /* "=", 29 */
|
||||
{0x00,0x00,0x00,0x44,0x28,0x10,0x00,0x00,}, /* ">", 30 */
|
||||
{0x00,0x00,0x0c,0xa2,0x92,0x1c,0x00,0x00,}, /* "?", 31 */
|
||||
{0x00,0x3c,0x42,0x99,0xa5,0xa2,0x3c,0x00,}, /* "@", 32 */
|
||||
{0x00,0xe0,0x1c,0x12,0x12,0x1c,0xe0,0x00,}, /* "A", 33 */
|
||||
{0x00,0xfe,0x92,0x92,0x9c,0x90,0x60,0x00,}, /* "B", 34 */
|
||||
{0x00,0x38,0x44,0x82,0x82,0x82,0x44,0x00,}, /* "C", 35 */
|
||||
{0x00,0xfe,0x82,0x82,0x82,0x82,0x7c,0x00,}, /* "D", 36 */
|
||||
{0x00,0xfe,0x92,0x92,0x92,0x92,0x92,0x00,}, /* "E", 37 */
|
||||
{0x00,0xfe,0x12,0x12,0x12,0x12,0x02,0x00,}, /* "F", 38 */
|
||||
{0x00,0x7c,0x82,0x92,0x92,0x72,0x00,0x00,}, /* "G", 39 */
|
||||
{0x00,0xfe,0x10,0x10,0x10,0x10,0xfe,0x00,}, /* "H", 40 */
|
||||
{0x00,0x82,0x82,0xfe,0x82,0x82,0x00,0x00,}, /* "I", 41 */
|
||||
{0x00,0x82,0x82,0x7e,0x02,0x02,0x00,0x00,}, /* "J", 42 */
|
||||
{0x00,0xfe,0x10,0x28,0x44,0x82,0x00,0x00,}, /* "K", 43 */
|
||||
{0x00,0xfe,0x80,0x80,0x80,0x80,0x00,0x00,}, /* "L", 44 */
|
||||
{0xfc,0x02,0x04,0xf8,0x04,0x02,0xfc,0x00,}, /* "M", 45 */
|
||||
{0x00,0xfe,0x04,0x18,0x30,0x40,0xfe,0x00,}, /* "N", 46 */
|
||||
{0x00,0x7c,0x82,0x82,0x82,0x82,0x7c,0x00,}, /* "O", 47 */
|
||||
{0x00,0x00,0xfe,0x12,0x12,0x0c,0x00,0x00,}, /* "P", 48 */
|
||||
{0x00,0x00,0x3c,0x42,0xc2,0xbc,0x00,0x00,}, /* "Q", 49 */
|
||||
{0x00,0x00,0xfe,0x32,0x52,0x8c,0x00,0x00,}, /* "R", 50 */
|
||||
{0x00,0x00,0x4c,0x92,0x92,0x64,0x00,0x00,}, /* "S", 51 */
|
||||
{0x00,0x02,0x02,0xfe,0x02,0x02,0x00,0x00,}, /* "T", 52 */
|
||||
{0x00,0x7e,0x80,0x80,0x80,0x80,0x7e,0x00,}, /* "U", 53 */
|
||||
{0x00,0x0c,0x30,0xc0,0x30,0x0c,0x00,0x00,}, /* "V", 54 */
|
||||
{0x7c,0x80,0x80,0x78,0x80,0x80,0x7c,0x00,}, /* "W", 55 */
|
||||
{0x00,0x84,0x48,0x30,0x30,0x48,0x84,0x00,}, /* "X", 56 */
|
||||
{0x00,0x06,0x08,0xf0,0x08,0x06,0x00,0x00,}, /* "Y", 57 */
|
||||
{0x00,0x00,0xc2,0xa2,0x92,0x8e,0x00,0x00,}, /* "Z", 58 */
|
||||
{0x00,0x00,0xfe,0x82,0x82,0x82,0x00,0x00,}, /* "[", 59 */
|
||||
{0x00,0x00,0x06,0x18,0x60,0x80,0x00,0x00,}, /* "\", 60 */
|
||||
{0x00,0x00,0x82,0x82,0x82,0xfe,0x00,0x00,}, /* "]", 61 */
|
||||
{0x00,0x30,0x0c,0x02,0x0c,0x30,0x00,0x00,}, /* "^", 62 */
|
||||
{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,}, /* "_", 63 */
|
||||
{0x00,0x00,0x04,0x0c,0x18,0x00,0x00,0x00,}, /* "`", 64 */
|
||||
{0x00,0x00,0x60,0x90,0x90,0xe0,0x00,0x00,}, /* "a", 65 */
|
||||
{0x00,0x00,0xf8,0xa0,0xe0,0x00,0x00,0x00,}, /* "b", 66 */
|
||||
{0x00,0x00,0x60,0x90,0x90,0x00,0x00,0x00,}, /* "c", 67 */
|
||||
{0x00,0x00,0xe0,0xa0,0xf8,0x00,0x00,0x00,}, /* "d", 68 */
|
||||
{0x00,0x00,0x70,0xa8,0xa8,0x90,0x00,0x00,}, /* "e", 69 */
|
||||
{0x00,0x00,0x10,0xf8,0x14,0x00,0x00,0x00,}, /* "f", 70 */
|
||||
{0x00,0x00,0xd8,0xa4,0x7c,0x00,0x00,0x00,}, /* "g", 71 */
|
||||
{0x00,0x00,0xf8,0x20,0xe0,0x00,0x00,0x00,}, /* "h", 72 */
|
||||
{0x00,0x00,0x00,0xe8,0x00,0x00,0x00,0x00,}, /* "i", 73 */
|
||||
{0x00,0x00,0x40,0x90,0x74,0x00,0x00,0x00,}, /* "j", 74 */
|
||||
{0x00,0x00,0xf8,0x60,0x90,0x00,0x00,0x00,}, /* "k", 75 */
|
||||
{0x00,0x00,0x78,0x80,0x80,0x00,0x00,0x00,}, /* "l", 76 */
|
||||
{0x00,0xe0,0x10,0xe0,0x10,0xe0,0x00,0x00,}, /* "m", 77 */
|
||||
{0x00,0x00,0xf0,0x10,0x10,0xe0,0x00,0x00,}, /* "n", 78 */
|
||||
{0x00,0x00,0x60,0x90,0x90,0x60,0x00,0x00,}, /* "o", 79 */
|
||||
{0x00,0x00,0xf0,0x48,0x48,0x30,0x00,0x00,}, /* "p", 80 */
|
||||
{0x00,0x00,0x30,0x48,0x48,0xf0,0x00,0x00,}, /* "q", 81 */
|
||||
{0x00,0x00,0x00,0xf0,0x20,0x10,0x00,0x00,}, /* "r", 82 */
|
||||
{0x00,0x00,0x90,0xa8,0xa8,0x48,0x00,0x00,}, /* "s", 83 */
|
||||
{0x00,0x10,0x10,0xf8,0x90,0x90,0x00,0x00,}, /* "t", 84 */
|
||||
{0x00,0x00,0x78,0x80,0x80,0xf8,0x00,0x00,}, /* "u", 85 */
|
||||
{0x00,0x18,0x60,0x80,0x60,0x18,0x00,0x00,}, /* "v", 86 */
|
||||
{0x00,0x38,0xc0,0x38,0xc0,0x38,0x00,0x00,}, /* "w", 87 */
|
||||
{0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,}, /* "x", 88 */
|
||||
{0x00,0x8c,0x50,0x20,0x10,0x0c,0x00,0x00,}, /* "y", 89 */
|
||||
{0x00,0x88,0xc8,0xa8,0x98,0x88,0x00,0x00,}, /* "z", 90 */
|
||||
{0x00,0x00,0x10,0x7c,0x82,0x00,0x00,0x00,}, /* "{", 91 */
|
||||
{0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,}, /* "|", 92 */
|
||||
{0x00,0x00,0x00,0x82,0x7c,0x10,0x00,0x00,}, /* "}", 93 */
|
||||
{0x00,0x08,0x04,0x04,0x08,0x10,0x10,0x08,}, /* "~", 94 */
|
||||
};
|
||||
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
void OLED_Init(void) {
|
||||
OLED_WriteCommand(0xAE); // 关闭显示
|
||||
OLED_WriteCommand(0x20); // 设置内存寻址模式
|
||||
OLED_WriteCommand(0x10); // 页寻址模式
|
||||
OLED_WriteCommand(0xB0); // 设置页起始地址
|
||||
OLED_WriteCommand(0xC8); // 设置COM扫描方向
|
||||
OLED_WriteCommand(0x00); // 设置低列地址
|
||||
OLED_WriteCommand(0x10); // 设置高列地址
|
||||
OLED_WriteCommand(0x40); // 设置显示起始行
|
||||
OLED_WriteCommand(0x81); // 设置对比度
|
||||
OLED_WriteCommand(0xFF); // 最大对比度
|
||||
OLED_WriteCommand(0xA1); // 设置段重映射
|
||||
OLED_WriteCommand(0xA6); // 正常显示
|
||||
OLED_WriteCommand(0xA8); // 设置多路复用比率
|
||||
OLED_WriteCommand(0x3F); // 1/64
|
||||
OLED_WriteCommand(0xA4); // 输出跟随 RAM 内容
|
||||
OLED_WriteCommand(0xD3); // 设置显示偏移
|
||||
OLED_WriteCommand(0x00); // 无偏移
|
||||
OLED_WriteCommand(0xD5); // 设置显示时钟分频比/振荡频率
|
||||
OLED_WriteCommand(0xF0); // 高频
|
||||
OLED_WriteCommand(0xD9); // 设置预充电周期
|
||||
OLED_WriteCommand(0x22); // 修复缺少分号
|
||||
OLED_WriteCommand(0xDA); // 设置COM引脚硬件配置
|
||||
OLED_WriteCommand(0x12); // 修复缺少分号
|
||||
OLED_WriteCommand(0xDB); // 设置VCOMH电压
|
||||
OLED_WriteCommand(0x20); // 修复缺少分号
|
||||
OLED_WriteCommand(0x8D); // 设置充电泵
|
||||
OLED_WriteCommand(0x14); // 修复缺少分号
|
||||
OLED_WriteCommand(0xAF); // 打开显示
|
||||
}
|
||||
|
||||
void OLED_Clear(void) {
|
||||
memset(oled_buffer, 0, sizeof(oled_buffer));
|
||||
dirty_rect.x_min = 0;
|
||||
dirty_rect.x_max = OLED_WIDTH - 1;
|
||||
dirty_rect.y_min = 0;
|
||||
dirty_rect.y_max = OLED_HEIGHT - 1;
|
||||
dirty_rect.dirty = 1;
|
||||
OLED_UpdateScreen();
|
||||
}
|
||||
|
||||
void OLED_UpdateScreen(void) {
|
||||
OLED_UpdateDirtyScreen();
|
||||
}
|
||||
|
||||
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) {
|
||||
if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;
|
||||
|
||||
if (color) {
|
||||
if (!(oled_buffer[x + (y / 8) * OLED_WIDTH] & (1 << (y % 8)))) {
|
||||
oled_buffer[x + (y / 8) * OLED_WIDTH] |= (1 << (y % 8));
|
||||
OLED_MarkDirty(x, y);
|
||||
}
|
||||
} else {
|
||||
if (oled_buffer[x + (y / 8) * OLED_WIDTH] & (1 << (y % 8))) {
|
||||
oled_buffer[x + (y / 8) * OLED_WIDTH] &= ~(1 << (y % 8));
|
||||
OLED_MarkDirty(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OLED_DrawChar(uint8_t x, uint8_t y, char ch, uint8_t color) {
|
||||
if (ch < ' ' || ch > '~') return;
|
||||
|
||||
if (x >= OLED_WIDTH || y >= OLED_HEIGHT || x + 8 > OLED_WIDTH || y + 8 > OLED_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t *font_data = oled_font[ch - ' '];
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
uint8_t column_data = font_data[i];
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (column_data & (1 << j)) {
|
||||
OLED_DrawPixel(x + i, y + j, color);
|
||||
} else {
|
||||
OLED_DrawPixel(x + i, y + j, !color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OLED_DrawString(uint8_t x, uint8_t y, const char *str, uint8_t color) {
|
||||
while (*str) {
|
||||
OLED_DrawChar(x, y, *str, color);
|
||||
x += 8;
|
||||
if (x + 8 > OLED_WIDTH) {
|
||||
x = 0;
|
||||
y += 8;
|
||||
}
|
||||
if (y + 8 > OLED_HEIGHT) {
|
||||
break;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Private functions -------------------------------------------------------- */
|
||||
|
||||
static void OLED_MarkDirty(uint8_t x, uint8_t y) {
|
||||
if (!dirty_rect.dirty) {
|
||||
dirty_rect.x_min = x;
|
||||
dirty_rect.x_max = x;
|
||||
dirty_rect.y_min = y;
|
||||
dirty_rect.y_max = y;
|
||||
dirty_rect.dirty = 1;
|
||||
} else {
|
||||
if (x < dirty_rect.x_min) dirty_rect.x_min = x;
|
||||
if (x > dirty_rect.x_max) dirty_rect.x_max = x;
|
||||
if (y < dirty_rect.y_min) dirty_rect.y_min = y;
|
||||
if (y > dirty_rect.y_max) dirty_rect.y_max = y;
|
||||
}
|
||||
}
|
||||
|
||||
static void OLED_UpdateDirtyScreen(void) {
|
||||
if (!dirty_rect.dirty) return;
|
||||
|
||||
uint8_t y_start = dirty_rect.y_min / 8;
|
||||
uint8_t y_end = dirty_rect.y_max / 8;
|
||||
|
||||
for (uint8_t i = y_start; i <= y_end; i++) {
|
||||
OLED_WriteCommand(0xB0 + i);
|
||||
OLED_WriteCommand(dirty_rect.x_min & 0x0F);
|
||||
OLED_WriteCommand(0x10 | (dirty_rect.x_min >> 4));
|
||||
uint8_t width = dirty_rect.x_max - dirty_rect.x_min + 1;
|
||||
OLED_WriteData(&oled_buffer[dirty_rect.x_min + i * OLED_WIDTH], width);
|
||||
}
|
||||
|
||||
dirty_rect.dirty = 0;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
#define OLED_COLOR_BLACK 0
|
||||
#define OLED_COLOR_WHITE 1
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
void OLED_Init(void);
|
||||
void OLED_Clear(void);
|
||||
void OLED_UpdateScreen(void);
|
||||
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color);
|
||||
void OLED_DrawString(uint8_t x, uint8_t y, const char *str, uint8_t color);
|
||||
void OLED_DrawChar(uint8_t x, uint8_t y, char ch, uint8_t color);
|
||||
void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t index);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,57 +0,0 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "pc_uart.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp\uart.h"
|
||||
#include "device.h"
|
||||
|
||||
#define UART_HANDLE BSP_UART_GetHandle(BSP_UART_PC)
|
||||
|
||||
#define AI_LEN_RX_BUFF (sizeof(UART_RxData_t))
|
||||
|
||||
static bool rx_flag = false;
|
||||
|
||||
static uint8_t rxbuf[AI_LEN_RX_BUFF];
|
||||
|
||||
static void UART_RxCpltCallback(void) { rx_flag = true; }
|
||||
|
||||
int UART_Init(UART_t *huart)
|
||||
{
|
||||
UNUSED(huart);
|
||||
//注册回调函数
|
||||
HAL_UART_RegisterCallback(UART_HANDLE, BSP_UART_RX_CPLT_CB, UART_RxCpltCallback);
|
||||
return DEVICE_OK
|
||||
}
|
||||
|
||||
int UART_StartReceive(UART_t *huart)
|
||||
{
|
||||
UNUSED(huart);
|
||||
HAL_UART_Receive_DMA(UART_HANDLE, rxbuf, AI_LEN_RX_BUFF);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
bool UART_IsReceiveComplete(void)
|
||||
{
|
||||
return rx_flag;
|
||||
}
|
||||
|
||||
int8_t UART_ParseData(UART_t *huart)
|
||||
{
|
||||
|
||||
memcpy(&huart->rx_data, rxbuf, sizeof(UART_RxData_t));
|
||||
}
|
||||
|
||||
void UART_PackTx(UART_t *huart, UART_TxData_t *tx_data)
|
||||
{
|
||||
memcpy(tx_data, huart->tx_data, sizeof(UART_TxData_t));
|
||||
}
|
||||
|
||||
int8_t UART_StartSend(UART_t *huart)
|
||||
{
|
||||
if (HAL_UART_Transmit_DMA(UART_HANDLE, huart->tx_data, sizeof(UART_TxData_t)) == HAL_OK)
|
||||
{
|
||||
return DEVICE_OK
|
||||
}
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/*
|
||||
UART通讯模板
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t head;
|
||||
uint8_t data;
|
||||
uint8_t crc;
|
||||
} UART_RxData_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t head;
|
||||
uint8_t data;
|
||||
uint8_t crc;
|
||||
} UART_TxData_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UART_RxData_t rx_data; // Received data buffer
|
||||
UART_TxData_t tx_data; // Transmit data buffer
|
||||
} UART_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
int UART_Init(UART_t *huart);
|
||||
int UART_StartReceive(UART_t *huart);
|
||||
bool UART_IsReceiveComplete(void);
|
||||
int8_t UART_ParseData(UART_t *huart);
|
||||
void UART_PackTx(UART_t *huart, UART_TxData_t *tx_data);
|
||||
int8_t UART_StartSend(UART_t *huart);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include "tim.h"
|
||||
#include "bsp/bsp.h"
|
||||
#include "bsp/servo_pwm.h"
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
extern int serve_Init(BSP_PWM_Channel_t ch);
|
||||
extern int set_servo_angle(BSP_PWM_Channel_t ch,float angle);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
// 定义任务运行时结构体
|
||||
typedef struct {
|
||||
/* 各任务,也可以叫做线程 */
|
||||
struct {
|
||||
{{thread_definitions}}
|
||||
} thread;
|
||||
|
||||
/* USER MESSAGE BEGIN */
|
||||
|
||||
struct {
|
||||
osMessageQueueId_t user_msg; /* 用户自定义任务消息队列 */
|
||||
} msgq;
|
||||
|
||||
/* USER MESSAGE END */
|
||||
|
||||
struct {
|
||||
{{freq_definitions}}
|
||||
} freq; /* 任务运行频率 */
|
||||
|
||||
struct {
|
||||
{{last_up_time_definitions}}
|
||||
} last_up_time; /* 任务最近运行时间 */
|
||||
} Task_Runtime_t;
|
||||
|
||||
// 任务频率
|
||||
{{task_frequency_definitions}}
|
||||
// 任务初始化延时
|
||||
#define TASK_INIT_DELAY (100u)
|
||||
{{task_init_delay_definitions}}
|
||||
|
||||
// 任务句柄
|
||||
typedef struct {
|
||||
{{task_handle_definitions}}
|
||||
} Task_Handles_t;
|
||||
|
||||
// 任务运行时结构体
|
||||
extern Task_Runtime_t task_runtime;
|
||||
|
||||
// 初始化任务句柄
|
||||
extern const osThreadAttr_t attr_init;
|
||||
{{task_attr_declarations}}
|
||||
|
||||
// 任务函数声明
|
||||
void Task_Init(void *argument);
|
||||
{{task_function_declarations}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
BIN
img/.DS_Store → bsp/.DS_Store
vendored
BIN
img/.DS_Store → bsp/.DS_Store
vendored
Binary file not shown.
@ -4,11 +4,24 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define BSP_OK (0)
|
||||
#define BSP_ERR (-1)
|
||||
#define BSP_ERR_NULL (-2)
|
||||
#define BSP_ERR_INITED (-3)
|
||||
#define BSP_ERR_NO_DEV (-4)
|
||||
#define BSP_ERR_TIMEOUT (-5)
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
659
bsp/can.c
Normal file
659
bsp/can.c
Normal file
@ -0,0 +1,659 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/bsp.h"
|
||||
#include <can.h>
|
||||
#include <cmsis_os2.h>
|
||||
#include <string.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define CAN_QUEUE_MUTEX_TIMEOUT 100 /* 队列互斥锁超时时间(ms) */
|
||||
#define CAN_TX_MAILBOX_NUM 3 /* CAN发送邮箱数量 */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
typedef struct BSP_CAN_QueueNode {
|
||||
BSP_CAN_t can; /* CAN通道 */
|
||||
uint32_t can_id; /* 解析后的CAN ID */
|
||||
osMessageQueueId_t queue; /* 消息队列ID */
|
||||
uint8_t queue_size; /* 队列大小 */
|
||||
struct BSP_CAN_QueueNode *next; /* 指向下一个节点的指针 */
|
||||
} BSP_CAN_QueueNode_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static BSP_CAN_QueueNode_t *queue_list = NULL;
|
||||
static osMutexId_t queue_mutex = NULL;
|
||||
static void (*CAN_Callback[BSP_CAN_NUM][BSP_CAN_CB_NUM])(void);
|
||||
static bool inited = false;
|
||||
static BSP_CAN_IdParser_t id_parser = NULL; /* ID解析器 */
|
||||
static BSP_CAN_TxQueue_t tx_queues[BSP_CAN_NUM]; /* 每个CAN的发送队列 */
|
||||
|
||||
/* Private function prototypes ---------------------------------------------- */
|
||||
static BSP_CAN_t CAN_Get(CAN_HandleTypeDef *hcan);
|
||||
static osMessageQueueId_t BSP_CAN_FindQueue(BSP_CAN_t can, uint32_t can_id);
|
||||
static int8_t BSP_CAN_CreateIdQueue(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size);
|
||||
static void BSP_CAN_RxFifo0Callback(void);
|
||||
static void BSP_CAN_RxFifo1Callback(void);
|
||||
static void BSP_CAN_TxCompleteCallback(void);
|
||||
static BSP_CAN_FrameType_t BSP_CAN_GetFrameType(CAN_RxHeaderTypeDef *header);
|
||||
static uint32_t BSP_CAN_DefaultIdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
static void BSP_CAN_TxQueueInit(BSP_CAN_t can);
|
||||
static bool BSP_CAN_TxQueuePush(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg);
|
||||
static bool BSP_CAN_TxQueuePop(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg);
|
||||
static bool BSP_CAN_TxQueueIsEmpty(BSP_CAN_t can);
|
||||
|
||||
/* Private functions -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
/**
|
||||
* @brief 根据CAN句柄获取BSP_CAN实例
|
||||
*/
|
||||
static BSP_CAN_t CAN_Get(CAN_HandleTypeDef *hcan) {
|
||||
if (hcan == NULL) return BSP_CAN_ERR;
|
||||
|
||||
/* AUTO GENERATED CAN_GET */
|
||||
else
|
||||
return BSP_CAN_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找指定CAN ID的消息队列
|
||||
* @note 调用前需要获取互斥锁
|
||||
*/
|
||||
static osMessageQueueId_t BSP_CAN_FindQueue(BSP_CAN_t can, uint32_t can_id) {
|
||||
BSP_CAN_QueueNode_t *node = queue_list;
|
||||
while (node != NULL) {
|
||||
if (node->can == can && node->can_id == can_id) {
|
||||
return node->queue;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建指定CAN ID的消息队列
|
||||
* @note 内部函数,已包含互斥锁保护
|
||||
*/
|
||||
static int8_t BSP_CAN_CreateIdQueue(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size) {
|
||||
if (queue_size == 0) {
|
||||
queue_size = BSP_CAN_DEFAULT_QUEUE_SIZE;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
BSP_CAN_QueueNode_t *node = queue_list;
|
||||
while (node != NULL) {
|
||||
if (node->can == can && node->can_id == can_id) {
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR; // 已存在
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
BSP_CAN_QueueNode_t *new_node = (BSP_CAN_QueueNode_t *)BSP_Malloc(sizeof(BSP_CAN_QueueNode_t));
|
||||
if (new_node == NULL) {
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
new_node->queue = osMessageQueueNew(queue_size, sizeof(BSP_CAN_Message_t), NULL);
|
||||
if (new_node->queue == NULL) {
|
||||
BSP_Free(new_node);
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_ERR;
|
||||
}
|
||||
new_node->can = can;
|
||||
new_node->can_id = can_id;
|
||||
new_node->queue_size = queue_size;
|
||||
new_node->next = queue_list;
|
||||
queue_list = new_node;
|
||||
osMutexRelease(queue_mutex);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取帧类型
|
||||
*/
|
||||
static BSP_CAN_FrameType_t BSP_CAN_GetFrameType(CAN_RxHeaderTypeDef *header) {
|
||||
if (header->RTR == CAN_RTR_REMOTE) {
|
||||
return (header->IDE == CAN_ID_EXT) ? BSP_CAN_FRAME_EXT_REMOTE : BSP_CAN_FRAME_STD_REMOTE;
|
||||
} else {
|
||||
return (header->IDE == CAN_ID_EXT) ? BSP_CAN_FRAME_EXT_DATA : BSP_CAN_FRAME_STD_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 默认ID解析器(直接返回原始ID)
|
||||
*/
|
||||
static uint32_t BSP_CAN_DefaultIdParser(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
|
||||
(void)frame_type; // 避免未使用参数警告
|
||||
return original_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化发送队列
|
||||
*/
|
||||
static void BSP_CAN_TxQueueInit(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return;
|
||||
|
||||
tx_queues[can].head = 0;
|
||||
tx_queues[can].tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向发送队列添加消息(无锁)
|
||||
*/
|
||||
static bool BSP_CAN_TxQueuePush(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg) {
|
||||
if (can >= BSP_CAN_NUM || msg == NULL) return false;
|
||||
|
||||
BSP_CAN_TxQueue_t *queue = &tx_queues[can];
|
||||
uint32_t next_head = (queue->head + 1) % BSP_CAN_TX_QUEUE_SIZE;
|
||||
|
||||
// 队列满
|
||||
if (next_head == queue->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 复制消息
|
||||
queue->buffer[queue->head] = *msg;
|
||||
|
||||
// 更新头指针(原子操作)
|
||||
queue->head = next_head;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 从发送队列取出消息(无锁)
|
||||
*/
|
||||
static bool BSP_CAN_TxQueuePop(BSP_CAN_t can, BSP_CAN_TxMessage_t *msg) {
|
||||
if (can >= BSP_CAN_NUM || msg == NULL) return false;
|
||||
|
||||
BSP_CAN_TxQueue_t *queue = &tx_queues[can];
|
||||
|
||||
// 队列空
|
||||
if (queue->head == queue->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 复制消息
|
||||
*msg = queue->buffer[queue->tail];
|
||||
|
||||
// 更新尾指针(原子操作)
|
||||
queue->tail = (queue->tail + 1) % BSP_CAN_TX_QUEUE_SIZE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查发送队列是否为空
|
||||
*/
|
||||
static bool BSP_CAN_TxQueueIsEmpty(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) return true;
|
||||
|
||||
return tx_queues[can].head == tx_queues[can].tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理所有CAN实例的发送队列
|
||||
*/
|
||||
static void BSP_CAN_TxCompleteCallback(void) {
|
||||
// 处理所有CAN实例的发送队列
|
||||
for (int i = 0; i < BSP_CAN_NUM; i++) {
|
||||
BSP_CAN_t can = (BSP_CAN_t)i;
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle(can);
|
||||
if (hcan == NULL) continue;
|
||||
|
||||
BSP_CAN_TxMessage_t msg;
|
||||
uint32_t mailbox;
|
||||
|
||||
// 尝试发送队列中的消息
|
||||
while (!BSP_CAN_TxQueueIsEmpty(can)) {
|
||||
// 检查是否有空闲邮箱
|
||||
if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) == 0) {
|
||||
break; // 没有空闲邮箱,等待下次中断
|
||||
}
|
||||
|
||||
// 从队列中取出消息
|
||||
if (!BSP_CAN_TxQueuePop(can, &msg)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
if (HAL_CAN_AddTxMessage(hcan, &msg.header, msg.data, &mailbox) != HAL_OK) {
|
||||
// 发送失败,消息已经从队列中移除,直接丢弃
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief FIFO0接收处理函数
|
||||
*/
|
||||
static void BSP_CAN_RxFifo0Callback(void) {
|
||||
CAN_RxHeaderTypeDef rx_header;
|
||||
uint8_t rx_data[BSP_CAN_MAX_DLC];
|
||||
for (int can_idx = 0; can_idx < BSP_CAN_NUM; can_idx++) {
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle((BSP_CAN_t)can_idx);
|
||||
if (hcan == NULL) continue;
|
||||
while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0) > 0) {
|
||||
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) {
|
||||
uint32_t original_id = (rx_header.IDE == CAN_ID_STD) ? rx_header.StdId : rx_header.ExtId;
|
||||
BSP_CAN_FrameType_t frame_type = BSP_CAN_GetFrameType(&rx_header);
|
||||
uint32_t parsed_id = BSP_CAN_ParseId(original_id, frame_type);
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue((BSP_CAN_t)can_idx, parsed_id);
|
||||
if (queue != NULL) {
|
||||
BSP_CAN_Message_t msg = {0};
|
||||
msg.frame_type = frame_type;
|
||||
msg.original_id = original_id;
|
||||
msg.parsed_id = parsed_id;
|
||||
msg.dlc = rx_header.DLC;
|
||||
if (rx_header.RTR == CAN_RTR_DATA) {
|
||||
memcpy(msg.data, rx_data, rx_header.DLC);
|
||||
}
|
||||
msg.timestamp = HAL_GetTick();
|
||||
osMessageQueuePut(queue, &msg, 0, BSP_CAN_TIMEOUT_IMMEDIATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FIFO1接收处理函数
|
||||
*/
|
||||
static void BSP_CAN_RxFifo1Callback(void) {
|
||||
CAN_RxHeaderTypeDef rx_header;
|
||||
uint8_t rx_data[BSP_CAN_MAX_DLC];
|
||||
for (int can_idx = 0; can_idx < BSP_CAN_NUM; can_idx++) {
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle((BSP_CAN_t)can_idx);
|
||||
if (hcan == NULL) continue;
|
||||
while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO1) > 0) {
|
||||
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &rx_header, rx_data) == HAL_OK) {
|
||||
uint32_t original_id = (rx_header.IDE == CAN_ID_STD) ? rx_header.StdId : rx_header.ExtId;
|
||||
BSP_CAN_FrameType_t frame_type = BSP_CAN_GetFrameType(&rx_header);
|
||||
uint32_t parsed_id = BSP_CAN_ParseId(original_id, frame_type);
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue((BSP_CAN_t)can_idx, parsed_id);
|
||||
if (queue != NULL) {
|
||||
BSP_CAN_Message_t msg = {0};
|
||||
msg.frame_type = frame_type;
|
||||
msg.original_id = original_id;
|
||||
msg.parsed_id = parsed_id;
|
||||
msg.dlc = rx_header.DLC;
|
||||
if (rx_header.RTR == CAN_RTR_DATA) {
|
||||
memcpy(msg.data, rx_data, rx_header.DLC);
|
||||
}
|
||||
msg.timestamp = HAL_GetTick();
|
||||
osMessageQueuePut(queue, &msg, 0, BSP_CAN_TIMEOUT_IMMEDIATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* HAL Callback Functions --------------------------------------------------- */
|
||||
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_CPLT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox0AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX0_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox1AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX1_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_TxMailbox2AbortCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
// 调用用户回调
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_TX_MAILBOX2_ABORT_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO0_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_MSG_PENDING_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_RxFifo1FullCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_RX_FIFO1_FULL_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_SleepCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_SLEEP_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_WakeUpFromRxMsgCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_WAKEUP_FROM_RX_MSG_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan) {
|
||||
BSP_CAN_t bsp_can = CAN_Get(hcan);
|
||||
if (bsp_can != BSP_CAN_ERR) {
|
||||
if (CAN_Callback[bsp_can][HAL_CAN_ERROR_CB])
|
||||
CAN_Callback[bsp_can][HAL_CAN_ERROR_CB]();
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
int8_t BSP_CAN_Init(void) {
|
||||
if (inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
|
||||
// 清零回调函数数组
|
||||
memset(CAN_Callback, 0, sizeof(CAN_Callback));
|
||||
|
||||
// 初始化发送队列
|
||||
for (int i = 0; i < BSP_CAN_NUM; i++) {
|
||||
BSP_CAN_TxQueueInit((BSP_CAN_t)i);
|
||||
}
|
||||
|
||||
// 初始化ID解析器为默认解析器
|
||||
id_parser = BSP_CAN_DefaultIdParser;
|
||||
|
||||
// 创建互斥锁
|
||||
queue_mutex = osMutexNew(NULL);
|
||||
if (queue_mutex == NULL) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
/* AUTO GENERATED CAN_INIT */
|
||||
|
||||
inited = true;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can) {
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (can) {
|
||||
/* AUTO GENERATED BSP_CAN_GET_HANDLE */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void)) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (callback == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
if (type >= BSP_CAN_CB_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
CAN_Callback[can][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_Transmit(BSP_CAN_t can, BSP_CAN_Format_t format,
|
||||
uint32_t id, uint8_t *data, uint8_t dlc) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (can >= BSP_CAN_NUM) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
if (data == NULL && format != BSP_CAN_FORMAT_STD_REMOTE && format != BSP_CAN_FORMAT_EXT_REMOTE) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (dlc > BSP_CAN_MAX_DLC) {
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
CAN_HandleTypeDef *hcan = BSP_CAN_GetHandle(can);
|
||||
if (hcan == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
|
||||
// 准备发送消息
|
||||
BSP_CAN_TxMessage_t tx_msg = {0};
|
||||
|
||||
switch (format) {
|
||||
case BSP_CAN_FORMAT_STD_DATA:
|
||||
tx_msg.header.StdId = id;
|
||||
tx_msg.header.IDE = CAN_ID_STD;
|
||||
tx_msg.header.RTR = CAN_RTR_DATA;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_EXT_DATA:
|
||||
tx_msg.header.ExtId = id;
|
||||
tx_msg.header.IDE = CAN_ID_EXT;
|
||||
tx_msg.header.RTR = CAN_RTR_DATA;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_STD_REMOTE:
|
||||
tx_msg.header.StdId = id;
|
||||
tx_msg.header.IDE = CAN_ID_STD;
|
||||
tx_msg.header.RTR = CAN_RTR_REMOTE;
|
||||
break;
|
||||
case BSP_CAN_FORMAT_EXT_REMOTE:
|
||||
tx_msg.header.ExtId = id;
|
||||
tx_msg.header.IDE = CAN_ID_EXT;
|
||||
tx_msg.header.RTR = CAN_RTR_REMOTE;
|
||||
break;
|
||||
default:
|
||||
return BSP_ERR;
|
||||
}
|
||||
|
||||
tx_msg.header.DLC = dlc;
|
||||
tx_msg.header.TransmitGlobalTime = DISABLE;
|
||||
|
||||
// 复制数据
|
||||
if (data != NULL && dlc > 0) {
|
||||
memcpy(tx_msg.data, data, dlc);
|
||||
}
|
||||
|
||||
// 尝试直接发送到邮箱
|
||||
uint32_t mailbox;
|
||||
if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {
|
||||
HAL_StatusTypeDef result = HAL_CAN_AddTxMessage(hcan, &tx_msg.header, tx_msg.data, &mailbox);
|
||||
if (result == HAL_OK) {
|
||||
return BSP_OK; // 发送成功
|
||||
}
|
||||
}
|
||||
|
||||
// 邮箱满,尝试放入队列
|
||||
if (BSP_CAN_TxQueuePush(can, &tx_msg)) {
|
||||
return BSP_OK; // 成功放入队列
|
||||
}
|
||||
|
||||
// 队列也满,丢弃数据
|
||||
return BSP_ERR; // 数据丢弃
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitStdDataFrame(BSP_CAN_t can, BSP_CAN_StdDataFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
return BSP_CAN_Transmit(can, BSP_CAN_FORMAT_STD_DATA, frame->id, frame->data, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitExtDataFrame(BSP_CAN_t can, BSP_CAN_ExtDataFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
return BSP_CAN_Transmit(can, BSP_CAN_FORMAT_EXT_DATA, frame->id, frame->data, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_TransmitRemoteFrame(BSP_CAN_t can, BSP_CAN_RemoteFrame_t *frame) {
|
||||
if (frame == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
BSP_CAN_Format_t format = frame->is_extended ? BSP_CAN_FORMAT_EXT_REMOTE : BSP_CAN_FORMAT_STD_REMOTE;
|
||||
return BSP_CAN_Transmit(can, format, frame->id, NULL, frame->dlc);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterId(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
return BSP_CAN_CreateIdQueue(can, can_id, queue_size);
|
||||
}
|
||||
|
||||
|
||||
int8_t BSP_CAN_GetMessage(BSP_CAN_t can, uint32_t can_id, BSP_CAN_Message_t *msg, uint32_t timeout) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (msg == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return BSP_ERR_NO_DEV;
|
||||
}
|
||||
osStatus_t result = osMessageQueueGet(queue, msg, NULL, timeout);
|
||||
return (result == osOK) ? BSP_OK : BSP_ERR;
|
||||
}
|
||||
|
||||
int32_t BSP_CAN_GetQueueCount(BSP_CAN_t can, uint32_t can_id) {
|
||||
if (!inited) {
|
||||
return -1;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return -1;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return (int32_t)osMessageQueueGetCount(queue);
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_FlushQueue(BSP_CAN_t can, uint32_t can_id) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (osMutexAcquire(queue_mutex, CAN_QUEUE_MUTEX_TIMEOUT) != osOK) {
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
osMessageQueueId_t queue = BSP_CAN_FindQueue(can, can_id);
|
||||
osMutexRelease(queue_mutex);
|
||||
if (queue == NULL) {
|
||||
return BSP_ERR_NO_DEV;
|
||||
}
|
||||
BSP_CAN_Message_t temp_msg;
|
||||
while (osMessageQueueGet(queue, &temp_msg, NULL, BSP_CAN_TIMEOUT_IMMEDIATE) == osOK) {
|
||||
// 清空
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_CAN_RegisterIdParser(BSP_CAN_IdParser_t parser) {
|
||||
if (!inited) {
|
||||
return BSP_ERR_INITED;
|
||||
}
|
||||
if (parser == NULL) {
|
||||
return BSP_ERR_NULL;
|
||||
}
|
||||
|
||||
id_parser = parser;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
uint32_t BSP_CAN_ParseId(uint32_t original_id, BSP_CAN_FrameType_t frame_type) {
|
||||
if (id_parser != NULL) {
|
||||
return id_parser(original_id, frame_type);
|
||||
}
|
||||
return BSP_CAN_DefaultIdParser(original_id, frame_type);
|
||||
}
|
||||
|
||||
|
||||
259
bsp/can.h
Normal file
259
bsp/can.h
Normal file
@ -0,0 +1,259 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <can.h>
|
||||
#include "bsp/bsp.h"
|
||||
#include "bsp/mm.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <cmsis_os.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
#define BSP_CAN_MAX_DLC 8
|
||||
#define BSP_CAN_DEFAULT_QUEUE_SIZE 10
|
||||
#define BSP_CAN_TIMEOUT_IMMEDIATE 0
|
||||
#define BSP_CAN_TIMEOUT_FOREVER osWaitForever
|
||||
#define BSP_CAN_TX_QUEUE_SIZE 32 /* 发送队列大小 */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
BSP_CAN_1,
|
||||
BSP_CAN_2,
|
||||
BSP_CAN_NUM,
|
||||
BSP_CAN_ERR,
|
||||
} BSP_CAN_t;
|
||||
|
||||
typedef enum {
|
||||
HAL_CAN_TX_MAILBOX0_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_CPLT_CB,
|
||||
HAL_CAN_TX_MAILBOX0_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX1_ABORT_CB,
|
||||
HAL_CAN_TX_MAILBOX2_ABORT_CB,
|
||||
HAL_CAN_RX_FIFO0_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO0_FULL_CB,
|
||||
HAL_CAN_RX_FIFO1_MSG_PENDING_CB,
|
||||
HAL_CAN_RX_FIFO1_FULL_CB,
|
||||
HAL_CAN_SLEEP_CB,
|
||||
HAL_CAN_WAKEUP_FROM_RX_MSG_CB,
|
||||
HAL_CAN_ERROR_CB,
|
||||
BSP_CAN_CB_NUM,
|
||||
} BSP_CAN_Callback_t;
|
||||
|
||||
/* CAN消息格式枚举 - 用于发送和接收消息时指定格式 */
|
||||
typedef enum {
|
||||
BSP_CAN_FORMAT_STD_DATA, /* 标准数据帧 */
|
||||
BSP_CAN_FORMAT_EXT_DATA, /* 扩展数据帧 */
|
||||
BSP_CAN_FORMAT_STD_REMOTE, /* 标准远程帧 */
|
||||
BSP_CAN_FORMAT_EXT_REMOTE, /* 扩展远程帧 */
|
||||
} BSP_CAN_Format_t;
|
||||
|
||||
/* CAN帧类型枚举 - 用于区分不同类型的CAN帧 */
|
||||
typedef enum {
|
||||
BSP_CAN_FRAME_STD_DATA, /* 标准数据帧 */
|
||||
BSP_CAN_FRAME_EXT_DATA, /* 扩展数据帧 */
|
||||
BSP_CAN_FRAME_STD_REMOTE, /* 标准远程帧 */
|
||||
BSP_CAN_FRAME_EXT_REMOTE, /* 扩展远程帧 */
|
||||
} BSP_CAN_FrameType_t;
|
||||
|
||||
/* CAN消息结构体 - 支持不同类型帧 */
|
||||
typedef struct {
|
||||
BSP_CAN_FrameType_t frame_type; /* 帧类型 */
|
||||
uint32_t original_id; /* 原始ID(未解析) */
|
||||
uint32_t parsed_id; /* 解析后的实际ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
uint32_t timestamp; /* 时间戳(可选) */
|
||||
} BSP_CAN_Message_t;
|
||||
|
||||
/* 标准数据帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* CAN ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_StdDataFrame_t;
|
||||
|
||||
/* 扩展数据帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* 扩展CAN ID */
|
||||
uint8_t dlc; /* 数据长度 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_ExtDataFrame_t;
|
||||
|
||||
/* 远程帧结构 */
|
||||
typedef struct {
|
||||
uint32_t id; /* CAN ID */
|
||||
uint8_t dlc; /* 请求的数据长度 */
|
||||
bool is_extended; /* 是否为扩展帧 */
|
||||
} BSP_CAN_RemoteFrame_t;
|
||||
|
||||
/* ID解析回调函数类型 */
|
||||
typedef uint32_t (*BSP_CAN_IdParser_t)(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
|
||||
/* CAN发送消息结构体 */
|
||||
typedef struct {
|
||||
CAN_TxHeaderTypeDef header; /* 发送头 */
|
||||
uint8_t data[BSP_CAN_MAX_DLC]; /* 数据 */
|
||||
} BSP_CAN_TxMessage_t;
|
||||
|
||||
/* 无锁环形队列结构体 */
|
||||
typedef struct {
|
||||
BSP_CAN_TxMessage_t buffer[BSP_CAN_TX_QUEUE_SIZE]; /* 缓冲区 */
|
||||
volatile uint32_t head; /* 队列头 */
|
||||
volatile uint32_t tail; /* 队列尾 */
|
||||
} BSP_CAN_TxQueue_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化 CAN 模块
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 获取 CAN 句柄
|
||||
* @param can CAN 枚举
|
||||
* @return CAN_HandleTypeDef 指针,失败返回 NULL
|
||||
*/
|
||||
CAN_HandleTypeDef *BSP_CAN_GetHandle(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 注册 CAN 回调函数
|
||||
* @param can CAN 枚举
|
||||
* @param type 回调类型
|
||||
* @param callback 回调函数指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterCallback(BSP_CAN_t can, BSP_CAN_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
/**
|
||||
* @brief 发送 CAN 消息
|
||||
* @param can CAN 枚举
|
||||
* @param format 消息格式
|
||||
* @param id CAN ID
|
||||
* @param data 数据指针
|
||||
* @param dlc 数据长度
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_Transmit(BSP_CAN_t can, BSP_CAN_Format_t format,
|
||||
uint32_t id, uint8_t *data, uint8_t dlc);
|
||||
|
||||
/**
|
||||
* @brief 发送标准数据帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 标准数据帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitStdDataFrame(BSP_CAN_t can, BSP_CAN_StdDataFrame_t *frame);
|
||||
|
||||
/**
|
||||
* @brief 发送扩展数据帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 扩展数据帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitExtDataFrame(BSP_CAN_t can, BSP_CAN_ExtDataFrame_t *frame);
|
||||
|
||||
/**
|
||||
* @brief 发送远程帧
|
||||
* @param can CAN 枚举
|
||||
* @param frame 远程帧指针
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_TransmitRemoteFrame(BSP_CAN_t can, BSP_CAN_RemoteFrame_t *frame);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取发送队列中待发送消息数量
|
||||
* @param can CAN 枚举
|
||||
* @return 队列中消息数量,-1表示错误
|
||||
*/
|
||||
int32_t BSP_CAN_GetTxQueueCount(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 清空发送队列
|
||||
* @param can CAN 枚举
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_FlushTxQueue(BSP_CAN_t can);
|
||||
|
||||
/**
|
||||
* @brief 注册 CAN ID 接收队列
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @param queue_size 队列大小,0使用默认值
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterId(BSP_CAN_t can, uint32_t can_id, uint8_t queue_size);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取 CAN 消息
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @param msg 存储消息的结构体指针
|
||||
* @param timeout 超时时间(毫秒),0为立即返回,osWaitForever为永久等待
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_GetMessage(BSP_CAN_t can, uint32_t can_id, BSP_CAN_Message_t *msg, uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief 获取指定ID队列中的消息数量
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @return 消息数量,-1表示队列不存在
|
||||
*/
|
||||
int32_t BSP_CAN_GetQueueCount(BSP_CAN_t can, uint32_t can_id);
|
||||
|
||||
/**
|
||||
* @brief 清空指定ID队列中的所有消息
|
||||
* @param can CAN 枚举
|
||||
* @param can_id 解析后的CAN ID
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_FlushQueue(BSP_CAN_t can, uint32_t can_id);
|
||||
|
||||
/**
|
||||
* @brief 注册ID解析器
|
||||
* @param parser ID解析回调函数
|
||||
* @return BSP_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t BSP_CAN_RegisterIdParser(BSP_CAN_IdParser_t parser);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 解析CAN ID
|
||||
* @param original_id 原始ID
|
||||
* @param frame_type 帧类型
|
||||
* @return 解析后的ID
|
||||
*/
|
||||
uint32_t BSP_CAN_ParseId(uint32_t original_id, BSP_CAN_FrameType_t frame_type);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
11
bsp/describe.csv
Normal file
11
bsp/describe.csv
Normal file
@ -0,0 +1,11 @@
|
||||
uart,请开启uart的dma和中断
|
||||
can,请开启can中断,使用函数前请确保can已经初始化。一定要开启can发送中断!!!
|
||||
gpio,会自动读取cubemx中配置为gpio的引脚,并自动区分输入输出和中断。
|
||||
spi,请开启spi的dma和中断
|
||||
i2c,要求开始spi中断
|
||||
mm,这是套了一层的动态内存分配
|
||||
time,获取时间戳函数,需要开启freerots
|
||||
dwt,需要开启dwt,获取时间
|
||||
i2c,请开启i2c的dma和中断
|
||||
pwm,用于选择那些勇于输出pwm
|
||||
|
||||
|
138
bsp/dwt.c
Normal file
138
bsp/dwt.c
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file dwt.c
|
||||
* @author Wang Hongxi
|
||||
* @version V1.1.0
|
||||
* @date 2022/3/8
|
||||
* @brief
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#include "bsp/dwt.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
DWT_Time_t SysTime;
|
||||
static uint32_t CPU_FREQ_Hz, CPU_FREQ_Hz_ms, CPU_FREQ_Hz_us;
|
||||
static uint32_t CYCCNT_RountCount;
|
||||
static uint32_t CYCCNT_LAST;
|
||||
uint64_t CYCCNT64;
|
||||
static void DWT_CNT_Update(void);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
void DWT_Init(uint32_t CPU_Freq_mHz)
|
||||
{
|
||||
/* 使能DWT外设 */
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
|
||||
/* DWT CYCCNT寄存器计数清0 */
|
||||
DWT->CYCCNT = (uint32_t)0u;
|
||||
|
||||
/* 使能Cortex-M DWT CYCCNT寄存器 */
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
CPU_FREQ_Hz = CPU_Freq_mHz * 1000000;
|
||||
CPU_FREQ_Hz_ms = CPU_FREQ_Hz / 1000;
|
||||
CPU_FREQ_Hz_us = CPU_FREQ_Hz / 1000000;
|
||||
CYCCNT_RountCount = 0;
|
||||
}
|
||||
|
||||
float DWT_GetDeltaT(uint32_t *cnt_last)
|
||||
{
|
||||
volatile uint32_t cnt_now = DWT->CYCCNT;
|
||||
float dt = ((uint32_t)(cnt_now - *cnt_last)) / ((float)(CPU_FREQ_Hz));
|
||||
*cnt_last = cnt_now;
|
||||
|
||||
DWT_CNT_Update();
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
double DWT_GetDeltaT64(uint32_t *cnt_last)
|
||||
{
|
||||
volatile uint32_t cnt_now = DWT->CYCCNT;
|
||||
double dt = ((uint32_t)(cnt_now - *cnt_last)) / ((double)(CPU_FREQ_Hz));
|
||||
*cnt_last = cnt_now;
|
||||
|
||||
DWT_CNT_Update();
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
void DWT_SysTimeUpdate(void)
|
||||
{
|
||||
volatile uint32_t cnt_now = DWT->CYCCNT;
|
||||
static uint64_t CNT_TEMP1, CNT_TEMP2, CNT_TEMP3;
|
||||
|
||||
DWT_CNT_Update();
|
||||
|
||||
CYCCNT64 = (uint64_t)CYCCNT_RountCount * (uint64_t)UINT32_MAX + (uint64_t)cnt_now;
|
||||
CNT_TEMP1 = CYCCNT64 / CPU_FREQ_Hz;
|
||||
CNT_TEMP2 = CYCCNT64 - CNT_TEMP1 * CPU_FREQ_Hz;
|
||||
SysTime.s = CNT_TEMP1;
|
||||
SysTime.ms = CNT_TEMP2 / CPU_FREQ_Hz_ms;
|
||||
CNT_TEMP3 = CNT_TEMP2 - SysTime.ms * CPU_FREQ_Hz_ms;
|
||||
SysTime.us = CNT_TEMP3 / CPU_FREQ_Hz_us;
|
||||
}
|
||||
|
||||
float DWT_GetTimeline_s(void)
|
||||
{
|
||||
DWT_SysTimeUpdate();
|
||||
|
||||
float DWT_Timelinef32 = SysTime.s + SysTime.ms * 0.001f + SysTime.us * 0.000001f;
|
||||
|
||||
return DWT_Timelinef32;
|
||||
}
|
||||
|
||||
float DWT_GetTimeline_ms(void)
|
||||
{
|
||||
DWT_SysTimeUpdate();
|
||||
|
||||
float DWT_Timelinef32 = SysTime.s * 1000 + SysTime.ms + SysTime.us * 0.001f;
|
||||
|
||||
return DWT_Timelinef32;
|
||||
}
|
||||
|
||||
uint64_t DWT_GetTimeline_us(void)
|
||||
{
|
||||
DWT_SysTimeUpdate();
|
||||
|
||||
uint64_t DWT_Timelinef32 = SysTime.s * 1000000 + SysTime.ms * 1000 + SysTime.us;
|
||||
|
||||
return DWT_Timelinef32;
|
||||
}
|
||||
|
||||
static void DWT_CNT_Update(void)
|
||||
{
|
||||
volatile uint32_t cnt_now = DWT->CYCCNT;
|
||||
|
||||
if (cnt_now < CYCCNT_LAST)
|
||||
CYCCNT_RountCount++;
|
||||
|
||||
CYCCNT_LAST = cnt_now;
|
||||
}
|
||||
|
||||
void DWT_Delay(float Delay)
|
||||
{
|
||||
uint32_t tickstart = DWT->CYCCNT;
|
||||
float wait = Delay;
|
||||
|
||||
while ((DWT->CYCCNT - tickstart) < wait * (float)CPU_FREQ_Hz)
|
||||
{
|
||||
}
|
||||
}
|
||||
53
bsp/dwt.h
Normal file
53
bsp/dwt.h
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file dwt.h
|
||||
* @author Wang Hongxi
|
||||
* @version V1.1.0
|
||||
* @date 2022/3/8
|
||||
* @brief
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef _DWT_H
|
||||
#define _DWT_H
|
||||
|
||||
#include "main.h"
|
||||
#include "stdint.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t s;
|
||||
uint16_t ms;
|
||||
uint16_t us;
|
||||
} DWT_Time_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
void DWT_Init(uint32_t CPU_Freq_mHz);
|
||||
float DWT_GetDeltaT(uint32_t *cnt_last);
|
||||
double DWT_GetDeltaT64(uint32_t *cnt_last);
|
||||
float DWT_GetTimeline_s(void);
|
||||
float DWT_GetTimeline_ms(void);
|
||||
uint64_t DWT_GetTimeline_us(void);
|
||||
void DWT_Delay(float Delay);
|
||||
void DWT_SysTimeUpdate(void);
|
||||
|
||||
extern DWT_Time_t SysTime;
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#endif /* DWT_H_ */
|
||||
98
bsp/gpio.c
Normal file
98
bsp/gpio.c
Normal file
@ -0,0 +1,98 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/gpio.h"
|
||||
|
||||
#include <gpio.h>
|
||||
#include <main.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
typedef struct {
|
||||
uint16_t pin;
|
||||
GPIO_TypeDef *gpio;
|
||||
} BSP_GPIO_MAP_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static const BSP_GPIO_MAP_t GPIO_Map[BSP_GPIO_NUM] = {
|
||||
/* AUTO GENERATED BSP_GPIO_MAP */
|
||||
};
|
||||
|
||||
static void (*GPIO_Callback[16])(void);
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
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(BSP_GPIO_t gpio, void (*callback)(void)) {
|
||||
if (callback == NULL) return BSP_ERR_NULL;
|
||||
if (gpio >= BSP_GPIO_NUM) return BSP_ERR;
|
||||
|
||||
// 从GPIO映射中获取对应的pin值
|
||||
uint16_t pin = GPIO_Map[gpio].pin;
|
||||
|
||||
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(BSP_GPIO_t gpio) {
|
||||
switch (gpio) {
|
||||
/* AUTO GENERATED BSP_GPIO_ENABLE_IRQ */
|
||||
default:
|
||||
return BSP_ERR;
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_GPIO_DisableIRQ(BSP_GPIO_t gpio) {
|
||||
switch (gpio) {
|
||||
/* AUTO GENERATED BSP_GPIO_DISABLE_IRQ */
|
||||
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;
|
||||
}
|
||||
@ -6,32 +6,42 @@ extern "C" {
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* GPIO设备枚举,与设备对应 */
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef enum {
|
||||
BSP_GPIO_USER_KEY,
|
||||
/* BSP_GPIO_XXX, */
|
||||
/* AUTO GENERATED BSP_GPIO_ENUM */
|
||||
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_RegisterCallback(BSP_GPIO_t gpio, 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);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,18 +1,27 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp\i2c.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static void (*I2C_Callback[BSP_I2C_NUM][BSP_I2C_CB_NUM])(void);
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static BSP_I2C_t I2C_Get(I2C_HandleTypeDef *hi2c) {
|
||||
if (hi2c->Instance == I2C1)
|
||||
return BSP_I2C_EXAMPLE;
|
||||
// else if (hi2c->Instance == I2CX)
|
||||
// return BSP_I2C_XXX;
|
||||
/* AUTO GENERATED I2C_GET */
|
||||
else
|
||||
return BSP_I2C_ERR;
|
||||
}
|
||||
@ -92,10 +101,7 @@ void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c) {
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
I2C_HandleTypeDef *BSP_I2C_GetHandle(BSP_I2C_t i2c) {
|
||||
switch (i2c) {
|
||||
case BSP_I2C_EXAMPLE:
|
||||
return &hi2c1;
|
||||
// case BSP_I2C_XXX:
|
||||
// return &hi2cX;
|
||||
/* AUTO GENERATED BSP_I2C_GET_HANDLE */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -107,3 +113,76 @@ int8_t BSP_I2C_RegisterCallback(BSP_I2C_t i2c, BSP_I2C_Callback_t type,
|
||||
I2C_Callback[i2c][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_I2C_Transmit(BSP_I2C_t i2c, uint16_t devAddr, uint8_t *data,
|
||||
uint16_t size, bool dma) {
|
||||
if (i2c >= BSP_I2C_NUM) return BSP_ERR;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_I2C_Master_Transmit_DMA(hi2c, devAddr, data, size);
|
||||
} else {
|
||||
return HAL_I2C_Master_Transmit(hi2c, devAddr, data, size, 10);
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_I2C_Receive(BSP_I2C_t i2c, uint16_t devAddr, uint8_t *data,
|
||||
uint16_t size, bool dma) {
|
||||
if (i2c >= BSP_I2C_NUM) return BSP_ERR;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_I2C_Master_Receive_DMA(hi2c, devAddr, data, size);
|
||||
} else {
|
||||
return HAL_I2C_Master_Receive(hi2c, devAddr, data, size, 10);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BSP_I2C_MemReadByte(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr) {
|
||||
if (i2c >= BSP_I2C_NUM) return 0xFF;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return 0xFF;
|
||||
|
||||
uint8_t data;
|
||||
HAL_I2C_Mem_Read(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, &data, 1, HAL_MAX_DELAY);
|
||||
return data;
|
||||
}
|
||||
|
||||
int8_t BSP_I2C_MemWriteByte(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t data) {
|
||||
if (i2c >= BSP_I2C_NUM) return BSP_ERR;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return BSP_ERR;
|
||||
|
||||
return HAL_I2C_Mem_Write(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, &data, 1, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
int8_t BSP_I2C_MemRead(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t *data, uint16_t size, bool dma) {
|
||||
if (i2c >= BSP_I2C_NUM || data == NULL || size == 0) return BSP_ERR;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_I2C_Mem_Read_DMA(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, data, size);
|
||||
}
|
||||
else {
|
||||
return HAL_I2C_Mem_Read(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, data, size, HAL_MAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int8_t BSP_I2C_MemWrite(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t *data, uint16_t size, bool dma) {
|
||||
if (i2c >= BSP_I2C_NUM || data == NULL || size == 0) return BSP_ERR;
|
||||
I2C_HandleTypeDef *hi2c = BSP_I2C_GetHandle(i2c);
|
||||
if (hi2c == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_I2C_Mem_Write_DMA(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, data, size);
|
||||
} else {
|
||||
return HAL_I2C_Mem_Write(hi2c, devAddr, memAddr, I2C_MEMADD_SIZE_16BIT, data, size, HAL_MAX_DELAY);
|
||||
}
|
||||
}
|
||||
@ -6,19 +6,31 @@ extern "C" {
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <i2c.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* 要添加使用I2C的新设备,需要先在此添加对应的枚举值 */
|
||||
|
||||
/* I2C实体枚举,与设备对应 */
|
||||
typedef enum {
|
||||
BSP_I2C_EXAMPLE,
|
||||
/* BSP_I2C_XXX,*/
|
||||
/* AUTO GENERATED BSP_I2C_NAME */
|
||||
/* USER BSP_I2C BEGIN*/
|
||||
/* USER_I2C_XXX */
|
||||
/* USER BSP_I2C END */
|
||||
BSP_I2C_NUM,
|
||||
BSP_I2C_ERR,
|
||||
} BSP_I2C_t;
|
||||
@ -42,6 +54,25 @@ I2C_HandleTypeDef *BSP_I2C_GetHandle(BSP_I2C_t i2c);
|
||||
int8_t BSP_I2C_RegisterCallback(BSP_I2C_t i2c, BSP_I2C_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
int8_t BSP_I2C_Transmit(BSP_I2C_t i2c, uint16_t devAddr, uint8_t *data,
|
||||
uint16_t size, bool dma);
|
||||
int8_t BSP_I2C_Receive(BSP_I2C_t i2c, uint16_t devAddr, uint8_t *data,
|
||||
uint16_t size, bool dma);
|
||||
|
||||
|
||||
uint8_t BSP_I2C_MemReadByte(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr);
|
||||
int8_t BSP_I2C_MemWriteByte(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t data);
|
||||
|
||||
int8_t BSP_I2C_MemRead(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t *data, uint16_t size, bool dma);
|
||||
int8_t BSP_I2C_MemWrite(BSP_I2C_t i2c, uint16_t devAddr, uint16_t memAddr,
|
||||
uint8_t *data, uint16_t size, bool dma);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,36 +1,30 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "main.h"
|
||||
#include "servo.h"
|
||||
#include "bsp/mm.h"
|
||||
|
||||
#include "bsp/servo_pwm.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define MIN_CYCLE 0.5f //change begin
|
||||
#define MAX_CYCLE 2.5f
|
||||
#define ANGLE_LIMIT 180 //change end
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
int serve_Init(BSP_PWM_Channel_t ch)
|
||||
{
|
||||
if(BSP_PWM_Start(ch)!=0){
|
||||
return -1;
|
||||
}else return 0;
|
||||
}
|
||||
inline void *BSP_Malloc(size_t size) { return pvPortMalloc(size); }
|
||||
|
||||
inline void BSP_Free(void *pv) { vPortFree(pv); }
|
||||
|
||||
int set_servo_angle(BSP_PWM_Channel_t ch,float angle)
|
||||
{
|
||||
if (angle < 0.0f || angle > ANGLE_LIMIT) {
|
||||
return -1; // ÎÞЧµÄ½Ç¶È
|
||||
}
|
||||
|
||||
float duty_cycle=MIN_CYCLE+(MAX_CYCLE-MIN_CYCLE)*(angle/ANGLE_LIMIT);
|
||||
if(BSP_PWM_Set(ch,duty_cycle)!=0){
|
||||
return -1;
|
||||
}else return 0;
|
||||
}
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -1,20 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* 设置BUZZER状态 */
|
||||
typedef enum
|
||||
{
|
||||
BSP_BUZZER_ON,
|
||||
BSP_BUZZER_OFF,
|
||||
BSP_BUZZER_TAGGLE,
|
||||
} BSP_Buzzer_Status_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
void *BSP_Malloc(size_t size);
|
||||
void BSP_Free(void *pv);
|
||||
|
||||
int8_t BSP_Buzzer_Set(BSP_Buzzer_Status_t s);
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
110
bsp/pwm.c
Normal file
110
bsp/pwm.c
Normal file
@ -0,0 +1,110 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "tim.h"
|
||||
#include "bsp/pwm.h"
|
||||
#include "bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
typedef struct {
|
||||
TIM_HandleTypeDef *tim;
|
||||
uint16_t channel;
|
||||
} BSP_PWM_Config_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static const BSP_PWM_Config_t PWM_Map[BSP_PWM_NUM] = {
|
||||
/* AUTO GENERATED BSP_PWM_MAP */
|
||||
};
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
int8_t BSP_PWM_Start(BSP_PWM_Channel_t ch) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
HAL_TIM_PWM_Start(PWM_Map[ch].tim, PWM_Map[ch].channel);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_SetComp(BSP_PWM_Channel_t ch, float duty_cycle) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
if (duty_cycle > 1.0f) {
|
||||
duty_cycle = 1.0f;
|
||||
}
|
||||
if (duty_cycle < 0.0f) {
|
||||
duty_cycle = 0.0f;
|
||||
}
|
||||
// 获取ARR值(周期值)
|
||||
uint32_t arr = __HAL_TIM_GET_AUTORELOAD(PWM_Map[ch].tim);
|
||||
|
||||
// 计算比较值:CCR = duty_cycle * (ARR + 1)
|
||||
uint32_t ccr = (uint32_t)(duty_cycle * (arr + 1));
|
||||
|
||||
__HAL_TIM_SET_COMPARE(PWM_Map[ch].tim, PWM_Map[ch].channel, ccr);
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_SetFreq(BSP_PWM_Channel_t ch, float freq) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
uint32_t timer_clock = HAL_RCC_GetPCLK1Freq(); // Get the timer clock frequency
|
||||
uint32_t prescaler = PWM_Map[ch].tim->Init.Prescaler;
|
||||
uint32_t period = (timer_clock / (prescaler + 1)) / freq - 1;
|
||||
|
||||
if (period > UINT16_MAX) {
|
||||
return BSP_ERR; // Frequency too low
|
||||
}
|
||||
__HAL_TIM_SET_AUTORELOAD(PWM_Map[ch].tim, period);
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_Stop(BSP_PWM_Channel_t ch) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
HAL_TIM_PWM_Stop(PWM_Map[ch].tim, PWM_Map[ch].channel);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
uint32_t BSP_PWM_GetAutoReloadPreload(BSP_PWM_Channel_t ch) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
return PWM_Map[ch].tim->Init.AutoReloadPreload;
|
||||
}
|
||||
|
||||
TIM_HandleTypeDef* BSP_PWM_GetHandle(BSP_PWM_Channel_t ch) {
|
||||
return PWM_Map[ch].tim;
|
||||
}
|
||||
|
||||
|
||||
uint16_t BSP_PWM_GetChannel(BSP_PWM_Channel_t ch) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
return PWM_Map[ch].channel;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_Start_DMA(BSP_PWM_Channel_t ch, uint32_t *pData, uint16_t Length) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
HAL_TIM_PWM_Start_DMA(PWM_Map[ch].tim, PWM_Map[ch].channel, pData, Length);
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_PWM_Stop_DMA(BSP_PWM_Channel_t ch) {
|
||||
if (ch >= BSP_PWM_NUM) return BSP_ERR;
|
||||
|
||||
HAL_TIM_PWM_Stop_DMA(PWM_Map[ch].tim, PWM_Map[ch].channel);
|
||||
return BSP_OK;
|
||||
}
|
||||
48
bsp/pwm.h
Normal file
48
bsp/pwm.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
#include "tim.h"
|
||||
#include "bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
/* PWM通道 */
|
||||
typedef enum {
|
||||
/* AUTO GENERATED BSP_PWM_ENUM */
|
||||
BSP_PWM_NUM,
|
||||
BSP_PWM_ERR,
|
||||
} BSP_PWM_Channel_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
int8_t BSP_PWM_Start(BSP_PWM_Channel_t ch);
|
||||
int8_t BSP_PWM_SetComp(BSP_PWM_Channel_t ch, float duty_cycle);
|
||||
int8_t BSP_PWM_SetFreq(BSP_PWM_Channel_t ch, float freq);
|
||||
int8_t BSP_PWM_Stop(BSP_PWM_Channel_t ch);
|
||||
uint32_t BSP_PWM_GetAutoReloadPreload(BSP_PWM_Channel_t ch);
|
||||
uint16_t BSP_PWM_GetChannel(BSP_PWM_Channel_t ch);
|
||||
TIM_HandleTypeDef* BSP_PWM_GetHandle(BSP_PWM_Channel_t ch);
|
||||
int8_t BSP_PWM_Start_DMA(BSP_PWM_Channel_t ch, uint32_t *pData, uint16_t Length);
|
||||
int8_t BSP_PWM_Stop_DMA(BSP_PWM_Channel_t ch);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,20 +1,28 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp\spi.h"
|
||||
#include <spi.h>
|
||||
#include "bsp/spi.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static void (*SPI_Callback[BSP_SPI_NUM][BSP_SPI_CB_NUM])(void);
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static BSP_SPI_t SPI_Get(SPI_HandleTypeDef *hspi) {
|
||||
if (hspi->Instance == SPI1)
|
||||
return BSP_SPI_EXAMPLE;
|
||||
/*
|
||||
else if (hspi->Instance == SPIX)
|
||||
return BSP_SPI_XXX;
|
||||
*/
|
||||
/* AUTO GENERATED SPI_GET */
|
||||
else
|
||||
return BSP_SPI_ERR;
|
||||
}
|
||||
@ -87,12 +95,7 @@ void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
SPI_HandleTypeDef *BSP_SPI_GetHandle(BSP_SPI_t spi) {
|
||||
switch (spi) {
|
||||
case BSP_SPI_EXAMPLE:
|
||||
return &hspi1;
|
||||
/*
|
||||
case BSP_SPI_XXX:
|
||||
return &hspiX;
|
||||
*/
|
||||
/* AUTO GENERATED BSP_SPI_GET_HANDLE */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -104,3 +107,73 @@ int8_t BSP_SPI_RegisterCallback(BSP_SPI_t spi, BSP_SPI_Callback_t type,
|
||||
SPI_Callback[spi][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_Transmit(BSP_SPI_t spi, uint8_t *data, uint16_t size, bool dma) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
SPI_HandleTypeDef *hspi = BSP_SPI_GetHandle(spi);
|
||||
if (hspi == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_SPI_Transmit_DMA(hspi, data, size)!= HAL_OK;;
|
||||
} else {
|
||||
return HAL_SPI_Transmit(hspi, data, size, 20)!= HAL_OK;;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_Receive(BSP_SPI_t spi, uint8_t *data, uint16_t size, bool dma) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
SPI_HandleTypeDef *hspi = BSP_SPI_GetHandle(spi);
|
||||
if (hspi == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_SPI_Receive_DMA(hspi, data, size)!= HAL_OK;;
|
||||
} else {
|
||||
return HAL_SPI_Receive(hspi, data, size, 20)!= HAL_OK;;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_TransmitReceive(BSP_SPI_t spi, uint8_t *txData, uint8_t *rxData,
|
||||
uint16_t size, bool dma) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
SPI_HandleTypeDef *hspi = BSP_SPI_GetHandle(spi);
|
||||
if (hspi == NULL) return BSP_ERR;
|
||||
|
||||
if (dma) {
|
||||
return HAL_SPI_TransmitReceive_DMA(hspi, txData, rxData, size)!= HAL_OK;;
|
||||
} else {
|
||||
return HAL_SPI_TransmitReceive(hspi, txData, rxData, size, 20)!= HAL_OK;;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BSP_SPI_MemReadByte(BSP_SPI_t spi, uint8_t reg) {
|
||||
if (spi >= BSP_SPI_NUM) return 0xFF;
|
||||
uint8_t tmp[2] = {reg | 0x80, 0x00};
|
||||
BSP_SPI_TransmitReceive(spi, tmp, tmp, 2u, true);
|
||||
return tmp[1];
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_MemWriteByte(BSP_SPI_t spi, uint8_t reg, uint8_t data) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
uint8_t tmp[2] = {reg & 0x7f, data};
|
||||
return BSP_SPI_Transmit(spi, tmp, 2u, true);
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_MemRead(BSP_SPI_t spi, uint8_t reg, uint8_t *data, uint16_t size) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
if (data == NULL || size == 0) return BSP_ERR_NULL;
|
||||
reg = reg | 0x80;
|
||||
BSP_SPI_Transmit(spi, ®, 1u, true);
|
||||
return BSP_SPI_Receive(spi, data, size, true);
|
||||
}
|
||||
|
||||
int8_t BSP_SPI_MemWrite(BSP_SPI_t spi, uint8_t reg, uint8_t *data, uint16_t size) {
|
||||
if (spi >= BSP_SPI_NUM) return BSP_ERR;
|
||||
if (data == NULL || size == 0) return BSP_ERR_NULL;
|
||||
reg = reg & 0x7f;
|
||||
BSP_SPI_Transmit(spi, ®, 1u, true);
|
||||
return BSP_SPI_Transmit(spi, data, size, true);
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -6,19 +6,28 @@ extern "C" {
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <spi.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* 要添加使用SPI的新设备,需要先在此添加对应的枚举值 */
|
||||
|
||||
/* SPI实体枚举,与设备对应 */
|
||||
typedef enum {
|
||||
BSP_SPI_EXAMPLE,
|
||||
/* BSP_SPI_XXX,*/
|
||||
/* AUTO GENERATED BSP_SPI_NAME */
|
||||
BSP_SPI_NUM,
|
||||
BSP_SPI_ERR,
|
||||
} BSP_SPI_t;
|
||||
@ -41,6 +50,21 @@ SPI_HandleTypeDef *BSP_SPI_GetHandle(BSP_SPI_t spi);
|
||||
int8_t BSP_SPI_RegisterCallback(BSP_SPI_t spi, BSP_SPI_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
|
||||
int8_t BSP_SPI_Transmit(BSP_SPI_t spi, uint8_t *data, uint16_t size, bool dma);
|
||||
int8_t BSP_SPI_Receive(BSP_SPI_t spi, uint8_t *data, uint16_t size, bool dma);
|
||||
int8_t BSP_SPI_TransmitReceive(BSP_SPI_t spi, uint8_t *txData, uint8_t *rxData,
|
||||
uint16_t size, bool dma);
|
||||
|
||||
uint8_t BSP_SPI_MemReadByte(BSP_SPI_t spi, uint8_t reg);
|
||||
int8_t BSP_SPI_MemWriteByte(BSP_SPI_t spi, uint8_t reg, uint8_t data);
|
||||
int8_t BSP_SPI_MemRead(BSP_SPI_t spi, uint8_t reg, uint8_t *data, uint16_t size);
|
||||
int8_t BSP_SPI_MemWrite(BSP_SPI_t spi, uint8_t reg, uint8_t *data, uint16_t size);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
81
bsp/time.c
Normal file
81
bsp/time.c
Normal file
@ -0,0 +1,81 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/time.h"
|
||||
#include "bsp.h"
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "main.h"
|
||||
#include "task.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
uint32_t BSP_TIME_Get_ms() { return xTaskGetTickCount(); }
|
||||
|
||||
uint64_t BSP_TIME_Get_us() {
|
||||
uint32_t tick_freq = osKernelGetTickFreq();
|
||||
uint32_t ticks_old = xTaskGetTickCount()*(1000/tick_freq);
|
||||
uint32_t tick_value_old = SysTick->VAL;
|
||||
uint32_t ticks_new = xTaskGetTickCount()*(1000/tick_freq);
|
||||
uint32_t tick_value_new = SysTick->VAL;
|
||||
if (ticks_old == ticks_new) {
|
||||
return ticks_new * 1000 + 1000 - tick_value_old * 1000 / (SysTick->LOAD + 1);
|
||||
} else {
|
||||
return ticks_new * 1000 + 1000 - tick_value_new * 1000 / (SysTick->LOAD + 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t BSP_TIME_Get() __attribute__((alias("BSP_TIME_Get_us")));
|
||||
|
||||
int8_t BSP_TIME_Delay_ms(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;
|
||||
}
|
||||
|
||||
/*阻塞us延迟*/
|
||||
int8_t BSP_TIME_Delay_us(uint32_t us) {
|
||||
uint64_t start = BSP_TIME_Get_us();
|
||||
while (BSP_TIME_Get_us() - start < us) {
|
||||
// 等待us时间
|
||||
}
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_TIME_Delay(uint32_t ms) __attribute__((alias("BSP_TIME_Delay_ms")));
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
43
bsp/time.h
Normal file
43
bsp/time.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
uint32_t BSP_TIME_Get_ms();
|
||||
|
||||
uint64_t BSP_TIME_Get_us();
|
||||
|
||||
uint64_t BSP_TIME_Get();
|
||||
|
||||
int8_t BSP_TIME_Delay_ms(uint32_t ms);
|
||||
|
||||
/*微秒阻塞延时,一般别用*/
|
||||
int8_t BSP_TIME_Delay_us(uint32_t us);
|
||||
|
||||
int8_t BSP_TIME_Delay(uint32_t ms);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,18 +1,29 @@
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp\uart.h"
|
||||
#include <usart.h>
|
||||
|
||||
#include "bsp/uart.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
static void (*UART_Callback[BSP_UART_NUM][BSP_UART_CB_NUM])(void);
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static BSP_UART_t UART_Get(UART_HandleTypeDef *huart) {
|
||||
if (huart->Instance == USART1)
|
||||
return BSP_UART_EXAMPLE;
|
||||
// else if (huart->Instance == USARTX)
|
||||
// return BSP_UART_XXX;
|
||||
/* AUTO GENERATED UART_GET */
|
||||
else
|
||||
return BSP_UART_ERR;
|
||||
}
|
||||
@ -101,10 +112,7 @@ void BSP_UART_IRQHandler(UART_HandleTypeDef *huart) {
|
||||
|
||||
UART_HandleTypeDef *BSP_UART_GetHandle(BSP_UART_t uart) {
|
||||
switch (uart) {
|
||||
case BSP_UART_EXAMPLE:
|
||||
return &huart1;
|
||||
// case BSP_UART_XXX:
|
||||
// return &huartX;
|
||||
/* AUTO GENERATED BSP_UART_GET_HANDLE */
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -113,6 +121,33 @@ UART_HandleTypeDef *BSP_UART_GetHandle(BSP_UART_t uart) {
|
||||
int8_t BSP_UART_RegisterCallback(BSP_UART_t uart, BSP_UART_Callback_t type,
|
||||
void (*callback)(void)) {
|
||||
if (callback == NULL) return BSP_ERR_NULL;
|
||||
if (uart >= BSP_UART_NUM || type >= BSP_UART_CB_NUM) return BSP_ERR;
|
||||
UART_Callback[uart][type] = callback;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
int8_t BSP_UART_Transmit(BSP_UART_t uart, uint8_t *data, uint16_t size, bool dma) {
|
||||
if (uart >= BSP_UART_NUM) return BSP_ERR;
|
||||
if (data == NULL || size == 0) return BSP_ERR_NULL;
|
||||
|
||||
if (dma) {
|
||||
return HAL_UART_Transmit_DMA(BSP_UART_GetHandle(uart), data, size);
|
||||
} else {
|
||||
return HAL_UART_Transmit_IT(BSP_UART_GetHandle(uart), data, size);
|
||||
}
|
||||
}
|
||||
|
||||
int8_t BSP_UART_Receive(BSP_UART_t uart, uint8_t *data, uint16_t size, bool dma) {
|
||||
if (uart >= BSP_UART_NUM) return BSP_ERR;
|
||||
if (data == NULL || size == 0) return BSP_ERR_NULL;
|
||||
|
||||
if (dma) {
|
||||
return HAL_UART_Receive_DMA(BSP_UART_GetHandle(uart), data, size);
|
||||
} else {
|
||||
return HAL_UART_Receive_IT(BSP_UART_GetHandle(uart), data, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -6,19 +6,28 @@ extern "C" {
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <usart.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bsp/bsp.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/* 要添加使用UART的新设备,需要先在此添加对应的枚举值 */
|
||||
|
||||
/* UART实体枚举,与设备对应 */
|
||||
typedef enum {
|
||||
BSP_UART_EXAMPLE,
|
||||
/*BSP_UART_XXX*/
|
||||
/* AUTO GENERATED BSP_UART_NAME */
|
||||
BSP_UART_NUM,
|
||||
BSP_UART_ERR,
|
||||
} BSP_UART_t;
|
||||
@ -39,10 +48,21 @@ typedef enum {
|
||||
} BSP_UART_Callback_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
UART_HandleTypeDef *BSP_UART_GetHandle(BSP_UART_t uart);
|
||||
|
||||
void BSP_UART_IRQHandler(UART_HandleTypeDef *huart);
|
||||
|
||||
int8_t BSP_UART_RegisterCallback(BSP_UART_t uart, BSP_UART_Callback_t type,
|
||||
void (*callback)(void));
|
||||
|
||||
int8_t BSP_UART_Transmit(BSP_UART_t uart, uint8_t *data, uint16_t size, bool dma);
|
||||
int8_t BSP_UART_Receive(BSP_UART_t uart, uint8_t *data, uint16_t size, bool dma);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
352
component/FreeRTOS_CLI.c
Normal file
352
component/FreeRTOS_CLI.c
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* FreeRTOS+CLI V1.0.4
|
||||
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
/* Standard includes. */
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* FreeRTOS includes. */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
/* Utils includes. */
|
||||
#include "FreeRTOS_CLI.h"
|
||||
|
||||
/* If the application writer needs to place the buffer used by the CLI at a
|
||||
fixed address then set configAPPLICATION_PROVIDES_cOutputBuffer to 1 in
|
||||
FreeRTOSConfig.h, then declare an array with the following name and size in
|
||||
one of the application files:
|
||||
char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
|
||||
*/
|
||||
#ifndef configAPPLICATION_PROVIDES_cOutputBuffer
|
||||
#define configAPPLICATION_PROVIDES_cOutputBuffer 0
|
||||
#endif
|
||||
|
||||
typedef struct xCOMMAND_INPUT_LIST
|
||||
{
|
||||
const CLI_Command_Definition_t *pxCommandLineDefinition;
|
||||
struct xCOMMAND_INPUT_LIST *pxNext;
|
||||
} CLI_Definition_List_Item_t;
|
||||
|
||||
/*
|
||||
* The callback function that is executed when "help" is entered. This is the
|
||||
* only default command that is always present.
|
||||
*/
|
||||
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
|
||||
|
||||
/*
|
||||
* Return the number of parameters that follow the command name.
|
||||
*/
|
||||
static int8_t prvGetNumberOfParameters( const char *pcCommandString );
|
||||
|
||||
/* The definition of the "help" command. This command is always at the front
|
||||
of the list of registered commands. */
|
||||
static const CLI_Command_Definition_t xHelpCommand =
|
||||
{
|
||||
"help",
|
||||
"\r\nhelp:\r\n Lists all the registered commands\r\n\r\n",
|
||||
prvHelpCommand,
|
||||
0
|
||||
};
|
||||
|
||||
/* The definition of the list of commands. Commands that are registered are
|
||||
added to this list. */
|
||||
static CLI_Definition_List_Item_t xRegisteredCommands =
|
||||
{
|
||||
&xHelpCommand, /* The first command in the list is always the help command, defined in this file. */
|
||||
NULL /* The next pointer is initialised to NULL, as there are no other registered commands yet. */
|
||||
};
|
||||
|
||||
/* A buffer into which command outputs can be written is declared here, rather
|
||||
than in the command console implementation, to allow multiple command consoles
|
||||
to share the same buffer. For example, an application may allow access to the
|
||||
command interpreter by UART and by Ethernet. Sharing a buffer is done purely
|
||||
to save RAM. Note, however, that the command console itself is not re-entrant,
|
||||
so only one command interpreter interface can be used at any one time. For that
|
||||
reason, no attempt at providing mutual exclusion to the cOutputBuffer array is
|
||||
attempted.
|
||||
|
||||
configAPPLICATION_PROVIDES_cOutputBuffer is provided to allow the application
|
||||
writer to provide their own cOutputBuffer declaration in cases where the
|
||||
buffer needs to be placed at a fixed address (rather than by the linker). */
|
||||
|
||||
#if( configAPPLICATION_PROVIDES_cOutputBuffer == 0 )
|
||||
static char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
|
||||
#else
|
||||
extern char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
|
||||
#endif
|
||||
|
||||
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister )
|
||||
{
|
||||
static CLI_Definition_List_Item_t *pxLastCommandInList = &xRegisteredCommands;
|
||||
CLI_Definition_List_Item_t *pxNewListItem;
|
||||
BaseType_t xReturn = pdFAIL;
|
||||
|
||||
/* Check the parameter is not NULL. */
|
||||
configASSERT( pxCommandToRegister );
|
||||
|
||||
/* Create a new list item that will reference the command being registered. */
|
||||
pxNewListItem = ( CLI_Definition_List_Item_t * ) pvPortMalloc( sizeof( CLI_Definition_List_Item_t ) );
|
||||
configASSERT( pxNewListItem );
|
||||
|
||||
if( pxNewListItem != NULL )
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
/* Reference the command being registered from the newly created
|
||||
list item. */
|
||||
pxNewListItem->pxCommandLineDefinition = pxCommandToRegister;
|
||||
|
||||
/* The new list item will get added to the end of the list, so
|
||||
pxNext has nowhere to point. */
|
||||
pxNewListItem->pxNext = NULL;
|
||||
|
||||
/* Add the newly created list item to the end of the already existing
|
||||
list. */
|
||||
pxLastCommandInList->pxNext = pxNewListItem;
|
||||
|
||||
/* Set the end of list marker to the new list item. */
|
||||
pxLastCommandInList = pxNewListItem;
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
xReturn = pdPASS;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen )
|
||||
{
|
||||
static const CLI_Definition_List_Item_t *pxCommand = NULL;
|
||||
BaseType_t xReturn = pdTRUE;
|
||||
const char *pcRegisteredCommandString;
|
||||
size_t xCommandStringLength;
|
||||
|
||||
/* Note: This function is not re-entrant. It must not be called from more
|
||||
thank one task. */
|
||||
|
||||
if( pxCommand == NULL )
|
||||
{
|
||||
/* Search for the command string in the list of registered commands. */
|
||||
for( pxCommand = &xRegisteredCommands; pxCommand != NULL; pxCommand = pxCommand->pxNext )
|
||||
{
|
||||
pcRegisteredCommandString = pxCommand->pxCommandLineDefinition->pcCommand;
|
||||
xCommandStringLength = strlen( pcRegisteredCommandString );
|
||||
|
||||
/* To ensure the string lengths match exactly, so as not to pick up
|
||||
a sub-string of a longer command, check the byte after the expected
|
||||
end of the string is either the end of the string or a space before
|
||||
a parameter. */
|
||||
if( ( pcCommandInput[ xCommandStringLength ] == ' ' ) || ( pcCommandInput[ xCommandStringLength ] == 0x00 ) )
|
||||
{
|
||||
if( strncmp( pcCommandInput, pcRegisteredCommandString, xCommandStringLength ) == 0 )
|
||||
{
|
||||
/* The command has been found. Check it has the expected
|
||||
number of parameters. If cExpectedNumberOfParameters is -1,
|
||||
then there could be a variable number of parameters and no
|
||||
check is made. */
|
||||
if( pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters >= 0 )
|
||||
{
|
||||
if( prvGetNumberOfParameters( pcCommandInput ) != pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters )
|
||||
{
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ( pxCommand != NULL ) && ( xReturn == pdFALSE ) )
|
||||
{
|
||||
/* The command was found, but the number of parameters with the command
|
||||
was incorrect. */
|
||||
strncpy( pcWriteBuffer, "Incorrect command parameter(s). Enter \"help\" to view a list of available commands.\r\n\r\n", xWriteBufferLen );
|
||||
pxCommand = NULL;
|
||||
}
|
||||
else if( pxCommand != NULL )
|
||||
{
|
||||
/* Call the callback function that is registered to this command. */
|
||||
xReturn = pxCommand->pxCommandLineDefinition->pxCommandInterpreter( pcWriteBuffer, xWriteBufferLen, pcCommandInput );
|
||||
|
||||
/* If xReturn is pdFALSE, then no further strings will be returned
|
||||
after this one, and pxCommand can be reset to NULL ready to search
|
||||
for the next entered command. */
|
||||
if( xReturn == pdFALSE )
|
||||
{
|
||||
pxCommand = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pxCommand was NULL, the command was not found. */
|
||||
strncpy( pcWriteBuffer, "Command not recognised. Enter 'help' to view a list of available commands.\r\n\r\n", xWriteBufferLen );
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
char *FreeRTOS_CLIGetOutputBuffer( void )
|
||||
{
|
||||
return cOutputBuffer;
|
||||
}
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength )
|
||||
{
|
||||
UBaseType_t uxParametersFound = 0;
|
||||
const char *pcReturn = NULL;
|
||||
|
||||
*pxParameterStringLength = 0;
|
||||
|
||||
while( uxParametersFound < uxWantedParameter )
|
||||
{
|
||||
/* Index the character pointer past the current word. If this is the start
|
||||
of the command string then the first word is the command itself. */
|
||||
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
|
||||
{
|
||||
pcCommandString++;
|
||||
}
|
||||
|
||||
/* Find the start of the next string. */
|
||||
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) == ' ' ) )
|
||||
{
|
||||
pcCommandString++;
|
||||
}
|
||||
|
||||
/* Was a string found? */
|
||||
if( *pcCommandString != 0x00 )
|
||||
{
|
||||
/* Is this the start of the required parameter? */
|
||||
uxParametersFound++;
|
||||
|
||||
if( uxParametersFound == uxWantedParameter )
|
||||
{
|
||||
/* How long is the parameter? */
|
||||
pcReturn = pcCommandString;
|
||||
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
|
||||
{
|
||||
( *pxParameterStringLength )++;
|
||||
pcCommandString++;
|
||||
}
|
||||
|
||||
if( *pxParameterStringLength == 0 )
|
||||
{
|
||||
pcReturn = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pcReturn;
|
||||
}
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
|
||||
{
|
||||
static const CLI_Definition_List_Item_t * pxCommand = NULL;
|
||||
BaseType_t xReturn;
|
||||
|
||||
( void ) pcCommandString;
|
||||
|
||||
if( pxCommand == NULL )
|
||||
{
|
||||
/* Reset the pxCommand pointer back to the start of the list. */
|
||||
pxCommand = &xRegisteredCommands;
|
||||
}
|
||||
|
||||
/* Return the next command help string, before moving the pointer on to
|
||||
the next command in the list. */
|
||||
strncpy( pcWriteBuffer, pxCommand->pxCommandLineDefinition->pcHelpString, xWriteBufferLen );
|
||||
pxCommand = pxCommand->pxNext;
|
||||
|
||||
if( pxCommand == NULL )
|
||||
{
|
||||
/* There are no more commands in the list, so there will be no more
|
||||
strings to return after this one and pdFALSE should be returned. */
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
static int8_t prvGetNumberOfParameters( const char *pcCommandString )
|
||||
{
|
||||
int8_t cParameters = 0;
|
||||
BaseType_t xLastCharacterWasSpace = pdFALSE;
|
||||
|
||||
/* Count the number of space delimited words in pcCommandString. */
|
||||
while( *pcCommandString != 0x00 )
|
||||
{
|
||||
if( ( *pcCommandString ) == ' ' )
|
||||
{
|
||||
if( xLastCharacterWasSpace != pdTRUE )
|
||||
{
|
||||
cParameters++;
|
||||
xLastCharacterWasSpace = pdTRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xLastCharacterWasSpace = pdFALSE;
|
||||
}
|
||||
|
||||
pcCommandString++;
|
||||
}
|
||||
|
||||
/* If the command string ended with spaces, then there will have been too
|
||||
many parameters counted. */
|
||||
if( xLastCharacterWasSpace == pdTRUE )
|
||||
{
|
||||
cParameters--;
|
||||
}
|
||||
|
||||
/* The value returned is one less than the number of space delimited words,
|
||||
as the first word should be the command itself. */
|
||||
return cParameters;
|
||||
}
|
||||
|
||||
108
component/FreeRTOS_CLI.h
Normal file
108
component/FreeRTOS_CLI.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* FreeRTOS+CLI V1.0.4
|
||||
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
#ifndef COMMAND_INTERPRETER_H
|
||||
#define COMMAND_INTERPRETER_H
|
||||
|
||||
/* This config should be defined in FreeRTOSConfig.h. But due to the limition of CubeMX I put it here. */
|
||||
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 512
|
||||
|
||||
/* The prototype to which callback functions used to process command line
|
||||
commands must comply. pcWriteBuffer is a buffer into which the output from
|
||||
executing the command can be written, xWriteBufferLen is the length, in bytes of
|
||||
the pcWriteBuffer buffer, and pcCommandString is the entire string as input by
|
||||
the user (from which parameters can be extracted).*/
|
||||
typedef BaseType_t (*pdCOMMAND_LINE_CALLBACK)( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
|
||||
|
||||
/* The structure that defines command line commands. A command line command
|
||||
should be defined by declaring a const structure of this type. */
|
||||
typedef struct xCOMMAND_LINE_INPUT
|
||||
{
|
||||
const char * const pcCommand; /* The command that causes pxCommandInterpreter to be executed. For example "help". Must be all lower case. */
|
||||
const char * const pcHelpString; /* String that describes how to use the command. Should start with the command itself, and end with "\r\n". For example "help: Returns a list of all the commands\r\n". */
|
||||
const pdCOMMAND_LINE_CALLBACK pxCommandInterpreter; /* A pointer to the callback function that will return the output generated by the command. */
|
||||
int8_t cExpectedNumberOfParameters; /* Commands expect a fixed number of parameters, which may be zero. */
|
||||
} CLI_Command_Definition_t;
|
||||
|
||||
/* For backward compatibility. */
|
||||
#define xCommandLineInput CLI_Command_Definition_t
|
||||
|
||||
/*
|
||||
* Register the command passed in using the pxCommandToRegister parameter.
|
||||
* Registering a command adds the command to the list of commands that are
|
||||
* handled by the command interpreter. Once a command has been registered it
|
||||
* can be executed from the command line.
|
||||
*/
|
||||
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister );
|
||||
|
||||
/*
|
||||
* Runs the command interpreter for the command string "pcCommandInput". Any
|
||||
* output generated by running the command will be placed into pcWriteBuffer.
|
||||
* xWriteBufferLen must indicate the size, in bytes, of the buffer pointed to
|
||||
* by pcWriteBuffer.
|
||||
*
|
||||
* FreeRTOS_CLIProcessCommand should be called repeatedly until it returns pdFALSE.
|
||||
*
|
||||
* pcCmdIntProcessCommand is not reentrant. It must not be called from more
|
||||
* than one task - or at least - by more than one task at a time.
|
||||
*/
|
||||
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen );
|
||||
|
||||
/*---------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* A buffer into which command outputs can be written is declared in the
|
||||
* main command interpreter, rather than in the command console implementation,
|
||||
* to allow application that provide access to the command console via multiple
|
||||
* interfaces to share a buffer, and therefore save RAM. Note, however, that
|
||||
* the command interpreter itself is not re-entrant, so only one command
|
||||
* console interface can be used at any one time. For that reason, no attempt
|
||||
* is made to provide any mutual exclusion mechanism on the output buffer.
|
||||
*
|
||||
* FreeRTOS_CLIGetOutputBuffer() returns the address of the output buffer.
|
||||
*/
|
||||
char *FreeRTOS_CLIGetOutputBuffer( void );
|
||||
|
||||
/*
|
||||
* Return a pointer to the xParameterNumber'th word in pcCommandString.
|
||||
*/
|
||||
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength );
|
||||
|
||||
#endif /* COMMAND_INTERPRETER_H */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
417
component/ahrs.c
Normal file
417
component/ahrs.c
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
开源的AHRS算法。
|
||||
MadgwickAHRS
|
||||
*/
|
||||
|
||||
#include "ahrs.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define BETA_IMU (0.033f)
|
||||
#define BETA_AHRS (0.041f)
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 2 * proportional gain (Kp) */
|
||||
static float beta = BETA_IMU;
|
||||
|
||||
/**
|
||||
* @brief 不使用磁力计计算姿态
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
static int8_t AHRS_UpdateIMU(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro) {
|
||||
if (ahrs == NULL) return -1;
|
||||
if (accl == NULL) return -1;
|
||||
if (gyro == NULL) return -1;
|
||||
|
||||
beta = BETA_IMU;
|
||||
|
||||
float ax = accl->x;
|
||||
float ay = accl->y;
|
||||
float az = accl->z;
|
||||
|
||||
float gx = gyro->x;
|
||||
float gy = gyro->y;
|
||||
float gz = gyro->z;
|
||||
|
||||
float recip_norm;
|
||||
float s0, s1, s2, s3;
|
||||
float q_dot1, q_dot2, q_dot3, q_dot4;
|
||||
float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2,
|
||||
q3q3;
|
||||
|
||||
/* Rate of change of quaternion from gyroscope */
|
||||
q_dot1 = 0.5f * (-ahrs->quat.q1 * gx - ahrs->quat.q2 * gy -
|
||||
ahrs->quat.q3 * gz);
|
||||
q_dot2 = 0.5f * (ahrs->quat.q0 * gx + ahrs->quat.q2 * gz -
|
||||
ahrs->quat.q3 * gy);
|
||||
q_dot3 = 0.5f * (ahrs->quat.q0 * gy - ahrs->quat.q1 * gz +
|
||||
ahrs->quat.q3 * gx);
|
||||
q_dot4 = 0.5f * (ahrs->quat.q0 * gz + ahrs->quat.q1 * gy -
|
||||
ahrs->quat.q2 * gx);
|
||||
|
||||
/* Compute feedback only if accelerometer measurement valid (avoids NaN in
|
||||
* accelerometer normalisation) */
|
||||
if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
|
||||
/* Normalise accelerometer measurement */
|
||||
recip_norm = InvSqrt(ax * ax + ay * ay + az * az);
|
||||
ax *= recip_norm;
|
||||
ay *= recip_norm;
|
||||
az *= recip_norm;
|
||||
|
||||
/* Auxiliary variables to avoid repeated arithmetic */
|
||||
_2q0 = 2.0f * ahrs->quat.q0;
|
||||
_2q1 = 2.0f * ahrs->quat.q1;
|
||||
_2q2 = 2.0f * ahrs->quat.q2;
|
||||
_2q3 = 2.0f * ahrs->quat.q3;
|
||||
_4q0 = 4.0f * ahrs->quat.q0;
|
||||
_4q1 = 4.0f * ahrs->quat.q1;
|
||||
_4q2 = 4.0f * ahrs->quat.q2;
|
||||
_8q1 = 8.0f * ahrs->quat.q1;
|
||||
_8q2 = 8.0f * ahrs->quat.q2;
|
||||
q0q0 = ahrs->quat.q0 * ahrs->quat.q0;
|
||||
q1q1 = ahrs->quat.q1 * ahrs->quat.q1;
|
||||
q2q2 = ahrs->quat.q2 * ahrs->quat.q2;
|
||||
q3q3 = ahrs->quat.q3 * ahrs->quat.q3;
|
||||
|
||||
/* Gradient decent algorithm corrective step */
|
||||
s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;
|
||||
s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * ahrs->quat.q1 -
|
||||
_2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;
|
||||
s2 = 4.0f * q0q0 * ahrs->quat.q2 + _2q0 * ax + _4q2 * q3q3 -
|
||||
_2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;
|
||||
s3 = 4.0f * q1q1 * ahrs->quat.q3 - _2q1 * ax +
|
||||
4.0f * q2q2 * ahrs->quat.q3 - _2q2 * ay;
|
||||
|
||||
/* normalise step magnitude */
|
||||
recip_norm = InvSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
|
||||
|
||||
s0 *= recip_norm;
|
||||
s1 *= recip_norm;
|
||||
s2 *= recip_norm;
|
||||
s3 *= recip_norm;
|
||||
|
||||
/* Apply feedback step */
|
||||
q_dot1 -= beta * s0;
|
||||
q_dot2 -= beta * s1;
|
||||
q_dot3 -= beta * s2;
|
||||
q_dot4 -= beta * s3;
|
||||
}
|
||||
|
||||
/* Integrate rate of change of quaternion to yield quaternion */
|
||||
ahrs->quat.q0 += q_dot1 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q1 += q_dot2 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q2 += q_dot3 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q3 += q_dot4 * ahrs->inv_sample_freq;
|
||||
|
||||
/* Normalise quaternion */
|
||||
recip_norm = InvSqrt(ahrs->quat.q0 * ahrs->quat.q0 +
|
||||
ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
ahrs->quat.q0 *= recip_norm;
|
||||
ahrs->quat.q1 *= recip_norm;
|
||||
ahrs->quat.q2 *= recip_norm;
|
||||
ahrs->quat.q3 *= recip_norm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化姿态解算
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param magn 磁力计数据
|
||||
* @param sample_freq 采样频率
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Init(AHRS_t *ahrs, const AHRS_Magn_t *magn, float sample_freq) {
|
||||
if (ahrs == NULL) return -1;
|
||||
|
||||
ahrs->inv_sample_freq = 1.0f / sample_freq;
|
||||
|
||||
ahrs->quat.q0 = 1.0f;
|
||||
ahrs->quat.q1 = 0.0f;
|
||||
ahrs->quat.q2 = 0.0f;
|
||||
ahrs->quat.q3 = 0.0f;
|
||||
|
||||
if (magn) {
|
||||
float yaw = -atan2(magn->y, magn->x);
|
||||
|
||||
if ((magn->x == 0.0f) && (magn->y == 0.0f) && (magn->z == 0.0f)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < (M_PI / 2.0f)) || (yaw > 0.0f)) {
|
||||
ahrs->quat.q0 = 0.997458339f;
|
||||
ahrs->quat.q1 = 0.000336312107f;
|
||||
ahrs->quat.q2 = -0.0057230792f;
|
||||
ahrs->quat.q3 = 0.0740156546;
|
||||
|
||||
} else if ((yaw < M_PI) || (yaw > (M_PI / 2.0f))) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < 90.0f) || (yaw > M_PI)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
|
||||
} else if ((yaw < 90.0f) || (yaw > 0.0f)) {
|
||||
ahrs->quat.q0 = 0.800884545f;
|
||||
ahrs->quat.q1 = 0.00862364192f;
|
||||
ahrs->quat.q2 = -0.00283267116f;
|
||||
ahrs->quat.q3 = 0.598749936f;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 姿态运算更新一次
|
||||
* @note 输入数据必须是NED(North East Down) 参考坐标系
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @param magn 磁力计数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Update(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro, const AHRS_Magn_t *magn) {
|
||||
if (ahrs == NULL) return -1;
|
||||
if (accl == NULL) return -1;
|
||||
if (gyro == NULL) return -1;
|
||||
|
||||
beta = BETA_AHRS;
|
||||
|
||||
float recip_norm;
|
||||
float s0, s1, s2, s3;
|
||||
float q_dot1, q_dot2, q_dot3, q_dot4;
|
||||
float hx, hy;
|
||||
float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1,
|
||||
_2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3,
|
||||
q2q2, q2q3, q3q3;
|
||||
|
||||
if (magn == NULL) return AHRS_UpdateIMU(ahrs, accl, gyro);
|
||||
|
||||
float mx = magn->x;
|
||||
float my = magn->y;
|
||||
float mz = magn->z;
|
||||
|
||||
/* Use IMU algorithm if magnetometer measurement invalid (avoids NaN in */
|
||||
/* magnetometer normalisation) */
|
||||
if ((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) {
|
||||
return AHRS_UpdateIMU(ahrs, accl, gyro);
|
||||
}
|
||||
|
||||
float ax = accl->x;
|
||||
float ay = accl->y;
|
||||
float az = accl->z;
|
||||
|
||||
float gx = gyro->x;
|
||||
float gy = gyro->y;
|
||||
float gz = gyro->z;
|
||||
|
||||
/* Rate of change of quaternion from gyroscope */
|
||||
q_dot1 = 0.5f * (-ahrs->quat.q1 * gx - ahrs->quat.q2 * gy -
|
||||
ahrs->quat.q3 * gz);
|
||||
q_dot2 = 0.5f * (ahrs->quat.q0 * gx + ahrs->quat.q2 * gz -
|
||||
ahrs->quat.q3 * gy);
|
||||
q_dot3 = 0.5f * (ahrs->quat.q0 * gy - ahrs->quat.q1 * gz +
|
||||
ahrs->quat.q3 * gx);
|
||||
q_dot4 = 0.5f * (ahrs->quat.q0 * gz + ahrs->quat.q1 * gy -
|
||||
ahrs->quat.q2 * gx);
|
||||
|
||||
/* Compute feedback only if accelerometer measurement valid (avoids NaN in
|
||||
* accelerometer normalisation) */
|
||||
if (!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {
|
||||
/* Normalise accelerometer measurement */
|
||||
recip_norm = InvSqrt(ax * ax + ay * ay + az * az);
|
||||
ax *= recip_norm;
|
||||
ay *= recip_norm;
|
||||
az *= recip_norm;
|
||||
|
||||
/* Normalise magnetometer measurement */
|
||||
recip_norm = InvSqrt(mx * mx + my * my + mz * mz);
|
||||
mx *= recip_norm;
|
||||
my *= recip_norm;
|
||||
mz *= recip_norm;
|
||||
|
||||
/* Auxiliary variables to avoid repeated arithmetic */
|
||||
_2q0mx = 2.0f * ahrs->quat.q0 * mx;
|
||||
_2q0my = 2.0f * ahrs->quat.q0 * my;
|
||||
_2q0mz = 2.0f * ahrs->quat.q0 * mz;
|
||||
_2q1mx = 2.0f * ahrs->quat.q1 * mx;
|
||||
_2q0 = 2.0f * ahrs->quat.q0;
|
||||
_2q1 = 2.0f * ahrs->quat.q1;
|
||||
_2q2 = 2.0f * ahrs->quat.q2;
|
||||
_2q3 = 2.0f * ahrs->quat.q3;
|
||||
_2q0q2 = 2.0f * ahrs->quat.q0 * ahrs->quat.q2;
|
||||
_2q2q3 = 2.0f * ahrs->quat.q2 * ahrs->quat.q3;
|
||||
q0q0 = ahrs->quat.q0 * ahrs->quat.q0;
|
||||
q0q1 = ahrs->quat.q0 * ahrs->quat.q1;
|
||||
q0q2 = ahrs->quat.q0 * ahrs->quat.q2;
|
||||
q0q3 = ahrs->quat.q0 * ahrs->quat.q3;
|
||||
q1q1 = ahrs->quat.q1 * ahrs->quat.q1;
|
||||
q1q2 = ahrs->quat.q1 * ahrs->quat.q2;
|
||||
q1q3 = ahrs->quat.q1 * ahrs->quat.q3;
|
||||
q2q2 = ahrs->quat.q2 * ahrs->quat.q2;
|
||||
q2q3 = ahrs->quat.q2 * ahrs->quat.q3;
|
||||
q3q3 = ahrs->quat.q3 * ahrs->quat.q3;
|
||||
|
||||
/* Reference direction of Earth's magnetic field */
|
||||
hx = mx * q0q0 - _2q0my * ahrs->quat.q3 +
|
||||
_2q0mz * ahrs->quat.q2 + mx * q1q1 +
|
||||
_2q1 * my * ahrs->quat.q2 + _2q1 * mz * ahrs->quat.q3 -
|
||||
mx * q2q2 - mx * q3q3;
|
||||
hy = _2q0mx * ahrs->quat.q3 + my * q0q0 -
|
||||
_2q0mz * ahrs->quat.q1 + _2q1mx * ahrs->quat.q2 -
|
||||
my * q1q1 + my * q2q2 + _2q2 * mz * ahrs->quat.q3 - my * q3q3;
|
||||
// _2bx = sqrtf(hx * hx + hy * hy);
|
||||
// 改为invsqrt
|
||||
_2bx = 1.f / InvSqrt(hx * hx + hy * hy);
|
||||
_2bz = -_2q0mx * ahrs->quat.q2 + _2q0my * ahrs->quat.q1 +
|
||||
mz * q0q0 + _2q1mx * ahrs->quat.q3 - mz * q1q1 +
|
||||
_2q2 * my * ahrs->quat.q3 - mz * q2q2 + mz * q3q3;
|
||||
_4bx = 2.0f * _2bx;
|
||||
_4bz = 2.0f * _2bz;
|
||||
|
||||
/* Gradient decent algorithm corrective step */
|
||||
s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q1 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
_2bz * ahrs->quat.q2 *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(-_2bx * ahrs->quat.q3 + _2bz * ahrs->quat.q1) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
_2bx * ahrs->quat.q2 *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q0 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
4.0f * ahrs->quat.q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) +
|
||||
_2bz * ahrs->quat.q3 *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(_2bx * ahrs->quat.q2 + _2bz * ahrs->quat.q0) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
(_2bx * ahrs->quat.q3 - _4bz * ahrs->quat.q1) *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q3 * (2.0f * q0q1 + _2q2q3 - ay) -
|
||||
4.0f * ahrs->quat.q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) +
|
||||
(-_4bx * ahrs->quat.q2 - _2bz * ahrs->quat.q0) *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(_2bx * ahrs->quat.q1 + _2bz * ahrs->quat.q3) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
(_2bx * ahrs->quat.q0 - _4bz * ahrs->quat.q2) *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) +
|
||||
_2q2 * (2.0f * q0q1 + _2q2q3 - ay) +
|
||||
(-_4bx * ahrs->quat.q3 + _2bz * ahrs->quat.q1) *
|
||||
(_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) +
|
||||
(-_2bx * ahrs->quat.q0 + _2bz * ahrs->quat.q2) *
|
||||
(_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) +
|
||||
_2bx * ahrs->quat.q1 *
|
||||
(_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);
|
||||
/* normalise step magnitude */
|
||||
recip_norm = InvSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
|
||||
s0 *= recip_norm;
|
||||
s1 *= recip_norm;
|
||||
s2 *= recip_norm;
|
||||
s3 *= recip_norm;
|
||||
|
||||
/* Apply feedback step */
|
||||
q_dot1 -= beta * s0;
|
||||
q_dot2 -= beta * s1;
|
||||
q_dot3 -= beta * s2;
|
||||
q_dot4 -= beta * s3;
|
||||
}
|
||||
|
||||
/* Integrate rate of change of quaternion to yield quaternion */
|
||||
ahrs->quat.q0 += q_dot1 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q1 += q_dot2 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q2 += q_dot3 * ahrs->inv_sample_freq;
|
||||
ahrs->quat.q3 += q_dot4 * ahrs->inv_sample_freq;
|
||||
|
||||
/* Normalise quaternion */
|
||||
recip_norm = InvSqrt(ahrs->quat.q0 * ahrs->quat.q0 +
|
||||
ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
ahrs->quat.q0 *= recip_norm;
|
||||
ahrs->quat.q1 *= recip_norm;
|
||||
ahrs->quat.q2 *= recip_norm;
|
||||
ahrs->quat.q3 *= recip_norm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通过姿态解算主结构体中的四元数计算欧拉角
|
||||
*
|
||||
* @param eulr 欧拉角
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_GetEulr(AHRS_Eulr_t *eulr, const AHRS_t *ahrs) {
|
||||
if (eulr == NULL) return -1;
|
||||
if (ahrs == NULL) return -1;
|
||||
|
||||
const float sinr_cosp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q3);
|
||||
const float cosr_cosp =
|
||||
1.0f - 2.0f * (ahrs->quat.q1 * ahrs->quat.q1 +
|
||||
ahrs->quat.q2 * ahrs->quat.q2);
|
||||
eulr->pit = atan2f(sinr_cosp, cosr_cosp);
|
||||
|
||||
const float sinp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q2 -
|
||||
ahrs->quat.q3 * ahrs->quat.q1);
|
||||
|
||||
if (fabsf(sinp) >= 1.0f)
|
||||
eulr->rol = copysignf(M_PI / 2.0f, sinp);
|
||||
else
|
||||
eulr->rol = asinf(sinp);
|
||||
|
||||
const float siny_cosp = 2.0f * (ahrs->quat.q0 * ahrs->quat.q3 +
|
||||
ahrs->quat.q1 * ahrs->quat.q2);
|
||||
const float cosy_cosp =
|
||||
1.0f - 2.0f * (ahrs->quat.q2 * ahrs->quat.q2 +
|
||||
ahrs->quat.q3 * ahrs->quat.q3);
|
||||
eulr->yaw = atan2f(siny_cosp, cosy_cosp);
|
||||
|
||||
#if 0
|
||||
eulr->yaw *= M_RAD2DEG_MULT;
|
||||
eulr->rol *= M_RAD2DEG_MULT;
|
||||
eulr->pit *= M_RAD2DEG_MULT;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 将对应数据置零
|
||||
*
|
||||
* \param eulr 被操作的数据
|
||||
*/
|
||||
void AHRS_ResetEulr(AHRS_Eulr_t *eulr) { memset(eulr, 0, sizeof(*eulr)); }
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
114
component/ahrs.h
Normal file
114
component/ahrs.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
开源的AHRS算法。
|
||||
MadgwickAHRS
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 欧拉角(Euler angle) */
|
||||
typedef struct {
|
||||
float yaw; /* 偏航角(Yaw angle) */
|
||||
float pit; /* 俯仰角(Pitch angle) */
|
||||
float rol; /* 翻滚角(Roll angle) */
|
||||
} AHRS_Eulr_t;
|
||||
|
||||
/* 加速度计 Accelerometer */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Accl_t;
|
||||
|
||||
/* 陀螺仪 Gyroscope */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Gyro_t;
|
||||
|
||||
/* 磁力计 Magnetometer */
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} AHRS_Magn_t;
|
||||
|
||||
/* 四元数 */
|
||||
typedef struct {
|
||||
float q0;
|
||||
float q1;
|
||||
float q2;
|
||||
float q3;
|
||||
} AHRS_Quaternion_t;
|
||||
|
||||
/* 姿态解算算法主结构体 */
|
||||
typedef struct {
|
||||
/* 四元数 */
|
||||
AHRS_Quaternion_t quat;
|
||||
|
||||
float inv_sample_freq; /* 采样频率的的倒数 */
|
||||
} AHRS_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/**
|
||||
* @brief 初始化姿态解算
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param magn 磁力计数据
|
||||
* @param sample_freq 采样频率
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Init(AHRS_t *ahrs, const AHRS_Magn_t *magn, float sample_freq);
|
||||
|
||||
/**
|
||||
* @brief 姿态运算更新一次
|
||||
*
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @param accl 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @param magn 磁力计数据
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_Update(AHRS_t *ahrs, const AHRS_Accl_t *accl,
|
||||
const AHRS_Gyro_t *gyro, const AHRS_Magn_t *magn);
|
||||
|
||||
/**
|
||||
* @brief 通过姿态解算主结构体中的四元数计算欧拉角
|
||||
*
|
||||
* @param eulr 欧拉角
|
||||
* @param ahrs 姿态解算主结构体
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t AHRS_GetEulr(AHRS_Eulr_t *eulr, const AHRS_t *ahrs);
|
||||
|
||||
/**
|
||||
* \brief 将对应数据置零
|
||||
*
|
||||
* \param eulr 被操作的数据
|
||||
*/
|
||||
void AHRS_ResetEulr(AHRS_Eulr_t *eulr);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
70
component/capacity.c
Normal file
70
component/capacity.c
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
剩余电量算法。
|
||||
通过电压值计算剩余电量。
|
||||
*/
|
||||
|
||||
#include "capacity.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/**
|
||||
* @brief 通过电压计算电池剩余电量
|
||||
*
|
||||
* @param volt 电压值
|
||||
* @return float 剩余电量比例
|
||||
*/
|
||||
float Capacity_GetBatteryRemain(float volt) {
|
||||
float percentage;
|
||||
float volt_2 = volt * volt;
|
||||
float volt_3 = volt_2 * volt;
|
||||
|
||||
if (volt < 19.5f)
|
||||
percentage = 0.0f;
|
||||
|
||||
else if (volt < 21.9f)
|
||||
percentage = 0.005664f * volt_3 - 0.3386f * volt_2 + 6.765f * volt - 45.17f;
|
||||
|
||||
else if (volt < 25.5f)
|
||||
percentage = 0.02269f * volt_3 - 1.654f * volt_2 + 40.34f * volt - 328.4f;
|
||||
|
||||
else
|
||||
percentage = 1.0f;
|
||||
|
||||
if (percentage < 0.0f)
|
||||
percentage = 0.0f;
|
||||
|
||||
else if (percentage > 1.0f)
|
||||
percentage = 1.0f;
|
||||
|
||||
return percentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param vcap 电容电压
|
||||
* @param vbat 电池电压
|
||||
* @param v_cutoff 截止电压
|
||||
* @return float 电容剩余电量比例
|
||||
*/
|
||||
float Capacity_GetCapacitorRemain(float vcap, float vbat, float v_cutoff) {
|
||||
float percentage = (vcap - v_cutoff) / (vbat - v_cutoff);
|
||||
|
||||
if (percentage < 0.0f)
|
||||
percentage = 0.0f;
|
||||
|
||||
else if (percentage > 1.0f)
|
||||
percentage = 1.0f;
|
||||
|
||||
return percentage;
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
47
component/capacity.h
Normal file
47
component/capacity.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
剩余电量算法。
|
||||
|
||||
通过电压值计算剩余电量。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/**
|
||||
* @brief 通过电压计算电池剩余电量
|
||||
*
|
||||
* @param volt 电压值
|
||||
* @return float 剩余电量比例
|
||||
*/
|
||||
float Capacity_GetBatteryRemain(float volt);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param vcap 电容电压
|
||||
* @param vbat 电池电压
|
||||
* @param v_cutoff 截止电压
|
||||
* @return float 电容剩余电量比例
|
||||
*/
|
||||
float Capacity_GetCapacitorRemain(float vcap, float vbat, float v_cutoff);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
387
component/cmd.c
Normal file
387
component/cmd.c
Normal file
@ -0,0 +1,387 @@
|
||||
/*
|
||||
控制命令
|
||||
*/
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/**
|
||||
* @brief 行为转换为对应按键
|
||||
*
|
||||
* @param cmd 主结构体
|
||||
* @param behavior 行为
|
||||
* @return uint16_t 行为对应的按键
|
||||
*/
|
||||
static inline CMD_KeyValue_t CMD_BehaviorToKey(CMD_t *cmd,
|
||||
CMD_Behavior_t behavior) {
|
||||
return cmd->param->map.key_map[behavior].key;
|
||||
}
|
||||
|
||||
static inline CMD_ActiveType_t CMD_BehaviorToActive(CMD_t *cmd,
|
||||
CMD_Behavior_t behavior) {
|
||||
return cmd->param->map.key_map[behavior].active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否按下
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param key 按键名称
|
||||
* @param stateful 是否为状态切换按键
|
||||
* @return true 按下
|
||||
* @return false 未按下
|
||||
*/
|
||||
static bool CMD_KeyPressedRc(const CMD_RC_t *rc, CMD_KeyValue_t key) {
|
||||
/* 按下按键为鼠标左、右键 */
|
||||
if (key == CMD_L_CLICK) {
|
||||
return rc->mouse.l_click;
|
||||
}
|
||||
if (key == CMD_R_CLICK) {
|
||||
return rc->mouse.r_click;
|
||||
}
|
||||
return rc->key & (1u << key);
|
||||
}
|
||||
|
||||
static bool CMD_BehaviorOccurredRc(const CMD_RC_t *rc, CMD_t *cmd,
|
||||
CMD_Behavior_t behavior) {
|
||||
CMD_KeyValue_t key = CMD_BehaviorToKey(cmd, behavior);
|
||||
CMD_ActiveType_t active = CMD_BehaviorToActive(cmd, behavior);
|
||||
|
||||
bool now_key_pressed, last_key_pressed;
|
||||
|
||||
/* 按下按键为鼠标左、右键 */
|
||||
if (key == CMD_L_CLICK) {
|
||||
now_key_pressed = rc->mouse.l_click;
|
||||
last_key_pressed = cmd->mouse_last.l_click;
|
||||
} else if (key == CMD_R_CLICK) {
|
||||
now_key_pressed = rc->mouse.r_click;
|
||||
last_key_pressed = cmd->mouse_last.r_click;
|
||||
} else {
|
||||
now_key_pressed = rc->key & (1u << key);
|
||||
last_key_pressed = cmd->key_last & (1u << key);
|
||||
}
|
||||
|
||||
switch (active) {
|
||||
case CMD_ACTIVE_PRESSING:
|
||||
return now_key_pressed && !last_key_pressed;
|
||||
case CMD_ACTIVE_RASING:
|
||||
return !now_key_pressed && last_key_pressed;
|
||||
case CMD_ACTIVE_PRESSED:
|
||||
return now_key_pressed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析pc行为逻辑
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param cmd 主结构体
|
||||
* @param dt_sec 两次解析的间隔
|
||||
*/
|
||||
static void CMD_PcLogic(const CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
|
||||
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
|
||||
|
||||
/* 云台设置为鼠标控制欧拉角的变化,底盘的控制向量设置为零 */
|
||||
cmd->gimbal.delta_eulr.yaw =
|
||||
(float)rc->mouse.x * dt_sec * cmd->param->sens_mouse;
|
||||
cmd->gimbal.delta_eulr.pit =
|
||||
(float)(-rc->mouse.y) * dt_sec * cmd->param->sens_mouse;
|
||||
cmd->chassis.ctrl_vec.vx = cmd->chassis.ctrl_vec.vy = 0.0f;
|
||||
cmd->shoot.reverse_trig = false;
|
||||
|
||||
/* 按键行为映射相关逻辑 */
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FORE)) {
|
||||
cmd->chassis.ctrl_vec.vy += cmd->param->move.move_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_BACK)) {
|
||||
cmd->chassis.ctrl_vec.vy -= cmd->param->move.move_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_LEFT)) {
|
||||
cmd->chassis.ctrl_vec.vx -= cmd->param->move.move_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_RIGHT)) {
|
||||
cmd->chassis.ctrl_vec.vx += cmd->param->move.move_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_ACCELERATE)) {
|
||||
cmd->chassis.ctrl_vec.vx *= cmd->param->move.move_fast_sense;
|
||||
cmd->chassis.ctrl_vec.vy *= cmd->param->move.move_fast_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_DECELEBRATE)) {
|
||||
cmd->chassis.ctrl_vec.vx *= cmd->param->move.move_slow_sense;
|
||||
cmd->chassis.ctrl_vec.vy *= cmd->param->move.move_slow_sense;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FIRE)) {
|
||||
/* 切换至开火模式,设置相应的射击频率和弹丸初速度 */
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
cmd->shoot.fire = true;
|
||||
} else {
|
||||
/* 切换至准备模式,停止射击 */
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
cmd->shoot.fire = false;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FIRE_MODE)) {
|
||||
/* 每按一次依次切换开火下一个模式 */
|
||||
cmd->shoot.fire_mode++;
|
||||
cmd->shoot.fire_mode %= FIRE_MODE_NUM;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_ROTOR)) {
|
||||
/* 切换到小陀螺模式 */
|
||||
cmd->chassis.mode = CHASSIS_MODE_ROTOR;
|
||||
cmd->chassis.mode_rotor = ROTOR_MODE_RAND;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_OPENCOVER)) {
|
||||
/* 每按一次开、关弹舱盖 */
|
||||
cmd->shoot.cover_open = !cmd->shoot.cover_open;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_BUFF)) {
|
||||
if (cmd->ai_status == AI_STATUS_HITSWITCH) {
|
||||
/* 停止ai的打符模式,停用host控制 */
|
||||
CMD_RefereeAdd(&(cmd->referee), CMD_UI_HIT_SWITCH_STOP);
|
||||
cmd->host_overwrite = false;
|
||||
cmd->ai_status = AI_STATUS_STOP;
|
||||
} else if (cmd->ai_status == AI_STATUS_AUTOAIM) {
|
||||
/* 自瞄模式中切换失败提醒 */
|
||||
} else {
|
||||
/* ai切换至打符模式,启用host控制 */
|
||||
CMD_RefereeAdd(&(cmd->referee), CMD_UI_HIT_SWITCH_START);
|
||||
cmd->ai_status = AI_STATUS_HITSWITCH;
|
||||
cmd->host_overwrite = true;
|
||||
}
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_AUTOAIM)) {
|
||||
if (cmd->ai_status == AI_STATUS_AUTOAIM) {
|
||||
/* 停止ai的自瞄模式,停用host控制 */
|
||||
cmd->host_overwrite = false;
|
||||
cmd->ai_status = AI_STATUS_STOP;
|
||||
CMD_RefereeAdd(&(cmd->referee), CMD_UI_AUTO_AIM_STOP);
|
||||
} else {
|
||||
/* ai切换至自瞄模式,启用host控制 */
|
||||
cmd->ai_status = AI_STATUS_AUTOAIM;
|
||||
cmd->host_overwrite = true;
|
||||
CMD_RefereeAdd(&(cmd->referee), CMD_UI_AUTO_AIM_START);
|
||||
}
|
||||
} else {
|
||||
cmd->host_overwrite = false;
|
||||
// TODO: 修复逻辑
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_REVTRIG)) {
|
||||
/* 按下拨弹反转 */
|
||||
cmd->shoot.reverse_trig = true;
|
||||
}
|
||||
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FOLLOWGIMBAL35)) {
|
||||
cmd->chassis.mode = CHASSIS_MODE_FOLLOW_GIMBAL_35;
|
||||
}
|
||||
/* 保存当前按下的键位状态 */
|
||||
cmd->key_last = rc->key;
|
||||
memcpy(&(cmd->mouse_last), &(rc->mouse), sizeof(cmd->mouse_last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析rc行为逻辑
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param cmd 主结构体
|
||||
* @param dt_sec 两次解析的间隔
|
||||
*/
|
||||
static void CMD_RcLogic(const CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
|
||||
switch (rc->sw_l) {
|
||||
/* 左拨杆相应行为选择和解析 */
|
||||
case CMD_SW_UP:
|
||||
cmd->chassis.mode = CHASSIS_MODE_BREAK;
|
||||
break;
|
||||
|
||||
case CMD_SW_MID:
|
||||
cmd->chassis.mode = CHASSIS_MODE_FOLLOW_GIMBAL;
|
||||
break;
|
||||
|
||||
case CMD_SW_DOWN:
|
||||
cmd->chassis.mode = CHASSIS_MODE_ROTOR;
|
||||
cmd->chassis.mode_rotor = ROTOR_MODE_CW;
|
||||
break;
|
||||
|
||||
case CMD_SW_ERR:
|
||||
cmd->chassis.mode = CHASSIS_MODE_RELAX;
|
||||
break;
|
||||
}
|
||||
switch (rc->sw_r) {
|
||||
/* 右拨杆相应行为选择和解析*/
|
||||
case CMD_SW_UP:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
|
||||
cmd->shoot.mode = SHOOT_MODE_SAFE;
|
||||
break;
|
||||
|
||||
case CMD_SW_MID:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
|
||||
cmd->shoot.fire = false;
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
break;
|
||||
|
||||
case CMD_SW_DOWN:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
cmd->shoot.fire_mode = FIRE_MODE_SINGLE;
|
||||
cmd->shoot.fire = true;
|
||||
break;
|
||||
/*
|
||||
case CMD_SW_UP:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
|
||||
cmd->shoot.mode = SHOOT_MODE_SAFE;
|
||||
break;
|
||||
|
||||
case CMD_SW_MID:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
|
||||
cmd->shoot.fire = false;
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
break;
|
||||
|
||||
case CMD_SW_DOWN:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
cmd->shoot.fire_mode = FIRE_MODE_SINGLE;
|
||||
cmd->shoot.fire = true;
|
||||
break;
|
||||
*/
|
||||
case CMD_SW_ERR:
|
||||
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
|
||||
cmd->shoot.mode = SHOOT_MODE_RELAX;
|
||||
}
|
||||
/* 将操纵杆的对应值转换为底盘的控制向量和云台变化的欧拉角 */
|
||||
cmd->chassis.ctrl_vec.vx = rc->ch_l_x;
|
||||
cmd->chassis.ctrl_vec.vy = rc->ch_l_y;
|
||||
cmd->gimbal.delta_eulr.yaw = rc->ch_r_x * dt_sec * cmd->param->sens_rc;
|
||||
cmd->gimbal.delta_eulr.pit = rc->ch_r_y * dt_sec * cmd->param->sens_rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief rc失控时机器人恢复放松模式
|
||||
*
|
||||
* @param cmd 主结构体
|
||||
*/
|
||||
static void CMD_RcLostLogic(CMD_t *cmd) {
|
||||
/* 机器人底盘、云台、射击运行模式恢复至放松模式 */
|
||||
cmd->chassis.mode = CHASSIS_MODE_RELAX;
|
||||
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
|
||||
cmd->shoot.mode = SHOOT_MODE_RELAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化命令解析
|
||||
*
|
||||
* @param cmd 主结构体
|
||||
* @param param 参数
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_Init(CMD_t *cmd, const CMD_Params_t *param) {
|
||||
/* 指针检测 */
|
||||
if (cmd == NULL) return -1;
|
||||
if (param == NULL) return -1;
|
||||
|
||||
/* 设置机器人的命令参数,初始化控制方式为rc控制 */
|
||||
cmd->pc_ctrl = false;
|
||||
cmd->param = param;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用上位机控制指令覆盖
|
||||
*
|
||||
* @param cmd 主结构体
|
||||
* @return true 启用
|
||||
* @return false 不启用
|
||||
*/
|
||||
inline bool CMD_CheckHostOverwrite(CMD_t *cmd) { return cmd->host_overwrite; }
|
||||
|
||||
/**
|
||||
* @brief 解析命令
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param cmd 命令
|
||||
* @param dt_sec 两次解析的间隔
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_ParseRc(CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
|
||||
/* 指针检测 */
|
||||
if (rc == NULL) return -1;
|
||||
if (cmd == NULL) return -1;
|
||||
|
||||
/* 在pc控制和rc控制间切换 */
|
||||
if (CMD_KeyPressedRc(rc, CMD_KEY_SHIFT) &&
|
||||
CMD_KeyPressedRc(rc, CMD_KEY_CTRL) && CMD_KeyPressedRc(rc, CMD_KEY_Q))
|
||||
cmd->pc_ctrl = true;
|
||||
|
||||
if (CMD_KeyPressedRc(rc, CMD_KEY_SHIFT) &&
|
||||
CMD_KeyPressedRc(rc, CMD_KEY_CTRL) && CMD_KeyPressedRc(rc, CMD_KEY_E))
|
||||
cmd->pc_ctrl = false;
|
||||
/*c当rc丢控时,恢复机器人至默认状态 */
|
||||
if ((rc->sw_l == CMD_SW_ERR) || (rc->sw_r == CMD_SW_ERR)) {
|
||||
CMD_RcLostLogic(cmd);
|
||||
} else {
|
||||
if (cmd->pc_ctrl) {
|
||||
CMD_PcLogic(rc, cmd, dt_sec);
|
||||
} else {
|
||||
CMD_RcLogic(rc, cmd, dt_sec);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析上位机命令
|
||||
*
|
||||
* @param host host数据
|
||||
* @param cmd 命令
|
||||
* @param dt_sec 两次解析的间隔
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_ParseHost(const CMD_Host_t *host, CMD_t *cmd, float dt_sec) {
|
||||
(void)dt_sec; /* 未使用dt_sec,消除警告 */
|
||||
/* 指针检测 */
|
||||
if (host == NULL) return -1;
|
||||
if (cmd == NULL) return -1;
|
||||
|
||||
/* 云台欧拉角设置为host相应的变化的欧拉角 */
|
||||
cmd->gimbal.delta_eulr.yaw = host->gimbal_delta.yaw;
|
||||
cmd->gimbal.delta_eulr.pit = host->gimbal_delta.pit;
|
||||
|
||||
/* host射击命令,设置不同的射击频率和弹丸初速度 */
|
||||
if (host->fire) {
|
||||
cmd->shoot.mode = SHOOT_MODE_LOADED;
|
||||
cmd->shoot.fire = true;
|
||||
} else {
|
||||
cmd->shoot.mode = SHOOT_MODE_SAFE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加向Referee发送的命令
|
||||
*
|
||||
* @param ref 命令队列
|
||||
* @param cmd 要添加的命令
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_RefereeAdd(CMD_RefereeCmd_t *ref, CMD_UI_t cmd) {
|
||||
/* 指针检测 */
|
||||
if (ref == NULL) return -1;
|
||||
/* 越界检测 */
|
||||
if (ref->counter >= CMD_REFEREE_MAX_NUM || ref->counter < 0) return -1;
|
||||
|
||||
/* 添加机器人当前行为状态到画图的命令队列中 */
|
||||
ref->cmd[ref->counter] = cmd;
|
||||
ref->counter++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
318
component/cmd.h
Normal file
318
component/cmd.h
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
控制命令
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "component/ahrs.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define CMD_REFEREE_MAX_NUM (3) /* Lines 16 omitted */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 机器人型号 */
|
||||
typedef enum {
|
||||
ROBOT_MODEL_INFANTRY = 0, /* 步兵机器人 */
|
||||
ROBOT_MODEL_HERO, /* 英雄机器人 */
|
||||
ROBOT_MODEL_ENGINEER, /* 工程机器人 */
|
||||
ROBOT_MODEL_DRONE, /* 空中机器人 */
|
||||
ROBOT_MODEL_SENTRY, /* 哨兵机器人 */
|
||||
ROBOT_MODEL_NUM, /* 型号数量 */
|
||||
} CMD_RobotModel_t;
|
||||
|
||||
/* 底盘运行模式 */
|
||||
typedef enum {
|
||||
CHASSIS_MODE_RELAX, /* 放松模式,电机不输出。一般情况底盘初始化之后的模式 */
|
||||
CHASSIS_MODE_BREAK, /* 刹车模式,电机闭环控制保持静止。用于机器人停止状态 */
|
||||
CHASSIS_MODE_FOLLOW_GIMBAL, /* 通过闭环控制使车头方向跟随云台 */
|
||||
CHASSIS_MODE_FOLLOW_GIMBAL_35, /* 通过闭环控制使车头方向35度跟随云台 */
|
||||
CHASSIS_MODE_ROTOR, /* 小陀螺模式,通过闭环控制使底盘不停旋转 */
|
||||
CHASSIS_MODE_INDENPENDENT, /* 独立模式。底盘运行不受云台影响 */
|
||||
CHASSIS_MODE_OPEN, /* 开环模式。底盘运行不受PID控制,直接输出到电机 */
|
||||
} CMD_ChassisMode_t;
|
||||
|
||||
/* 云台运行模式 */
|
||||
typedef enum {
|
||||
GIMBAL_MODE_RELAX, /* 放松模式,电机不输出。一般情况云台初始化之后的模式 */
|
||||
GIMBAL_MODE_ABSOLUTE, /* 绝对坐标系控制,控制在空间内的绝对姿态 */
|
||||
GIMBAL_MODE_RELATIVE, /* 相对坐标系控制,控制相对于底盘的姿态 */
|
||||
} CMD_GimbalMode_t;
|
||||
|
||||
/* 射击运行模式 */
|
||||
typedef enum {
|
||||
SHOOT_MODE_RELAX, /* 放松模式,电机不输出 */
|
||||
SHOOT_MODE_SAFE, /* 保险模式,电机闭环控制保持静止 */
|
||||
SHOOT_MODE_LOADED, /* 上膛模式,摩擦轮开启。随时准备开火 */
|
||||
} CMD_ShootMode_t;
|
||||
|
||||
typedef enum {
|
||||
FIRE_MODE_SINGLE, /* 单发开火模式 */
|
||||
FIRE_MODE_BURST, /* N连发开火模式 */
|
||||
FIRE_MODE_CONT, /* 持续开火模式 */
|
||||
FIRE_MODE_NUM,
|
||||
} CMD_FireMode_t;
|
||||
|
||||
/* 小陀螺转动模式 */
|
||||
typedef enum {
|
||||
ROTOR_MODE_CW, /* 顺时针转动 */
|
||||
ROTOR_MODE_CCW, /* 逆时针转动 */
|
||||
ROTOR_MODE_RAND, /* 随机转动 */
|
||||
} CMD_RotorMode_t;
|
||||
|
||||
/* 底盘控制命令 */
|
||||
typedef struct {
|
||||
CMD_ChassisMode_t mode; /* 底盘运行模式 */
|
||||
CMD_RotorMode_t mode_rotor; /* 小陀螺转动模式 */
|
||||
MoveVector_t ctrl_vec; /* 底盘控制向量 */
|
||||
} CMD_ChassisCmd_t;
|
||||
|
||||
/* 云台控制命令 */
|
||||
typedef struct {
|
||||
CMD_GimbalMode_t mode; /* 云台运行模式 */
|
||||
AHRS_Eulr_t delta_eulr; /* 欧拉角变化角度 */
|
||||
} CMD_GimbalCmd_t;
|
||||
|
||||
/* 射击控制命令 */
|
||||
typedef struct {
|
||||
CMD_ShootMode_t mode; /* 射击运行模式 */
|
||||
CMD_FireMode_t fire_mode; /* 开火模式 */
|
||||
bool fire; /*开火*/
|
||||
bool cover_open; /* 弹舱盖开关 */
|
||||
bool reverse_trig; /* 拨弹电机状态 */
|
||||
} CMD_ShootCmd_t;
|
||||
|
||||
/* 拨杆位置 */
|
||||
typedef enum {
|
||||
CMD_SW_ERR = 0,
|
||||
CMD_SW_UP = 1,
|
||||
CMD_SW_MID = 3,
|
||||
CMD_SW_DOWN = 2,
|
||||
} CMD_SwitchPos_t;
|
||||
|
||||
/* 键盘按键值 */
|
||||
typedef enum {
|
||||
CMD_KEY_W = 0,
|
||||
CMD_KEY_S,
|
||||
CMD_KEY_A,
|
||||
CMD_KEY_D,
|
||||
CMD_KEY_SHIFT,
|
||||
CMD_KEY_CTRL,
|
||||
CMD_KEY_Q,
|
||||
CMD_KEY_E,
|
||||
CMD_KEY_R,
|
||||
CMD_KEY_F,
|
||||
CMD_KEY_G,
|
||||
CMD_KEY_Z,
|
||||
CMD_KEY_X,
|
||||
CMD_KEY_C,
|
||||
CMD_KEY_V,
|
||||
CMD_KEY_B,
|
||||
CMD_L_CLICK,
|
||||
CMD_R_CLICK,
|
||||
CMD_KEY_NUM,
|
||||
} CMD_KeyValue_t;
|
||||
|
||||
/* 行为值序列 */
|
||||
typedef enum {
|
||||
CMD_BEHAVIOR_FORE = 0, /* 向前 */
|
||||
CMD_BEHAVIOR_BACK, /* 向后 */
|
||||
CMD_BEHAVIOR_LEFT, /* 向左 */
|
||||
CMD_BEHAVIOR_RIGHT, /* 向右 */
|
||||
CMD_BEHAVIOR_ACCELERATE, /* 加速 */
|
||||
CMD_BEHAVIOR_DECELEBRATE, /* 减速 */
|
||||
CMD_BEHAVIOR_FIRE, /* 开火 */
|
||||
CMD_BEHAVIOR_FIRE_MODE, /* 切换开火模式 */
|
||||
CMD_BEHAVIOR_BUFF, /* 打符模式 */
|
||||
CMD_BEHAVIOR_AUTOAIM, /* 自瞄模式 */
|
||||
CMD_BEHAVIOR_OPENCOVER, /* 弹舱盖开关 */
|
||||
CMD_BEHAVIOR_ROTOR, /* 小陀螺模式 */
|
||||
CMD_BEHAVIOR_REVTRIG, /* 反转拨弹 */
|
||||
CMD_BEHAVIOR_FOLLOWGIMBAL35, /* 跟随云台呈35度 */
|
||||
CMD_BEHAVIOR_NUM,
|
||||
} CMD_Behavior_t;
|
||||
|
||||
typedef enum {
|
||||
CMD_ACTIVE_PRESSING, /* 按下时触发 */
|
||||
CMD_ACTIVE_RASING, /* 抬起时触发 */
|
||||
CMD_ACTIVE_PRESSED, /* 按住时触发 */
|
||||
} CMD_ActiveType_t;
|
||||
|
||||
typedef struct {
|
||||
CMD_ActiveType_t active;
|
||||
CMD_KeyValue_t key;
|
||||
} CMD_KeyMapItem_t;
|
||||
|
||||
/* 行为映射的对应按键数组 */
|
||||
typedef struct {
|
||||
CMD_KeyMapItem_t key_map[CMD_BEHAVIOR_NUM];
|
||||
} CMD_KeyMap_Params_t;
|
||||
|
||||
/* 位移灵敏度参数 */
|
||||
typedef struct {
|
||||
float move_sense; /* 移动灵敏度 */
|
||||
float move_fast_sense; /* 加速灵敏度 */
|
||||
float move_slow_sense; /* 减速灵敏度 */
|
||||
} CMD_Move_Params_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
} CMD_Screen_t;
|
||||
|
||||
/* 命令参数 */
|
||||
typedef struct {
|
||||
float sens_mouse; /* 鼠标灵敏度 */
|
||||
float sens_rc; /* 遥控器摇杆灵敏度 */
|
||||
CMD_KeyMap_Params_t map; /* 按键映射行为命令 */
|
||||
CMD_Move_Params_t move; /* 位移灵敏度参数 */
|
||||
CMD_Screen_t screen; /* 屏幕分辨率参数 */
|
||||
} CMD_Params_t;
|
||||
|
||||
/* AI行为状态 */
|
||||
typedef enum {
|
||||
AI_STATUS_STOP, /* 停止状态 */
|
||||
AI_STATUS_AUTOAIM, /* 自瞄状态 */
|
||||
AI_STATUS_HITSWITCH, /* 打符状态 */
|
||||
AI_STATUS_AUTOMATIC /* 自动状态 */
|
||||
} CMD_AI_Status_t;
|
||||
|
||||
/* UI所用行为状态 */
|
||||
typedef enum {
|
||||
CMD_UI_NOTHING, /* 当前无状态 */
|
||||
CMD_UI_AUTO_AIM_START, /* 自瞄状态开启 */
|
||||
CMD_UI_AUTO_AIM_STOP, /* 自瞄状态关闭 */
|
||||
CMD_UI_HIT_SWITCH_START, /* 打符状态开启 */
|
||||
CMD_UI_HIT_SWITCH_STOP /* 打符状态关闭 */
|
||||
} CMD_UI_t;
|
||||
|
||||
/*裁判系统发送的命令*/
|
||||
typedef struct {
|
||||
CMD_UI_t cmd[CMD_REFEREE_MAX_NUM]; /* 命令数组 */
|
||||
uint8_t counter; /* 命令计数 */
|
||||
} CMD_RefereeCmd_t;
|
||||
|
||||
typedef struct {
|
||||
bool pc_ctrl; /* 是否使用键鼠控制 */
|
||||
bool host_overwrite; /* 是否Host控制 */
|
||||
uint16_t key_last; /* 上次按键键值 */
|
||||
|
||||
struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
bool l_click; /* 左键 */
|
||||
bool r_click; /* 右键 */
|
||||
} mouse_last; /* 鼠标值 */
|
||||
|
||||
CMD_AI_Status_t ai_status; /* AI状态 */
|
||||
|
||||
const CMD_Params_t *param; /* 命令参数 */
|
||||
|
||||
CMD_ChassisCmd_t chassis; /* 底盘控制命令 */
|
||||
CMD_GimbalCmd_t gimbal; /* 云台控制命令 */
|
||||
CMD_ShootCmd_t shoot; /* 射击控制命令 */
|
||||
CMD_RefereeCmd_t referee; /* 裁判系统发送命令 */
|
||||
} CMD_t;
|
||||
|
||||
typedef struct {
|
||||
float ch_l_x; /* 遥控器左侧摇杆横轴值,上为正 */
|
||||
float ch_l_y; /* 遥控器左侧摇杆纵轴值,右为正 */
|
||||
float ch_r_x; /* 遥控器右侧摇杆横轴值,上为正 */
|
||||
float ch_r_y; /* 遥控器右侧摇杆纵轴值,右为正 */
|
||||
|
||||
float ch_res; /* 第五通道值 */
|
||||
|
||||
CMD_SwitchPos_t sw_r; /* 右侧拨杆位置 */
|
||||
CMD_SwitchPos_t sw_l; /* 左侧拨杆位置 */
|
||||
|
||||
struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
bool l_click; /* 左键 */
|
||||
bool r_click; /* 右键 */
|
||||
} mouse; /* 鼠标值 */
|
||||
|
||||
uint16_t key; /* 按键值 */
|
||||
|
||||
uint16_t res; /* 保留,未启用 */
|
||||
} CMD_RC_t;
|
||||
|
||||
typedef struct {
|
||||
AHRS_Eulr_t gimbal_delta; /* 欧拉角的变化量 */
|
||||
|
||||
struct {
|
||||
float vx; /* x轴移动速度 */
|
||||
float vy; /* y轴移动速度 */
|
||||
float wz; /* z轴转动速度 */
|
||||
} chassis_move_vec; /* 底盘移动向量 */
|
||||
|
||||
bool fire; /* 开火状态 */
|
||||
} CMD_Host_t;
|
||||
|
||||
/**
|
||||
* @brief 解析行为命令
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param cmd 主结构体
|
||||
*/
|
||||
int8_t CMD_Init(CMD_t *cmd, const CMD_Params_t *param);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用上位机控制指令覆盖
|
||||
*
|
||||
* @param cmd 主结构体
|
||||
* @return true 启用
|
||||
* @return false 不启用
|
||||
*/
|
||||
bool CMD_CheckHostOverwrite(CMD_t *cmd);
|
||||
|
||||
/**
|
||||
* @brief 解析命令
|
||||
*
|
||||
* @param rc 遥控器数据
|
||||
* @param cmd 命令
|
||||
* @param dt_sec 两次解析的间隔
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_ParseRc(CMD_RC_t *rc, CMD_t *cmd, float dt_sec);
|
||||
|
||||
/**
|
||||
* @brief 解析上位机命令
|
||||
*
|
||||
* @param host host数据
|
||||
* @param cmd 命令
|
||||
* @param dt_sec 两次解析的间隔
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_ParseHost(const CMD_Host_t *host, CMD_t *cmd, float dt_sec);
|
||||
|
||||
/**
|
||||
* @brief 添加向Referee发送的命令
|
||||
*
|
||||
* @param ref 命令队列
|
||||
* @param cmd 要添加的命令
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t CMD_RefereeAdd(CMD_RefereeCmd_t *ref, CMD_UI_t cmd);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,4 +1,12 @@
|
||||
#include "crc16_rm.h"
|
||||
#include "crc16.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
static const uint16_t crc16_tab[256] = {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48,
|
||||
@ -48,3 +56,7 @@ bool CRC16_Verify(const uint8_t *buf, size_t len) {
|
||||
((const uint16_t *)((const uint8_t *)buf +
|
||||
(len % 2)))[len / sizeof(uint16_t) - 1];
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -6,11 +6,25 @@ extern "C" {
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define CRC16_INIT 0XFFFF
|
||||
|
||||
uint16_t CRC16_Calc(const uint8_t *buf, size_t len, uint16_t crc);
|
||||
bool CRC16_Verify(const uint8_t *buf, size_t len);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,8 +1,12 @@
|
||||
/*
|
||||
参考了Linux
|
||||
*/
|
||||
#include "crc8.h"
|
||||
|
||||
#include "crc8_rm.h"
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
static const uint8_t crc8_tab[256] = {
|
||||
0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20,
|
||||
@ -42,3 +46,7 @@ bool CRC8_Verify(const uint8_t *buf, size_t len) {
|
||||
uint8_t expected = CRC8_Calc(buf, len - sizeof(uint8_t), CRC8_INIT);
|
||||
return expected == buf[len - sizeof(uint8_t)];
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -1,7 +1,3 @@
|
||||
/*
|
||||
参考了Linux
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -12,11 +8,23 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define CRC8_INIT 0xFF
|
||||
|
||||
uint8_t CRC8_Calc(const uint8_t *buf, size_t len, uint8_t crc);
|
||||
bool CRC8_Verify(const uint8_t *buf, size_t len);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
8
component/dependencies.csv
Normal file
8
component/dependencies.csv
Normal file
@ -0,0 +1,8 @@
|
||||
ahrs,component/user_math.h
|
||||
capacity,component/user_math.h
|
||||
cmd,component/ahrs
|
||||
error_detect,bsp/mm
|
||||
pid,component/filter
|
||||
filter,component/ahrs
|
||||
mixer,component/user_math.h
|
||||
ui,component/user_math.h
|
||||
|
14
component/describe.csv
Normal file
14
component/describe.csv
Normal file
@ -0,0 +1,14 @@
|
||||
pid,好用的
|
||||
ahrs,开源的AHRS算法,MadgwickAHRS
|
||||
capacity,电池容量计算
|
||||
cmd,通用控制命令
|
||||
crc8,CRC8校验rm
|
||||
crc16,CRC16校验rm
|
||||
error_detect,错误检测
|
||||
filter,各类滤波器
|
||||
FreeRTOS_CLI,FreeRTOS命令行接口
|
||||
limiter,限幅器
|
||||
mixer,混控器
|
||||
ui,用户交互
|
||||
user_math,用户自定义数学函数
|
||||
pid,PID控制器
|
||||
|
67
component/error_detect.c
Normal file
67
component/error_detect.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
错误检测。
|
||||
*/
|
||||
|
||||
#include "error_detect.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/mm.h"
|
||||
|
||||
static ErrorDetect_t ged;
|
||||
static bool inited = false;
|
||||
|
||||
int8_t ErrorDetect_Init(void) {
|
||||
if (inited) return -1;
|
||||
|
||||
memset(&ged, 0x00, sizeof(ged));
|
||||
|
||||
for (uint8_t i = 0; i < ERROR_DETECT_UNIT_NUM; i++) {
|
||||
ged.error[i].enable = true;
|
||||
ged.error[i].priority = i;
|
||||
ged.error[i].patient_lost = 500;
|
||||
ged.error[i].patient_work = 500;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ErrorDetect_Processing(uint32_t sys_time) {
|
||||
for (uint8_t i = 0; i < ERROR_DETECT_UNIT_NUM; i++) {
|
||||
if (!ged.error[i].enable) continue;
|
||||
|
||||
if (sys_time - ged.error[i].showup > ged.error[i].patient_lost) {
|
||||
ged.error[i].is_lost = true;
|
||||
ged.error[i].found_lost = sys_time;
|
||||
} else if (sys_time - ged.error[i].showup > ged.error[i].patient_lost) {
|
||||
} else {
|
||||
ged.error[i].cycle_time = ged.error[i].showup - ged.error[i].showup_last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ErrorDetect_ErrorExist(ErrorDetect_Unit_t unit) {
|
||||
if (unit == ERROR_DETECT_UNIT_NO_DEV) {
|
||||
for (uint8_t i = ERROR_DETECT_UNIT_NUM; i > 0; i--) {
|
||||
if (ged.error[i].error_exist) return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return ged.error[unit].error_exist;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorDetect_Unit_t ErrorDetect_GetErrorUnit(void) {
|
||||
for (uint8_t i = ERROR_DETECT_UNIT_NUM; i > 0; i--) {
|
||||
if (ged.error[i].error_exist) return i;
|
||||
}
|
||||
return ERROR_DETECT_UNIT_NO_DEV;
|
||||
}
|
||||
|
||||
const ErrorDetect_Error_t *ErrorDetect_GetDetail(ErrorDetect_Unit_t unit) {
|
||||
return &ged.error[unit];
|
||||
}
|
||||
|
||||
void ErrorDetect_Update(ErrorDetect_Unit_t unit, uint32_t time_current) {
|
||||
ged.error[unit].showup = time_current;
|
||||
}
|
||||
82
component/error_detect.h
Normal file
82
component/error_detect.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
错误检测。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
typedef enum {
|
||||
/* Low priority */
|
||||
ERROR_DETECT_UNIT_NO_DEV = 0,
|
||||
ERROR_DETECT_UNIT_REFEREE,
|
||||
ERROR_DETECT_UNIT_CHASSIS_M1,
|
||||
ERROR_DETECT_UNIT_CHASSIS_M2,
|
||||
ERROR_DETECT_UNIT_CHASSIS_M3,
|
||||
ERROR_DETECT_UNIT_CHASSIS_M4,
|
||||
ERROR_DETECT_UNIT_TRIGGER,
|
||||
ERROR_DETECT_UNIT_FEED,
|
||||
ERROR_DETECT_UNIT_GIMBAL_YAW,
|
||||
ERROR_DETECT_UNIT_GIMBAL_PIT,
|
||||
ERROR_DETECT_UNIT_GYRO,
|
||||
ERROR_DETECT_UNIT_ACCL,
|
||||
ERROR_DETECT_UNIT_MAGN,
|
||||
ERROR_DETECT_UNIT_DBUS,
|
||||
ERROR_DETECT_UNIT_NUM,
|
||||
/* High priority */
|
||||
} ErrorDetect_Unit_t;
|
||||
|
||||
typedef struct {
|
||||
bool enable;
|
||||
uint8_t priority;
|
||||
uint32_t patient_lost;
|
||||
uint32_t patient_work;
|
||||
|
||||
uint32_t showup;
|
||||
uint32_t showup_last;
|
||||
uint32_t cycle_time;
|
||||
uint32_t duration_lost;
|
||||
uint32_t duration_work;
|
||||
uint32_t found_lost;
|
||||
bool error_exist;
|
||||
bool is_lost;
|
||||
uint8_t data_is_error;
|
||||
|
||||
} ErrorDetect_Error_t;
|
||||
|
||||
typedef struct {
|
||||
ErrorDetect_Error_t error[ERROR_DETECT_UNIT_NUM];
|
||||
} ErrorDetect_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
int8_t ErrorDetect_Init(void);
|
||||
void ErrorDetect_Processing(uint32_t sys_time);
|
||||
bool ErrorDetect_ErrorExist(ErrorDetect_Unit_t unit);
|
||||
ErrorDetect_Unit_t ErrorDetect_GetErrorUnit(void);
|
||||
const ErrorDetect_Error_t *ErrorDetect_GetDetail(ErrorDetect_Unit_t unit);
|
||||
|
||||
void ErrorDetect_Update(ErrorDetect_Unit_t unit, uint32_t time_current);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
@ -8,6 +8,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* 二阶低通滤波器 */
|
||||
typedef struct {
|
||||
float cutoff_freq; /* 截止频率 */
|
||||
@ -40,6 +50,10 @@ typedef struct {
|
||||
|
||||
} NotchFilter_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/**
|
||||
* @brief 初始化滤波器
|
||||
*
|
||||
@ -97,6 +111,10 @@ float NotchFilter_Apply(NotchFilter_t *f, float sample);
|
||||
*/
|
||||
float NotchFilter_Reset(NotchFilter_t *f, float sample);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
107
component/limiter.c
Normal file
107
component/limiter.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
限制器
|
||||
*/
|
||||
|
||||
#include "limiter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define POWER_BUFF_THRESHOLD 20
|
||||
#define CHASSIS_POWER_CHECK_FREQ 10
|
||||
#define CHASSIS_POWER_FACTOR_PASS 0.9f
|
||||
#define CHASSIS_POWER_FACTOR_NO_PASS 1.5f
|
||||
|
||||
#define CHASSIS_MOTOR_CIRCUMFERENCE 0.12f
|
||||
|
||||
/**
|
||||
* @brief 限制底盘功率不超过power_limit
|
||||
*
|
||||
* @param power_limit 最大功率
|
||||
* @param motor_out 电机输出值
|
||||
* @param speed 电机转速
|
||||
* @param len 电机数量
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t PowerLimit_ChassicOutput(float power_limit, float *motor_out,
|
||||
float *speed, uint32_t len) {
|
||||
/* power_limit小于0时不进行限制 */
|
||||
if (motor_out == NULL || speed == NULL || power_limit < 0) return -1;
|
||||
|
||||
float sum_motor_out = 0.0f;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
/* 总功率计算 P=F(由转矩电流表示)*V(由转速表示) */
|
||||
sum_motor_out +=
|
||||
fabsf(motor_out[i]) * fabsf(speed[i]) * CHASSIS_MOTOR_CIRCUMFERENCE;
|
||||
}
|
||||
|
||||
/* 保持每个电机输出值缩小时比例不变 */
|
||||
if (sum_motor_out > power_limit) {
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
motor_out[i] *= power_limit / sum_motor_out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 电容输入功率计算
|
||||
*
|
||||
* @param power_in 底盘当前功率
|
||||
* @param power_limit 裁判系统功率限制值
|
||||
* @param power_buffer 缓冲能量
|
||||
* @return float 裁判系统输出最大值
|
||||
*/
|
||||
float PowerLimit_CapInput(float power_in, float power_limit,
|
||||
float power_buffer) {
|
||||
float target_power = 0.0f;
|
||||
|
||||
/* 计算下一个检测周期的剩余缓冲能量 */
|
||||
float heat_buff = power_buffer - (float)(power_in - power_limit) /
|
||||
(float)CHASSIS_POWER_CHECK_FREQ;
|
||||
if (heat_buff < POWER_BUFF_THRESHOLD) { /* 功率限制 */
|
||||
target_power = power_limit * CHASSIS_POWER_FACTOR_PASS;
|
||||
} else {
|
||||
target_power = power_limit * CHASSIS_POWER_FACTOR_NO_PASS;
|
||||
}
|
||||
|
||||
return target_power;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使用缓冲能量计算底盘最大功率
|
||||
*
|
||||
* @param power_limit 裁判系统功率限制值
|
||||
* @param power_buffer 缓冲能量
|
||||
* @return float 底盘输出最大值
|
||||
*/
|
||||
float PowerLimit_TargetPower(float power_limit, float power_buffer) {
|
||||
float target_power = 0.0f;
|
||||
|
||||
/* 根据剩余缓冲能量计算输出功率 */
|
||||
target_power = power_limit * (power_buffer - 10.0f) / 20.0f;
|
||||
if (target_power < 0.0f) target_power = 0.0f;
|
||||
|
||||
return target_power;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 射击频率控制
|
||||
*
|
||||
* @param heat 当前热量
|
||||
* @param heat_limit 热量上限
|
||||
* @param cooling_rate 冷却速率
|
||||
* @param heat_increase 冷却增加
|
||||
* @param shoot_freq 经过热量限制后的射击频率
|
||||
* @return float 射击频率
|
||||
*/
|
||||
float HeatLimit_ShootFreq(float heat, float heat_limit, float cooling_rate,
|
||||
float heat_increase, bool is_big) {
|
||||
float heat_percent = heat / heat_limit;
|
||||
float stable_freq = cooling_rate / heat_increase;
|
||||
if (is_big)
|
||||
return stable_freq;
|
||||
else
|
||||
return (heat_percent > 0.7f) ? stable_freq : 3.0f * stable_freq;
|
||||
}
|
||||
63
component/limiter.h
Normal file
63
component/limiter.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
限制器
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/**
|
||||
* @brief 限制底盘功率不超过power_limit
|
||||
*
|
||||
* @param power_limit 最大功率
|
||||
* @param motor_out 电机输出值
|
||||
* @param speed 电机转速
|
||||
* @param len 电机数量
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t PowerLimit_ChassicOutput(float power_limit, float *motor_out,
|
||||
float *speed, uint32_t len);
|
||||
/**
|
||||
* @brief 电容输入功率计算
|
||||
*
|
||||
* @param power_in 底盘当前功率
|
||||
* @param power_limit 裁判系统功率限制值
|
||||
* @param power_buffer 缓冲能量
|
||||
* @return float 裁判系统输出最大值
|
||||
*/
|
||||
float PowerLimit_CapInput(float power_in, float power_limit,
|
||||
float power_buffer);
|
||||
/**
|
||||
* @brief 使用缓冲能量计算底盘最大功率
|
||||
*
|
||||
* @param power_limit 裁判系统功率限制值
|
||||
* @param power_buffer 缓冲能量
|
||||
* @return float 底盘输出最大值
|
||||
*/
|
||||
float PowerLimit_TargetPower(float power_limit, float power_buffer);
|
||||
|
||||
/**
|
||||
* @brief 射击频率控制
|
||||
*
|
||||
* @param heat 当前热量
|
||||
* @param heat_limit 热量上限
|
||||
* @param cooling_rate 冷却速率
|
||||
* @param heat_increase 冷却增加
|
||||
* @param shoot_freq 经过热量限制后的射击频率
|
||||
* @return float 射击频率
|
||||
*/
|
||||
float HeatLimit_ShootFreq(float heat, float heat_limit, float cooling_rate,
|
||||
float heat_increase, bool is_big);
|
||||
94
component/mixer.c
Normal file
94
component/mixer.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
混合器
|
||||
*/
|
||||
|
||||
#include "mixer.h"
|
||||
|
||||
#include "math.h"
|
||||
|
||||
/**
|
||||
* @brief 初始化混合器
|
||||
*
|
||||
* @param mixer 混合器
|
||||
* @param mode 混合器模式
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t Mixer_Init(Mixer_t *mixer, Mixer_Mode_t mode) {
|
||||
if (mixer == NULL) return -1;
|
||||
|
||||
mixer->mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算输出
|
||||
*
|
||||
* @param mixer 混合器
|
||||
* @param move_vec 运动向量
|
||||
* @param out 输出数组
|
||||
* @param len 输出数组长短
|
||||
* @param scale 输出放大因子
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t Mixer_Apply(Mixer_t *mixer, MoveVector_t *move_vec, float *out,
|
||||
int8_t len, float scale) {
|
||||
if (mixer == NULL) return -1;
|
||||
|
||||
switch (mixer->mode) {
|
||||
case MIXER_MECANUM:
|
||||
if (len == 4) {
|
||||
out[0] = move_vec->vx - move_vec->vy + move_vec->wz;
|
||||
out[1] = move_vec->vx + move_vec->vy + move_vec->wz;
|
||||
out[2] = -move_vec->vx + move_vec->vy + move_vec->wz;
|
||||
out[3] = -move_vec->vx - move_vec->vy + move_vec->wz;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case MIXER_PARLFIX4:
|
||||
if (len == 4) {
|
||||
out[0] = -move_vec->vx;
|
||||
out[1] = move_vec->vx;
|
||||
out[2] = move_vec->vx;
|
||||
out[3] = -move_vec->vx;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
case MIXER_PARLFIX2:
|
||||
if (len == 2) {
|
||||
out[0] = -move_vec->vx;
|
||||
out[1] = move_vec->vx;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
case MIXER_SINGLE:
|
||||
if (len == 1) {
|
||||
out[0] = move_vec->vx;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
case MIXER_OMNICROSS:
|
||||
case MIXER_OMNIPLUS:
|
||||
goto error;
|
||||
}
|
||||
|
||||
float abs_max = 0.f;
|
||||
for (int8_t i = 0; i < len; i++) {
|
||||
const float abs_val = fabsf(out[i]);
|
||||
abs_max = (abs_val > abs_max) ? abs_val : abs_max;
|
||||
}
|
||||
if (abs_max > 1.f) {
|
||||
for (int8_t i = 0; i < len; i++) {
|
||||
out[i] /= abs_max;
|
||||
}
|
||||
}
|
||||
for (int8_t i = 0; i < len; i++) {
|
||||
out[i] *= scale;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (uint8_t i = 0; i < len; i++) out[i] = 0;
|
||||
return -1;
|
||||
}
|
||||
76
component/mixer.h
Normal file
76
component/mixer.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
混合器
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/** 四轮布局 */
|
||||
/* 前 */
|
||||
/* 2 1 */
|
||||
/* 3 4 */
|
||||
|
||||
/* 两轮布局 */
|
||||
/* 前 */
|
||||
/* 2 1 */
|
||||
|
||||
/* 混合器模式 */
|
||||
typedef enum {
|
||||
MIXER_MECANUM, /* 麦克纳姆轮 */
|
||||
MIXER_PARLFIX4, /* 平行四驱动轮 */
|
||||
MIXER_PARLFIX2, /* 平行对侧两驱动轮 */
|
||||
MIXER_OMNICROSS, /* 叉形全向轮 */
|
||||
MIXER_OMNIPLUS, /* 十字全向轮 */
|
||||
MIXER_SINGLE, /* 单个摩擦轮 */
|
||||
} Mixer_Mode_t;
|
||||
|
||||
typedef struct {
|
||||
Mixer_Mode_t mode;
|
||||
} Mixer_t; /* 混合器主结构体 */
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/**
|
||||
* @brief 初始化混合器
|
||||
*
|
||||
* @param mixer 混合器
|
||||
* @param mode 混合器模式
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t Mixer_Init(Mixer_t *mixer, Mixer_Mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief 计算输出
|
||||
*
|
||||
* @param mixer 混合器
|
||||
* @param move_vec 运动向量
|
||||
* @param out 输出数组
|
||||
* @param len 输出数组长短
|
||||
* @param scale 输出放大因子
|
||||
* @return int8_t 0对应没有错误
|
||||
*/
|
||||
int8_t Mixer_Apply(Mixer_t *mixer, MoveVector_t *move_vec, float *out,
|
||||
int8_t len, float scale);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -13,9 +13,6 @@
|
||||
|
||||
#include "pid.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include "user_math.h"
|
||||
|
||||
#define SIGMA 0.000001f
|
||||
|
||||
/**
|
||||
@ -12,6 +12,15 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
|
||||
#include "filter.h"
|
||||
#include "user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* PID模式 */
|
||||
typedef enum {
|
||||
@ -89,6 +98,10 @@ int8_t PID_ResetIntegral(KPID_t *pid);
|
||||
*/
|
||||
int8_t PID_Reset(KPID_t *pid);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
301
component/ui.c
Normal file
301
component/ui.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
UI相关命令
|
||||
*/
|
||||
#include "component/ui.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @brief UI_绘制直线段
|
||||
*
|
||||
* @param grapic_line 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param x_end 终点x坐标
|
||||
* @param y_end 终点y坐标
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawLine(UI_Ele_t *grapic_line, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start, uint16_t x_end,
|
||||
uint16_t y_end) {
|
||||
if (grapic_line == NULL) return -1;
|
||||
snprintf((char *)grapic_line->name, 2, "%s", name);
|
||||
grapic_line->layer = layer;
|
||||
grapic_line->type_op = type_op;
|
||||
grapic_line->type_ele = 0;
|
||||
grapic_line->color = color;
|
||||
grapic_line->width = width;
|
||||
grapic_line->x_start = x_start;
|
||||
grapic_line->y_start = y_start;
|
||||
grapic_line->x_end = x_end;
|
||||
grapic_line->y_end = y_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制矩形
|
||||
*
|
||||
* @param grapic_rectangle 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param x_end 对角顶点x坐标
|
||||
* @param y_end 对角顶点y坐标
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawRectangle(UI_Ele_t *grapic_rectangle, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t width, uint16_t x_start, uint16_t y_start,
|
||||
uint16_t x_end, uint16_t y_end) {
|
||||
if (grapic_rectangle == NULL) return -1;
|
||||
snprintf((char *)grapic_rectangle->name, 2, "%s", name);
|
||||
grapic_rectangle->type_op = type_op;
|
||||
grapic_rectangle->type_ele = 1;
|
||||
grapic_rectangle->layer = layer;
|
||||
grapic_rectangle->color = color;
|
||||
grapic_rectangle->width = width;
|
||||
grapic_rectangle->x_start = x_start;
|
||||
grapic_rectangle->y_start = y_start;
|
||||
grapic_rectangle->x_end = x_end;
|
||||
grapic_rectangle->y_end = y_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制正圆
|
||||
*
|
||||
* @param grapic_cycle 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param radius 半径
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawCycle(UI_Ele_t *grapic_cycle, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_center, uint16_t y_center, uint16_t radius) {
|
||||
if (grapic_cycle == NULL) return -1;
|
||||
snprintf((char *)grapic_cycle->name, 2, "%s", name);
|
||||
grapic_cycle->type_op = type_op;
|
||||
grapic_cycle->layer = layer;
|
||||
grapic_cycle->type_ele = 2;
|
||||
grapic_cycle->color = color;
|
||||
grapic_cycle->width = width;
|
||||
grapic_cycle->x_start = x_center;
|
||||
grapic_cycle->y_start = y_center;
|
||||
grapic_cycle->radius = radius;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制椭圆
|
||||
*
|
||||
* @param grapic_oval 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param x_semiaxis x半轴长度
|
||||
* @param y_semiaxis y半轴长度
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawOval(UI_Ele_t *grapic_oval, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_center, uint16_t y_center, uint16_t x_semiaxis,
|
||||
uint16_t y_semiaxis) {
|
||||
if (grapic_oval == NULL) return -1;
|
||||
snprintf((char *)grapic_oval->name, 2, "%s", name);
|
||||
grapic_oval->type_op = type_op;
|
||||
grapic_oval->type_ele = 3;
|
||||
grapic_oval->layer = layer;
|
||||
grapic_oval->color = color;
|
||||
grapic_oval->width = width;
|
||||
grapic_oval->x_start = x_center;
|
||||
grapic_oval->y_start = y_center;
|
||||
grapic_oval->x_end = x_semiaxis;
|
||||
grapic_oval->y_end = y_semiaxis;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制圆弧
|
||||
*
|
||||
* @param grapic_arc 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param angle_start 起始角度
|
||||
* @param angle_end 终止角度
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param x_semiaxis x半轴长度
|
||||
* @param y_semiaxis y半轴长度
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawArc(UI_Ele_t *grapic_arc, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t angle_start,
|
||||
uint16_t angle_end, uint16_t width, uint16_t x_center,
|
||||
uint16_t y_center, uint16_t x_semiaxis, uint16_t y_semiaxis) {
|
||||
if (grapic_arc == NULL) return -1;
|
||||
snprintf((char *)grapic_arc->name, 2, "%s", name);
|
||||
grapic_arc->type_op = type_op;
|
||||
grapic_arc->type_ele = 4;
|
||||
grapic_arc->layer = layer;
|
||||
grapic_arc->color = color;
|
||||
grapic_arc->angle_start = angle_start;
|
||||
grapic_arc->angle_end = angle_end;
|
||||
grapic_arc->width = width;
|
||||
grapic_arc->x_start = x_center;
|
||||
grapic_arc->y_start = y_center;
|
||||
grapic_arc->x_end = x_semiaxis;
|
||||
grapic_arc->y_end = y_semiaxis;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制浮点数
|
||||
*
|
||||
* @param grapic_float 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param digits 小数点后有效位数
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param float_high 32位浮点数
|
||||
* @param float_middle 32位浮点数
|
||||
* @param float_low 32位浮点数
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawFloating(UI_Ele_t *grapic_floating, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t digits, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start, uint16_t float_high,
|
||||
uint16_t float_middle, uint16_t float_low) {
|
||||
if (grapic_floating == NULL) return -1;
|
||||
snprintf((char *)grapic_floating->name, 2, "%s", name);
|
||||
grapic_floating->type_op = type_op;
|
||||
grapic_floating->type_ele = 5;
|
||||
grapic_floating->layer = layer;
|
||||
grapic_floating->color = color;
|
||||
grapic_floating->angle_start = font_size;
|
||||
grapic_floating->angle_end = digits;
|
||||
grapic_floating->width = width;
|
||||
grapic_floating->x_start = x_start;
|
||||
grapic_floating->y_start = y_start;
|
||||
grapic_floating->radius = float_high;
|
||||
grapic_floating->x_end = float_middle;
|
||||
grapic_floating->y_end = float_low;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制整型数
|
||||
*
|
||||
* @param grapic_integer 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param int32_t_high 32位整型数
|
||||
* @param int32_t_middle 32位整型数
|
||||
* @param int32_t_low 32位整型数
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawInteger(UI_Ele_t *grapic_integer, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t width, uint16_t x_start,
|
||||
uint16_t y_start, uint16_t int32_t_high,
|
||||
uint16_t int32_t_middle, uint16_t int32_t_low) {
|
||||
if (grapic_integer == NULL) return -1;
|
||||
snprintf((char *)grapic_integer->name, 2, "%s", name);
|
||||
grapic_integer->type_op = type_op;
|
||||
grapic_integer->type_ele = 6;
|
||||
grapic_integer->layer = layer;
|
||||
grapic_integer->color = color;
|
||||
grapic_integer->angle_start = font_size;
|
||||
grapic_integer->width = width;
|
||||
grapic_integer->x_start = x_start;
|
||||
grapic_integer->y_start = y_start;
|
||||
grapic_integer->radius = int32_t_high;
|
||||
grapic_integer->x_end = int32_t_middle;
|
||||
grapic_integer->y_end = int32_t_low;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_绘制字符
|
||||
*
|
||||
* @param grapic_character 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param length 字符长度
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param character 字符串首地址
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawCharacter(UI_Drawcharacter_t *grapic_character, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t length, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start,
|
||||
const char *character) {
|
||||
if (grapic_character == NULL) return -1;
|
||||
snprintf((char *)grapic_character->grapic.name, 2, "%s", name);
|
||||
grapic_character->grapic.type_op = type_op;
|
||||
grapic_character->grapic.type_ele = 7;
|
||||
grapic_character->grapic.layer = layer;
|
||||
grapic_character->grapic.color = color;
|
||||
grapic_character->grapic.angle_start = font_size;
|
||||
grapic_character->grapic.angle_end = length;
|
||||
grapic_character->grapic.width = width;
|
||||
grapic_character->grapic.x_start = x_start;
|
||||
grapic_character->grapic.y_start = y_start;
|
||||
snprintf((char *)grapic_character->character, 29, "%s", character);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UI_删除图层
|
||||
*
|
||||
* @param del 结构体
|
||||
* @param opt 操作
|
||||
* @param layer 图层
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DelLayer(UI_Del_t *del, uint8_t opt, uint8_t layer) {
|
||||
if (del == NULL) return -1;
|
||||
del->del_operation = opt;
|
||||
del->layer = layer;
|
||||
return 0;
|
||||
}
|
||||
284
component/ui.h
Normal file
284
component/ui.h
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
UI相关命令
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "component/user_math.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define UI_DEL_OPERATION_NOTHING (0)
|
||||
#define UI_DEL_OPERATION_DEL (1)
|
||||
#define UI_DEL_OPERATION_DEL_ALL (2)
|
||||
|
||||
#define UI_GRAPIC_OPERATION_NOTHING (0)
|
||||
#define UI_GRAPIC_OPERATION_ADD (1)
|
||||
#define UI_GRAPIC_OPERATION_REWRITE (2)
|
||||
#define UI_GRAPIC_OPERATION_DEL (3)
|
||||
|
||||
#define UI_GRAPIC_LAYER_CONST (0)
|
||||
#define UI_GRAPIC_LAYER_AUTOAIM (1)
|
||||
#define UI_GRAPIC_LAYER_CHASSIS (2)
|
||||
#define UI_GRAPIC_LAYER_CAP (3)
|
||||
#define UI_GRAPIC_LAYER_GIMBAL (4)
|
||||
#define UI_GRAPIC_LAYER_SHOOT (5)
|
||||
#define UI_GRAPIC_LAYER_CMD (6)
|
||||
|
||||
#define UI_DEFAULT_WIDTH (0x01)
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
#define UI_CHAR_DEFAULT_WIDTH (0x02)
|
||||
|
||||
typedef enum {
|
||||
RED_BLUE,
|
||||
YELLOW,
|
||||
GREEN,
|
||||
ORANGE,
|
||||
PURPLISH_RED,
|
||||
PINK,
|
||||
CYAN,
|
||||
BLACK,
|
||||
WHITE
|
||||
} UI_Color_t;
|
||||
|
||||
typedef struct __packed {
|
||||
uint8_t op;
|
||||
uint8_t num_layer;
|
||||
} UI_InterStudent_UIDel_t;
|
||||
|
||||
typedef struct __packed {
|
||||
uint8_t name[3];
|
||||
uint8_t type_op : 3;
|
||||
uint8_t type_ele : 3;
|
||||
uint8_t layer : 4;
|
||||
uint8_t color : 4;
|
||||
uint16_t angle_start : 9;
|
||||
uint16_t angle_end : 9;
|
||||
uint16_t width : 10;
|
||||
uint16_t x_start : 11;
|
||||
uint16_t y_start : 11;
|
||||
uint16_t radius : 10;
|
||||
uint16_t x_end : 11;
|
||||
uint16_t y_end : 11;
|
||||
} UI_Ele_t;
|
||||
|
||||
typedef struct __packed {
|
||||
UI_Ele_t grapic;
|
||||
} UI_Drawgrapic_1_t;
|
||||
|
||||
typedef struct __packed {
|
||||
UI_Ele_t grapic[2];
|
||||
} UI_Drawgrapic_2_t;
|
||||
|
||||
typedef struct __packed {
|
||||
UI_Ele_t grapic[5];
|
||||
} UI_Drawgrapic_5_t;
|
||||
|
||||
typedef struct __packed {
|
||||
UI_Ele_t grapic[7];
|
||||
} UI_Drawgrapic_7_t;
|
||||
|
||||
typedef struct __packed {
|
||||
UI_Ele_t grapic;
|
||||
uint8_t character[30];
|
||||
} UI_Drawcharacter_t;
|
||||
|
||||
typedef struct __packed {
|
||||
uint8_t del_operation;
|
||||
uint8_t layer;
|
||||
} UI_Del_t;
|
||||
|
||||
/**
|
||||
* @brief UI_绘制直线段
|
||||
*
|
||||
* @param grapic_line 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param x_end 终点x坐标
|
||||
* @param y_end 终点y坐标
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawLine(UI_Ele_t *grapic_line, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start, uint16_t x_end,
|
||||
uint16_t y_end);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制矩形
|
||||
*
|
||||
* @param grapic_rectangle 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param x_end 对角顶点x坐标
|
||||
* @param y_end 对角顶点y坐标
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawRectangle(UI_Ele_t *grapic_rectangle, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t width, uint16_t x_start, uint16_t y_start,
|
||||
uint16_t x_end, uint16_t y_end);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制正圆
|
||||
*
|
||||
* @param grapic_cycle 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param radius 半径
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawCycle(UI_Ele_t *grapic_cycle, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_center, uint16_t y_center, uint16_t radius);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制椭圆
|
||||
*
|
||||
* @param grapic_oval 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param x_semiaxis x半轴长度
|
||||
* @param y_semiaxis y半轴长度
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawOval(UI_Ele_t *grapic_oval, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t width,
|
||||
uint16_t x_center, uint16_t y_center, uint16_t x_semiaxis,
|
||||
uint16_t y_semiaxis);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制圆弧
|
||||
*
|
||||
* @param grapic_arc 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param angle_start 起始角度
|
||||
* @param angle_end 终止角度
|
||||
* @param width 线条宽度
|
||||
* @param x_center 圆心x坐标
|
||||
* @param y_center 圆心y坐标
|
||||
* @param x_semiaxis x半轴长度
|
||||
* @param y_semiaxis y半轴长度
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawArc(UI_Ele_t *grapic_arc, const char *name, uint8_t type_op,
|
||||
uint8_t layer, uint8_t color, uint16_t angle_start,
|
||||
uint16_t angle_end, uint16_t width, uint16_t x_center,
|
||||
uint16_t y_center, uint16_t x_semiaxis, uint16_t y_semiaxis);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制浮点数
|
||||
*
|
||||
* @param grapic_float 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param digits 小数点后有效位数
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param float_high 32位浮点数
|
||||
* @param float_middle 32位浮点数
|
||||
* @param float_low 32位浮点数
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawFloating(UI_Ele_t *grapic_floating, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t digits, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start, uint16_t float_high,
|
||||
uint16_t float_middle, uint16_t float_low);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制整型数
|
||||
*
|
||||
* @param grapic_integer 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param int32_t_high 32位整型数
|
||||
* @param int32_t_middle 32位整型数
|
||||
* @param int32_t_low 32位整型数
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawInteger(UI_Ele_t *grapic_integer, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t width, uint16_t x_start,
|
||||
uint16_t y_start, uint16_t int32_t_high,
|
||||
uint16_t int32_t_middle, uint16_t int32_t_low);
|
||||
|
||||
/**
|
||||
* @brief UI_绘制字符
|
||||
*
|
||||
* @param grapic_character 结构体
|
||||
* @param name 图形名首地址
|
||||
* @param type_op 操作类型
|
||||
* @param layer 图层数
|
||||
* @param color 颜色
|
||||
* @param font_size 字体大小
|
||||
* @param length 字符长度
|
||||
* @param width 线条宽度
|
||||
* @param x_start 起点x坐标
|
||||
* @param y_start 起点y坐标
|
||||
* @param character 字符串首地址
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DrawCharacter(UI_Drawcharacter_t *grapic_character, const char *name,
|
||||
uint8_t type_op, uint8_t layer, uint8_t color,
|
||||
uint16_t font_size, uint16_t length, uint16_t width,
|
||||
uint16_t x_start, uint16_t y_start,
|
||||
const char *character);
|
||||
|
||||
/**
|
||||
* @brief UI_删除图层
|
||||
*
|
||||
* @param del 结构体
|
||||
* @param opt 操作
|
||||
* @param layer 图层
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t UI_DelLayer(UI_Del_t *del, uint8_t opt, uint8_t layer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -3,9 +3,11 @@
|
||||
*/
|
||||
|
||||
#include "user_math.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
inline float InvSqrt(float x) {
|
||||
//#if 0
|
||||
/* Fast inverse square-root */
|
||||
@ -39,13 +41,18 @@ inline void Clip(float *origin, float min, float max) {
|
||||
inline float Sign(float in) { return (in > 0) ? 1.0f : 0.0f; }
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器:相差1.5PI其实等于相差-0.5PI
|
||||
* \brief 将运动向量置零
|
||||
*
|
||||
* \param sp 被操作的值
|
||||
* \param fb 变化量
|
||||
* \param mv 被操作的值
|
||||
*/
|
||||
inline void ResetMoveVector(MoveVector_t *mv) { memset(mv, 0, sizeof(*mv)); }
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,适用于设定值与反馈值均在(x,y)范围内循环的情况,range应设定为y-x
|
||||
* 例如:(-M_PI,M_PI)range=M_2PI;(0,M_2PI)range=M_2PI;(a,a+b)range=b;
|
||||
* \param sp 设定值
|
||||
* \param fb 反馈值
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*
|
||||
* \return 函数运行结果
|
||||
*/
|
||||
inline float CircleError(float sp, float fb, float range) {
|
||||
@ -62,9 +69,7 @@ inline float CircleError(float sp, float fb, float range) {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 循环加法,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器,在0-2PI内变化,1.5PI + 1.5PI = 1PI
|
||||
*
|
||||
* \brief 循环加法,适用于被操作的值在(0,range)范围内循环的情况
|
||||
* \param origin 被操作的值
|
||||
* \param delta 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
@ -86,3 +91,44 @@ inline void CircleAdd(float *origin, float delta, float range) {
|
||||
* @param origin 被操作的值
|
||||
*/
|
||||
inline void CircleReverse(float *origin) { *origin = -(*origin) + M_2PI; }
|
||||
|
||||
/**
|
||||
* @brief 根据目标弹丸速度计算摩擦轮转速
|
||||
*
|
||||
* @param bullet_speed 弹丸速度
|
||||
* @param fric_radius 摩擦轮半径
|
||||
* @param is17mm 是否为17mm
|
||||
* @return 摩擦轮转速
|
||||
*/
|
||||
inline float CalculateRpm(float bullet_speed, float fric_radius, bool is17mm) {
|
||||
if (bullet_speed == 0.0f) return 0.f;
|
||||
if (is17mm) {
|
||||
if (bullet_speed == 15.0f) return 4670.f;
|
||||
if (bullet_speed == 18.0f) return 5200.f;
|
||||
if (bullet_speed == 30.0f) return 7350.f;
|
||||
} else {
|
||||
if (bullet_speed == 10.0f) return 4450.f;
|
||||
if (bullet_speed == 16.0f) return 5800.f;
|
||||
}
|
||||
|
||||
/* 不为裁判系统设定值时,计算转速 */
|
||||
return 60.0f * (float)bullet_speed / (M_2PI * fric_radius);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief 断言失败处理
|
||||
// *
|
||||
// * @param file 文件名
|
||||
// * @param line 行号
|
||||
// */
|
||||
// void VerifyFailed(const char *file, uint32_t line) {
|
||||
// UNUSED(file);
|
||||
// UNUSED(line);
|
||||
// while (1) {
|
||||
// __NOP();
|
||||
// }
|
||||
// }
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
@ -12,9 +12,19 @@ extern "C" {
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
#define M_DEG2RAD_MULT (0.01745329251f)
|
||||
#define M_RAD2DEG_MULT (57.2957795131f)
|
||||
|
||||
#ifndef M_PI_2
|
||||
#define M_PI_2 1.57079632679f
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
@ -23,6 +33,10 @@ extern "C" {
|
||||
#define M_2PI 6.28318530717958647692f
|
||||
#endif
|
||||
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((__packed__))
|
||||
#endif /* __packed */
|
||||
|
||||
#define max(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
@ -37,6 +51,12 @@ extern "C" {
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
|
||||
|
||||
/* 移动向量 */
|
||||
typedef struct {
|
||||
float vx; /* 前后平移 */
|
||||
@ -44,6 +64,10 @@ typedef struct {
|
||||
float wz; /* 转动 */
|
||||
} MoveVector_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
float InvSqrt(float x);
|
||||
|
||||
float AbsClip(float in, float limit);
|
||||
@ -55,21 +79,24 @@ void Clip(float *origin, float min, float max);
|
||||
float Sign(float in);
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器:相差1.5PI其实等于相差-0.5PI
|
||||
* \brief 将运动向量置零
|
||||
*
|
||||
* \param sp 被操作的值
|
||||
* \param fb 变化量
|
||||
* \param mv 被操作的值
|
||||
*/
|
||||
void ResetMoveVector(MoveVector_t *mv);
|
||||
|
||||
/**
|
||||
* \brief 计算循环值的误差,适用于设定值与反馈值均在(x,y)范围内循环的情况,range应设定为y-x
|
||||
* 例如:(-M_PI,M_PI)range=M_2PI;(0,M_2PI)range=M_2PI;(a,a+b)range=b;
|
||||
* \param sp 设定值
|
||||
* \param fb 反馈值
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
*
|
||||
* \return 函数运行结果
|
||||
*/
|
||||
float CircleError(float sp, float fb, float range);
|
||||
|
||||
/**
|
||||
* \brief 循环加法,用于没有负数值,并在一定范围内变化的值
|
||||
* 例如编码器,在0-2PI内变化,1.5PI + 1.5PI = 1PI
|
||||
*
|
||||
* \brief 循环加法,适用于被操作的值在(0,range)范围内循环的情况
|
||||
* \param origin 被操作的值
|
||||
* \param delta 变化量
|
||||
* \param range 被操作的值变化范围,正数时起效
|
||||
@ -83,6 +110,16 @@ void CircleAdd(float *origin, float delta, float range);
|
||||
*/
|
||||
void CircleReverse(float *origin);
|
||||
|
||||
/**
|
||||
* @brief 根据目标弹丸速度计算摩擦轮转速
|
||||
*
|
||||
* @param bullet_speed 弹丸速度
|
||||
* @param fric_radius 摩擦轮半径
|
||||
* @param is17mm 是否为17mm
|
||||
* @return 摩擦轮转速
|
||||
*/
|
||||
float CalculateRpm(float bullet_speed, float fric_radius, bool is17mm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -128,3 +165,15 @@ void CircleReverse(float *origin);
|
||||
*/
|
||||
#define VERIFY(expr) ((void)(expr))
|
||||
#endif
|
||||
|
||||
// /**
|
||||
// * @brief 断言失败处理
|
||||
// *
|
||||
// * @param file 文件名
|
||||
// * @param line 行号
|
||||
// */
|
||||
// void VerifyFailed(const char *file, uint32_t line);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
4
config.csv
Normal file
4
config.csv
Normal file
@ -0,0 +1,4 @@
|
||||
bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
|
||||
component,ahrs,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
|
||||
device,dr16,bmi088,ist8310,motor,motor_rm,motor_dm,motor_vesc,motor_lk,motor_lz,motor_odrive,dm_imu,rc_can,servo,buzzer,led,ws2812,vofa,ops9
|
||||
module,config,
|
||||
|
BIN
User/device/.DS_Store → device/.DS_Store
vendored
BIN
User/device/.DS_Store → device/.DS_Store
vendored
Binary file not shown.
381
device/bmi088.c
Normal file
381
device/bmi088.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
/* 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;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* 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 -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 2u, false);
|
||||
|
||||
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);
|
||||
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 1u, false);
|
||||
BSP_SPI_Receive(BSP_SPI_BMI088, buffer, 2u, false);
|
||||
|
||||
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);
|
||||
BSP_SPI_Transmit(BSP_SPI_BMI088, buffer, 1u, false);
|
||||
BSP_SPI_Receive(BSP_SPI_BMI088, data, len, true);
|
||||
}
|
||||
|
||||
static void BMI088_RxCpltCallback(void) {
|
||||
if (BSP_GPIO_ReadPin(BSP_GPIO_ACCL_CS) == GPIO_PIN_RESET) {
|
||||
BMI088_ACCL_NSS_SET();
|
||||
osThreadFlagsSet(thread_alert, SIGNAL_BMI088_ACCL_RAW_REDY);
|
||||
}
|
||||
if (BSP_GPIO_ReadPin(BSP_GPIO_GYRO_CS) == 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(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(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;
|
||||
}
|
||||
81
device/bmi088.h
Normal file
81
device/bmi088.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "component/ahrs.h"
|
||||
#include "device/device.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} gyro_offset; /* 陀螺仪偏置 */
|
||||
} BMI088_Cali_t; /* BMI088校准数据 */
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
AHRS_Accl_t accl;
|
||||
AHRS_Gyro_t gyro;
|
||||
|
||||
float temp; /* 温度 */
|
||||
|
||||
const BMI088_Cali_t *cali;
|
||||
} BMI088_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* 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);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
171
device/buzzer.c
Normal file
171
device/buzzer.c
Normal file
@ -0,0 +1,171 @@
|
||||
#include "device/buzzer.h"
|
||||
#include "bsp/time.h"
|
||||
#include <math.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define MUSIC_DEFAULT_VOLUME 0.5f
|
||||
#define MUSIC_A4_FREQ 440.0f // A4音符频率
|
||||
|
||||
/* USER MUSIC MENU BEGIN */
|
||||
// RM音乐
|
||||
const Tone_t RM[] = {
|
||||
{NOTE_B, 5, 200},
|
||||
{NOTE_G, 4, 200},
|
||||
{NOTE_B, 5, 400},
|
||||
{NOTE_G, 4, 200},
|
||||
{NOTE_B, 5, 400},
|
||||
{NOTE_G, 4, 200},
|
||||
{NOTE_D, 5, 400},
|
||||
{NOTE_G, 4, 200},
|
||||
{NOTE_C, 5, 200},
|
||||
{NOTE_C, 5, 200},
|
||||
{NOTE_G, 4, 200},
|
||||
{NOTE_B, 5, 200},
|
||||
{NOTE_C, 5, 200}
|
||||
};
|
||||
|
||||
// Nokia 经典铃声音符
|
||||
const Tone_t NOKIA[] = {
|
||||
{NOTE_E, 5, 125}, {NOTE_D, 5, 125}, {NOTE_FS, 4, 250}, {NOTE_GS, 4, 250},
|
||||
{NOTE_CS, 5, 125}, {NOTE_B, 4, 125}, {NOTE_D, 4, 250}, {NOTE_E, 4, 250},
|
||||
{NOTE_B, 4, 125}, {NOTE_A, 4, 125}, {NOTE_CS, 4, 250}, {NOTE_E, 4, 250},
|
||||
{NOTE_A, 4, 500}
|
||||
};
|
||||
/* USER MUSIC MENU END */
|
||||
|
||||
static void BUZZER_Update(BUZZER_t *buzzer){
|
||||
buzzer->header.online = true;
|
||||
buzzer->header.last_online_time = BSP_TIME_Get_ms();
|
||||
}
|
||||
|
||||
// 根据音符和八度计算频率的辅助函数
|
||||
static float BUZZER_CalcFreq(NOTE_t note, uint8_t octave) {
|
||||
if (note == NOTE_REST) {
|
||||
return 0.0f; // 休止符返回0频率
|
||||
}
|
||||
|
||||
// 将音符和八度转换为MIDI音符编号
|
||||
int midi_num = (int)note + (int)((octave + 1) * 12);
|
||||
|
||||
// 使用A4 (440Hz) 作为参考,计算频率
|
||||
// 公式: freq = 440 * 2^((midi_num - 69)/12)
|
||||
float freq = 440.0f * powf(2.0f, ((float)midi_num - 69.0f) / 12.0f);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
// 播放单个音符
|
||||
static int8_t BUZZER_PlayTone(BUZZER_t *buzzer, NOTE_t note, uint8_t octave, uint16_t duration_ms) {
|
||||
if (buzzer == NULL || !buzzer->header.online)
|
||||
return DEVICE_ERR;
|
||||
|
||||
float freq = BUZZER_CalcFreq(note, octave);
|
||||
|
||||
if (freq > 0.0f) {
|
||||
// 播放音符
|
||||
if (BUZZER_Set(buzzer, freq, MUSIC_DEFAULT_VOLUME) != DEVICE_OK)
|
||||
return DEVICE_ERR;
|
||||
|
||||
if (BUZZER_Start(buzzer) != DEVICE_OK)
|
||||
return DEVICE_ERR;
|
||||
} else {
|
||||
// 休止符,停止播放
|
||||
BUZZER_Stop(buzzer);
|
||||
}
|
||||
|
||||
// 等待指定时间
|
||||
BSP_TIME_Delay_ms(duration_ms);
|
||||
|
||||
// 停止当前音符,为下一个音符做准备
|
||||
BUZZER_Stop(buzzer);
|
||||
BSP_TIME_Delay_ms(20); // 短暂间隔
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel) {
|
||||
if (buzzer == NULL) return DEVICE_ERR;
|
||||
|
||||
buzzer->channel = channel;
|
||||
buzzer->header.online = true;
|
||||
|
||||
BUZZER_Stop(buzzer);
|
||||
|
||||
return DEVICE_OK ;
|
||||
}
|
||||
|
||||
int8_t BUZZER_Start(BUZZER_t *buzzer) {
|
||||
if (buzzer == NULL || !buzzer->header.online)
|
||||
return DEVICE_ERR;
|
||||
BUZZER_Update(buzzer);
|
||||
return (BSP_PWM_Start(buzzer->channel) == BSP_OK) ?
|
||||
DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
int8_t BUZZER_Stop(BUZZER_t *buzzer) {
|
||||
if (buzzer == NULL || !buzzer->header.online)
|
||||
return DEVICE_ERR;
|
||||
BUZZER_Update(buzzer);
|
||||
return (BSP_PWM_Stop(buzzer->channel) == BSP_OK) ?
|
||||
DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle) {
|
||||
if (buzzer == NULL || !buzzer->header.online)
|
||||
return DEVICE_ERR;
|
||||
|
||||
int result = DEVICE_OK ;
|
||||
BUZZER_Update(buzzer);
|
||||
if (BSP_PWM_SetFreq(buzzer->channel, freq) != BSP_OK)
|
||||
result = DEVICE_ERR;
|
||||
|
||||
if (BSP_PWM_SetComp(buzzer->channel, duty_cycle) != BSP_OK)
|
||||
result = DEVICE_ERR;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music) {
|
||||
if (buzzer == NULL || !buzzer->header.online)
|
||||
return DEVICE_ERR;
|
||||
|
||||
const Tone_t *melody = NULL;
|
||||
size_t melody_length = 0;
|
||||
|
||||
// 根据音乐类型选择对应的音符数组
|
||||
switch (music) {
|
||||
case MUSIC_RM:
|
||||
melody = RM;
|
||||
melody_length = sizeof(RM) / sizeof(Tone_t);
|
||||
break;
|
||||
case MUSIC_NOKIA:
|
||||
melody = NOKIA;
|
||||
melody_length = sizeof(NOKIA) / sizeof(Tone_t);
|
||||
break;
|
||||
default:
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
// 播放整首音乐
|
||||
for (size_t i = 0; i < melody_length; i++) {
|
||||
if (BUZZER_PlayTone(buzzer, melody[i].note, melody[i].octave, melody[i].duration_ms) != DEVICE_OK) {
|
||||
BUZZER_Stop(buzzer); // 出错时停止播放
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
// 音乐播放完成后停止
|
||||
BUZZER_Stop(buzzer);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
138
device/buzzer.h
Normal file
138
device/buzzer.h
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @file buzzer.h
|
||||
* @brief 蜂鸣器设备驱动头文件
|
||||
* @details 提供蜂鸣器音频播放功能,支持单音符播放和预设音乐播放
|
||||
* @author Generated by STM32CubeMX
|
||||
* @date 2025年10月23日
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "bsp/pwm.h" // PWM底层硬件抽象层
|
||||
#include "device.h" // 设备通用头文件
|
||||
#include <stddef.h> // 标准定义
|
||||
#include <stdint.h> // 标准整型定义
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 音符枚举类型
|
||||
* @details 定义标准十二平均律音符,用于音乐播放
|
||||
*/
|
||||
typedef enum {
|
||||
NOTE_C = 0, ///< Do音符
|
||||
NOTE_CS = 1, ///< Do#音符 (升Do)
|
||||
NOTE_D = 2, ///< Re音符
|
||||
NOTE_DS = 3, ///< Re#音符 (升Re)
|
||||
NOTE_E = 4, ///< Mi音符
|
||||
NOTE_F = 5, ///< Fa音符
|
||||
NOTE_FS = 6, ///< Fa#音符 (升Fa)
|
||||
NOTE_G = 7, ///< Sol音符
|
||||
NOTE_GS = 8, ///< Sol#音符 (升Sol)
|
||||
NOTE_A = 9, ///< La音符
|
||||
NOTE_AS = 10, ///< La#音符 (升La)
|
||||
NOTE_B = 11, ///< Si音符
|
||||
NOTE_REST = 255 ///< 休止符 (无声音)
|
||||
} NOTE_t;
|
||||
|
||||
/**
|
||||
* @brief 音调结构体
|
||||
* @details 定义一个完整的音调信息,包括音符、八度和持续时间
|
||||
*/
|
||||
typedef struct {
|
||||
NOTE_t note; ///< 音符名称 (使用NOTE_t枚举)
|
||||
uint8_t octave; ///< 八度 (0-8,通常使用3-7)
|
||||
uint16_t duration_ms; ///< 持续时间,单位毫秒
|
||||
} Tone_t;
|
||||
|
||||
/**
|
||||
* @brief 预设音乐枚举类型
|
||||
* @details 定义可播放的预设音乐类型
|
||||
*/
|
||||
typedef enum {
|
||||
/* USER MUSIC MENU BEGIN */
|
||||
MUSIC_RM, ///< RM战队音乐
|
||||
MUSIC_NOKIA, ///< 诺基亚经典铃声
|
||||
/* USER MUSIC MENU END */
|
||||
} MUSIC_t;
|
||||
|
||||
/**
|
||||
* @brief 蜂鸣器设备结构体
|
||||
* @details 蜂鸣器设备的完整描述,包含设备头信息和PWM通道
|
||||
*/
|
||||
typedef struct {
|
||||
DEVICE_Header_t header; ///< 设备通用头信息 (在线状态、时间戳等)
|
||||
BSP_PWM_Channel_t channel; ///< PWM输出通道
|
||||
} BUZZER_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化蜂鸣器设备
|
||||
* @param buzzer 蜂鸣器设备结构体指针
|
||||
* @param channel PWM输出通道
|
||||
* @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败
|
||||
* @note 初始化后蜂鸣器处于停止状态
|
||||
*/
|
||||
int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel);
|
||||
|
||||
/**
|
||||
* @brief 启动蜂鸣器播放
|
||||
* @param buzzer 蜂鸣器设备结构体指针
|
||||
* @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败
|
||||
* @note 需要先调用BUZZER_Set设置频率和占空比
|
||||
*/
|
||||
int8_t BUZZER_Start(BUZZER_t *buzzer);
|
||||
|
||||
/**
|
||||
* @brief 停止蜂鸣器播放
|
||||
* @param buzzer 蜂鸣器设备结构体指针
|
||||
* @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败
|
||||
*/
|
||||
int8_t BUZZER_Stop(BUZZER_t *buzzer);
|
||||
|
||||
/**
|
||||
* @brief 设置蜂鸣器频率和占空比
|
||||
* @param buzzer 蜂鸣器设备结构体指针
|
||||
* @param freq 频率 (Hz),通常范围20Hz-20kHz
|
||||
* @param duty_cycle 占空比 (0.0-1.0),影响音量大小
|
||||
* @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败
|
||||
* @note 设置后需要调用BUZZER_Start才能听到声音
|
||||
*/
|
||||
int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle);
|
||||
|
||||
/**
|
||||
* @brief 播放预设音乐
|
||||
* @param buzzer 蜂鸣器设备结构体指针
|
||||
* @param music 音乐类型 (使用MUSIC_t枚举)
|
||||
* @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败
|
||||
* @note 这是一个阻塞函数,会播放完整首音乐后返回
|
||||
*/
|
||||
int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
227
device/config.yaml
Normal file
227
device/config.yaml
Normal file
@ -0,0 +1,227 @@
|
||||
devices:
|
||||
dr16:
|
||||
name: "DR16"
|
||||
description: "大疆遥控器接收机"
|
||||
dependencies:
|
||||
bsp: ["uart"]
|
||||
component: ["user_math"]
|
||||
bsp_requirements:
|
||||
- type: "uart"
|
||||
var_name: "BSP_UART_DR16" # 需要替换的变量名
|
||||
description: "用于接收遥控器数据"
|
||||
thread_signals:
|
||||
- name: "SIGNAL_DR16_RAW_REDY"
|
||||
files:
|
||||
header: "dr16.h"
|
||||
source: "dr16.c"
|
||||
|
||||
ops9:
|
||||
name: "OPS9"
|
||||
description: "ACTION OPS9 码盘"
|
||||
dependencies:
|
||||
bsp: ["uart"]
|
||||
component: ["user_math"]
|
||||
bsp_requirements:
|
||||
- type: "uart"
|
||||
var_name: "BSP_UART_OPS9" # 需要替换的变量名
|
||||
description: "用于接收码盘"
|
||||
thread_signals:
|
||||
- name: "SIGNAL_OPS9_RAW_REDY"
|
||||
files:
|
||||
header: "ops9.h"
|
||||
source: "ops9.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"
|
||||
|
||||
ist8310:
|
||||
name: "IST8310"
|
||||
description: "IST8310 地磁传感器"
|
||||
dependencies:
|
||||
bsp: ["i2c", "gpio"]
|
||||
component: []
|
||||
bsp_requirements:
|
||||
- type: "i2c"
|
||||
var_name: "BSP_I2C_COMP"
|
||||
description: "用于与 IST8310 通信的 I2C 总线"
|
||||
- type: "gpio"
|
||||
var_name: "CMPS_RST_Pin"
|
||||
description: "IST8310 复位引脚"
|
||||
gpio_type: "output"
|
||||
- type: "gpio"
|
||||
var_name: "CMPS_INT_Pin"
|
||||
description: "IST8310 数据中断引脚"
|
||||
gpio_type: "EXTI"
|
||||
thread_signals:
|
||||
- name: "SIGNAL_IST8310_MAGN_RAW_REDY"
|
||||
- name: "SIGNAL_IST8310_MAGN_NEW_DATA"
|
||||
files:
|
||||
header: "ist8310.h"
|
||||
source: "ist8310.c"
|
||||
|
||||
motor_vesc:
|
||||
name: "VESC 电调"
|
||||
description: "VESC 电调驱动"
|
||||
dependencies:
|
||||
bsp: ["can", "time", "mm"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "motor_vesc.h"
|
||||
source: "motor_vesc.c"
|
||||
|
||||
motor_odrive:
|
||||
name: "ODrive 电机"
|
||||
description: "ODrive 电机驱动"
|
||||
dependencies:
|
||||
bsp: ["can", "time", "mm"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "motor_odrive.h"
|
||||
source: "motor_odrive.c"
|
||||
|
||||
motor_rm:
|
||||
name: "RM 电机"
|
||||
description: "RM 电机驱动"
|
||||
dependencies:
|
||||
bsp: ["can", "time", "mm"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "motor_rm.h"
|
||||
source: "motor_rm.c"
|
||||
|
||||
motor:
|
||||
name: "通用电机"
|
||||
description: "通用电机驱动"
|
||||
dependencies:
|
||||
bsp: []
|
||||
component: []
|
||||
bsp_requirements: []
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "motor.h"
|
||||
source: "motor.c"
|
||||
|
||||
ws2812:
|
||||
name: "WS2812 LED 灯"
|
||||
description: "WS2812 RGB LED 灯驱动"
|
||||
dependencies:
|
||||
bsp: ["pwm", "time"]
|
||||
component: []
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "ws2812.h"
|
||||
source: "ws2812.c"
|
||||
|
||||
buzzer:
|
||||
name: "蜂鸣器"
|
||||
description: "蜂鸣器驱动"
|
||||
dependencies:
|
||||
bsp: ["pwm"]
|
||||
component: []
|
||||
bsp_requirements:
|
||||
- type: "pwm"
|
||||
var_name: "BSP_PWM_BUZZER"
|
||||
description: "用于蜂鸣器的PWM通道"
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "buzzer.h"
|
||||
source: "buzzer.c"
|
||||
|
||||
dm_imu:
|
||||
name: "DM IMU"
|
||||
description: "DM IMU 传感器"
|
||||
dependencies:
|
||||
bsp: ["can", "time"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "dm_imu.h"
|
||||
source: "dm_imu.c"
|
||||
|
||||
led:
|
||||
name: "LED 灯"
|
||||
description: "LED 灯驱动"
|
||||
dependencies:
|
||||
bsp: ["gpio", "pwm"]
|
||||
component: []
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "led.h"
|
||||
source: "led.c"
|
||||
|
||||
motor_lk:
|
||||
name: "LK 电机"
|
||||
description: "LK 电机驱动"
|
||||
dependencies:
|
||||
bsp: ["can", "time", "mm"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "motor_lk.h"
|
||||
source: "motor_lk.c"
|
||||
|
||||
motor_lz:
|
||||
name: "LZ 电机"
|
||||
description: "LZ 电机驱动"
|
||||
dependencies:
|
||||
bsp: ["can", "time", "mm"]
|
||||
component: ["user_math"]
|
||||
files:
|
||||
header: "motor_lz.h"
|
||||
source: "motor_lz.c"
|
||||
|
||||
servo:
|
||||
name: "舵机"
|
||||
description: "舵机驱动"
|
||||
dependencies:
|
||||
bsp: ["pwm"]
|
||||
component: []
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "servo.h"
|
||||
source: "servo.c"
|
||||
|
||||
vofa:
|
||||
name: "VOFA"
|
||||
description: "VOFA 数据传输协议"
|
||||
dependencies:
|
||||
bsp: ["uart"]
|
||||
component: []
|
||||
bsp_requirements:
|
||||
- type: "uart"
|
||||
var_name: "BSP_UART_VOFA" # 需要替换的变量名
|
||||
description: "用于VOFA数据传输"
|
||||
thread_signals: []
|
||||
files:
|
||||
header: "vofa.h"
|
||||
source: "vofa.c"
|
||||
47
device/device.h
Normal file
47
device/device.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
#define DEVICE_OK (0)
|
||||
#define DEVICE_ERR (-1)
|
||||
#define DEVICE_ERR_NULL (-2)
|
||||
#define DEVICE_ERR_INITED (-3)
|
||||
#define DEVICE_ERR_NO_DEV (-4)
|
||||
|
||||
/* AUTO GENERATED SIGNALS BEGIN */
|
||||
|
||||
/* AUTO GENERATED SIGNALS END */
|
||||
|
||||
/* USER SIGNALS BEGIN */
|
||||
|
||||
/* USER SIGNALS END */
|
||||
/*设备层通用Header*/
|
||||
typedef struct {
|
||||
bool online;
|
||||
uint64_t last_online_time;
|
||||
} DEVICE_Header_t;
|
||||
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
271
device/dm_imu.c
Normal file
271
device/dm_imu.c
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
DM_IMU数据获取(CAN)
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "dm_imu.h"
|
||||
|
||||
#include "bsp/can.h"
|
||||
#include "bsp/time.h"
|
||||
#include "component/user_math.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define DM_IMU_OFFLINE_TIMEOUT 1000 // 设备离线判定时间1000ms
|
||||
|
||||
#define ACCEL_CAN_MAX (58.8f)
|
||||
#define ACCEL_CAN_MIN (-58.8f)
|
||||
#define GYRO_CAN_MAX (34.88f)
|
||||
#define GYRO_CAN_MIN (-34.88f)
|
||||
#define PITCH_CAN_MAX (90.0f)
|
||||
#define PITCH_CAN_MIN (-90.0f)
|
||||
#define ROLL_CAN_MAX (180.0f)
|
||||
#define ROLL_CAN_MIN (-180.0f)
|
||||
#define YAW_CAN_MAX (180.0f)
|
||||
#define YAW_CAN_MIN (-180.0f)
|
||||
#define TEMP_MIN (0.0f)
|
||||
#define TEMP_MAX (60.0f)
|
||||
#define Quaternion_MIN (-1.0f)
|
||||
#define Quaternion_MAX (1.0f)
|
||||
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
/* Private function --------------------------------------------------------- */
|
||||
|
||||
static uint8_t count = 0; // 计数器,用于判断设备是否离线
|
||||
/**
|
||||
* @brief: 无符号整数转换为浮点数函数
|
||||
*/
|
||||
static float uint_to_float(int x_int, float x_min, float x_max, int bits)
|
||||
{
|
||||
float span = x_max - x_min;
|
||||
float offset = x_min;
|
||||
return ((float)x_int)*span/((float)((1<<bits)-1)) + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析加速度计数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseAccelData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
int8_t temp = data[1];
|
||||
uint16_t acc_x_raw = (data[3] << 8) | data[2];
|
||||
uint16_t acc_y_raw = (data[5] << 8) | data[4];
|
||||
uint16_t acc_z_raw = (data[7] << 8) | data[6];
|
||||
imu->data.temp = (float)temp;
|
||||
imu->data.accl.x = uint_to_float(acc_x_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
imu->data.accl.y = uint_to_float(acc_y_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
imu->data.accl.z = uint_to_float(acc_z_raw, ACCEL_CAN_MIN, ACCEL_CAN_MAX, 16);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
/**
|
||||
* @brief 解析陀螺仪数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseGyroData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
uint16_t gyro_x_raw = (data[3] << 8) | data[2];
|
||||
uint16_t gyro_y_raw = (data[5] << 8) | data[4];
|
||||
uint16_t gyro_z_raw = (data[7] << 8) | data[6];
|
||||
imu->data.gyro.x = uint_to_float(gyro_x_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
imu->data.gyro.y = uint_to_float(gyro_y_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
imu->data.gyro.z = uint_to_float(gyro_z_raw, GYRO_CAN_MIN, GYRO_CAN_MAX, 16);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
/**
|
||||
* @brief 解析欧拉角数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseEulerData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
uint16_t pit_raw = (data[3] << 8) | data[2];
|
||||
uint16_t yaw_raw = (data[5] << 8) | data[4];
|
||||
uint16_t rol_raw = (data[7] << 8) | data[6];
|
||||
imu->data.euler.pit = uint_to_float(pit_raw, PITCH_CAN_MIN, PITCH_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
imu->data.euler.yaw = uint_to_float(yaw_raw, YAW_CAN_MIN, YAW_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
imu->data.euler.rol = uint_to_float(rol_raw, ROLL_CAN_MIN, ROLL_CAN_MAX, 16) * M_DEG2RAD_MULT;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解析四元数数据
|
||||
*/
|
||||
static int8_t DM_IMU_ParseQuaternionData(DM_IMU_t *imu, uint8_t *data, uint8_t len) {
|
||||
if (imu == NULL || data == NULL || len < 8) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
int w = (data[1] << 6) | ((data[2] & 0xF8) >> 2);
|
||||
int x = ((data[2] & 0x03) << 12) | (data[3] << 4) | ((data[4] & 0xF0) >> 4);
|
||||
int y = ((data[4] & 0x0F) << 10) | (data[5] << 2) | ((data[6] & 0xC0) >> 6);
|
||||
int z = ((data[6] & 0x3F) << 8) | data[7];
|
||||
imu->data.quat.q0 = uint_to_float(w, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q1 = uint_to_float(x, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q2 = uint_to_float(y, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
imu->data.quat.q3 = uint_to_float(z, Quaternion_MIN, Quaternion_MAX, 14);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化DM IMU设备
|
||||
*/
|
||||
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param) {
|
||||
if (imu == NULL || param == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
// 初始化设备头部
|
||||
imu->header.online = false;
|
||||
imu->header.last_online_time = 0;
|
||||
|
||||
// 配置参数
|
||||
imu->param.can = param->can;
|
||||
imu->param.can_id = param->can_id;
|
||||
imu->param.device_id = param->device_id;
|
||||
imu->param.master_id = param->master_id;
|
||||
|
||||
// 清零数据
|
||||
memset(&imu->data, 0, sizeof(DM_IMU_Data_t));
|
||||
|
||||
// 注册CAN接收队列,用于接收回复报文
|
||||
int8_t result = BSP_CAN_RegisterId(imu->param.can, imu->param.master_id, 10);
|
||||
if (result != BSP_OK) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 请求IMU数据
|
||||
*/
|
||||
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid) {
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
// 构造发送数据:id_L, id_H(DM_IMU_ID), RID, 0xcc
|
||||
uint8_t tx_data[4] = {
|
||||
imu->param.device_id & 0xFF, // id_L
|
||||
(imu->param.device_id >> 8) & 0xFF, // id_H
|
||||
(uint8_t)rid, // RID
|
||||
0xCC // 固定值
|
||||
};
|
||||
|
||||
// 发送标准数据帧
|
||||
BSP_CAN_StdDataFrame_t frame = {
|
||||
.id = imu->param.can_id,
|
||||
.dlc = 4,
|
||||
};
|
||||
memcpy(frame.data, tx_data, 4);
|
||||
int8_t result = BSP_CAN_TransmitStdDataFrame(imu->param.can, &frame);
|
||||
return (result == BSP_OK) ? DEVICE_OK : DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新IMU数据(从CAN中获取所有数据并解析)
|
||||
*/
|
||||
int8_t DM_IMU_Update(DM_IMU_t *imu) {
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
|
||||
BSP_CAN_Message_t msg;
|
||||
int8_t result;
|
||||
bool data_received = false;
|
||||
|
||||
// 持续接收所有可用消息
|
||||
while ((result = BSP_CAN_GetMessage(imu->param.can, imu->param.master_id, &msg, BSP_CAN_TIMEOUT_IMMEDIATE)) == BSP_OK) {
|
||||
// 验证回复数据格式(至少检查数据长度)
|
||||
if (msg.dlc < 3) {
|
||||
continue; // 跳过无效消息
|
||||
}
|
||||
|
||||
// 根据数据位的第0位确定反馈报文类型
|
||||
uint8_t rid = msg.data[0] & 0x0F; // 取第0位的低4位作为RID
|
||||
|
||||
// 根据RID类型解析数据
|
||||
int8_t parse_result = DEVICE_ERR;
|
||||
switch (rid) {
|
||||
case 0x01: // RID_ACCL
|
||||
parse_result = DM_IMU_ParseAccelData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x02: // RID_GYRO
|
||||
parse_result = DM_IMU_ParseGyroData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x03: // RID_EULER
|
||||
parse_result = DM_IMU_ParseEulerData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
case 0x04: // RID_QUATERNION
|
||||
parse_result = DM_IMU_ParseQuaternionData(imu, msg.data, msg.dlc);
|
||||
break;
|
||||
default:
|
||||
continue; // 跳过未知类型的消息
|
||||
}
|
||||
|
||||
// 如果解析成功,标记为收到数据
|
||||
if (parse_result == DEVICE_OK) {
|
||||
data_received = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果收到任何有效数据,更新设备状态
|
||||
if (data_received) {
|
||||
imu->header.online = true;
|
||||
imu->header.last_online_time = BSP_TIME_Get_ms();
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动更新IMU所有数据(包括加速度计、陀螺仪、欧拉角和四元数,最高1khz)
|
||||
*/
|
||||
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu){
|
||||
if (imu == NULL) {
|
||||
return DEVICE_ERR_NULL;
|
||||
}
|
||||
switch (count) {
|
||||
case 0:
|
||||
DM_IMU_Request(imu, RID_ACCL);
|
||||
break;
|
||||
case 1:
|
||||
DM_IMU_Request(imu, RID_GYRO);
|
||||
break;
|
||||
case 2:
|
||||
DM_IMU_Request(imu, RID_EULER);
|
||||
break;
|
||||
case 3:
|
||||
DM_IMU_Request(imu, RID_QUATERNION);
|
||||
DM_IMU_Update(imu); // 更新所有数据
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
if (count >= 4) {
|
||||
count = 0; // 重置计数器
|
||||
return DEVICE_OK;
|
||||
}
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查设备是否在线
|
||||
*/
|
||||
bool DM_IMU_IsOnline(DM_IMU_t *imu) {
|
||||
if (imu == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t current_time = BSP_TIME_Get_ms();
|
||||
return imu->header.online &&
|
||||
(current_time - imu->header.last_online_time < DM_IMU_OFFLINE_TIMEOUT);
|
||||
}
|
||||
90
device/dm_imu.h
Normal file
90
device/dm_imu.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "device/device.h"
|
||||
#include "component/ahrs.h"
|
||||
#include "bsp/can.h"
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
|
||||
#define DM_IMU_CAN_ID_DEFAULT 0x6FF
|
||||
#define DM_IMU_ID_DEFAULT 0x42
|
||||
#define DM_IMU_MST_ID_DEFAULT 0x43
|
||||
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
BSP_CAN_t can; // CAN总线句柄
|
||||
uint16_t can_id; // CAN通信ID
|
||||
uint8_t device_id; // 设备ID
|
||||
uint8_t master_id; // 主机ID
|
||||
} DM_IMU_Param_t;
|
||||
typedef enum {
|
||||
RID_ACCL = 0x01, // 加速度计
|
||||
RID_GYRO = 0x02, // 陀螺仪
|
||||
RID_EULER = 0x03, // 欧拉角
|
||||
RID_QUATERNION = 0x04, // 四元数
|
||||
} DM_IMU_RID_t;
|
||||
|
||||
typedef struct {
|
||||
AHRS_Accl_t accl; // 加速度计
|
||||
AHRS_Gyro_t gyro; // 陀螺仪
|
||||
AHRS_Eulr_t euler; // 欧拉角
|
||||
AHRS_Quaternion_t quat; // 四元数
|
||||
float temp; // 温度
|
||||
} DM_IMU_Data_t;
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
DM_IMU_Param_t param; // IMU参数配置
|
||||
DM_IMU_Data_t data; // IMU数据
|
||||
} DM_IMU_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief 初始化DM IMU设备
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @param param IMU参数配置指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Init(DM_IMU_t *imu, DM_IMU_Param_t *param);
|
||||
|
||||
/**
|
||||
* @brief 请求IMU数据
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @param rid 请求的数据类型
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Request(DM_IMU_t *imu, DM_IMU_RID_t rid);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 更新IMU数据(从CAN中获取所有数据并解析)
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_Update(DM_IMU_t *imu);
|
||||
|
||||
/**
|
||||
* @brief 自动更新IMU所有数据(包括加速度计、陀螺仪、欧拉角和四元数,最高1khz,运行4次才有完整数据)
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return DEVICE_OK 成功,其他值失败
|
||||
*/
|
||||
int8_t DM_IMU_AutoUpdateAll(DM_IMU_t *imu);
|
||||
|
||||
/**
|
||||
* @brief 检查设备是否在线
|
||||
* @param imu DM IMU设备结构体指针
|
||||
* @return true 在线,false 离线
|
||||
*/
|
||||
bool DM_IMU_IsOnline(DM_IMU_t *imu);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
169
device/dr16.c
Normal file
169
device/dr16.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
DR16接收机
|
||||
Example:
|
||||
|
||||
DR16_Init(&dr16);
|
||||
|
||||
while (1) {
|
||||
DR16_StartDmaRecv(&dr16);
|
||||
if (DR16_WaitDmaCplt(20)) {
|
||||
DR16_ParseData(&dr16);
|
||||
} else {
|
||||
DR16_Offline(&dr16);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "dr16.h"
|
||||
#include "bsp/uart.h"
|
||||
#include "bsp/time.h"
|
||||
#include "device.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define DR16_CH_VALUE_MIN (364u)
|
||||
#define DR16_CH_VALUE_MID (1024u)
|
||||
#define DR16_CH_VALUE_MAX (1684u)
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
|
||||
static osThreadId_t thread_alert;
|
||||
static bool inited = false;
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
static void DR16_RxCpltCallback(void) {
|
||||
osThreadFlagsSet(thread_alert, SIGNAL_DR16_RAW_REDY);
|
||||
}
|
||||
|
||||
static bool DR16_DataCorrupted(const DR16_t *dr16) {
|
||||
if (dr16 == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
if ((dr16->raw_data.ch_r_x < DR16_CH_VALUE_MIN) ||
|
||||
(dr16->raw_data.ch_r_x > DR16_CH_VALUE_MAX))
|
||||
return DEVICE_ERR;
|
||||
|
||||
if ((dr16->raw_data.ch_r_y < DR16_CH_VALUE_MIN) ||
|
||||
(dr16->raw_data.ch_r_y > DR16_CH_VALUE_MAX))
|
||||
return DEVICE_ERR;
|
||||
|
||||
if ((dr16->raw_data.ch_l_x < DR16_CH_VALUE_MIN) ||
|
||||
(dr16->raw_data.ch_l_x > DR16_CH_VALUE_MAX))
|
||||
return DEVICE_ERR;
|
||||
|
||||
if ((dr16->raw_data.ch_l_y < DR16_CH_VALUE_MIN) ||
|
||||
(dr16->raw_data.ch_l_y > DR16_CH_VALUE_MAX))
|
||||
return DEVICE_ERR;
|
||||
|
||||
if (dr16->raw_data.sw_l == 0) return DEVICE_ERR;
|
||||
|
||||
if (dr16->raw_data.sw_r == 0) return DEVICE_ERR;
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
int8_t DR16_Init(DR16_t *dr16) {
|
||||
if (dr16 == NULL) return DEVICE_ERR_NULL;
|
||||
if (inited) return DEVICE_ERR_INITED;
|
||||
if ((thread_alert = osThreadGetId()) == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
BSP_UART_RegisterCallback(BSP_UART_DR16, BSP_UART_RX_CPLT_CB,
|
||||
DR16_RxCpltCallback);
|
||||
|
||||
inited = true;
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t DR16_Restart(void) {
|
||||
__HAL_UART_DISABLE(BSP_UART_GetHandle(BSP_UART_DR16));
|
||||
__HAL_UART_ENABLE(BSP_UART_GetHandle(BSP_UART_DR16));
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t DR16_StartDmaRecv(DR16_t *dr16) {
|
||||
if (HAL_UART_Receive_DMA(BSP_UART_GetHandle(BSP_UART_DR16),
|
||||
(uint8_t *)&(dr16->raw_data),
|
||||
sizeof(dr16->raw_data)) == HAL_OK)
|
||||
return DEVICE_OK;
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
bool DR16_WaitDmaCplt(uint32_t timeout) {
|
||||
return (osThreadFlagsWait(SIGNAL_DR16_RAW_REDY, osFlagsWaitAll, timeout) ==
|
||||
SIGNAL_DR16_RAW_REDY);
|
||||
}
|
||||
|
||||
int8_t DR16_ParseData(DR16_t *dr16){
|
||||
if (dr16 == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
if (DR16_DataCorrupted(dr16)) {
|
||||
return DEVICE_ERR;
|
||||
}
|
||||
|
||||
dr16->header.online = true;
|
||||
dr16->header.last_online_time = BSP_TIME_Get_us();
|
||||
|
||||
memset(&(dr16->data), 0, sizeof(dr16->data));
|
||||
|
||||
float full_range = (float)(DR16_CH_VALUE_MAX - DR16_CH_VALUE_MIN);
|
||||
|
||||
// 解析摇杆数据
|
||||
dr16->data.ch_r_x = 2.0f * ((float)dr16->raw_data.ch_r_x - DR16_CH_VALUE_MID) / full_range;
|
||||
dr16->data.ch_r_y = 2.0f * ((float)dr16->raw_data.ch_r_y - DR16_CH_VALUE_MID) / full_range;
|
||||
dr16->data.ch_l_x = 2.0f * ((float)dr16->raw_data.ch_l_x - DR16_CH_VALUE_MID) / full_range;
|
||||
dr16->data.ch_l_y = 2.0f * ((float)dr16->raw_data.ch_l_y - DR16_CH_VALUE_MID) / full_range;
|
||||
|
||||
// 解析拨杆位置
|
||||
dr16->data.sw_l = (DR16_SwitchPos_t)dr16->raw_data.sw_l;
|
||||
dr16->data.sw_r = (DR16_SwitchPos_t)dr16->raw_data.sw_r;
|
||||
|
||||
// 解析鼠标数据
|
||||
dr16->data.mouse.x = dr16->raw_data.x;
|
||||
dr16->data.mouse.y = dr16->raw_data.y;
|
||||
dr16->data.mouse.z = dr16->raw_data.z;
|
||||
|
||||
dr16->data.mouse.l_click = dr16->raw_data.press_l;
|
||||
dr16->data.mouse.r_click = dr16->raw_data.press_r;
|
||||
|
||||
// 解析键盘按键 - 使用union简化代码
|
||||
uint16_t key_value = dr16->raw_data.key;
|
||||
|
||||
// 解析键盘位映射(W-B键,位0-15)
|
||||
for (int i = DR16_KEY_W; i <= DR16_KEY_B; i++) {
|
||||
dr16->data.keyboard.key[i] = (key_value & (1 << i)) != 0;
|
||||
}
|
||||
|
||||
// 解析鼠标点击
|
||||
dr16->data.keyboard.key[DR16_L_CLICK] = dr16->data.mouse.l_click;
|
||||
dr16->data.keyboard.key[DR16_R_CLICK] = dr16->data.mouse.r_click;
|
||||
|
||||
// 解析第五通道
|
||||
dr16->data.ch_res = 2.0f * ((float)dr16->raw_data.res - DR16_CH_VALUE_MID) / full_range;
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
int8_t DR16_Offline(DR16_t *dr16){
|
||||
if (dr16 == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
dr16->header.online = false;
|
||||
memset(&(dr16->data), 0, sizeof(dr16->data));
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
117
device/dr16.h
Normal file
117
device/dr16.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <cmsis_os2.h>
|
||||
|
||||
#include "component/user_math.h"
|
||||
#include "device.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Exported constants ------------------------------------------------------- */
|
||||
/* Exported macro ----------------------------------------------------------- */
|
||||
/* Exported types ----------------------------------------------------------- */
|
||||
typedef struct __packed {
|
||||
uint16_t ch_r_x : 11;
|
||||
uint16_t ch_r_y : 11;
|
||||
uint16_t ch_l_x : 11;
|
||||
uint16_t ch_l_y : 11;
|
||||
uint8_t sw_r : 2;
|
||||
uint8_t sw_l : 2;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
uint8_t press_l;
|
||||
uint8_t press_r;
|
||||
uint16_t key;
|
||||
uint16_t res;
|
||||
} DR16_RawData_t;
|
||||
|
||||
typedef enum {
|
||||
DR16_SW_ERR = 0,
|
||||
DR16_SW_UP = 1,
|
||||
DR16_SW_MID = 3,
|
||||
DR16_SW_DOWN = 2,
|
||||
} DR16_SwitchPos_t;
|
||||
|
||||
/* 键盘按键值 */
|
||||
typedef enum {
|
||||
DR16_KEY_W = 0,
|
||||
DR16_KEY_S,
|
||||
DR16_KEY_A,
|
||||
DR16_KEY_D,
|
||||
DR16_KEY_SHIFT,
|
||||
DR16_KEY_CTRL,
|
||||
DR16_KEY_Q,
|
||||
DR16_KEY_E,
|
||||
DR16_KEY_R,
|
||||
DR16_KEY_F,
|
||||
DR16_KEY_G,
|
||||
DR16_KEY_Z,
|
||||
DR16_KEY_X,
|
||||
DR16_KEY_C,
|
||||
DR16_KEY_V,
|
||||
DR16_KEY_B,
|
||||
DR16_L_CLICK,
|
||||
DR16_R_CLICK,
|
||||
DR16_KEY_NUM,
|
||||
} DR16_Key_t;
|
||||
|
||||
typedef struct {
|
||||
float ch_l_x; /* 遥控器左侧摇杆横轴值,上为正 */
|
||||
float ch_l_y; /* 遥控器左侧摇杆纵轴值,右为正 */
|
||||
float ch_r_x; /* 遥控器右侧摇杆横轴值,上为正 */
|
||||
float ch_r_y; /* 遥控器右侧摇杆纵轴值,右为正 */
|
||||
|
||||
float ch_res; /* 第五通道值 */
|
||||
|
||||
DR16_SwitchPos_t sw_r; /* 右侧拨杆位置 */
|
||||
DR16_SwitchPos_t sw_l; /* 左侧拨杆位置 */
|
||||
|
||||
struct {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
bool l_click; /* 左键 */
|
||||
bool r_click; /* 右键 */
|
||||
} mouse; /* 鼠标值 */
|
||||
|
||||
union {
|
||||
bool key[DR16_KEY_NUM]; /* 键盘按键值 */
|
||||
uint16_t value; /* 键盘按键值的位映射 */
|
||||
} keyboard;
|
||||
|
||||
uint16_t res; /* 保留,未启用 */
|
||||
} DR16_Data_t;
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
DR16_RawData_t raw_data;
|
||||
DR16_Data_t data;
|
||||
} DR16_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
int8_t DR16_Init(DR16_t *dr16);
|
||||
int8_t DR16_Restart(void);
|
||||
int8_t DR16_StartDmaRecv(DR16_t *dr16);
|
||||
bool DR16_WaitDmaCplt(uint32_t timeout);
|
||||
int8_t DR16_ParseData(DR16_t *dr16);
|
||||
int8_t DR16_Offline(DR16_t *dr16);
|
||||
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
178
device/ist8310.c
Normal file
178
device/ist8310.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
IST8310 地磁传感器。
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include "ist8310.h"
|
||||
|
||||
#include <gpio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp/time.h"
|
||||
#include "bsp/gpio.h"
|
||||
#include "bsp/i2c.h"
|
||||
|
||||
/* USER INCLUDE BEGIN */
|
||||
|
||||
/* USER INCLUDE END */
|
||||
|
||||
/* Private define ----------------------------------------------------------- */
|
||||
#define IST8310_WAI (0x00)
|
||||
#define IST8310_STAT1 (0x02)
|
||||
#define IST8310_DATAXL (0x03)
|
||||
#define IST8310_STAT2 (0x09)
|
||||
#define IST8310_CNTL1 (0x0A)
|
||||
#define IST8310_CNTL2 (0x0B)
|
||||
#define IST8310_STR (0x0C)
|
||||
#define IST8310_TEMPL (0x1C)
|
||||
#define IST8310_TEMPH (0x1D)
|
||||
#define IST8310_AVGCNTL (0x41)
|
||||
#define IST8310_PDCNTL (0x42)
|
||||
|
||||
#define IST8310_CHIP_ID (0x10)
|
||||
|
||||
#define IST8310_IIC_ADDRESS (0x0E << 1)
|
||||
|
||||
#define IST8310_LEN_RX_BUFF (6)
|
||||
|
||||
/* USER DEFINE BEGIN */
|
||||
|
||||
/* USER DEFINE END */
|
||||
|
||||
/* Private macro ------------------------------------------------------------ */
|
||||
#define IST8310_SET() \
|
||||
BSP_GPIO_WritePin(CMPS_RST_Pin, GPIO_PIN_SET)
|
||||
#define IST8310_RESET() \
|
||||
BSP_GPIO_WritePin(CMPS_RST_Pin, GPIO_PIN_RESET)
|
||||
|
||||
/* Private typedef ---------------------------------------------------------- */
|
||||
/* USER STRUCT BEGIN */
|
||||
|
||||
/* USER STRUCT END */
|
||||
|
||||
/* Private variables -------------------------------------------------------- */
|
||||
uint8_t ist8310_rxbuf[IST8310_LEN_RX_BUFF];
|
||||
|
||||
static osThreadId_t thread_alert;
|
||||
static bool inited = false;
|
||||
|
||||
/* Private function -------------------------------------------------------- */
|
||||
/* USER FUNCTION BEGIN */
|
||||
|
||||
/* USER FUNCTION END */
|
||||
|
||||
static void IST8310_WriteSingle(uint8_t reg, uint8_t data) {
|
||||
BSP_I2C_MemWriteByte(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg, data);
|
||||
}
|
||||
|
||||
static uint8_t IST8310_ReadSingle(uint8_t reg) {
|
||||
return BSP_I2C_MemReadByte(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg);
|
||||
}
|
||||
|
||||
|
||||
static void IST8310_Read(uint8_t reg, uint8_t *data, uint8_t len) {
|
||||
if (data == NULL) return;
|
||||
BSP_I2C_MemRead(BSP_I2C_COMP, IST8310_IIC_ADDRESS, reg, data, len, true);
|
||||
}
|
||||
|
||||
static void IST8310_MemRxCpltCallback(void) {
|
||||
osThreadFlagsSet(thread_alert, SIGNAL_IST8310_MAGN_RAW_REDY);
|
||||
}
|
||||
|
||||
static void IST8310_IntCallback(void) {
|
||||
osThreadFlagsSet(thread_alert, SIGNAL_IST8310_MAGN_NEW_DATA);
|
||||
}
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
int8_t IST8310_Init(IST8310_t *ist8310, const IST8310_Cali_t *cali) {
|
||||
if (ist8310 == 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;
|
||||
|
||||
ist8310->cali = cali;
|
||||
|
||||
IST8310_RESET();
|
||||
BSP_TIME_Delay(50);
|
||||
IST8310_SET();
|
||||
BSP_TIME_Delay(50);
|
||||
|
||||
if (IST8310_ReadSingle(IST8310_WAI) != IST8310_CHIP_ID)
|
||||
return DEVICE_ERR_NO_DEV;
|
||||
|
||||
BSP_GPIO_DisableIRQ(CMPS_INT_Pin);
|
||||
|
||||
BSP_I2C_RegisterCallback(BSP_I2C_COMP, HAL_I2C_MEM_RX_CPLT_CB,
|
||||
IST8310_MemRxCpltCallback);
|
||||
BSP_GPIO_RegisterCallback(CMPS_INT_Pin, IST8310_IntCallback);
|
||||
|
||||
/* Init. */
|
||||
/* 0x00: Stand-By mode. 0x01: Single measurement mode. */
|
||||
|
||||
/* 0x08: Data ready function enable. DRDY signal active low*/
|
||||
IST8310_WriteSingle(IST8310_CNTL2, 0x08);
|
||||
|
||||
IST8310_WriteSingle(IST8310_AVGCNTL, 0x09);
|
||||
IST8310_WriteSingle(IST8310_PDCNTL, 0xC0);
|
||||
IST8310_WriteSingle(IST8310_CNTL1, 0x0B);
|
||||
BSP_TIME_Delay(10);
|
||||
|
||||
inited = true;
|
||||
|
||||
BSP_GPIO_EnableIRQ(CMPS_INT_Pin);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
bool IST8310_WaitNew(uint32_t timeout) {
|
||||
return (osThreadFlagsWait(SIGNAL_IST8310_MAGN_NEW_DATA, osFlagsWaitAll,
|
||||
timeout) == SIGNAL_IST8310_MAGN_NEW_DATA);
|
||||
}
|
||||
|
||||
int8_t IST8310_StartDmaRecv() {
|
||||
IST8310_Read(IST8310_DATAXL, ist8310_rxbuf, IST8310_LEN_RX_BUFF);
|
||||
return DEVICE_OK;
|
||||
}
|
||||
|
||||
uint32_t IST8310_WaitDmaCplt() {
|
||||
return osThreadFlagsWait(SIGNAL_IST8310_MAGN_RAW_REDY, osFlagsWaitAll,
|
||||
osWaitForever);
|
||||
}
|
||||
|
||||
int8_t IST8310_Parse(IST8310_t *ist8310) {
|
||||
if (ist8310 == NULL) return DEVICE_ERR_NULL;
|
||||
|
||||
#if 1
|
||||
/* Magn -> T */
|
||||
int16_t raw_x, raw_y, raw_z;
|
||||
memcpy(&raw_x, ist8310_rxbuf + 0, sizeof(raw_x));
|
||||
memcpy(&raw_y, ist8310_rxbuf + 2, sizeof(raw_y));
|
||||
memcpy(&raw_z, ist8310_rxbuf + 4, sizeof(raw_z));
|
||||
|
||||
ist8310->magn.x = (float)raw_x;
|
||||
ist8310->magn.y = (float)raw_y;
|
||||
ist8310->magn.z = (float)-raw_z;
|
||||
|
||||
#else
|
||||
const int16_t *raw_x = (int16_t *)(ist8310_rxbuf + 0);
|
||||
const int16_t *raw_y = (int16_t *)(ist8310_rxbuf + 2);
|
||||
const int16_t *raw_z = (int16_t *)(ist8310_rxbuf + 4);
|
||||
|
||||
ist8310->magn.x = (float)*raw_x;
|
||||
ist8310->magn.y = (float)*raw_y;
|
||||
ist8310->magn.z = -(float)*raw_z;
|
||||
#endif
|
||||
|
||||
ist8310->magn.x *= 3.0f / 20.0f;
|
||||
ist8310->magn.y *= 3.0f / 20.0f;
|
||||
ist8310->magn.z *= 3.0f / 20.0f;
|
||||
|
||||
ist8310->magn.x = (ist8310->magn.x - ist8310->cali->magn_offset.x) *
|
||||
ist8310->cali->magn_scale.x;
|
||||
ist8310->magn.y = (ist8310->magn.y - ist8310->cali->magn_offset.y) *
|
||||
ist8310->cali->magn_scale.y;
|
||||
ist8310->magn.z = (ist8310->magn.z - ist8310->cali->magn_offset.y) *
|
||||
ist8310->cali->magn_scale.z;
|
||||
|
||||
return DEVICE_OK;
|
||||
}
|
||||
49
device/ist8310.h
Normal file
49
device/ist8310.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ----------------------------------------------------------------- */
|
||||
#include <cmsis_os2.h>
|
||||
#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;
|
||||
} magn_offset; /* 磁力计偏置 */
|
||||
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} magn_scale; /* 磁力计缩放 */
|
||||
} IST8310_Cali_t; /* IST8310校准数据 */
|
||||
|
||||
typedef struct {
|
||||
DEVICE_Header_t header;
|
||||
AHRS_Magn_t magn;
|
||||
const IST8310_Cali_t *cali;
|
||||
} IST8310_t;
|
||||
|
||||
/* Exported functions prototypes -------------------------------------------- */
|
||||
int8_t IST8310_Init(IST8310_t *ist8310, const IST8310_Cali_t *cali);
|
||||
int8_t IST8310_Restart(void);
|
||||
|
||||
bool IST8310_WaitNew(uint32_t timeout);
|
||||
int8_t IST8310_StartDmaRecv();
|
||||
uint32_t IST8310_WaitDmaCplt();
|
||||
int8_t IST8310_Parse(IST8310_t *ist8310);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user