I made a bootloader that makes it possible to program the device in application. The bootloader software uses interrupts and communicate over an RS485 bus. When I jump to the base address of my firmware, it seems that the interrupts aren't working. I use the command below to redirect the vectortable in the firmware.
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08008000);
The method of jumping to my applicaton is the one documented in the AN2557.
static const uint32_t FIRMWARE_START_ADDRESS = 0x08008000ul; typedef void (*pFunction)(void); ... //Initialize jump to firmware uint32_t startAddress = *(__IO uint32_t*)(FIRMWARE_START_ADDRESS + 4); pFunction RestartFirmware = (pFunction)startAddress; //Initialize firmware Stack Pointer */ __set_MSP(*(__IO uint32_t*)FIRMWARE_START_ADDRESS);
I've tested the code and the jump occurs, and the code is run, but none of my interrupt driven deviced work properly.
I've tested the code by using the example from the AN2557 to jump to the my bootloader (recompiled to address 0x08002000) and then the jump to my bootloader works. Then I let my bootloader jump back (FIRMWARE_START_ADDRESS = 0x08000000) to the AN2557 bootloader and that also worked. But when I jump for a second time to my bootloader it doesn't work anymore. I've added the vector redirection to both projects and it still doesn't work.
My bootloader is written in c++. Most of my hardware is managed by singleton classes. Is something not initialized properly (statics)?
Hi Bjorn,
I have developed a similar library starting from the same application note, but I approached it in a slightly different way.
The loader is located at 0x08000000 and just jumps (if there is valid code) at a given location. Actually it can jump at several locations.
I have placed an updater at UPDTADDR = 0x08000000+OFFSETUPDT and my application at APPLADDR = 0x08000000+OFFSETAPPL. They both relocate the vector table at the proper address.
In normal cases the loader jumps to APPLADDR.
But when I need to update the code of the application I force the bootloader to jump to UPDTADDR and I do whatever I need (in your case use an USART to communicate, in my case ethernet). At the end of the operation, I dont jump to any location but I prefer to force a reset passing again from the loader.
The reason is that if I jumped I should deinit every HW peripheral, every ISR etc.
You can use code as in the following to jump or to restart:
static void s_jump_to(uint32_t appaddr) { volatile uint32_t jumpaddr; void (*app_fn)(void) = NULL; // prepare jump address jumpaddr = *(__IO uint32_t*) (appaddr + 4); // prepare jumping function app_fn = (void (*)(void)) jumpaddr; // initialize user application's stack pointer __set_MSP(*(__IO uint32_t*) appaddr); // jump. app_fn(); } extern void system_restart(void) { ... // disable irqs .... is it really necessary? __disable_irq(); // restart system NVIC_SystemReset(); // should be never in here assert(0); }
I hope it can help.
Reagrds, Marco.
I've tried to reset my system using the following piece of testcode:
uint8_t loopcounter = 0; while (1) { loopcounter++; DEBUG("Loop %d",loopcounter); if (loopcounter >= 10) { __disable_irq(); NVIC_SystemReset(); ASSERT(false); } waitSeconds(1); }
What can be the problem for not resetting? DEBUG an ASSERT are macro's for sending information to the USART1 port. When the loop is run for the 10th time the system is reset.
With the ULINK2 i can break the operation and find that the system is waiting to reset in the while(1) loop in NVIC_SystemReset().
static __INLINE void NVIC_SystemReset(void) { SCB->AIRCR = (NVIC_AIRCR_VECTKEY | (SCB->AIRCR & (0x700)) | (1<<NVIC_SYSRESETREQ)); /* Keep priority group unchanged */ __DSB(); /* Ensure completion of memory access */ while(1); /* wait until reset */ }
If you use ulink2, then it is correct that the micro won't reset and stay in the forever loop of NVIC_SystemReset().
I think that if you dont use ulink2 then your DEBUG() should keep on repeating "Loop 1" ... "Loop 10". That would prove an effective reset.
I tested it with something like that, where the led showed me the effective reset.
int main() { uint32_t cnt = 10; <put a led on (or blink it fast) for 5 seconds> for(;;) { <toggle led> <wait for 1 second> if(--cnt == 0) { <put a led off> NVIC_SystemReset(); for(;;); } } }
Try again ....
Cheers, Marco.
If I run the controller without the ULINK2 the code still doesn't reset either. It keeps running in the while(1) loop (I guess, can't really tell because there is no debug, only can see that a led stops blinking).
Is there some register that has to be set, the only reset that works right now is the external reset, but than isn't very practical to trigger remotely when the device.
Can it be that the externa reset-input has some sort of influance on the functioning of the internal reset circuit?
The following works on my MCBSTM32C board.
You can verify if it works for you too and then find what does not make it work in your project.
1. Download latest CMSIS at www.onarm.com/.../download396.asp
2. Open project CM3\Example\arm\STM32F107\CMSIS_Example_STM32F107.uv2 for Keil's MCBSTM32C.
3. edit main_STM32F107.c and add tagged lines:
int main (void) { uint32_t nn = 50; // tag: added to test reset if (SysTick_Config(SystemCoreClock / 1000)) { /* Setup SysTick Timer for 1 msec interrupts */ while (1); /* Capture error */ } LED_Config(); // tag: added to test reset LED_On (0x100); Delay (5*1000); while(1) { LED_On (0x100); /* Turn on the LED. */ Delay (100); /* delay 100 Msec */ LED_Off (0x100); /* Turn off the LED. */ Delay (100); /* delay 100 Msec */ // tag: added to test reset if(--nn == 0) { NVIC_SystemReset(); } } }
If you have a MCBSTM32 board you can use the project CMSIS_Example_STM32F103.uv2 but remember to chacneh some settings (type of micro, inhibit use of external memory) and change startup file.
Regards, Marco.
I've solved the problem. The reset input was connected in such a way that the internal reset circuit couldn't drive the reset correctly. I've change de external reset circuit (is now open collector driver) and the NVIC_SystemReset() works. (The Watchdog didn't work either, but works fine now).
I've also changed part of the code so that I de-initialize all the periferals before I try to jump to the other application. Every class will now neatly deinitialize the used periferals on destruction.
well done ;-).