使用cloner工具烧写固件需要在上电之前让boot_sel[2:0]处于boot from USB模式,但是电路板装在机壳内部后不方便改变boot_sel[2:0]的状态,如果要升级固件,需要通过机壳留出的USB口、网口、或者无线网络进行固件更新。
一、升级方案
1、固件分析
X2100的固件由两部分组成,bootloader和rtos app,spl bin文件就是bootloader,启动时由处理器内部固化的代码将bootloader拷贝到DDR,然后执行bootloader,bootloader再将rtos app拷贝到DDR,再执行rtos app。rtos app拷贝到DDR的地址由config文件指定,可通过配置界面->通用选项查看或修改。
2、启动流程
再增加一个二级bootloader,启动流程改为spl--->2ndboot--->RTOS App。
二、开发二级bootloader
1、修改配置文件
创建一个应用,配置文件只保留一些基本功能。
设置固件使用的内存大小为3MB,剩余的留给RTOS App,所以这个够用就行。
开启USB CDC功能 :
2、程序
参考sdk /freertos/example/usb/device/gadget_cdc_serial_boot_app.c文件。代码运行逻辑:
1、创建cdc_serial接收线程;
2、接收到上位机发送的boot-app消息后执行boot-app()函数;
3、boot-app()先查找指定的镜像分区,需要和cloner烧录工具中指定的名字一样;
#define PARTITON_NAME "app"
4、查找到分区后检查镜像的头部内容,如果头部内容的tag是0x534f5452(“RTOS”)再判断头部内容中指定的内存加载地址是否和当前运行的程序冲突,没有冲突就继续执行,有冲突就立即退出。
5、将镜像的内容从拷贝到镜像头部内容指定的地址,然后运行。
3、文件内容
#include <stdio.h>
#include <cpu/irqflags.h>
#include <driver/irq.h>
#include <cpu/cpu.h>
#include <common.h>
#include <usb/gadget_serial.h>
#include <os.h>
#include "filesystem/include/mtd_driver_nor.h"
#define PARTITON_NAME "app"
struct rtos_header {
unsigned int code[2];
unsigned int tag;
unsigned int version;
unsigned long img_start;
unsigned long img_end;
unsigned long heap_start;
unsigned long heap_end;
unsigned long mapped_rtosdata_size;
};
static const struct gadget_id serial_id = {
.vendor_id = 0x0525,
.product_id = 0xa4a7
};
static const struct usb_cdc_serial_param serial_parameters ={
.dwDTERate = 115200,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8
};
static void serial_param_callback(struct usb_cdc_serial_param *p)
{
printf("usb serial: dwDTERate %d, bCharFormat %d, bParityType %d, bDataBits %d\n",
p->dwDTERate, p->bCharFormat, p->bParityType, p->bDataBits);
}
static void serial_connect_callback(int connect)
{
printf("serial_connect_callback %d\n", connect);
}
static inline int rtos_check_header(struct rtos_header *rtos)
{
if (rtos->tag != 0x534f5452) {
printf("rtos bad tag: %x\n", rtos->tag);
return -1;
}
if (rtos->img_start >= rtos->img_end) {
printf("rtos bad off: %lx %lx\n", rtos->img_start, rtos->img_end);
return -1;
}
return 0;
}
static inline int rtos_check_intersection(unsigned int start0, unsigned int end0, unsigned int start1, unsigned int end1)
{
if (start1 < start0 && end1 < start0)
return 0;
if (start1 >= end0 && end1 >= end0)
return 0;
return 1;
}
static inline void rtos_start(struct rtos_header *rtos, void *arg)
{
void (*func)(void *arg) = (void *)rtos->img_start;
flush_cache_all();
func(arg);
}
static void release_all_resources(void)
{
usb_core_exit();
local_irq_disable();
arch_deinit_cpu();
// lcd
// disable_irq(IRQ_LCD);
// release_irq(IRQ_LCD);
// intc
disable_irq(IRQ_V_IP2);
release_irq(IRQ_V_IP2);
// ost
disable_irq(IRQ_V_IP4);
release_irq(IRQ_V_IP4);
local_irq_enable();
}
int read_rtos_start_addr_by_name(struct rtos_header *rtos, int *offset, const char *find_node_name)
{
struct mtd_nor_partition *mtd_part;
struct mtd_nor_partition *parts = sfc_nor_flash_partition_information();
if (parts == NULL) {
return -EIO;
}
for (mtd_part = parts; mtd_part->name != NULL; mtd_part++) {
if ( !strcmp(find_node_name, mtd_part->name) ) {
*offset = mtd_part->offset;
sfc_nor_flash_read( mtd_part->offset, sizeof(struct rtos_header), (void *)rtos);
break;
}
}
return 0;
}
static void boot_app(void)
{
int ret = 0;
int offset = 0;
struct rtos_header *rtos = malloc(sizeof(struct rtos_header));
assert(rtos != NULL);
memset(rtos, 0, sizeof(struct rtos_header));
// 根据烧录工具中设置的分区名字获取镜像信息
ret = read_rtos_start_addr_by_name(rtos, &offset, PARTITON_NAME);
if (ret != 0) {
printf("get partition_information fail!\n");
goto error;
}
if (rtos_check_header(rtos)) {
goto error;
}
// 检查加载是否会覆盖到旧系统地址上
if (rtos_check_intersection(CONFIG_OS_MEM_ADDR, (CONFIG_OS_MEM_ADDR + CONFIG_OS_MEM_SIZE), rtos->img_start, rtos->img_end)) {
printf("address overlap\n");
goto error;
}
sfc_nor_flash_read(offset, rtos->img_end -rtos->img_start, (void *)rtos->img_start);
release_all_resources();
rtos_start(rtos, NULL);
error:
free(rtos);
return ;
}
static unsigned char usb_test_buf[1024];
static void usb_gadget_serial_thread(void *data)
{
int len, i;
while (1) {
msleep(1000);
len = gadget_serial_read(usb_test_buf, sizeof(usb_test_buf), 0, 0);
if (len > 0) {
for (i = 0; i < len; i++){
printf("usb read %c\n", usb_test_buf[i]);
}
if ( !strncmp(usb_test_buf, "boot-app", strlen("boot-app"))) {
boot_app();
}
}
}
}
int application_2nd_boot_app(void)
{
gadget_serial_init(&serial_id, &serial_parameters, serial_connect_callback, serial_param_callback);
thread_create("usb gadget serial thread", 8192, usb_gadget_serial_thread, NULL);
return 0;
}
三、开发应用
1、修改配置文件
修改配置,不选spl文件,固件加载的地址需要≥二级bootloader的固件加载的地址+固件使用的内存大小,固件使用的内存大小为总的DDR大小 - 二级bootloader固件使用的内存大小 - 其余部分。如X2100的DDR大小是64MB,二级bootloader固件使用的内存大小是3MB,其余部分是1MB,那么应用的固件使用的内存大小是62914560(60MB)。
2、编译生成固件
修改配置文件之后先make 配置文件,然后执行make,在freertos目录下会生成一个zero.bin,可将其改为其它名字。
四、烧录和测试
1、SFC添加分区
启动cloner->配置->SFC->分区信息->添加一个app分区:
2、添加app烧录
3、 测试
烧录之后,设备运行的是二级bootloader的程序,用上位机给设备的CDC串口发送"boot-app"消息,二级bootloader会将app.bin拷贝到指定的内部地址,然后运行。
五、更新app
可在二级bootloader程序程序中加入Y-mode文件接收程序,上位机发送新的app.bin文件给设备,覆盖当前app.bin所在flash的内容。