generated from hulk/gd32e23x_template_cmake_vscode
476 lines
16 KiB
C
476 lines
16 KiB
C
#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;
|
||
}
|
||
}
|
||
}
|
||
} |