跳转到stm32f4上的第二个固件
Jumping to a second firmware on stm32f4
我正在为 stm32 上的应用程序 运行 构建引导加载程序。
这样做的目的是能够更新主应用程序。
由于我们的软件非常模块化,我的想法是只配置它的最小版本。所有初始化都是相同的,它跳转到包含所有引导加载程序功能的 main
函数(检查外部闪存上是否有新固件可用,如果是,则将其写入内部闪存),最后跳转到实际的应用程序 - 重新进行初始化,但这次使用额外的外围设备等,最终调用真正的 main
.
内部flash上的内存布局是这样的
|0x08000000 boot loader
|----------------------
|0x08006000 application
bootloader 主要是这样的
extern void CallApplication(void);
int main(void) {
printf("starting bootloader\n");
printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");
CallApplication();
return 0;
}
其中 CallApplication
是用汇编语言编写的
#define VTABLE_START_ADDRESS APP_START_ADDRESS
#define NVIC_VTABLE 0xE000ED08 // Vector Table Offset
.globl CallApplication
.thumb_func
CallApplication:
// Set the application's vector table start address.
movw r0, #(VTABLE_START_ADDRESS & 0xffff)
movt r0, #(VTABLE_START_ADDRESS >> 16)
movw r1, #(NVIC_VTABLE & 0xffff)
movt r1, #(NVIC_VTABLE >> 16)
str r0, [r1]
// Load the stack pointer from the application's vector table.
ldr sp, [r0]
// Load the initial PC from the application's vector table and branch to
// the application's entry point.
ldr r0, [r0, #4]
bx r0
这几乎可以工作 - 'real' 应用程序被调用,进行初始化但最终由于未知原因崩溃。
有趣的是,引导加载程序 (0x080022ae
) 的故障 ISR 被调用,而不是实际应用程序 (> 0x08006000
) 的故障 ISR,因此设置新向量 table 显然失败了.
2016-02-11 00:21:16,958 - INFO # init UART
2016-02-11 00:21:16,963 - INFO # Application: boot_loader
2016-02-11 00:21:16,973 - INFO # -- init done, starting main --
2016-02-11 00:21:16,974 - INFO # starting bootloader
2016-02-11 00:21:16,976 - INFO # will jump to 0x8006000
2016-02-11 00:21:16,978 - INFO # init UART
2016-02-11 00:21:16,985 - INFO # Application: hello_world
2016-02-11 00:21:17,797 - INFO # -- init done, starting main --
(hard fault led starts flashing)
我在这里错过了什么?
主应用程序的链接描述文件定义
MEMORY
{
FLASH (rx) : ORIGIN = 0x08006000, LENGTH = 488K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
而引导加载程序
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 24K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
其余的共享
SECTIONS
{
.text :
{
_text = .;
/*
* The vector table must be placed to the top of the
* memory map. To achieve this, it was assigned to a
* special section called ".isr_vector"
*/
KEEP(*(.isr_vector))
/* followed by .text and .rodata: */
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
/* Just to make sure that the contents does not exceed the flash size */
. = ORIGIN(FLASH) + LENGTH(FLASH);
/*
* .data and .bss are placed into SRAM:
*/
.data : AT(ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(.data*)
_edata = .;
} > SRAM
.bss :
{
/* _bss and _ebss will be required during initialization */
_bss = .;
*(.bss*)
_ebss = .;
} > SRAM
.aux : {
. = ALIGN(4);
*(.auxdata) /* .auxdata section */
. = ALIGN(4);
} > SRAM
/* Just to make sure that the contents does not exceed the SRAM size */
. = ORIGIN(SRAM) + LENGTH(SRAM);
}
编辑: 我重写了在 C 中设置 VTOR
的部分,以使我更清楚发生了什么,但我仍然在引导加载程序的 DefaultISR 中结束
printf("starting bootloader\n");
printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");
printf("before: %x\n", SCB->VTOR);
SCB->VTOR += APP_START_ADDRESS;
printf("after: %x\n", SCB->VTOR);
asm volatile("mov r0, #0x6000");
asm volatile("ldr sp, [r0]");
asm volatile("ldr r0, [r0, #4]");
asm volatile("bx r0");
产出
2016-02-11 23:49:31,833 - INFO # starting bootloader
2016-02-11 23:49:31,835 - INFO # will jump to 0x6000
2016-02-11 23:49:31,836 - INFO # before: 8000000
2016-02-11 23:49:31,837 - INFO # after: 8006000
2016-02-11 23:49:31,839 - INFO # init UART
2016-02-11 23:49:31,841 - INFO # …
HAL 的 CPU init 函数正在做
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
然后将我的设置覆盖为 SCB->VTOR
。
移除后即可使用,无需其他魔法。
在我的例子中是 STM32L Cortex-M3,但我认为它的工作方式相同。
在引导加载程序中,禁用所有中断源(不屏蔽它们)后,我执行以下操作:
#define APP_LOCATION 0x08006000
typedef void (*pFunction)(void);
pFunction jump;
volatile uint32_t jumpAddress;
register uint32_t regMainStackPointer __ASM("msp");
void Jump( void ) {
jumpAddress = *( volatile uint32_t* )( APP_LOCATION + 4 );
jump = ( pFunction )jumpAddress;
mainStackPointer = *( volatile uint32_t* )APP_LOCATION;
jump();
}
在应用程序本身中,启用任何中断之前要做的第一件事是:
SCB->VTOR = 0x0x08006000;
这里的链接器是相等的。
我注意到你的代码有些奇怪:
SCB->VTOR += APP_START_ADDRESS;
如果APP_START_ADDRESS包含地址(0x08006000)而不是偏移量(0x6000),则VTOR中的结果值为0x08000000 + 0x08006000,也许问题出在这里?
如果您显示应用程序中的一些代码,可能会有所帮助。
希望对你有帮助。
我正在为 stm32 上的应用程序 运行 构建引导加载程序。 这样做的目的是能够更新主应用程序。
由于我们的软件非常模块化,我的想法是只配置它的最小版本。所有初始化都是相同的,它跳转到包含所有引导加载程序功能的 main
函数(检查外部闪存上是否有新固件可用,如果是,则将其写入内部闪存),最后跳转到实际的应用程序 - 重新进行初始化,但这次使用额外的外围设备等,最终调用真正的 main
.
内部flash上的内存布局是这样的
|0x08000000 boot loader
|----------------------
|0x08006000 application
bootloader 主要是这样的
extern void CallApplication(void);
int main(void) {
printf("starting bootloader\n");
printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");
CallApplication();
return 0;
}
其中 CallApplication
是用汇编语言编写的
#define VTABLE_START_ADDRESS APP_START_ADDRESS
#define NVIC_VTABLE 0xE000ED08 // Vector Table Offset
.globl CallApplication
.thumb_func
CallApplication:
// Set the application's vector table start address.
movw r0, #(VTABLE_START_ADDRESS & 0xffff)
movt r0, #(VTABLE_START_ADDRESS >> 16)
movw r1, #(NVIC_VTABLE & 0xffff)
movt r1, #(NVIC_VTABLE >> 16)
str r0, [r1]
// Load the stack pointer from the application's vector table.
ldr sp, [r0]
// Load the initial PC from the application's vector table and branch to
// the application's entry point.
ldr r0, [r0, #4]
bx r0
这几乎可以工作 - 'real' 应用程序被调用,进行初始化但最终由于未知原因崩溃。
有趣的是,引导加载程序 (0x080022ae
) 的故障 ISR 被调用,而不是实际应用程序 (> 0x08006000
) 的故障 ISR,因此设置新向量 table 显然失败了.
2016-02-11 00:21:16,958 - INFO # init UART
2016-02-11 00:21:16,963 - INFO # Application: boot_loader
2016-02-11 00:21:16,973 - INFO # -- init done, starting main --
2016-02-11 00:21:16,974 - INFO # starting bootloader
2016-02-11 00:21:16,976 - INFO # will jump to 0x8006000
2016-02-11 00:21:16,978 - INFO # init UART
2016-02-11 00:21:16,985 - INFO # Application: hello_world
2016-02-11 00:21:17,797 - INFO # -- init done, starting main --
(hard fault led starts flashing)
我在这里错过了什么?
主应用程序的链接描述文件定义
MEMORY
{
FLASH (rx) : ORIGIN = 0x08006000, LENGTH = 488K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
而引导加载程序
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 24K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
其余的共享
SECTIONS
{
.text :
{
_text = .;
/*
* The vector table must be placed to the top of the
* memory map. To achieve this, it was assigned to a
* special section called ".isr_vector"
*/
KEEP(*(.isr_vector))
/* followed by .text and .rodata: */
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
/* Just to make sure that the contents does not exceed the flash size */
. = ORIGIN(FLASH) + LENGTH(FLASH);
/*
* .data and .bss are placed into SRAM:
*/
.data : AT(ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(.data*)
_edata = .;
} > SRAM
.bss :
{
/* _bss and _ebss will be required during initialization */
_bss = .;
*(.bss*)
_ebss = .;
} > SRAM
.aux : {
. = ALIGN(4);
*(.auxdata) /* .auxdata section */
. = ALIGN(4);
} > SRAM
/* Just to make sure that the contents does not exceed the SRAM size */
. = ORIGIN(SRAM) + LENGTH(SRAM);
}
编辑: 我重写了在 C 中设置 VTOR
的部分,以使我更清楚发生了什么,但我仍然在引导加载程序的 DefaultISR 中结束
printf("starting bootloader\n");
printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");
printf("before: %x\n", SCB->VTOR);
SCB->VTOR += APP_START_ADDRESS;
printf("after: %x\n", SCB->VTOR);
asm volatile("mov r0, #0x6000");
asm volatile("ldr sp, [r0]");
asm volatile("ldr r0, [r0, #4]");
asm volatile("bx r0");
产出
2016-02-11 23:49:31,833 - INFO # starting bootloader
2016-02-11 23:49:31,835 - INFO # will jump to 0x6000
2016-02-11 23:49:31,836 - INFO # before: 8000000
2016-02-11 23:49:31,837 - INFO # after: 8006000
2016-02-11 23:49:31,839 - INFO # init UART
2016-02-11 23:49:31,841 - INFO # …
HAL 的 CPU init 函数正在做
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
然后将我的设置覆盖为 SCB->VTOR
。
移除后即可使用,无需其他魔法。
在我的例子中是 STM32L Cortex-M3,但我认为它的工作方式相同。
在引导加载程序中,禁用所有中断源(不屏蔽它们)后,我执行以下操作:
#define APP_LOCATION 0x08006000
typedef void (*pFunction)(void);
pFunction jump;
volatile uint32_t jumpAddress;
register uint32_t regMainStackPointer __ASM("msp");
void Jump( void ) {
jumpAddress = *( volatile uint32_t* )( APP_LOCATION + 4 );
jump = ( pFunction )jumpAddress;
mainStackPointer = *( volatile uint32_t* )APP_LOCATION;
jump();
}
在应用程序本身中,启用任何中断之前要做的第一件事是:
SCB->VTOR = 0x0x08006000;
这里的链接器是相等的。
我注意到你的代码有些奇怪:
SCB->VTOR += APP_START_ADDRESS;
如果APP_START_ADDRESS包含地址(0x08006000)而不是偏移量(0x6000),则VTOR中的结果值为0x08000000 + 0x08006000,也许问题出在这里?
如果您显示应用程序中的一些代码,可能会有所帮助。 希望对你有帮助。