feat: 新增IAP功能(Ymodem+Flash编程+跳转)
This commit is contained in:
@@ -96,6 +96,7 @@ file(GLOB_RECURSE APP_SOURCES
|
||||
bsp/device/touch/*.cpp
|
||||
bsp/device/sdram/*.cpp
|
||||
bsp/device/flash/*.cpp
|
||||
libs/common/iap/*.cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
#include "iap/iap.h"
|
||||
#include "uart_driver.h"
|
||||
#include "flash_manager.h"
|
||||
#include "gd32f4xx.h"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#define YM_SOH 0x01
|
||||
#define YM_STX 0x02
|
||||
#define YM_EOT 0x04
|
||||
#define YM_ACK 0x06
|
||||
#define YM_NAK 0x15
|
||||
#define YM_CAN 0x18
|
||||
#define YM_CRC 0x43
|
||||
|
||||
#define YM_PACKET_SIZE_128 128
|
||||
#define YM_PACKET_SIZE_1024 1024
|
||||
#define YM_PACKET_OVERHEAD 5
|
||||
|
||||
__attribute__((section(".sdram"))) static uint8_t s_firmware_buffer[IAP_BUFFER_SIZE];
|
||||
|
||||
static uint16_t crc16_ccitt(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
for (uint32_t 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;
|
||||
}
|
||||
|
||||
Iap::Iap()
|
||||
: initialized_(false), firmware_size_(0)
|
||||
{
|
||||
firmware_name_[0] = '\0';
|
||||
}
|
||||
|
||||
Iap &Iap::instance()
|
||||
{
|
||||
static Iap inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
RetCode Iap::init()
|
||||
{
|
||||
initialized_ = true;
|
||||
firmware_size_ = 0;
|
||||
firmware_name_[0] = '\0';
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
RetCode Iap::receiveFirmware(UartBus &uart, uint32_t timeout_ms)
|
||||
{
|
||||
if (!initialized_)
|
||||
return RET_NOT_INITIALIZED;
|
||||
|
||||
firmware_size_ = 0;
|
||||
firmware_name_[0] = '\0';
|
||||
|
||||
RetCode ret = ymodemReceive(uart, timeout_ms);
|
||||
if (ret != RET_OK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (firmware_size_ == 0 || firmware_size_ > IAP_FIRMWARE_MAX_SIZE)
|
||||
{
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
RetCode Iap::programFirmware(uint32_t flash_addr)
|
||||
{
|
||||
if (!initialized_)
|
||||
return RET_NOT_INITIALIZED;
|
||||
if (firmware_size_ == 0)
|
||||
return RET_ERROR;
|
||||
|
||||
FlashManager &flash = FlashManager::instance();
|
||||
|
||||
if (!flash.is_initialized())
|
||||
{
|
||||
RetCode ret = flash.init();
|
||||
if (ret != RET_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t sector_size = flash.sector_size();
|
||||
uint32_t start_aligned = flash_addr & ~(sector_size - 1);
|
||||
uint32_t end_addr = flash_addr + firmware_size_;
|
||||
uint32_t end_aligned = (end_addr + sector_size - 1) & ~(sector_size - 1);
|
||||
uint32_t erase_size = end_aligned - start_aligned;
|
||||
|
||||
RetCode ret = flash.erase(start_aligned, erase_size);
|
||||
if (ret != RET_OK)
|
||||
return ret;
|
||||
|
||||
ret = flash.write(flash_addr, s_firmware_buffer, firmware_size_);
|
||||
if (ret != RET_OK)
|
||||
return ret;
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
RetCode Iap::verifyFirmware(uint32_t flash_addr)
|
||||
{
|
||||
if (!initialized_)
|
||||
return RET_NOT_INITIALIZED;
|
||||
if (firmware_size_ == 0)
|
||||
return RET_ERROR;
|
||||
|
||||
FlashManager &flash = FlashManager::instance();
|
||||
if (!flash.is_initialized())
|
||||
{
|
||||
RetCode ret = flash.init();
|
||||
if (ret != RET_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t verify_buffer[256];
|
||||
uint32_t remaining = firmware_size_;
|
||||
uint32_t offset = 0;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
uint32_t chunk = (remaining > sizeof(verify_buffer)) ? sizeof(verify_buffer) : remaining;
|
||||
RetCode ret = flash.read(flash_addr + offset, verify_buffer, chunk);
|
||||
if (ret != RET_OK)
|
||||
return ret;
|
||||
|
||||
if (memcmp(verify_buffer, &s_firmware_buffer[offset], chunk) != 0)
|
||||
{
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
offset += chunk;
|
||||
remaining -= chunk;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
void Iap::jumpToApp(uint32_t app_addr)
|
||||
{
|
||||
__disable_irq();
|
||||
|
||||
for (uint32_t i = 0; i < 8; i++)
|
||||
{
|
||||
NVIC->ICER[i] = 0xFFFFFFFF;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
SCB->VTOR = app_addr;
|
||||
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
uint32_t msp = *(volatile uint32_t *)app_addr;
|
||||
uint32_t reset_vector = *(volatile uint32_t *)(app_addr + 4);
|
||||
|
||||
__set_MSP(msp);
|
||||
__set_PSP(msp);
|
||||
|
||||
void (*app_entry)(void) = (void (*)(void))reset_vector;
|
||||
app_entry();
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
bool Iap::isValidApp(uint32_t app_addr)
|
||||
{
|
||||
if (app_addr == 0 || (app_addr & 0x1FF) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t msp = *(volatile uint32_t *)app_addr;
|
||||
uint32_t reset = *(volatile uint32_t *)(app_addr + 4);
|
||||
|
||||
bool sp_ok = (msp >= 0x10000000 && msp <= 0x10010000) ||
|
||||
(msp >= 0x20000000 && msp <= 0x20030000);
|
||||
|
||||
bool reset_ok = (reset >= 0x08000000 && reset <= 0x08100000);
|
||||
|
||||
return sp_ok && reset_ok;
|
||||
}
|
||||
|
||||
RetCode Iap::backupDomainEnable(void)
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_PMU);
|
||||
pmu_backup_write_enable();
|
||||
|
||||
if (!(RCU_BDCTL & RCU_BDCTL_RTCEN))
|
||||
{
|
||||
RCU_BDCTL |= RCU_BDCTL_RTCEN;
|
||||
}
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
void Iap::bootFlagWrite(uint32_t magic)
|
||||
{
|
||||
backupDomainEnable();
|
||||
RTC_BKP0 = magic;
|
||||
}
|
||||
|
||||
uint32_t Iap::bootFlagRead(void)
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_PMU);
|
||||
|
||||
if (!(RCU_BDCTL & RCU_BDCTL_RTCEN))
|
||||
{
|
||||
RCU_BDCTL |= RCU_BDCTL_RTCEN;
|
||||
}
|
||||
|
||||
return RTC_BKP0;
|
||||
}
|
||||
|
||||
// ���������� Ymodem Receive ����������
|
||||
|
||||
void Iap::ymodemSend(UartBus &uart, uint8_t b)
|
||||
{
|
||||
uart.send_byte(b);
|
||||
}
|
||||
|
||||
int Iap::ymodemRead(UartBus &uart, uint32_t timeout_ms)
|
||||
{
|
||||
uint8_t b;
|
||||
RetCode ret = uart.receive_byte(&b, timeout_ms);
|
||||
if (ret != RET_OK)
|
||||
return -1;
|
||||
return (int)b;
|
||||
}
|
||||
|
||||
RetCode Iap::ymodemReceive(UartBus &uart, uint32_t timeout_ms)
|
||||
{
|
||||
uint8_t pkt_buf[YM_PACKET_SIZE_1024 + YM_PACKET_OVERHEAD];
|
||||
uint32_t total_received = 0;
|
||||
uint8_t expected_seq = 1;
|
||||
bool first_packet = true;
|
||||
|
||||
printf("IAP: Waiting for Ymodem transfer (timeout=%lu ms)...\r\n", timeout_ms);
|
||||
|
||||
int remaining_ms = (int)timeout_ms;
|
||||
|
||||
while (remaining_ms > 0)
|
||||
{
|
||||
ymodemSend(uart, YM_CRC);
|
||||
|
||||
int byte = ymodemRead(uart, 3000);
|
||||
if (byte < 0)
|
||||
{
|
||||
remaining_ms -= 3000;
|
||||
continue;
|
||||
}
|
||||
|
||||
int pkt_type = byte;
|
||||
int pkt_len = 0;
|
||||
|
||||
if (pkt_type == YM_SOH)
|
||||
{
|
||||
pkt_len = YM_PACKET_SIZE_128;
|
||||
}
|
||||
else if (pkt_type == YM_STX)
|
||||
{
|
||||
pkt_len = YM_PACKET_SIZE_1024;
|
||||
}
|
||||
else if (pkt_type == YM_EOT)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
byte = ymodemRead(uart, 3000);
|
||||
if (byte == YM_EOT)
|
||||
{
|
||||
ymodemSend(uart, YM_ACK);
|
||||
printf("IAP: Transfer complete (%lu bytes)\r\n", total_received);
|
||||
firmware_size_ = total_received;
|
||||
return RET_OK;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (pkt_type == YM_CAN)
|
||||
{
|
||||
printf("IAP: Transfer cancelled by sender\r\n");
|
||||
return RET_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt_buf[0] = (uint8_t)pkt_type;
|
||||
pkt_buf[1] = (uint8_t)ymodemRead(uart, 100);
|
||||
pkt_buf[2] = (uint8_t)ymodemRead(uart, 100);
|
||||
|
||||
uint8_t block_num = pkt_buf[1];
|
||||
uint8_t block_comp = pkt_buf[2];
|
||||
|
||||
if (block_num != (uint8_t)(~block_comp))
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool data_ok = true;
|
||||
for (int i = 0; i < pkt_len; i++)
|
||||
{
|
||||
int d = ymodemRead(uart, 100);
|
||||
if (d < 0)
|
||||
{
|
||||
data_ok = false;
|
||||
break;
|
||||
}
|
||||
pkt_buf[3 + i] = (uint8_t)d;
|
||||
}
|
||||
|
||||
if (!data_ok)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
int crc_h = ymodemRead(uart, 100);
|
||||
int crc_l = ymodemRead(uart, 100);
|
||||
if (crc_h < 0 || crc_l < 0)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t received_crc = (uint16_t)((uint16_t)crc_h << 8) | (uint16_t)crc_l;
|
||||
uint16_t calc_crc = crc16_ccitt(&pkt_buf[3], (uint32_t)pkt_len);
|
||||
|
||||
if (received_crc != calc_crc)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first_packet)
|
||||
{
|
||||
if (block_num != 0)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *data_str = (const char *)&pkt_buf[3];
|
||||
const char *fn = data_str;
|
||||
uint32_t fn_len = 0;
|
||||
while (fn_len < (uint32_t)pkt_len && data_str[fn_len] != '\0')
|
||||
fn_len++;
|
||||
|
||||
if (fn_len > 0 && fn_len < sizeof(firmware_name_))
|
||||
{
|
||||
memcpy(firmware_name_, fn, fn_len);
|
||||
firmware_name_[fn_len] = '\0';
|
||||
}
|
||||
|
||||
uint32_t name_end = fn_len + 1;
|
||||
if (name_end < (uint32_t)pkt_len)
|
||||
{
|
||||
const char *size_str = data_str + name_end;
|
||||
firmware_size_ = 0;
|
||||
while (*size_str >= '0' && *size_str <= '9')
|
||||
{
|
||||
firmware_size_ = firmware_size_ * 10 + (*size_str - '0');
|
||||
size_str++;
|
||||
}
|
||||
}
|
||||
|
||||
if (firmware_size_ == 0 || firmware_size_ > IAP_FIRMWARE_MAX_SIZE)
|
||||
{
|
||||
printf("IAP: Invalid firmware size: %lu\r\n", firmware_size_);
|
||||
ymodemSend(uart, YM_CAN);
|
||||
ymodemSend(uart, YM_CAN);
|
||||
return RET_INVALID_PARAM;
|
||||
}
|
||||
|
||||
printf("IAP: Receiving '%s' (%lu bytes)\r\n", firmware_name_, firmware_size_);
|
||||
ymodemSend(uart, YM_ACK);
|
||||
first_packet = false;
|
||||
expected_seq = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (block_num != expected_seq)
|
||||
{
|
||||
ymodemSend(uart, YM_NAK);
|
||||
remaining_ms -= 100;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (total_received + (uint32_t)pkt_len > IAP_BUFFER_SIZE)
|
||||
{
|
||||
printf("IAP: Buffer overflow\r\n");
|
||||
ymodemSend(uart, YM_CAN);
|
||||
ymodemSend(uart, YM_CAN);
|
||||
return RET_ERROR;
|
||||
}
|
||||
|
||||
memcpy(&s_firmware_buffer[total_received], &pkt_buf[3], (uint32_t)pkt_len);
|
||||
total_received += (uint32_t)pkt_len;
|
||||
|
||||
ymodemSend(uart, YM_ACK);
|
||||
expected_seq++;
|
||||
}
|
||||
|
||||
remaining_ms = (int)timeout_ms;
|
||||
}
|
||||
|
||||
printf("IAP: Timeout waiting for Ymodem transfer\r\n");
|
||||
return RET_TIMEOUT;
|
||||
}
|
||||
|
||||
int Iap::ymodemReadPacket(UartBus &uart, uint8_t *buf, uint32_t timeout_ms)
|
||||
{
|
||||
int byte = ymodemRead(uart, timeout_ms);
|
||||
if (byte < 0)
|
||||
return -1;
|
||||
|
||||
int pkt_type = byte;
|
||||
int data_len = 0;
|
||||
|
||||
if (pkt_type == YM_SOH)
|
||||
{
|
||||
data_len = YM_PACKET_SIZE_128;
|
||||
}
|
||||
else if (pkt_type == YM_STX)
|
||||
{
|
||||
data_len = YM_PACKET_SIZE_1024;
|
||||
}
|
||||
else if (pkt_type == YM_EOT || pkt_type == YM_CAN)
|
||||
{
|
||||
return pkt_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = (uint8_t)pkt_type;
|
||||
|
||||
for (int i = 1; i < data_len + YM_PACKET_OVERHEAD - 1; i++)
|
||||
{
|
||||
int d = ymodemRead(uart, 100);
|
||||
if (d < 0)
|
||||
return -1;
|
||||
buf[i] = (uint8_t)d;
|
||||
}
|
||||
|
||||
return pkt_type;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef __IAP_H__
|
||||
#define __IAP_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include "common_types.h"
|
||||
|
||||
class UartBus;
|
||||
|
||||
#define IAP_APP_BASE_ADDR 0x08010000U
|
||||
#define IAP_BOOTLOADER_SIZE (64U * 1024U)
|
||||
#define IAP_FIRMWARE_MAX_SIZE (1024U * 1024U - IAP_BOOTLOADER_SIZE)
|
||||
#define IAP_BUFFER_SIZE (1024U * 1024U)
|
||||
|
||||
class Iap {
|
||||
public:
|
||||
static Iap &instance();
|
||||
|
||||
RetCode init();
|
||||
|
||||
RetCode receiveFirmware(UartBus &uart, uint32_t timeout_ms = 120000);
|
||||
|
||||
RetCode programFirmware(uint32_t flash_addr);
|
||||
|
||||
RetCode verifyFirmware(uint32_t flash_addr);
|
||||
|
||||
__attribute__((noreturn)) void jumpToApp(uint32_t app_addr);
|
||||
|
||||
static bool isValidApp(uint32_t app_addr);
|
||||
|
||||
static void bootFlagWrite(uint32_t magic);
|
||||
static uint32_t bootFlagRead(void);
|
||||
|
||||
uint32_t firmwareSize() const { return firmware_size_; }
|
||||
const char *firmwareName() const { return firmware_name_; }
|
||||
|
||||
private:
|
||||
Iap();
|
||||
~Iap() = default;
|
||||
Iap(const Iap &) = delete;
|
||||
Iap &operator=(const Iap &) = delete;
|
||||
|
||||
static RetCode backupDomainEnable(void);
|
||||
|
||||
RetCode ymodemReceive(UartBus &uart, uint32_t timeout_ms);
|
||||
int ymodemReadPacket(UartBus &uart, uint8_t *buf, uint32_t timeout_ms);
|
||||
void ymodemSend(UartBus &uart, uint8_t b);
|
||||
int ymodemRead(UartBus &uart, uint32_t timeout_ms);
|
||||
|
||||
static uint16_t crc16(const uint8_t *data, uint32_t len);
|
||||
|
||||
bool initialized_;
|
||||
uint32_t firmware_size_;
|
||||
char firmware_name_[32];
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user