cancel
Showing results for 
Search instead for 
Did you mean: 

App works only once upon burning with Flash Loader

patricktoday
Associate II
Posted on May 28, 2012 at 04:44

Hi, I have just received a new Mini-STM32 board from here:

http://au.suntekstore.com/goods-14002458-32inch+TFT+LCD+Module++Mini+STM32+Development+Board++USB+Cable.html

I put together some sample code to, basically, flash an LED, tested it on a local debugger, no problems, ran it in the Keil uVision simulator software, all good, burned the binary to the chip using ST's Flash Loader demo, all was well, the light flashed normally; however, when I clicked the ''Reset'' button on the board, the app did not come back into operation (no activity at all on the board.)

So, basically, if I re-burn the app and choose the ''Jump to User Program'' option in Flash Loader Demo, the app will run, upon clicking Reset it will not.

I originally compiled this with the Code Sourcery Lite tools and the startup code from ST's Standard Periph. Lib. I then built a fresh project in Keil uVision and rebuilt from scratch using their supplied startup code - same result in either case.

Now, I had saved a binary of the demo app running on the board before I overwrote it. If I reload that binary, the board functions normally even after Reset; so I kind of think it's user error (me being the user 🙂 I'm an experienced programmer but completely new to Arm.

(Also, I only have USB access to the board so no way to see what it's actually doing...)

Any help would be greatly appreciated! I can't figure out what could be different between the Boot Loader's reset and the hardware Reset...

#stm32-startup #stm32-bootloader #stm32-bootloader-flash-loader
9 REPLIES 9
hfs99
Associate II
Posted on May 28, 2012 at 14:52

Whoah - good price. With all the money you saved you can splurge on a JTAG interface. 🙂

The first (little-endian) word loaded at 0x0800.0000 needs to be your stack pointer, and the next one needs to be the address of** your Reset_Handler. You can look at your .bin or .hex file along-side your map file and maybe get a clue what to look at next.

**The address vectors need to have the LSB set when they're pointing to Thumb code. For example, if the Reset_Handler is at 0x08000bfc, the vector in the table should be 08000bfd. I've read accounts of some tool chains / setups not setting the LSB, but have not experienced the problem myself.

patricktoday
Associate II
Posted on May 28, 2012 at 21:49

Yes! The price was a bargain, I just couldn't resist getting it. The support files are mainly in Chinese but it includes a clear schematic and I have been able to compile the sample programs so far with a little tweaking.

The first four bytes of the binary are: 00 50 00 20 (0x20005000) and the next four are: 81 3c 00 08 (0x08003C81). This is exactly where the Reset_Handler starts (minus 1 for the Thumb flag).

08003c80 <Reset_Handler>:

 8003c80: 2100       movs r1, #0

 8003c82: e003       b.n 8003c8c <LoopCopyDataInit>

08003c84 <CopyDataInit>:

 8003c84: 4b0a       ldr r3, [pc, #40] ; (8003cb0 <LoopFillZerobss+0x10>)

 8003c86: 585b       ldr r3, [r3, r1]

 8003c88: 5043       str r3, [r0, r1]

 8003c8a: 3104       adds r1, #4

08003c8c <LoopCopyDataInit>:

 8003c8c: 4809       ldr r0, [pc, #36] ; (8003cb4 <LoopFillZerobss+0x14>)

 8003c8e: 4b0a       ldr r3, [pc, #40] ; (8003cb8 <LoopFillZerobss+0x18>)

 8003c90: 1842       adds r2, r0, r1

 8003c92: 429a       cmp r2, r3

 8003c94: d3f6       bcc.n 8003c84 <CopyDataInit>

 8003c96: 4a09       ldr r2, [pc, #36] ; (8003cbc <LoopFillZerobss+0x1c>)

 8003c98: e002       b.n 8003ca0 <LoopFillZerobss>

08003c9a <FillZerobss>:

 8003c9a: 2300       movs r3, #0

 8003c9c: f842 3b04  str.w r3, [r2], #4

08003ca0 <LoopFillZerobss>:

 8003ca0: 4b07       ldr r3, [pc, #28] ; (8003cc0 <LoopFillZerobss+0x20>)

 8003ca2: 429a       cmp r2, r3

 8003ca4: d3f9       bcc.n 8003c9a <FillZerobss>

 8003ca6: f7ff fcad  bl 8003604 <SystemInit>

 8003caa: f7ff febf  bl 8003a2c <main>

 8003cae: 4770       bx lr So it looks like it ''should be'' calling SystemInit() and then main().

Maybe I'm setting this up wrong... I'm including these startup files from ST's Std Periph lib:

startup_stm32f10x_md.S

system_stm32f10x.c (contains SystemInit())

stm32_flash.ld (found at STM32F10x_StdPeriph_Template\TrueSTUDIO\STM3210B-EVAL)

I can get some of the examples that came with the board to work correctly (even after Reset) so I guess I'm doing something wrong. I am just trying to set up a ''correct'' minimal template by including only files from CMSIS and the ST Periph library and it's just not starting after Reset. Probably getting the JTAG wire is definitely not a bad idea 🙂

Posted on May 28, 2012 at 23:37

Well life certainly will be hard without some debug connectivity, be it JTAG/SWD or serial output.

You could check the .MAP file, and make sure the FLASH and RAM addresses are correct. With a more complete disassembly you could walk through the code. The listing provided doesn't seem to be a problem.

You could make sure main() does not return, this will certainly crash.

You could make sure the HSE_VALUE and PLL settings are suitable for the hardware on the board. If the clocks are mismatched it could get stuck in System_Init() bringing them up. A simple implementation could just run off HSI directly.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
hfs99
Associate II
Posted on May 29, 2012 at 00:41

Everything there looks exactly right. My best guess now is that you're not getting a mode register or some such set up quite right. Maybe in some cases you get a ''left over'' desired setup from the previous program until you reset. Although a SystemInit() issue is another possibility.

The .._md.S file looks right. The system_stm32f10x.c should be fine as long as you #define the HSE clock somewhere. (I assume you #defined STM32F10X_MD already to get this far.) I don't know about the .ld file but it appears that the linker put everything in the right place.

I confess I bought a very similar board a while back (at a higher price). Here is an almost minimal blinky that doesn't use the StdPeriphLib (in case one of those calls is part of your problem). LEDs might be on different pins.

#include ''stm32f10x.h''

__IO uint16_t ticks;

int main(void){

   RCC->APB2ENR = RCC_APB2ENR_IOPBEN;

   GPIOB->CRL = 0x00000022;                                    /* PB.0,1 2MHz out */

   SysTick_Config((SysTick->CALIB) & SysTick_LOAD_RELOAD_Msk); /* 1 msec          */

   while (1) {

      GPIOB->ODR = (ticks >> 12);

   }

}

void SysTick_Handler(void){ ticks++; }

If you can just get the serial port working, you can use that for debug. For simple stuff anyway...

patricktoday
Associate II
Posted on May 29, 2012 at 22:01

Hm, thanks for all the help! It's definitely very useful. In fact, this code works perfectly once I update the port and pin info, even on Reset. The only obvious difference (to me) is that you call SysTick_Config() and I don't. I am using TIM2 which fires an interrupt to toggle the LED. I kind of borrowed the code from here:

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32f100 how start TIM1 interrupt handler&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=915

Here's my code in case anything is obvious:

int main(void){

outpin_init();

timer_init(1);

while(1)

{

}

}

void outpin_init(){

GPIO_InitTypeDef gpio;

gpio.GPIO_Pin = GPIO_Pin_2;

gpio.GPIO_Mode = GPIO_Mode_Out_PP;

gpio.GPIO_Speed = GPIO_Speed_2MHz;

GPIO_Init(GPIOA, &gpio);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

}

void togglePin(){

GPIOA->ODR ^= GPIO_Pin_2;

}

void timer_init(uint tickFrequency){

TIM_TimeBaseInitTypeDef timebase;

NVIC_InitTypeDef nvic;

nvic.NVIC_IRQChannel = TIM2_IRQn;

nvic.NVIC_IRQChannelPreemptionPriority = 0;

nvic.NVIC_IRQChannelSubPriority = 1;

nvic.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&nvic);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

SetPeriod(tickFrequency, &timebase);

TIM_TimeBaseInit(TIM2, &timebase);

if (timebase.TIM_Prescaler == 0)

throw(''Overload xxyyzz'');

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

TIM_Cmd(TIM2, ENABLE);

}

void SetPeriod(uint tickFrequency, TIM_TimeBaseInitTypeDef* timebase){

uint prescale = SystemCoreClock / tickFrequency;

uint period = 0;

#ifdef KEILC

uint shifter = __clz(__rbit(prescale)); //count trailing zeros

int leftPusher = 16 - (_clz(prescale >> shifter)); //ensure none of the 1st 16 bits are used

#else

uint shifter = __builtin_ctz(prescale);

int leftPusher = 16 - (__builtin_clz(prescale >> shifter));

#endif

if (leftPusher>0)

shifter += leftPusher;

if (shifter > 0xF)

shifter = 0xF;

prescale = (prescale >> shifter) - 1;

period = (1 << shifter) - 1;

timebase->TIM_Period = period;

timebase->TIM_Prescaler = prescale;

timebase->TIM_CounterMode = TIM_CounterMode_Up;

timebase->TIM_ClockDivision = 0;

}

void TIM2_IRQHandler(void){

if (TIM_GetITStatus(TIM2, TIM_IT_Update) == RESET)

return;

TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

togglePin();

}

}

I checked the HSE_VALUE setting and it was supplied correctly in the library headers. I guess I'm probably getting something wrong with setting up the interrupt or the timer. Is calling Systick_Config() a requirement? I notice the Chinese code that came with the board (and works) calls it too but the sample timer code with the interrupts did not.

Posted on May 29, 2012 at 23:26

You must enable the clock before configuring the peripheral

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

before GPIO_Init();

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
patricktoday
Associate II
Posted on May 29, 2012 at 23:49

Awesome, you hit the nail on the head. Thank you!

hfs99
Associate II
Posted on May 30, 2012 at 04:34

Systick etc. absolutely not a requirement. That was just some code I had lying around. I should have taken 5 minutes to chop out the non-essentials. Anyway, sounds like you're up and running!

patricktoday
Associate II
Posted on May 30, 2012 at 06:44

Yeah, well it was a great help because I was thinking it had to do with startup code 'til I saw I was actually getting to main(). Plus, you forced me to actually go read all the bit configurations on setting up the IO port... yuck!