QString 实现 sprintf(buf, “0x02x“, 5)的方法

QString 实现 sprintf(buf, "0x02x", 5)的方法
在Qt中,QString 类提供了丰富的字符串处理功能,并且与C++的标准库兼容。若要使用类似于 sprintf 的功能来格式化字符串,你可以使用 QString::arg 方法,它提供了位置参数和填充、对齐等功能。

要实现 sprintf(buf, "0x02x", 5) 的功能,即将数字 5 格式化为两位的十六进制数,并带有 0x 前缀,你可以这样做:

cpp
Copy Code
#include <QString>
#include <QDebug>

int main() {
    int value = 5;
    QString formattedString = QString("0x%1").arg(value, 2, 16, QChar('0')).toUpper();
    qDebug() << formattedString; // 输出: "0X05"
    return 0;
}
在这个例子中:

QString("0x%1") 创建了一个包含占位符 %1 的字符串。
.arg(value, 2, 16, QChar('0')) 将 value 格式化为十六进制数,宽度为2,不足部分用 0 填充。16 表示使用十六进制基数。
.toUpper() 将结果转换为大写,因为默认情况下,arg 方法可能会生成小写的十六进制数字。
如果你不需要大写,可以去掉 .toUpper() 调用:

cpp
Copy Code
QString formattedString = QString("0x%1").arg(value, 2, 16, QChar('0'));
qDebug() << formattedString; // 输出: "0x05"
这样,你就可以在Qt中使用类似于 sprintf 的方式来格式化整数为带有前缀和特定宽度的十六进制字符串了。

再给一个QString实现sprintf的例子
有没有类似sprintf的QString方法?
有没有更简单的QString sprintf方法?


C++ 中类 static 修饰的函数 用法,并举例

在 C++ 中,static 关键字可以修饰类成员函数,使其成为静态成员函数。静态成员函数属于类本身,而不是类的某个特定对象。这意味着静态成员函数可以在没有类实例的情况下被调用,并且它们不能访问非静态成员变量或非静态成员函数。

静态成员函数的特点
属于类本身:静态成员函数属于类,而不是类的某个实例。
不依赖于对象:静态成员函数可以在没有类实例的情况下被调用。
不能访问非静态成员:静态成员函数不能访问非静态成员变量或非静态成员函数,因为它们需要类的实例来访问。
通过类名调用:静态成员函数可以通过类名直接调用,也可以通过类的实例调用。
静态成员函数的用法
示例 1:静态成员函数的基本用法
cpp
#include <iostream>

class MyClass {
public:
    static void staticFunction() {
        std::cout << "This is a static function." << std::endl;
    }

    void nonStaticFunction() {
        std::cout << "This is a non-static function." << std::endl;
    }
};

int main() {
    // 调用静态成员函数
    MyClass::staticFunction();  // 通过类名调用

    MyClass obj;
    obj.staticFunction();       // 也可以通过对象调用

    // 调用非静态成员函数
    obj.nonStaticFunction();

    return 0;
}
输出
This is a static function.
This is a static function.
This is a non-static function.
示例 2:静态成员函数访问静态成员变量
cpp
#include <iostream>

class MyClass {
public:
    static int count;  // 静态成员变量

    static void incrementCount() {
        count++;  // 静态成员函数可以访问静态成员变量
    }

    void printCount() {
        std::cout << "Count: " << count << std::endl;
    }
};

// 初始化静态成员变量
int MyClass::count = 0;

int main() {
    MyClass obj1, obj2;

    obj1.printCount();  // 输出 Count: 0
    MyClass::incrementCount();  // 通过类名调用静态成员函数
    obj2.printCount();  // 输出 Count: 1

    obj1.incrementCount();  // 也可以通过对象调用静态成员函数
    obj2.printCount();  // 输出 Count: 2

    return 0;
}
输出
Count: 0
Count: 1
Count: 2
注意事项
静态成员函数不能访问非静态成员:静态成员函数不能访问非静态成员变量或非静态成员函数,因为它们需要类的实例来访问。
静态成员变量需要初始化:静态成员变量需要在类外初始化。
静态成员函数的调用方式:静态成员函数可以通过类名直接调用,也可以通过类的实例调用,但推荐通过类名调用以明确其静态特性。
通过以上示例,你可以更好地理解 C++ 中静态成员函数的用法及其特点。

静态成员函数能访问实例变量吗?
静态成员变量和实例变量有什么区别?
静态成员函数在单例模式中有何作用?


 

#include "mainwindow.h" #include "ui_mainwindow.h" #include <QFileDialog> #include <QDebug> #include <QMessageBox> #include <QDateTime> #include <QSerialPortInfo> #include <QThread> #define PACKET_SIZE 256 #define HEAD_FROM_PC 0xA5 // PC → MCU #define HEAD_FROM_MCU 0x5A // MCU → PC #define CMD_DATA_BLOCK 0x21 #define CMD_CUSTOM_CMD 0x22 #define CMD_REQUEST_DATA 0x23 #define CMD_IAP_UPGRADE 0x30 #define CMD_IAP_DATA 0x31 #define CMD_IAP_END 0x32 #define STATUS_SUCCESS 0x01 #define STATUS_ERROR 0x02 #define STATUS_READY 0x03 // ==================== CRC16 实现 ==================== uint16_t MainWindow::crc16_ccitt(const uint8_t *data, int len) { uint16_t crc = 0xFFFF; for (int i = 0; i < len; i++) { crc ^= ((uint16_t)data[i]) << 8; for (int j = 0; j < 8; j++) { if (crc & 0x8000) { crc = (crc << 1) ^ 0x1021; } else { crc <<= 1; } } } return crc; } // ==================== 构造函数 ==================== MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_serial = new QSerialPort(this); m_timeoutTimer = new QTimer(this); m_firmwareFile = nullptr; m_state = Idle; m_isUpgradeMode = false; m_currentPacketIndex = 0; m_retryCount = 0; m_rxFrameBuffer.clear(); m_asciiBuffer.clear(); connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::onSerialDataAvailable); connect(m_serial, &QSerialPort::errorOccurred, this, &MainWindow::handleError); connect(m_timeoutTimer, &QTimer::timeout, this, &MainWindow::onTimeout); //connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::on_serialPort_readyRead); on_btnRefreshPorts_clicked(); ui->lineEditFilePath->setReadOnly(true); ui->textLog->setReadOnly(true); ui->btnStartUpdate->setEnabled(false); ui->btnSendData->setEnabled(false); ui->lineEditSend->setEnabled(false); logMessage("IAP升级工具已启动 - ASCII命令 + 协议包数据模式"); } // ==================== 串口 ==================== void MainWindow::on_serialPort_readyRead() { QByteArray data = serialPort->readAll(); qDebug() << "Received:" << data.toHex(); // 打印十六进制 } // ==================== 析构函数 ==================== MainWindow::~MainWindow() { if (m_serial->isOpen()) { m_serial->close(); } delete ui; } // ==================== 发送普通数据 ==================== void MainWindow::on_btnSendData_clicked() { if (!m_serial->isOpen()) return; QString text = ui->lineEditSend->text().trimmed(); if (text.isEmpty()) return; if (text == "APP1") { QByteArray cmd = "APP1"; QByteArray packet = createPacket(CMD_CUSTOM_CMD, cmd); logMessage("Sending packet: " + packet.toHex()); // 打印十六进制 m_serial->write(packet); m_serial->flush(); logMessage("已发送协议包命令: APP1"); m_state = WaitingAppAck; m_timeoutTimer->start(5000); } else if (text == "END") { QByteArray cmd = "END"; QByteArray packet = createPacket(CMD_IAP_END, cmd); m_serial->write(packet); m_serial->flush(); logMessage("已发送协议包命令: END"); m_state = WaitingEndAck; m_timeoutTimer->start(5000); } } // ==================== 开始升级 ==================== void MainWindow::on_btnStartUpdate_clicked() { if (!m_serial->isOpen()) { logMessage("串口未打开,请先连接串口"); return; } // 读取固件 QString filePath = ui->lineEditFilePath->text(); if (filePath.isEmpty()) { logMessage("请选择固件文件"); return; } m_firmwareFile = new QFile(filePath); if (!m_firmwareFile->open(QIODevice::ReadOnly)) { logMessage("无法打开固件文件"); return; } m_firmwareData = m_firmwareFile->readAll(); m_firmwareFile->close(); delete m_firmwareFile; m_firmwareFile = nullptr; logMessage(QString("固件大小: %1 字节").arg(m_firmwareData.size())); // 设置状态为发送固件数据 m_isUpgradeMode = true; m_state = Sending; // 直接进入发送状态 m_currentPacketIndex = 0; m_sentPackets = 0; m_totalPackets = (m_firmwareData.size() + PACKET_SIZE - 1) / PACKET_SIZE; // 计算总包数 m_retryCount = 0; m_rxFrameBuffer.clear(); m_waitingForAck = false; ui->btnStartUpdate->setEnabled(false); // 开始发送固件数据 sendNextPacket(); } // ==================== 选择文件 ==================== void MainWindow::on_btnSelectFile_clicked() { QString filePath = QFileDialog::getOpenFileName(this, "选择固件文件", "", "Bin Files (*.bin);;All Files (*)"); if (!filePath.isEmpty()) { ui->lineEditFilePath->setText(filePath); ui->btnStartUpdate->setEnabled(true); } } // ==================== 刷新串口 ==================== void MainWindow::on_btnRefreshPorts_clicked() { ui->comboBoxPort->clear(); for (const QSerialPortInfo &info : QSerialPortInfo::availablePorts()) { ui->comboBoxPort->addItem(info.portName()); } if (ui->comboBoxPort->count() == 0) { logMessage("未检测到串口设备"); } } // ==================== 发送下一包 ==================== void MainWindow::sendNextPacket() { if (!m_isUpgradeMode) return; if (m_currentPacketIndex * PACKET_SIZE >= m_firmwareData.size()) { logMessage("固件发送完成,等待MCU处理完最后一个包..."); QTimer::singleShot(1000, this, [this]() { logMessage("发送END命令..."); sendEndCommand(); }); return; } // 发送数据包(协议包) int start = m_currentPacketIndex * PACKET_SIZE; int len = qMin(PACKET_SIZE, m_firmwareData.size() - start); QByteArray packetData = m_firmwareData.mid(start, len); QByteArray packet = createPacket(CMD_IAP_DATA, packetData); qint64 sent = m_serial->write(packet); m_serial->flush(); // 强制发送完成 QThread::msleep(20); // 确保发送完成 if (sent != packet.size()) { logMessage("数据包发送失败!"); finishUpgrade(); return; } m_serial->flush(); logMessage(QString("发送包 %1/%2 (%3 字节)") .arg(m_currentPacketIndex + 1) .arg((m_firmwareData.size() + PACKET_SIZE - 1) / PACKET_SIZE) .arg(len)); m_sentPackets++; m_currentPacketIndex++; // 设置等待ACK状态 m_waitingForAck = true; m_state = WaitingDataAck; // 启动超时定时器 m_timeoutTimer->start(2000); // 2秒超时等待ACK } // ==================== 最后一包 ==================== void MainWindow::sendEndCommand() { QByteArray cmd = "END"; QByteArray packet = createPacket(CMD_IAP_END, cmd); qint64 sent = m_serial->write(packet); m_serial->flush(); if (sent != packet.size()) { logMessage("END命令发送失败!"); finishUpgrade(); return; } logMessage("已发送END命令"); m_state = WaitingEndAck; m_timeoutTimer->start(5000); // 5秒等待END响应 } // ==================== 创建数据包 ==================== QByteArray MainWindow::createPacket(uint8_t cmd, const QByteArray &data) { // payload = [CMD][LEN_H][LEN_L][DATA] QByteArray payload; payload.append(cmd); payload.append((data.size() >> 8) & 0xFF); payload.append(data.size() & 0xFF); payload.append(data); // CRC 只对 payload 计算(不含 0xA5) uint16_t crc = crc16_ccitt(reinterpret_cast<const uint8_t*>(payload.constData()), payload.size()); // 调试输出:打印完整包 QByteArray full_packet; full_packet.append(HEAD_FROM_PC); // 0xA5 full_packet.append(payload); full_packet.append((crc >> 8) & 0xFF); // CRC high full_packet.append(crc & 0xFF); // CRC low // 调试输出 qDebug() << "Payload for CRC calc:" << payload.toHex(); qDebug() << "CRC calculated:" << QString::number(crc, 16); qDebug() << "Expected len:" << data.size(); // 完整包 = [0xA5][payload][CRC_H][CRC_L] QByteArray packet; packet.append(HEAD_FROM_PC); // 0xA5 packet.append(payload); packet.append((crc >> 8) & 0xFF); // CRC high packet.append(crc & 0xFF); // CRC low qDebug() << "Full packet:" << packet.toHex(); return packet; } // ==================== 串口数据到达 ==================== void MainWindow::onSerialDataAvailable() { QByteArray newData = m_serial->readAll(); // 根据当前状态决定处理方式 if (m_state == WaitingAppAck || m_state == WaitingEndAck) { // 在等待响应时,处理ASCII响应 m_asciiBuffer.append(newData); int pos; while ((pos = m_asciiBuffer.indexOf('\n')) != -1 || (pos = m_asciiBuffer.indexOf('\r')) != -1) { QString line = QString::fromLatin1(m_asciiBuffer.left(pos)).trimmed(); logMessage("收到数据: " + line); // 检查是否是期望的响应 if (m_state == WaitingAppAck && line.contains("Ready to receive firmware")) { logMessage("APP1 命令成功,进入升级模式"); m_isUpgradeMode = true; // 现在可以设置升级模式了 m_state = Idle; // 或其他适当状态 m_timeoutTimer->stop(); } m_asciiBuffer.remove(0, pos + 1); } } else if ((m_isUpgradeMode && m_state == Sending)) { m_rxFrameBuffer.append(newData); } else { m_asciiBuffer.append(newData); int pos; while ((pos = m_asciiBuffer.indexOf('\n')) != -1 || (pos = m_asciiBuffer.indexOf('\r')) != -1) { QString line = QString::fromLatin1(m_asciiBuffer.left(pos)).trimmed(); logMessage("收到数据: " + line); m_asciiBuffer.remove(0, pos + 1); } } } // ==================== 处理升级响应 ==================== void MainWindow::handleUpgradeResponse(const QByteArray &response) { if (response.size() < 6) return; // 最小协议包大小 uint8_t head = static_cast<uint8_t>(response[0]); uint8_t cmd = static_cast<uint8_t>(response[1]); uint8_t lenH = static_cast<uint8_t>(response[2]); uint8_t lenL = static_cast<uint8_t>(response[3]); uint16_t len = (lenH << 8) | lenL; if (head != HEAD_FROM_MCU) return; if (len > 0) { QByteArray data = response.mid(5, len); QString msg = QString::fromLatin1(data); logMessage("MCU 消息: " + msg); } } // ==================== 超时处理 ==================== void MainWindow::onTimeout() { m_retryCount++; if (m_retryCount > 3) { logMessage("重试超限,升级失败"); finishUpgrade(); return; } logMessage("超时重试,当前状态: " + QString::number(m_state)); if (m_state == WaitingAppAck) { // 重试发送 APP1(协议包) QByteArray cmd = "APP1"; QByteArray packet = createPacket(CMD_CUSTOM_CMD, cmd); m_serial->write(packet); m_serial->flush(); logMessage("重试发送APP1命令包"); m_timeoutTimer->start(5000); } else if (m_state == WaitingDataAck) { // 重试发送当前数据包 logMessage("数据包超时,重试发送"); m_currentPacketIndex--; // 回退索引,重新发送 m_sentPackets--; sendNextPacket(); } else if (m_state == WaitingEndAck) { // 重试发送 END(协议包) sendEndCommand(); // 使用我们新增的函数,发送协议包格式的END命令 } } // ==================== 日志输出 ==================== void MainWindow::logMessage(const QString &msg) { ui->textLog->append(QDateTime::currentDateTime().toString("hh:mm:ss") + " - " + msg); } // ==================== 串口错误 ==================== void MainWindow::handleError(QSerialPort::SerialPortError error) { if (error != QSerialPort::NoError) { logMessage("串口错误: " + m_serial->errorString()); if (m_state != Idle) { finishUpgrade(); } } } // ==================== 串口连接 ==================== void MainWindow::on_btnConnect_clicked() { if (m_serial->isOpen()) { m_serial->close(); ui->btnConnect->setText("连接"); ui->labelStatus->setText("未连接"); ui->lineEditSend->setEnabled(false); ui->btnSendData->setEnabled(false); logMessage("串口已断开"); } else { QString portName = ui->comboBoxPort->currentText(); if (portName.isEmpty()) { QMessageBox::warning(this, "错误", "请选择串口!"); return; } m_serial->setPortName(portName); m_serial->setBaudRate(QSerialPort::Baud115200); m_serial->setDataBits(QSerialPort::Data8); m_serial->setParity(QSerialPort::NoParity); m_serial->setStopBits(QSerialPort::OneStop); m_serial->setFlowControl(QSerialPort::NoFlowControl); if (!m_serial->open(QIODevice::ReadWrite)) { QMessageBox::warning(this, "错误", "无法打开串口:" + m_serial->errorString()); return; } ui->btnConnect->setText("断开"); ui->labelStatus->setText("已连接"); ui->lineEditSend->setEnabled(true); ui->btnSendData->setEnabled(true); logMessage("串口连接成功:" + portName); } } // ==================== finishUpgrade ==================== void MainWindow::finishUpgrade() { m_isUpgradeMode = false; m_state = Idle; m_timeoutTimer->stop(); ui->btnStartUpdate->setEnabled(true); ui->btnSelectFile->setEnabled(true); ui->btnConnect->setEnabled(true); ui->comboBoxPort->setEnabled(true); ui->lineEditSend->setEnabled(true); ui->btnSendData->setEnabled(true); ui->labelStatus->setText("升级完成或已终止"); logMessage("升级过程结束"); } 这是我QT的代码 IAP.c(#include "iap.h" #include "string.h" #include "stm32f4xx_hal_flash_ex.h" #include "protocol.h" #include "crc.h" // ===== 全局变量 ===== volatile IAP_StateTypeDef iap_state = IAP_IDLE; uint32_t write_addr = 0; static char cmd_line[64]; // 命令行缓冲区 static uint8_t cmd_line_idx = 0; // 命令行索引 extern uint32_t iap_buf_len; extern uint8_t iap_buf[IAP_BUF_SIZE]; extern protocol_handler_t g_protocol_handler; volatile uint8_t iap_prompt_end = 0; // 提示用户发送 END typedef void (*pFunction)(void); // 自定义strcasecmp函数(如果标准库不可用) int my_strcasecmp(const char *str1, const char *str2) { if (!str1 || !str2) return -1; while (*str1 && *str2) { char c1 = *str1; char c2 = *str2; // 转换为小写进行比较 if (c1 >= 'A' && c1 <= 'Z') c1 += 32; if (c2 >= 'A' && c2 <= 'Z') c2 += 32; if (c1 != c2) return c1 - c2; str1++; str2++; } return *str1 - *str2; } // 发送 CRC 格式的响应包 void IAP_Send_Response(uint8_t cmd, uint8_t status, const uint8_t* data, uint16_t data_len) { uint16_t total_len = 1 + 1 + 2 + data_len + 2; uint8_t response[total_len]; // 构造响应包... response[0] = 0x5A; response[1] = cmd; response[2] = (data_len >> 8) & 0xFF; response[3] = data_len & 0xFF; response[4] = status; if (data_len > 0 && data != NULL) { memcpy(&response[5], data, data_len); } uint16_t crc = crc16_ccitt(&response[1], total_len - 3); // 从response[1]开始计算,排除0x5A头 response[total_len - 2] = (crc >> 8) & 0xFF; response[total_len - 1] = crc & 0xFF; // 使用阻塞方式发送,确保发送完成 HAL_UART_Transmit(&huart6, response, total_len, 1000); // 1秒超时 // 添加调试信息 IAP_Send_String("Sending response: "); for (int i = 0; i < total_len; i++) { IAP_Send_Hex(response[i]); } IAP_Send_String("\r\n"); } // ==================== 串口发送函数(阻塞)==================== void IAP_Send_String(const char* str) { if (str == NULL) return; HAL_UART_Transmit(&huart6, (uint8_t*)str, strlen(str), 100); } // ==================== UART 接收回调 ==================== void IAP_UART_Receive_Callback(uint8_t data) { // 如果当前正在处理 END 命令,跳过协议解析 if (iap_state == IAP_PROCESSING_END) { return; } // 所有字节都尝试解析协议包 int protocol_result = protocol_parse_byte(&g_protocol_handler, data); if (protocol_result == 1) { protocol_process_command(&g_protocol_handler); return; } else if (protocol_result == -1) { // 协议解析错误(如长度超限),重置协议处理器 protocol_init(&g_protocol_handler); IAP_Send_String("Protocol parser reset due to error.\r\n"); return; } // // 如果不是协议包头,处理 ASCII 命令 // if (iap_state == IAP_IDLE) // { // if (data != '\r' && data != '\n') // { // if (cmd_line_idx < sizeof(cmd_line) - 1) // { // cmd_line[cmd_line_idx++] = data; // } // return; // } // if (cmd_line_idx > 0) // { // cmd_line[cmd_line_idx] = '\0'; // cmd_line_idx = 0; // if (my_strcasecmp(cmd_line, "APP1") == 0) // { // iap_state = IAP_RECEIVING_APP1; // write_addr = APP1_ADDR; // iap_buf_len = 0; // IAP_Send_String("Ready to receive firmware for APP1. Send binary data now.\r\n"); // } // else if (my_strcasecmp(cmd_line, "APP2") == 0) // { // iap_state = IAP_RECEIVING_APP2; // write_addr = APP2_ADDR; // iap_buf_len = 0; // IAP_Send_String("Ready to receive firmware for APP2. Send binary data now.\r\n"); // } // else // { // IAP_Send_String("Unknown command. Use: APP1, APP2\r\n"); // } // } // } // else if (iap_state == IAP_RECEIVING_APP1 || iap_state == IAP_RECEIVING_APP2) // { // // 接收二进制数据 // if (data == '\r' || data == '\n') // { // if (cmd_line_idx == 0 && iap_buf_len > 0) // { // iap_state = IAP_WAIT_FOR_END; // IAP_Send_String("Binary data received. Type 'END' to flash and run.\r\n"); // } // } // else // { // if (iap_buf_len < IAP_BUF_SIZE) // { // iap_buf[iap_buf_len++] = data; // } // else // { // IAP_Send_String("Buffer overflow! Data discarded.\r\n"); // } // } // } // else if (iap_state == IAP_WAIT_FOR_END) // { // if (data == '\r' || data == '\n') // { // if (cmd_line_idx > 0) // { // cmd_line[cmd_line_idx] = '\0'; // cmd_line_idx = 0; // if (my_strcasecmp(cmd_line, "END") == 0) // { // iap_state = IAP_PROCESSING_END; // IAP_Send_String("Processing firmware...\r\n"); // } // else // { // IAP_Send_String("Invalid command. Use: END\r\n"); // } // } // } // else if (cmd_line_idx < sizeof(cmd_line) - 1) // { // cmd_line[cmd_line_idx++] = data; // } // } } // ==================== 主处理函数(在 main 中调用)==================== void IAP_Process(void) { if (iap_state != IAP_PROCESSING_END) return; if (iap_buf_len == 0) { IAP_Send_String("No data received.\r\n"); HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // 直接发送 0x00 iap_state = IAP_IDLE; return; } if (iap_buf_len > IAP_BUF_SIZE) { IAP_Send_String("Data size exceeds buffer capacity.\r\n"); HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // 直接发送 0x00 iap_state = IAP_IDLE; return; } // 擦除Flash扇区 FLASH_EraseInitTypeDef EraseInitStruct; uint32_t ErrorStatus = 0; // 解锁Flash HAL_FLASH_Unlock(); // 确定要擦除的目标扇区 uint32_t target_sector = (iap_state == IAP_RECEIVING_APP1 || (iap_state == IAP_PROCESSING_END && write_addr == APP1_ADDR)) ? APP1_SECTOR : APP2_SECTOR; // 配置擦除参数 EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Sector = target_sector; EraseInitStruct.NbSectors = 1; EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 执行擦除操作 if (HAL_FLASHEx_Erase(&EraseInitStruct, &ErrorStatus) != HAL_OK) { IAP_Send_String("Erase failed!\r\n"); // 发送 NACK uint8_t nack = 0x00; HAL_UART_Transmit(&huart6, &nack, 1, 100); HAL_FLASH_Lock(); iap_state = IAP_IDLE; return; } // 写入数据到Flash uint32_t address = write_addr; uint32_t i = 0; // 按32位字为单位写入数据 while (i < iap_buf_len) { uint32_t word = 0xFFFFFFFF; // Flash 默认值 // 构建 32 位字(小端序) for (uint32_t j = 0; j < 4 && (i + j) < iap_buf_len; j++) { word &= ~(0xFFUL << (j * 8)); word |= ((uint32_t)iap_buf[i + j]) << (j * 8); } if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, word) != HAL_OK) { IAP_Send_String("Write error!\r\n"); // 发送 NACK uint8_t nack = 0x00; HAL_UART_Transmit(&huart6, &nack, 1, 100); HAL_FLASH_Lock(); iap_state = IAP_IDLE; return; } address += 4; // 地址递增4字节 i += 4; // 索引递增4字节 } // 写入后校验:检查向量表的有效性 uint32_t *vector_table = (uint32_t*)write_addr; uint32_t msp = vector_table[0]; // 主栈指针 uint32_t reset_handler = vector_table[1]; // 复位处理函数地址 // 验证栈指针是否在合理范围内 if (msp < 0x20000000 || msp > 0x200FFFFF) { IAP_Send_String("Verification failed! Invalid MSP: 0x"); IAP_Send_Hex(msp); IAP_Send_String("\r\n"); // 发送 NACK uint8_t nack = 0x00; HAL_UART_Transmit(&huart6, &nack, 1, 100); HAL_FLASH_Lock(); iap_state = IAP_IDLE; return; } // 验证复位处理函数地址是否在Flash范围内 if (reset_handler < 0x08000000 || reset_handler > 0x080FFFFF) { IAP_Send_String("Verification failed! Invalid Reset Handler: 0x"); IAP_Send_Hex(reset_handler); IAP_Send_String("\r\n"); // 发送 NACK uint8_t nack = 0x00; HAL_UART_Transmit(&huart6, &nack, 1, 100); HAL_FLASH_Lock(); iap_state = IAP_IDLE; return; } // 写入运行标志 uint8_t flag = (iap_state == IAP_RECEIVING_APP1 || (iap_state == IAP_PROCESSING_END && write_addr == APP1_ADDR)) ? RUN_FLAG_APP1 : RUN_FLAG_APP2; if (write_run_flag_full(flag) != HAL_OK) { IAP_Send_String("Failed to set run flag!\r\n"); // 发送 NACK uint8_t nack = 0x00; HAL_UART_Transmit(&huart6, &nack, 1, 100); HAL_FLASH_Lock(); iap_state = IAP_IDLE; return; } // 操作完成,锁定Flash HAL_FLASH_Lock(); // 写入完成后发送 ACK uint8_t ack = 0x55; HAL_UART_Transmit(&huart6, &ack, 1, 100); IAP_Send_String("Upgrade success! Jumping to app...\r\n"); HAL_Delay(100); // 跳转到应用程序 uint32_t app_addr = (iap_state == IAP_RECEIVING_APP1 || (iap_state == IAP_PROCESSING_END && write_addr == APP1_ADDR)) ? APP1_ADDR : APP2_ADDR; jump_to_app(app_addr); } // ==================== 辅助函数 ==================== HAL_StatusTypeDef write_run_flag_full(uint8_t flag) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef EraseInitStruct; uint32_t ErrorStatus = 0; // 擦除参数扇区 EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; EraseInitStruct.Sector = PARAM_SECTOR; EraseInitStruct.NbSectors = 1; EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; if (HAL_FLASHEx_Erase(&EraseInitStruct, &ErrorStatus) != HAL_OK) { HAL_FLASH_Lock(); return HAL_ERROR; } // 写入标志值 uint32_t data = 0xFFFFFF00U | flag; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, PARAM_ADDR, data) != HAL_OK) { HAL_FLASH_Lock(); return HAL_ERROR; } HAL_FLASH_Lock(); return HAL_OK; } uint8_t read_run_flag(void) { uint32_t flag_word = *(volatile uint32_t *)PARAM_ADDR; return (uint8_t)(flag_word & 0xFF); } uint8_t is_app_valid(uint32_t addr) { uint32_t sp = *(volatile uint32_t *)addr; // 检查栈指针是否在合理的RAM范围内 return (sp >= RAM_BASE && sp <= RAM_END); } void jump_to_app(uint32_t app_addr) { // 1. 检查地址对齐(必须4字节对齐) if ((app_addr & 0x3) != 0) { IAP_Send_String("Error: App address not aligned!\r\n"); return; } // 2. 检查地址是否在Flash范围内 if (app_addr < 0x08000000 || app_addr > 0x080FFFFF) { IAP_Send_String("Error: App address out of Flash range!\r\n"); return; } // 3. 读取栈顶地址 uint32_t msp_value = *(volatile uint32_t *)(app_addr); if (msp_value == 0xFFFFFFFF || msp_value < 0x20000000 || msp_value > 0x20040000) { IAP_Send_String("Error: Invalid MSP value in vector table!\r\n"); return; } // 4. 读取复位向量(程序入口地址,位于 app_addr + 4) uint32_t reset_handler_addr = *(volatile uint32_t *)(app_addr + 4); if (reset_handler_addr == 0xFFFFFFFF || reset_handler_addr < 0x08000000 || reset_handler_addr > 0x080FFFFF || (reset_handler_addr & 0x1) == 0) { // 必须是奇数(Thumb 模式) IAP_Send_String("Error: Invalid application entry point!\r\n"); return; } // 5. 设置主堆栈指针 __set_MSP(msp_value); // 6. 构造函数指针并跳转 void (*app_reset_handler)(void) = (void (*)(void))reset_handler_addr; IAP_Send_String("Jumping to application...\r\n"); HAL_Delay(50); // 确保串口发送完成 __disable_irq(); // 关闭所有中断 // 跳转到应用程序 app_reset_handler(); // 跳转!不会返回 } void IAP_Init(void) { iap_state = IAP_IDLE; iap_buf_len = 0; cmd_line_idx = 0; iap_prompt_end = 0; // 清空缓冲区 memset(iap_buf, 0xFF, IAP_BUF_SIZE); // 初始化协议处理器 protocol_init(&g_protocol_handler); IAP_Send_String("Protocol handler initialized.\r\n"); } // 发送一个32位整数的十六进制表示(8位大写) // void IAP_Send_Hex(uint32_t value) // { // char hex_str[11]; // "0x" + 8 digits + '\0' // snprintf(hex_str, sizeof(hex_str), "0x%08X", value); // IAP_Send_String(hex_str); // } void IAP_Send_Hex(uint32_t value) { char hex_str[9]; // 8 digits + '\0' snprintf(hex_str, sizeof(hex_str), "%02X", (uint8_t)(value & 0xFF)); IAP_Send_String(hex_str); } // // 或者创建一个新的函数专门打印字节 // void IAP_Send_Byte_Hex(uint8_t value) // { // char hex_str[3]; // 2 digits + '\0' // snprintf(hex_str, sizeof(hex_str), "%02X", value); // IAP_Send_String(hex_str); // })protocol.c(// protocol.c #include "protocol.h" #include "iap.h" #include <string.h> // 全局变量定义(仅在此处定义一次) uint32_t iap_buf_len = 0; uint8_t iap_buf[IAP_BUF_SIZE]; protocol_handler_t g_protocol_handler; // 协议初始化 void protocol_init(protocol_handler_t *handler) { if (handler == NULL) return; memset(handler->buffer, 0, sizeof(handler->buffer)); handler->state = PROTOCOL_IDLE; handler->data_index = 0; handler->expected_len = 0; handler->packet_complete = 0; handler->received_crc = 0; handler->command = 0; } // 解析单个字节 int protocol_parse_byte(protocol_handler_t *handler, uint8_t byte) { if (handler == NULL) return -1; // 添加调试信息 char debug_str[64]; sprintf(debug_str, "Received byte: 0x%02X, State: %d\r\n", byte, handler->state); IAP_Send_String(debug_str); switch (handler->state) { case PROTOCOL_IDLE: if (byte == 0xA5) { handler->buffer[0] = byte; handler->data_index = 1; handler->state = PROTOCOL_HEADER; IAP_Send_String("Header found! State changed to PROTOCOL_HEADER\r\n"); return 0; // 继续接收 } else { // 如果不是协议包头,重置状态 handler->state = PROTOCOL_IDLE; return 0; // 返回0,让IAP模块处理ASCII命令 } break; case PROTOCOL_HEADER: handler->buffer[1] = byte; handler->command = byte; handler->data_index = 2; handler->state = PROTOCOL_LENGTH_HIGH; return 0; // 继续接收 case PROTOCOL_LENGTH_HIGH: handler->buffer[2] = byte; handler->state = PROTOCOL_LENGTH_LOW; return 0; // 继续接收 case PROTOCOL_LENGTH_LOW: handler->buffer[3] = byte; handler->expected_len = ((uint16_t)handler->buffer[2] << 8) | handler->buffer[3]; if (handler->expected_len > IAP_BUF_SIZE) { IAP_Send_String("Length too large: "); IAP_Send_Hex(handler->expected_len); IAP_Send_String("\r\n"); handler->state = PROTOCOL_IDLE; return 0; } handler->data_index = 4; handler->state = (handler->expected_len > 0) ? PROTOCOL_DATA : PROTOCOL_CRC_HIGH; return 0; // 继续接收 case PROTOCOL_DATA: if (handler->data_index < 4 + handler->expected_len) { handler->buffer[handler->data_index++] = byte; } if (handler->data_index >= 4 + handler->expected_len) { handler->state = PROTOCOL_CRC_HIGH; } return 0; // 继续接收 case PROTOCOL_CRC_HIGH: handler->received_crc = ((uint16_t)byte) << 8; handler->state = PROTOCOL_CRC_LOW; return 0; // 继续接收 case PROTOCOL_CRC_LOW: handler->received_crc |= byte; // :打印整个接收到的包 IAP_Send_String("完整接收包 (hex): "); for (int i = 0; i < 4 + handler->expected_len + 2; i++) { IAP_Send_Hex(handler->buffer[i]); IAP_Send_String(" "); } IAP_Send_String("\r\n"); // CRC 只对 [CMD][LEN_H][LEN_L][DATA] 计算(跳过 0xA5) uint16_t calc_crc = crc16_ccitt(&handler->buffer[1], 3 + handler->expected_len); if (calc_crc == handler->received_crc) { handler->packet_complete = 1; handler->command = handler->buffer[1]; if (handler->expected_len > 0) { memcpy(iap_buf, &handler->buffer[4], handler->expected_len); iap_buf_len = handler->expected_len; } } else { handler->packet_complete = 0; IAP_Send_String("CRC Error! Received: 0x"); IAP_Send_Hex(handler->received_crc); IAP_Send_String(", Calculated: 0x"); IAP_Send_Hex(calc_crc); IAP_Send_String("\r\n"); } handler->state = PROTOCOL_IDLE; return handler->packet_complete ? 1 : 0; default: handler->state = PROTOCOL_IDLE; return 0; } return 0; } // 处理完整数据包 void protocol_process_command(protocol_handler_t *handler) { IAP_Send_String("Processing command...\r\n"); if (!handler->packet_complete) return; uint8_t cmd = handler->command; uint16_t len = handler->expected_len; IAP_Send_String("Received CMD: 0x"); IAP_Send_Hex(cmd); IAP_Send_String(", Len: "); IAP_Send_Hex(len); IAP_Send_String("\r\n"); switch (cmd) { case CMD_CUSTOM_CMD: IAP_Send_String("Processing CMD_CUSTOM_CMD...\r\n"); if (iap_state != IAP_IDLE) { IAP_Send_String("State not idle!\r\n"); return; } if (len < 4) { IAP_Send_String("Length too short!\r\n"); return; } if (iap_buf[0] == 'A' && iap_buf[1] == 'P' && iap_buf[2] == 'P' && iap_buf[3] == '1') { iap_state = IAP_RECEIVING_APP1; write_addr = APP1_ADDR; IAP_Send_Response(cmd, 0x55, NULL, 0); IAP_Send_String("APP1 command received. Ready to receive firmware.\r\n"); } else if (iap_buf[0] == 'A' && iap_buf[1] == 'P' && iap_buf[2] == 'P' && iap_buf[3] == '2') { iap_state = IAP_RECEIVING_APP2; write_addr = APP2_ADDR; IAP_Send_Response(cmd, 0x55, NULL, 0); IAP_Send_String("APP2 command received. Ready to receive firmware.\r\n"); } else { IAP_Send_Response(cmd, 0x00, NULL, 0); IAP_Send_String("Invalid APP command.\r\n"); } break; case CMD_IAP_DATA: if ((iap_state == IAP_RECEIVING_APP1 || iap_state == IAP_RECEIVING_APP2) && len > 0) { if ((iap_buf_len + len) <= IAP_BUF_SIZE) { memcpy(&iap_buf[iap_buf_len], &handler->buffer[4], len); iap_buf_len += len; HAL_UART_Transmit(&huart6, (uint8_t[]){0x55}, 1, 100); // ACK } else { HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // NACK IAP_Send_String("Buffer overflow!\r\n"); } } else { HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // NACK IAP_Send_String("Data received in wrong state.\r\n"); } break; case CMD_IAP_END: if (len >= 3 && iap_buf[0] == 'E' && iap_buf[1] == 'N' && iap_buf[2] == 'D') { iap_state = IAP_PROCESSING_END; IAP_Send_String("END command received. Processing firmware...\r\n"); } else { HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // NACK IAP_Send_String("Invalid END command.\r\n"); } break; default: HAL_UART_Transmit(&huart6, (uint8_t[]){0x00}, 1, 100); // NACK IAP_Send_String("Unknown command received.\r\n"); break; } // handler->packet_complete = 0; protocol_init(handler); }) 现在问题就是QT给MCU打包APP1数据包是15:08:40 - Sending packet: a52200044150503118f6传输MCU却接包解到5:08:40 - 收到数据: 完整接收包 (hex): A5 22 00 04 41 50 50 31 00 00 2、解包完成后还多回应一个Z(15:08:40 - 收到数据: Received CMD: 0x22, Len: 04 15:08:40 - 收到数据: Processing CMD_CUSTOM_CMD... 15:08:40 - 收到数据: Z" 15:08:40 - 收到数据: APP1 command received. Ready to receive firmware. 15:08:40 - APP1 命令成功,进入升级模式)
最新发布
01-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值