Follow the reference to port the bootloader

This commit is contained in:
2025-09-28 01:14:06 +08:00
parent 422c27846f
commit bd541d585e
23 changed files with 712 additions and 1638 deletions

476
Src/bootloader.c Normal file
View File

@@ -0,0 +1,476 @@
#include "bootloader.h"
#include "board_config.h"
#include "gd32e23x.h"
#include "uart_ring_buffer.h"
#include "systick.h"
uint8_t ymodem_rx_buffer[YMODEM_RX_BUFFER_SIZE] = {0};
uint16_t ymodem_rx_count = 0;
static uint8_t temp_buffer[PACKET_1K_SIZE]; // 临时缓冲区,用于接收数据包
/**
* @brief 擦除Flash页面适配同事的flash_earse函数
* @param page_address 要擦除的页面地址
* @return 擦除结果状态
*/
uint8_t flash_erase_page(uint32_t page_address)
{
fmc_state_enum fmc_state = FMC_READY;
// 清除Flash状态标志
fmc_flag_clear(FMC_FLAG_BUSY | FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR | FMC_FLAG_END);
// 解锁Flash
fmc_unlock();
// 执行页面擦除
fmc_state = fmc_page_erase(page_address);
// 返回结果0=成功1=失败)
return (fmc_state == FMC_READY) ? 0 : 1;
}
/**
* @brief 写入数据到Flash适配同事的write_data函数
* @param flash_address Flash地址指针会被更新
* @param data 要写入的数据指针
* @param data_length 数据长度以32位字为单位
* @return 写入结果0=成功1=写入错误2=验证错误
*/
uint8_t flash_write_data(__IO uint32_t* flash_address, uint32_t* data, uint32_t data_length)
{
uint32_t i = 0;
// 清除Flash状态标志可选根据需要启用
// fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR | FMC_FLAG_END);
// 逐字写入数据
for (i = 0; (i < data_length) && (*flash_address <= (APP_FLASH_END_ADDRESS - 4)); i++) {
// 写入32位数据到Flash
if (fmc_word_program(*flash_address, *(uint32_t*)(data + i)) == FMC_READY) {
// 验证写入的数据
if (*(uint32_t*)*flash_address != *(uint32_t*)(data + i)) {
// Flash内容与写入数据不匹配
return 2;
}
// 增加Flash地址指针下一个32位字
*flash_address += 4;
} else {
// Flash写入过程中发生错误
return 1;
}
}
// 写入成功
return 0;
}
/**
* @brief Check flash flag and jump to application if valid
* @param buf Pointer to flash memory location to check
* @return uint8_t 1=stay in bootloader, 0=jumped to application (won't return)
*/
uint8_t check_flash_and_jump(uint8_t *buf)
{
uint32_t jump_address;
pFunction jump_to_application;
if (*buf == 0xFF) {
// Flash为空继续bootloader模式
return 1;
}
fmc_lock(); // 锁定FMC以防止意外修改
// 读取应用程序的栈指针和复位向量
jump_address = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
// 验证栈指针是否有效应该在SRAM范围内
// if ((app_stack_pointer & 0xFFF00000) != 0x20000000) {
// // 栈指针无效继续bootloader模式
// return 1;
// }
// 验证跳转地址是否有效应该在Flash应用程序区域内且为奇数-Thumb模式
// if ((jump_address < APPLICATION_ADDRESS) ||
// (jump_address > 0x08010000) || // 假设Flash大小为64KB
// ((jump_address & 0x01) == 0)) { // Thumb模式地址应为奇数
// // 跳转地址无效继续bootloader模式
// return 1;
// }
/* Ensure flash operations are complete */
/* Get application reset vector */
jump_address = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
/* Basic validity check - reset vector should be in flash and odd (Thumb mode) */
// if ((jump_address < APPLICATION_ADDRESS) || ((jump_address & 0x01) == 0)) {
// /* Invalid jump address, stay in bootloader */
// return 1;
// }
/* Create function pointer */
jump_to_application = (pFunction)jump_address;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);
/* Disable interrupts for safe transition */
__disable_irq();
/* Jump to user application - this function will not return */
jump_to_application();
/* Should never reach here */
return 0; /* Should never reach here */
}
/**
* @brief Convert string to integer (supports hex, decimal, K, M suffixes)
* @param input_str Input string to convert
* @param int_num Pointer to store converted integer
* @return 1 if successful, 0 if failed
*/
uint32_t str_to_int(uint8_t *input_str, int32_t *int_num)
{
uint32_t i = 0, res = 0;
uint32_t val = 0;
if (input_str[0] == '0' && (input_str[1] == 'x' || input_str[1] == 'X')) {
// Hexadecimal input
if (input_str[2] == '\0') {
return 0;
}
for (i = 2; i < 11; i++) {
if (input_str[i] == '\0') {
*int_num = val;
res = 1;
break;
}
if (ISVALIDHEX(input_str[i])) {
val = (val << 4) + CONVERTHEX(input_str[i]);
} else {
res = 0;
break;
}
}
if (i >= 11) {
res = 0; // Over 8 digit hex - invalid
}
} else {
// Decimal input (max 10-digit)
for (i = 0; i < 11; i++) {
if (input_str[i] == '\0') {
*int_num = val;
res = 1;
break;
} else if ((input_str[i] == 'k' || input_str[i] == 'K') && (i > 0)) {
val = val << 10; // Multiply by 1024
*int_num = val;
res = 1;
break;
} else if ((input_str[i] == 'm' || input_str[i] == 'M') && (i > 0)) {
val = val << 20; // Multiply by 1024*1024
*int_num = val;
res = 1;
break;
} else if (ISVALIDDEC(input_str[i])) {
val = val * 10 + CONVERTDEC(input_str[i]);
} else {
res = 0;
break;
}
}
if (i >= 11) {
res = 0; // Over 10 digit decimal - invalid
}
}
return res;
}
/**
* @brief Update CRC16 value with a single byte
* @param crc_in Current CRC value
* @param byte Byte to process
* @return Updated CRC value
*/
uint16_t crc16_update(uint16_t crc_in, uint8_t byte)
{
uint32_t crc = crc_in;
uint32_t in = byte | 0x100;
do {
crc <<= 1;
in <<= 1;
if (in & 0x100) {
++crc;
}
if (crc & 0x10000) {
crc ^= 0x1021;
}
} while (!(in & 0x10000));
return crc & 0xFFFFU;
}
/**
* @brief Calculate CRC16 for data buffer
* @param data Pointer to data buffer
* @param size Size of data in bytes
* @return Calculated CRC16 value
*/
uint16_t crc16_calculate(const uint8_t* data, uint32_t size)
{
uint32_t crc = 0;
const uint8_t* data_end = data + size;
while (data < data_end) {
crc = crc16_update(crc, *data++);
}
crc = crc16_update(crc, 0);
crc = crc16_update(crc, 0);
return crc & 0xFFFFU;
}
/**
* @brief 接收Ymodem数据包数据
* 等待并读取完整的数据包到缓冲区中
* 该函数保持与原始实现相同的行为逻辑
*/
void ymodem_read_packet_data(void)
{
// 重置接收计数器
ymodem_rx_count = 0;
// 等待数据到达(阻塞等待)
while (uart_ring_buffer_available() <= 0) {
// 可选:发送确认字节(当前被禁用)
// ymodem_send_response(YMODEM_ACK);
delay_ms(1000); // 等待1秒后重新检查
}
// 等待数据传输完成,确保接收到完整数据包
delay_ms(200);
// 读取环形缓冲区中的所有可用数据
while (uart_ring_buffer_available() > 0) {
int received_byte = uart_ring_buffer_get();
if (received_byte >= 0) {
ymodem_rx_buffer[ymodem_rx_count++] = (uint8_t)received_byte;
}
}
}
/**
* @brief Ymodem接收函数直接移植同事的逻辑
* @param buf 未使用(保持接口兼容)
* @return 接收的文件大小,负数表示错误
*/
int32_t ymodem_receive(uint8_t *buf)
{
uint8_t file_size[FILE_SIZE_LENGTH], *file_ptr;
int32_t i, j = 0, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0, isize = 0, singledata = 0;
uint32_t flash_destination, ram_source;
uint8_t temp = 0, eot_flag = 0, crc_high = 0, crc_low = 0;
uint16_t crc_value = 0;
uint32_t erase_page = APPLICATION_ADDRESS; // 从应用程序地址开始擦除
uint8_t pack = 1;
// 解锁Flash并擦除应用程序区域
fmc_unlock();
flash_destination = APPLICATION_ADDRESS;
// 擦除应用程序区域从APPLICATION_ADDRESS到FLASH_END_ADDRESS
for (; erase_page < FLASH_END_ADDRESS; erase_page += 1024) {
flash_erase_page(erase_page);
}
temp = 0;
// 发送CRC16请求等待数据
while (uart_ring_buffer_available() <= 0) {
rs485_send_byte(CRC16);
delay_ms(1000);
}
delay_ms(200);
// 读取第一个数据包
ymodem_read_packet_data();
// 主循环
for (session_done = 0, errors = 0, session_begin = 0;;) {
for (packets_received = 0, file_done = 0;;) {
delay_ms(2000);
switch (temp) {
case 0: // 处理文件头数据包
if (ymodem_rx_buffer[0] == SOH) { // 128字节数据包
// 验证包序号
if (ymodem_rx_buffer[1] != ((ymodem_rx_buffer[2] ^ 0xFF) & 0xFF))
break;
if (ymodem_rx_buffer[3] != 0) { // 有文件信息
// 提取文件名
for (i = 0, file_ptr = ymodem_rx_buffer + PACKET_HEADER;
(*file_ptr != 0) && (i < FILE_NAME_LENGTH);) {
file_ptr++;
}
// 提取文件大小
for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);) {
file_size[i++] = *file_ptr++;
}
file_size[i++] = '\0';
// 转换文件大小
str_to_int(file_size, &size);
// 计算需要的1K数据包数量
isize = size / PACKET_1K_SIZE;
singledata = size - (isize * PACKET_1K_SIZE);
// 验证CRC
crc_value = crc16_calculate(&ymodem_rx_buffer[3], 128);
crc_low = crc_value & 0xFF;
crc_value >>= 8;
crc_high = crc_value & 0xFF;
if (crc_high != ymodem_rx_buffer[131] || crc_low != ymodem_rx_buffer[132]) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
ymodem_read_packet_data();
break;
}
temp = 1;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
rs485_send_byte(CRC16);
ymodem_read_packet_data();
}
} else if (ymodem_rx_buffer[0] == CA) { // 取消传输
if (ymodem_rx_buffer[1] == CA && ymodem_rx_buffer[2] == CA &&
ymodem_rx_buffer[3] == CA && ymodem_rx_buffer[4] == CA) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 0;
}
ymodem_rx_count = 0;
return -3;
}
break;
case 1: // 处理数据包
if (ymodem_rx_buffer[0] == STX) { // 1K字节数据包
// 验证包序号
if (ymodem_rx_buffer[1] != ((ymodem_rx_buffer[2] ^ 0xFF) & 0xFF) &&
ymodem_rx_buffer[1] == pack) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
break;
}
// 验证CRC
crc_value = crc16_calculate(&ymodem_rx_buffer[3], PACKET_1K_SIZE);
crc_low = crc_value & 0xFF;
crc_value >>= 8;
crc_high = crc_value & 0xFF;
if (crc_high != ymodem_rx_buffer[PACKET_1K_SIZE + 3] ||
crc_low != ymodem_rx_buffer[PACKET_1K_SIZE + 4]) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(NAK);
break;
}
// 处理单独数据最后一包可能不满1K
if (singledata != 0) {
// 预留处理不完整数据包的逻辑
}
// 复制数据到临时缓冲区
memcpy(temp_buffer, ymodem_rx_buffer + PACKET_HEADER, PACKET_1K_SIZE);
ram_source = (uint32_t)temp_buffer;
fmc_unlock();
// 写入Flash
if (flash_write_data(&flash_destination, (uint32_t*)ram_source, PACKET_1K_SIZE) == 0) {
isize--;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
pack++;
rs485_send_byte(ACK);
// 如果是最后一包,写入应用程序标志
if (isize == 1) {
fmc_unlock();
// flash_write_app_flag();
fmc_word_program(FLASH_FLAG_ADDRESS, 0xEEEE);
}
ymodem_read_packet_data();
} else {
rs485_send_byte(CA);
rs485_send_byte(CA);
temp = 0;
ymodem_rx_count = 0;
return -2;
}
} else if (ymodem_rx_buffer[0] == EOT) { // 传输结束
if (eot_flag == 1) {
eot_flag = 0;
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
rs485_send_byte(CRC16);
ymodem_read_packet_data();
temp = 3;
}
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 3;
} else if (ymodem_rx_buffer[0] == CA) { // 取消传输
if (ymodem_rx_buffer[1] == CA && ymodem_rx_buffer[2] == CA &&
ymodem_rx_buffer[3] == CA && ymodem_rx_buffer[4] == CA) {
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
temp = 0;
}
ymodem_rx_count = 0;
return -3;
}
ymodem_rx_count = 0;
break;
case 3: // 传输完成
temp = 0;
fmc_lock();
ymodem_rx_count = 0;
ymodem_rx_buffer[0] = 0;
rs485_send_byte(ACK);
ymodem_rx_count = 0;
return size;
}
}
}
}