Files
secs2-bootloader/usr/bsp/bsp_iap.c

456 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "bsp_iap.h"
#include <stdio.h>
#include <string.h>
#include "app.h"
#include "bsp_uart.h"
#include "usr_config.h"
#include "os_timer.h"
/* IAP 状态定义 */
#define BSP_IAP_STATE_NOMORAL (0) /* 正常模式,等待跳转 */
#define BSP_IAP_STATE_UPDATEING (1) /* 正在升级 */
#define BSP_IAP_STATE_SUCCEED (2) /* 升级完成,准备跳转 */
/* IAP 协议接收帧头 */
#define BSP_IAP_PROTO_RX_HEADER (0x5A)
/* IAP 协议接收命令码 */
#define BSP_IAP_PROTO_RX_CMD_GET_SV (0x01) /* 读取版本号 */
#define BSP_IAP_PROTO_RX_CMD_CODE_SIZE (0x02) /* 获取代码大小并擦除扇区 */
#define BSP_IAP_PROTO_RX_CMD_WRITE_DATA (0x03) /* 写入数据 */
#define BSP_IAP_PROTO_RX_CMD_UNKNOW_1 (0x04) /* 未知命令1 */
#define BSP_IAP_PROTO_RX_CMD_UNKNOW_2 (0x05) /* 未知命令2 */
#define BSP_IAP_PROTO_RX_CMD_UNKNOW_3 (0x06) /* 未知命令3 */
/* IAP 协议发送帧头 */
#define BSP_IAP_PROTO_TX_HEADER (0xA5)
/* IAP 协议错误码 */
#define BSP_IAP_PROTO_ERROR_CODE_SUCCEED (0) /* 解析成功 */
#define BSP_IAP_PROTO_ERROR_CODE_CHECK (1) /* 校验错误 */
#define BSP_IAP_PROTO_ERROR_CODE_CMD (2) /* 命令错误 */
#define BSP_IAP_PROTO_ERROR_CODE_LENGTH (3) /* 长度错误 */
/* 发送缓冲区大小 */
#define BSP_IAP_TX_LEN (32)
/* 静态变量 */
static u16 bsp_app_time_start; /* 等待跳转的起始时间(毫秒) */
static u8 bsp_iap_tx_buf[BSP_IAP_TX_LEN]; /* 发送缓冲区 */
/* 函数声明 */
static void app_jump(void);
static void flash_page_erase(u16 page_num);
static void flash_write_u32(u32 write_addr, u32 *p_buffer, u16 num_write);
static void flash_write_u8(u32 write_addr, u8 *p_buffer, u16 num_write);
static void bsp_iap_send(u8 *p_data, u16 len);
static void bsp_iap_rx_task(u8 *p_data, u16 len, void *other_data);
static void bsp_iap_init(void);
static void bsp_iap_task(void);
static u8 sum_check(u8 *p_data, u16 len);
/******************************************
* 结构体: bsp_iap
* 功能: IAP 控制实例
* 描述: 定义 IAP 的具体配置和回调函数
*******************************************/
bsp_iap_t iap =
{
.init = bsp_iap_init,
.task = bsp_iap_task,
};
/* 全局指针,指向 IAP 结构体 */
bsp_iap_t *p_iap = &iap;
bsp_Uart_t *p_rx_uart;
/* 应用程序起始地址 */
#define APP_START_ADDR 0x08002800
/* 扇区大小(字节) */
#define SECTOR_SIZE FLASH_PAGE_SIZE
/******************************************
* 函数: app_jump
* 功能: 跳转到应用程序
* 参数: 无
* 返回: 无
* 描述: 关闭所有外设和中断,设置堆栈指针,跳转到用户程序
*******************************************/
static void app_jump(void)
{
u32 app_addr = APP_START_ADDR;
/* 检查应用程序是否存在(堆栈指针有效) */
if (((*(u32*)app_addr) & 0x2FFE0000) == 0x20000000)
{
typedef void (*iapfun)(void);
iapfun jump_to_app;
/* 关闭所有中断 */
__disable_irq();
/* 设置所有时钟到默认状态使用HSI时钟 */
HAL_RCC_DeInit();
// LL_USART_Disable(USART1);
// LL_USART_Disable(USART2);
//
// LL_USART_DisableIT_RXNE_RXFNE(USART1);
// LL_USART_DisableIT_RXNE_RXFNE(USART2);
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 关闭所有中断,清除所有中断挂起标志 */
for (u8 i = 0; i < 8; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
/* 跳转到应用程序 */
jump_to_app = (iapfun)*(u32*)(app_addr + 4); /* 复位向量地址 */
__set_MSP(*(u32*)app_addr); /* 设置主堆栈指针 */
__enable_irq();
jump_to_app(); /* 跳转 */
while(1);
}
}
static void bsp_Flash_FLASH_ErasePage(uint32_t Address)
{
FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t PageError = 0;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.PageAddress = Address;
EraseInitStruct.NbPages = 1;
HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
}
/******************************************
* 函数: flash_page_erase
* 功能: 擦除指定数量的 Flash 扇区
* 参数: page_num - 要擦除的扇区数量
* 返回: 无
* 描述: 从 APP_START_ADDR 开始擦除连续 page_num 个扇区
*******************************************/
static void flash_page_erase(u16 page_num)
{
u16 i;
HAL_FLASH_Unlock();//解锁
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR );
for (i = 0; i < page_num; i++)
{
bsp_Flash_FLASH_ErasePage(i * SECTOR_SIZE+APP_START_ADDR);
}
HAL_FLASH_Lock();//上锁
}
#include <string.h> // 使用 memcpy
/**
* @brief 将数据缓冲区写入 Flash半字编程
* @param write_addr Flash 目标地址(必须是 8 字节对齐)
* @param code_buffer 源数据缓冲区(可以为任意对齐)
* @param size 要写入的字节数
*/
static void flash_code_write(uint32_t write_addr, uint8_t *code_buffer, uint16_t size)
{
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR);
// 按半字2字节写入
for (uint16_t i = 0; i < size; i += 2)
{
uint16_t data;
if (i + 1 < size)
data = ((uint16_t)code_buffer[i+1] << 8) | code_buffer[i];
else
data = (uint16_t)code_buffer[i]; // 最后一个字节高8位补0
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, write_addr + i, data);
}
HAL_FLASH_Lock();
}
/******************************************
* 函数: bsp_iap_send
* 功能: 通过 UART1 发送数据
* 参数: p_data - 数据缓冲区指针
* len - 数据长度
* 返回: 无
* 描述: 调用 UART1 的发送接口
*******************************************/
static void bsp_iap_send(u8 *p_data, u16 len)
{
if(p_rx_uart != NULL)
{
p_rx_uart->Send(p_rx_uart, p_data, len);
}
}
/******************************************
* 函数: bsp_iap_init
* 功能: IAP 初始化
* 参数: 无
* 返回: 无
* 描述: 设置初始状态,并注册接收处理函数
*******************************************/
static void bsp_iap_init(void)
{
p_iap->state = BSP_IAP_STATE_NOMORAL;
//COM_Uart1.Rx_DataAnalysis = bsp_iap_rx_task;
COM_Uart2.Rx_DataAnalysis = bsp_iap_rx_task;
}
/******************************************
* 函数: bsp_iap_task
* 功能: IAP 任务(主循环中调用)
* 参数: 无
* 返回: 无
* 描述: 根据当前状态执行相应操作
*******************************************/
static void bsp_iap_task(void)
{
switch (p_iap->state)
{
case BSP_IAP_STATE_NOMORAL: /* 正常模式2 秒后跳转 APP */
{
if (TIME_TRUE == OsTimer_CheckTimeOut(bsp_app_time_start, osTime_MSecTick, 1000))
{
app_jump();
}
} break;
case BSP_IAP_STATE_UPDATEING: /* 升级中 */
{
/* 预留,可能处理超时等 */
} break;
case BSP_IAP_STATE_SUCCEED: /* 升级完成,立即跳转 */
{
app_jump();
} break;
default:
break;
}
}
/******************************************
* 函数: sum_check
* 功能: 计算校验和(累加和取反)
* 参数: p_data - 数据指针
* len - 数据长度
* 返回: 校验和字节
* 描述: 对数据进行累加求和,然后取反(补码)
*******************************************/
static u8 sum_check(u8 *p_data, u16 len)
{
u16 i;
u8 sum = 0;
for (i = 0; i < len; i++)
{
sum += p_data[i];
}
return (-sum);
}
/******************************************
* 函数: bsp_iap_rx_task
* 功能: IAP 协议接收处理函数
* 参数: p_data - 接收数据缓冲区
* len - 数据长度
* other_data - 附加数据(未使用)
* 返回: 无
* 描述: 解析 IAP 协议帧,执行相应命令并回复
*******************************************/
static void bsp_iap_rx_task(u8 *p_data, u16 len, void *other_data)
{
u16 i, cmd, data_len, tx_len = 0;
u8 check_data, error_code = BSP_IAP_PROTO_ERROR_CODE_SUCCEED;
u8 *p_data_offset;
if(BSP_IAP_STATE_SUCCEED == p_iap->state)/*升级成功后,不再进行检测*/
{
return;
}
/* 帧头检测和长度检查 */
if (p_data[0] != BSP_IAP_PROTO_RX_HEADER || len < 4)
{
return;
}
data_len = p_data[1] << 8 | p_data[2];
if (len - 4 != data_len) /* 长度错误 */
{
error_code = BSP_IAP_PROTO_ERROR_CODE_LENGTH;
}
else
{
check_data = sum_check(p_data, len - 1);
if (check_data != p_data[len - 1]) /* 校验错误 */
{
error_code = BSP_IAP_PROTO_ERROR_CODE_CHECK;
}
}
p_rx_uart = (bsp_Uart_t*)(other_data);
cmd = p_data[3];
p_data_offset = &p_data[4];
/* 根据命令处理 */
switch (cmd)
{
case BSP_IAP_PROTO_RX_CMD_GET_SV: /* 获取软件版本号 */
{
u8 str_len, tx_data_len;
p_iap->state = BSP_IAP_STATE_UPDATEING; /* 进入升级状态 */
str_len = strlen(SwVersion) + 1;
memcpy(&bsp_iap_tx_buf[3], SwVersion, str_len);
tx_data_len = str_len;
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
case BSP_IAP_PROTO_RX_CMD_CODE_SIZE: /* 获取代码大小并擦除扇区 */
{
u8 tx_data_len;
u16 erase_size, erase_page, page_size;
erase_page = p_data_offset[0] << 8 | p_data_offset[1];
page_size = p_data_offset[2] << 8 | p_data_offset[3];
p_iap->page_size = page_size;
/* 计算需要擦除的扇区数 */
erase_page /= ((float)SECTOR_SIZE / (float)page_size);
flash_page_erase(erase_page);
tx_data_len = 0;
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
case BSP_IAP_PROTO_RX_CMD_WRITE_DATA: /* 写入数据 */
{
u8 tx_data_len;
u16 index, code_size, addr_offset, i;
u32 addr;
u8 *p_code_data;
index = p_data_offset[0] << 8 | p_data_offset[1];
code_size = data_len - 3;
addr = index * p_iap->page_size;
p_code_data = &p_data_offset[2];
flash_code_write(APP_START_ADDR+addr, p_code_data, code_size);
/* 构造应答帧,返回当前索引 */
bsp_iap_tx_buf[3] = index >> 8;
bsp_iap_tx_buf[4] = index & 0x00ff;
tx_data_len = 2;
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
case BSP_IAP_PROTO_RX_CMD_UNKNOW_1: /* 未知命令1升级完成 */
{
u8 tx_data_len;
tx_data_len = 0;
p_iap->state = BSP_IAP_STATE_SUCCEED; /* 升级完成 */
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
case BSP_IAP_PROTO_RX_CMD_UNKNOW_3: /* 未知命令3回传两个字节 */
{
u8 tx_data_len;
tx_data_len = 2;
bsp_iap_tx_buf[3] = p_data_offset[0];
bsp_iap_tx_buf[4] = p_data_offset[1];
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
case BSP_IAP_PROTO_RX_CMD_UNKNOW_2: /* 未知命令2简单应答 */
{
u8 tx_data_len;
tx_data_len = 0;
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = tx_data_len + 1;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[tx_data_len + 3] = sum_check(bsp_iap_tx_buf, bsp_iap_tx_buf[1] + 2);
tx_len = bsp_iap_tx_buf[1] + 3;
} break;
default:
{
error_code = BSP_IAP_PROTO_ERROR_CODE_CMD;
} break;
}
/* 错误处理 */
switch (error_code)
{
case BSP_IAP_PROTO_ERROR_CODE_SUCCEED:
{
bsp_iap_send(bsp_iap_tx_buf, tx_len);
} break;
case BSP_IAP_PROTO_ERROR_CODE_CHECK:
case BSP_IAP_PROTO_ERROR_CODE_CMD:
case BSP_IAP_PROTO_ERROR_CODE_LENGTH:
{
bsp_iap_tx_buf[0] = BSP_IAP_PROTO_TX_HEADER;
bsp_iap_tx_buf[1] = 0x01;
bsp_iap_tx_buf[2] = cmd;
bsp_iap_tx_buf[3] = error_code;
bsp_iap_tx_buf[4] = sum_check(bsp_iap_tx_buf, 4);
bsp_iap_send(bsp_iap_tx_buf, 5);
} break;
default:
break;
}
}