入了一个realforce

作者分享了从购买二手HHKB键盘到转向Realforce87U电容键盘的经历,详细对比了两种键盘的使用感受,特别是在长时间打字的舒适度上,Realforce表现更佳。

两年前我买了个hhkb。那时候我还是一个屌丝<span style="text-decoration:line-through">(虽然说现在也是)</span>,酝酿了两年最后才买了个二手的。 不过这货确实很好用,这两年里面我每天都在用这货在vim,emacs上写代码。虽然折腾,但是很开心。

不知道什么时候开始,我又重新回到了IDE的怀抱,也许是我也开始变懒了吧。于是就把hhkb买了,从日本亚马逊买了个Realforce 87U,转运公司用的是jshopper。从下单到最后收到货一共花了1周的时间,最后没想到被杭州海关税。

###使用体验

感觉也就那样,电容键盘的手感。对我来说是机械键盘没法比的。如果一定要我描述区别,对我来说就是我用机械键盘(plum茶轴)打字久了手会痛,用的是电容的话就不会痛。然后realforce的键帽材质非常好,hhkb用了两年之后其他人看了之后觉得不止9新。

###图片

转载于:https://my.oschina.net/u/236164/blog/297887

#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QMessageBox> #include <QDateTime> #include <QTimer> #include <QTextStream> #include <QApplication> #include <QScreen> #include <QInputDialog> #include <QTextCodec> #include <QProgressDialog> #include <QToolBar> #include <QThread> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) // 初始化父类QMainWindow , ui(new Ui::MainWindow) // 初始化UI对象 , m_serial(new SerialCommunication(this)) // 创建串口通信实例 , m_serialDialog(new SerialSettingDialog(m_serial, this)) // 创建串口设置对话框 { ui->setupUi(this);//初始化UI // 启用双缓冲绘制,减少闪烁 setAttribute(Qt::WA_NoSystemBackground, true); // 禁用系统背景绘制 setAttribute(Qt::WA_OpaquePaintEvent, true); // 确保完全覆盖背景,避免透底 setAttribute(Qt::WA_PaintOnScreen, false); // 使用Qt的双缓冲机制 // 取消固定比例,设置合理最小尺寸 //setMinimumSize(1024, 768); // 基于内容需求的最小尺寸 setMinimumSize(1600, 900); // 基于内容需求的最小尺寸 initUI(); // 初始化界面布局 m_modbus = new ModBus(m_serial); initMotorUnits(); // 初始化99个电机控制单元 updateStatusBar(); // 初始化状态栏 // 连接反馈信号 connect(m_modbus, &ModBus::motorFeedbackReceived, this, &MainWindow::onMotorFeedbackReceived); // 定时刷新双缓冲(20Hz频率,减少闪烁) QTimer *bufferTimer = new QTimer(this); connect(bufferTimer, &QTimer::timeout, this, [this](){ updateBuffer(); // 自定义缓冲更新逻辑 update(); // 触发界面重绘 }); bufferTimer->start(50); // 50ms触发一次(20Hz) // 连接串口数据接收信号(串口收到数据时,调用handleSerialData处理) connect(m_serial, &SerialCommunication::dataReceived, this, &MainWindow::handleSerialData); // 连接"关于"菜单动作(点击时显示关于对话框) connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::onAboutClicked); // 定时更新状态栏(1秒一次) QTimer *timeTimer = new QTimer(this); connect(timeTimer, &QTimer::timeout, this, &MainWindow::updateStatusBar); timeTimer->start(1000); } MainWindow::~MainWindow() { delete ui; delete m_modbus; delete m_serial; } void MainWindow::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); // 禁用所有子控件更新 setUpdatesEnabled(false); // 调整分页布局,不限制比例 adjustTabLayout(qobject_cast<QGridLayout*>(ui->tab1_20->layout())); adjustTabLayout(qobject_cast<QGridLayout*>(ui->tab21_40->layout())); adjustTabLayout(qobject_cast<QGridLayout*>(ui->tab41_60->layout())); adjustTabLayout(qobject_cast<QGridLayout*>(ui->tab61_80->layout())); adjustTabLayout(qobject_cast<QGridLayout*>(ui->tab81_100->layout())); // 启用更新并触发一次重绘 setUpdatesEnabled(true); updateBuffer(); // 刷新缓冲 } // 初始化界面(绑定分页控件、状态栏) void MainWindow::initUI() { // 深色主题全局样式表(增强对比度与专业性) setStyleSheet(R"( QMainWindow { background-color: #1A1A1A; color: #E6E6E6; font-family: "Segoe UI", "Microsoft YaHei", sans-serif; font-size: 10pt; /* 移除边框,避免拉伸时闪烁 */ } QWidget { background-color: #1A1A1A; color: #E6E6E6; font-family: "Segoe UI", "Microsoft YaHei", sans-serif; font-size: 10pt; border: 1px solid #333333; border-radius: 6px; } /* 分页控件样式 */ QTabWidget::pane { border: 1px solid #333333; background-color: #222222; border-radius: 6px; margin: 5px; } QTabBar::tab { background-color: #2A2A2A; color: #CCCCCC; padding: 8px 16px; border: 1px solid #444444; border-bottom-color: transparent; border-radius: 4px 4px 0 0; min-width: 90px; } QTabBar::tab:selected { background-color: #222222; color: #FFFFFF; border-color: #555555; border-bottom-color: #222222; font-weight: bold; } QTabBar::tab:hover:!selected { background-color: #333333; } /* 按钮样式(强化交互反馈) */ QPushButton { background-color: #3D5A80; color: #FFFFFF; border: none; border-radius: 3px; padding: 6px 12px; min-height: 28px; } QPushButton:hover { background-color: #4A6FA5; border: 1px solid #5C85B8; } QPushButton:pressed { background-color: #2A4365; } QPushButton:disabled { background-color: #333333; color: #888888; } /* 状态栏样式 */ QStatusBar { background-color: #1A1A1A; color: #BBBBBB; border-top: 1px solid #333333; padding: 2px 8px; } /* 菜单与工具栏 */ QMenuBar { background-color: #222222; color: #CCCCCC; border-bottom: 1px solid #333333; } QMenuBar::item:selected { background-color: #333333; color: #FFFFFF; } QToolBar { background-color: #222222; border-bottom: 1px solid #333333; spacing: 4px; } )"); // 添加快速定位区域(放在工具栏) QToolBar *navToolBar = new QToolBar("导航", this); navToolBar->setStyleSheet("QToolBar { border-bottom: 1px solid #333333; }"); addToolBar(navToolBar); QLabel *jumpLabel = new QLabel("跳转至电机: "); QLineEdit *jumpInput = new QLineEdit(); jumpInput->setValidator(new QIntValidator(1, 100, this)); jumpInput->setMaximumWidth(60); QPushButton *jumpBtn = new QPushButton("跳转"); navToolBar->addWidget(jumpLabel); navToolBar->addWidget(jumpInput); navToolBar->addWidget(jumpBtn); // 绑定跳转逻辑 connect(jumpBtn, &QPushButton::clicked, this, [=]() { int motorNum = jumpInput->text().toInt(); if (motorNum < 1 || motorNum > 100) return; // 计算目标分页(1-20→0,21-40→1,以此类推) int tabIndex = (motorNum - 1) / 20; m_tabWidget->setCurrentIndex(tabIndex); jumpInput->clear(); }); // 绑定UI中的分页控件 m_tabWidget = ui->tabWidget; m_statusBar = ui->statusBar; // 为每个分页签直接创建QGridLayout(避免多层嵌套) // 为每个分页签创建QGridLayout QLayout* layout = nullptr; if((layout = ui->tab1_20->layout()) != nullptr) delete layout; ui->tab1_20->setLayout(new QGridLayout()); if((layout = ui->tab21_40->layout()) != nullptr) delete layout; ui->tab21_40->setLayout(new QGridLayout()); if((layout = ui->tab41_60->layout()) != nullptr) delete layout; ui->tab41_60->setLayout(new QGridLayout()); if((layout = ui->tab61_80->layout()) != nullptr) delete layout; ui->tab61_80->setLayout(new QGridLayout()); if((layout = ui->tab81_100->layout()) != nullptr) delete layout; ui->tab81_100->setLayout(new QGridLayout()); // 5个标签页布局 QList<QGridLayout*> layouts = { qobject_cast<QGridLayout*>(ui->tab1_20->layout()), qobject_cast<QGridLayout*>(ui->tab21_40->layout()), qobject_cast<QGridLayout*>(ui->tab41_60->layout()), qobject_cast<QGridLayout*>(ui->tab61_80->layout()), qobject_cast<QGridLayout*>(ui->tab81_100->layout()) }; // 统一配置分页布局 for (QGridLayout *layout : layouts) { if (layout) { layout->setSpacing(8); layout->setContentsMargins(10, 10, 10, 10); adjustTabLayout(layout); // 配置行列拉伸 } } } // 辅助函数:配置分页布局的行列拉伸(5列5行,均匀分布25个电机) //void MainWindow::adjustTabLayout(QGridLayout *layout) //{ //// if (!layout) return; //// // 5列布局,加大最小宽度阈值 //// const int MIN_COL_WIDTH = 150; // 从150调整为200,减少频繁重排 //// const int MIN_ROW_HEIGHT = 120; // 从120调整为150,减少频繁重排 // if (!layout) return; // // 根据屏幕分辨率动态计算最小宽高(示例:屏幕宽度的1/8,高度的1/10) // QScreen *screen = QApplication::primaryScreen(); // int screenWidth = screen->geometry().width(); // int screenHeight = screen->geometry().height(); // const int MIN_COL_WIDTH = qMax(150, screenWidth / 8); // 最小150px,适配小屏 // const int MIN_ROW_HEIGHT = qMax(120, screenHeight /10); // // 5列:每列拉伸因子相同(均匀分配宽度) // for (int col = 0; col < 5; col++) { // layout->setColumnMinimumWidth(col, MIN_COL_WIDTH); // layout->setColumnStretch(col, 1); // 保持均匀拉伸 // } // // 4行:每行列拉伸因子相同(均匀分配高度) // int rowCount = layout->rowCount(); // for (int row = 0; row < qMax(4, rowCount); row++) { // 至少5行(25个电机5x5) // layout->setRowMinimumHeight(row, MIN_ROW_HEIGHT); // layout->setRowStretch(row, 1); // } //} //void MainWindow::adjustTabLayout(QGridLayout *layout) { // if (!layout) return; // // 基于当前窗口尺寸计算,而非屏幕尺寸 // int windowWidth = this->width(); // int windowHeight = this->height(); // // 5列布局,每列宽度随窗口自适应(保留边距) // const int margin = 20; // 窗口边缘留白 // const int colCount = 5; // int availableWidth = windowWidth - margin * 2; // int colWidth = qMax(180, availableWidth / colCount); // 最小180px避免拥挤 // // 4行布局,每行高度基于内容自适应 // const int rowCount = 4; // int availableHeight = windowHeight - 200; // 扣除标题栏、状态栏等高度 // int rowHeight = qMax(160, availableHeight / rowCount); // 最小160px保证控件完整 // // 设置列属性 // for (int col = 0; col < colCount; col++) { // layout->setColumnMinimumWidth(col, colWidth); // layout->setColumnStretch(col, 1); // 均等拉伸 // } // // 设置行属性 // for (int row = 0; row < rowCount; row++) { // layout->setRowMinimumHeight(row, rowHeight); // layout->setRowStretch(row, 1); // 均等拉伸 // } // // 统一网格布局边距 // layout->setContentsMargins(margin, margin, margin, margin); //} // 修改 mainwindow.cpp 中 adjustTabLayout() 函数 void MainWindow::adjustTabLayout(QGridLayout *layout) { if (!layout) return; // 基于当前分页控件尺寸计算(而非整个窗口) QWidget *tabWidget = layout->parentWidget(); if (!tabWidget) return; int tabWidth = tabWidget->width(); int tabHeight = tabWidget->height(); // 动态计算边距(随窗口大小变化) const int margin = qMax(10, tabWidth / 50); // 最小10px,最大窗口1/50 const int colCount = 5; const int rowCount = 4; // 可用空间 = 总尺寸 - 边距*2(减去两侧边距) int availableWidth = tabWidth - margin * 2; int availableHeight = tabHeight - margin * 2; // 列宽:确保最小宽度(180px),同时不超过可用空间的1/colCount int colWidth = qMin(availableWidth / colCount, 250); // 最大250px避免过大 colWidth = qMax(colWidth, 180); // 行高:类似列宽计算逻辑 int rowHeight = qMin(availableHeight / rowCount, 200); // 最大200px rowHeight = qMax(rowHeight, 160); // 设置行列属性 for (int col = 0; col < colCount; col++) { layout->setColumnMinimumWidth(col, colWidth); layout->setColumnStretch(col, 1); } for (int row = 0; row < rowCount; row++) { layout->setRowMinimumHeight(row, rowHeight); layout->setRowStretch(row, 1); } layout->setContentsMargins(margin, margin, margin, margin); } // 初始化99个电机控制单元(按5列5行分配到4个分页) void MainWindow::initMotorUnits() { // 分页布局列表(对应5个分页标签页) QList<QGridLayout*> layouts = { qobject_cast<QGridLayout*>(ui->tab1_20->layout()), // 第1-20号电机 qobject_cast<QGridLayout*>(ui->tab21_40->layout()), // 第21-40号电机 qobject_cast<QGridLayout*>(ui->tab41_60->layout()), // 第41-60号电机 qobject_cast<QGridLayout*>(ui->tab61_80->layout()), // 第61-80号电机 qobject_cast<QGridLayout*>(ui->tab81_100->layout()) // 第81-100号电机 }; // 每个分页20个电机(5×4) QList<int> motorCounts = {20, 20, 20, 20, 20}; int motorNum = 1; // 电机编号从1开始 // 遍历5个分页 for (int tabIdx = 0; tabIdx < 5; tabIdx++) { QGridLayout *layout = layouts[tabIdx]; // 当前分页的布局 int count = motorCounts[tabIdx]; // 当前分页的电机数量 int row = 0, col = 0; // 网格布局的行列索引 // 为当前分页创建电机控制单元 for (int i = 0; i < count; i++) { // 创建电机控制单元(编号motorNum,父窗口为当前主窗口) MotorControlUnit *motorUnit = new MotorControlUnit(motorNum, this); // 设置尺寸策略为自适应(随窗口拉伸) motorUnit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // 添加到电机单元列表(便于后续管理) m_motorUnits.append(motorUnit); // 将电机单元添加到当前分页的网格布局 layout->addWidget(motorUnit, row, col); // 连接电机发送指令信号(电机点击"发送"时,转发到串口) connect(motorUnit, &MotorControlUnit::sendCommand, this, &MainWindow::forwardMotorCommand); // 更新行列索引(4列满则换行) col++; if (col >= 5) { col = 0; row++; } motorNum++; // 电机编号递增 } } } // 更新状态栏(显示串口状态和当前时间) void MainWindow::updateStatusBar() { // 串口状态文本 QString serialStatus = m_serial->isSerialPortOpen() ? QString("串口:%1(%2)").arg(m_serialDialog->getCurrentPortName()).arg(m_serialDialog->getCurrentBaudRate()) : "串口:未打开"; // 当前时间(格式:yyyy-MM-dd HH:mm:ss) QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); // 更新状态栏 m_statusBar->showMessage(QString("%1 | %2").arg(serialStatus).arg(currentTime)); } // 串口设置按钮点击事件 void MainWindow::onSerialSettingClicked() { m_serialDialog->exec(); updateStatusBar(); // 关闭对话框后更新串口状态 } // 加载CSV按钮点击事件(从CSV读取电机参数) void MainWindow::onLoadCsvClicked() { // 打开文件选择对话框(选择CSV文件) QString filePath = QFileDialog::getOpenFileName(this, "选择CSV文件", "", "CSV Files (*.csv)"); if (filePath.isEmpty()) return; // 未选择文件则返回 QFile file(filePath); // 尝试打开文件(只读、文本模式) if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::warning(this, "错误", "无法打开CSV文件!"); return; } // 读取CSV内容(格式:电机编号,力控制(N),位置(cm),控制模式,使能状态) QTextStream in(&file); in.readLine(); // 跳过表头行 while (!in.atEnd()) { // 逐行读取 QString line = in.readLine(); QStringList parts = line.split(","); // 按逗号分割 if (parts.size() != 5) continue; // 格式错误则跳过 // 解析各字段 int motorNum = parts[0].toInt(); double force = parts[1].toDouble(); double pos = parts[2].toDouble(); QString mode = parts[3]; QString enable = parts[4]; // 找到对应电机并设置参数 for (MotorControlUnit *unit : m_motorUnits) { if (unit->getMotorNumber() == motorNum) { unit->setTargetForce(force); // 设置目标力 unit->setTargetPosition(pos); // 设置目标位置 unit->setControlMode(mode); // 设置控制模式 unit->setEnableState(enable); // 设置使能状态 break; } } } file.close(); QMessageBox::information(this, "成功", "CSV文件加载完成!"); } //保存CSV文件 void MainWindow::onSaveCsvClicked() { QString filePath = QFileDialog::getSaveFileName(this, "保存CSV文件", "", "CSV Files (*.csv)"); if (filePath.isEmpty()) return;// 未指定路径则返回 QFile file(filePath); // 尝试打开文件(只写、文本模式) if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, "错误", "无法创建CSV文件!"); return; } // 写CSV内容(表头+99个电机参数) QTextStream out(&file); // 精确设置编码和换行符 out.setCodec(QTextCodec::codecForName("UTF-8")); out.setGenerateByteOrderMark(false); // 手动控制BOM // 处理BOM标记(关键修正) const unsigned char utf8Bom[3] = {0xEF, 0xBB, 0xBF}; file.write(reinterpret_cast<const char*>(utf8Bom), 3); // 写CSV表头 - 使用本地化字符串处理中文 QString header = QString::fromLocal8Bit("电机编号,力控制(N),位置(cm),控制模式,使能状态\n"); out << header; // 写每个电机的参数 for (MotorControlUnit *unit : m_motorUnits) { out << QString("%1,%2,%3,%4,%5\n") .arg(unit->getMotorNumber()) .arg(unit->getTargetForce(), 0, 'f', 3) .arg(unit->getTargetPosition(), 0, 'f', 3) .arg(unit->getControlMode()) .arg(unit->getEnableState()); } // 刷新缓冲区(确保数据写文件) out.flush(); // 确保缓冲区刷新 file.close(); QMessageBox::information(this, "成功", "CSV文件保存完成!"); } // 全部发送按钮点击事件(发送所有电机的指令) void MainWindow::onSendAllClicked() { if (!m_serial->isSerialPortOpen()) { QMessageBox::warning(this, "警告", "串口未打开,无法发送数据!"); return; } // 遍历所有电机,发送指令 for (MotorControlUnit *unit : m_motorUnits) { forwardMotorCommand( unit->getMotorNumber(), unit->getTargetForce(), unit->getTargetPosition(), unit->getControlMode(), unit->getEnableState() ); } QMessageBox::information(this, "成功", "所有电机指令已发送!"); } // 恢复初始按钮点击事件(重置所有电机参数) void MainWindow::onRestoreDefaultClicked() { for (MotorControlUnit *unit : m_motorUnits) { unit->setTargetForce(0.000); unit->setTargetPosition(0.000); unit->setControlMode("闭环"); unit->setEnableState("失能"); // 重置反馈值 unit->setFeedbackForce(0.000); unit->setFeedbackPosition(0.000); unit->setFeedbackMode("闭环系统"); unit->setFeedbackEnable("电机失能"); } QMessageBox::information(this, "成功", "所有参数已恢复默认值!"); } // 转发电机指令到串口(指令格式:自定义协议,便于硬件解析) void MainWindow::forwardMotorCommand(int motorNum, double force, double pos, const QString &mode, const QString &enable) { if (!m_serial->isSerialPortOpen()) { QMessageBox::warning(this, "警告", "串口未打开,无法发送电机指令!"); return; } // 寄存器映射:0=电机序号,1-2=力,3-4=位置,5=模式,6=使能 uint16_t regs[7]; // 共7个寄存器 // 寄存器0:电机序号(16位整数) if (motorNum < 1) { QMessageBox::warning(this, "警告", "电机序号无效!"); return; } regs[0] = static_cast<uint16_t>(motorNum); // 寄存器1-2:力控制值(32位浮点数) float forceFloat = static_cast<float>(force); floatToRegisters(forceFloat, &regs[1]); // 寄存器3-4:位置控制值(32位浮点数) float posFloat = static_cast<float>(pos); floatToRegisters(posFloat, &regs[3]); // 寄存器5:控制模式(0=开环,1=闭环) regs[5] = (mode == "开环") ? 0 : 1; // 寄存器6:使能状态(0=失能,1=使能) regs[6] = (enable == "使能") ? 1 : 0; // 2. 计算该电机的起始寄存器地址 uint16_t startReg = (motorNum - 1) * 7; // 发送0x10指令(假设从站地址为1,可根据硬件调整) m_modbus->MODH_Send10H(1, startReg, 7,regs); // 从站地址1,起始寄存器0,7个寄存器 } // 处理串口接收的数据(解析硬件反馈,更新电机反馈值) void MainWindow::handleSerialData(const QByteArray &data) { // 自定义反馈数据格式(示例:<电机号,实际力,实际位置,实际模式,实际使能>) QString feedback = QString::fromUtf8(data); if (feedback.startsWith("<") && feedback.endsWith(">\n")) { feedback = feedback.mid(1, feedback.size() - 3); // 去除首尾标记 QStringList parts = feedback.split(","); if (parts.size() != 5) return; int motorNum = parts[0].toInt(); double realForce = parts[1].toDouble(); double realPos = parts[2].toDouble(); int realModeCode = parts[3].toInt(); int realEnableCode = parts[4].toInt(); // 解析反馈状态文本 QString realMode = (realModeCode == 0) ? "开环系统" : "闭环系统"; QString realEnable = (realEnableCode == 1) ? "电机使能" : "电机失能"; // 更新对应电机的反馈值 for (MotorControlUnit *unit : m_motorUnits) { if (unit->getMotorNumber() == motorNum) { unit->setFeedbackForce(realForce); unit->setFeedbackPosition(realPos); unit->setFeedbackMode(realMode); unit->setFeedbackEnable(realEnable); break; } } } } void MainWindow::onMotorFeedbackReceived(int motorNumber, double force, double position, int mode, int enable) { // // 查找对应的电机控制单元 // foreach (MotorControlUnit *unit, m_motorUnits) { // if (unit->getMotorNumber() == motorNumber) { // // 更新力反馈 // unit->setFeedbackForce(force); // // 更新位置反馈 // unit->setFeedbackPosition(position); // // 更新模式反馈 // QString modeStr = (mode == 0) ? "开环系统" : "闭环系统"; // unit->setFeedbackMode(modeStr); // // 更新使能反馈 // QString enableStr = (enable == 1) ? "电机使能" : "电机失能"; // unit->setFeedbackEnable(enableStr); // break; // } // } // 1. 校验电机编号范围(1~100) if (motorNumber < 1 || motorNumber > m_motorUnits.size()) { qDebug() << "未找到电机" << motorNumber << "的控制单元(编号超出范围)"; return; } // 2. 计算正确索引(编号-1) int index = motorNumber - 1; MotorControlUnit* unit = m_motorUnits[index]; // 3. 验证单元是否存在(防御性检查) if (!unit) { qDebug() << "未找到电机" << motorNumber << "的控制单元(索引无效)"; return; } // 获取对应电机控制单元并更新反馈 unit->setFeedbackForce(force); unit->setFeedbackPosition(position); unit->setFeedbackMode(mode == 0 ? "开环" : "闭环"); // 根据实际模式值映射 unit->setFeedbackEnable(enable == 1 ? "使能" : "失能"); // 根据实际使能值映射 qDebug() << "未找到电机" << motorNumber << "的控制单元"; } // 实现onAboutClicked函数(已存在于代码中) void MainWindow::onAboutClicked() { AboutDialog dialog(this); dialog.exec(); } 发送10H指令:从站 1 起始寄存器 0 数量 7 10H写确认:从站 1 电机编号 1 起始寄存器 0 写数量 7 发送03H请求:从站 1 寄存器 0 数量 7 解析到电机编号: 1 (原始字节: "00 01" ) 解析到力控制: 0 (原始字节: "00 00 00 00" ) 解析到电机位置: 0 (原始字节: "00 00 00 00" ) 未找到电机 1 的控制单元 发送10H指令:从站 1 起始寄存器 252 数量 7 10H写确认:从站 1 电机编号 37 起始寄存器 252 写数量 7 03H指令错误:寄存器地址超出范围(起始 252 ,数量 7 )
10-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值