#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 命令成功,进入升级模式)
最新发布