Tea
Tea

Reputation: 29

STM32F4-Disc1: user defined software delay in keil MDK version 5 not working

I am getting into learning embedded systems and I tried to implement blinky but the software delay gets skipped for some reason. I was expecting it to blink when I am pushing the button but instead the LEDs kept on.

Code I have used is shown below,

#include Board_LED.h
#include Board_Buttons.h
#include <stdint.h>

void delay(void);

void delay(void) {
  int i;
  for (i = 0; i < 5000000; i++)
    ;
}

int main(void) {
  LED_Initialize();
  Buttons_Initialize();

  while (1) {
    if (Buttons_GetState() == 1) {
      LED_On(0);
      LED_On(1);
      LED_On(2);
      LED_On(3);
      delay();
      LED_Off(0);
      LED_Off(1);
      LED_Off(2);
      LED_Off(3);
      delay();
    }
  }
  return 0;
}

I'm using board support LED and button APIs.

How do I fix this?

My debugger starts as follows:

Debugger view

Upvotes: 1

Views: 1023

Answers (4)

old_timer
old_timer

Reputation: 71516

The problem here is this is dead code, it does nothing interacts with nothing so can/should be optimized out. And an optimizer will often do this.

void delay(void)
{
    int i;
    for(i=0; i<5000000 ;i++);
}

optimized output:

00000000 <delay>:
   0:   4770        bx  lr

One way is to not optimize

00000000 <delay>:
   0:   b580        push    {r7, lr}
   2:   b082        sub sp, #8
   4:   af00        add r7, sp, #0
   6:   2300        movs    r3, #0
   8:   607b        str r3, [r7, #4]
   a:   e002        b.n 12 <delay+0x12>
   c:   687b        ldr r3, [r7, #4]
   e:   3301        adds    r3, #1
  10:   607b        str r3, [r7, #4]
  12:   687b        ldr r3, [r7, #4]
  14:   4a04        ldr r2, [pc, #16]   ; (28 <delay+0x28>)
  16:   4293        cmp r3, r2
  18:   ddf8        ble.n   c <delay+0xc>
  1a:   46c0        nop         ; (mov r8, r8)
  1c:   46c0        nop         ; (mov r8, r8)
  1e:   46bd        mov sp, r7
  20:   b002        add sp, #8
  22:   bc80        pop {r7}
  24:   bc01        pop {r0}
  26:   4700        bx  r0

But that's a bit brutal for an embedded platform so another is to beg the compiler to do something with the variable, keep it in memory and up to date:

void delay(void)
{
    volatile int i;
    for(i=0; i<5000000 ;i++);
}

It's still a bit ugly but that will burn some time:

00000000 <delay>:
   0:   2300        movs    r3, #0
   2:   b082        sub sp, #8
   4:   9301        str r3, [sp, #4]
   6:   9b01        ldr r3, [sp, #4]
   8:   4a05        ldr r2, [pc, #20]   ; (20 <delay+0x20>)
   a:   4293        cmp r3, r2
   c:   dc05        bgt.n   1a <delay+0x1a>
   e:   9b01        ldr r3, [sp, #4]
  10:   3301        adds    r3, #1
  12:   9301        str r3, [sp, #4]
  14:   9b01        ldr r3, [sp, #4]
  16:   4293        cmp r3, r2
  18:   ddf9        ble.n   e <delay+0xe>
  1a:   b002        add sp, #8
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   004c4b3f    .word   0x004c4b3f

The win-win way is to have another function outside the compile domain and let the optimizer work.

void dummy ( int );
void delay(void)
{
    int i;
    for(i=0; i<5000000 ;i++) dummy(i);
}

00000000 <delay>:
   0:   b570        push    {r4, r5, r6, lr}
   2:   2400        movs    r4, #0
   4:   4d04        ldr r5, [pc, #16]   ; (18 <delay+0x18>)
   6:   0020        movs    r0, r4
   8:   3401        adds    r4, #1
   a:   f7ff fffe   bl  0 <dummy>
   e:   42ac        cmp r4, r5
  10:   d1f9        bne.n   6 <delay+0x6>
  12:   bc70        pop {r4, r5, r6}
  14:   bc01        pop {r0}
  16:   4700        bx  r0
  18:   004c4b40    .word   0x004c4b40

A little cleaner, burns some time but isn't excessive, yes note this is all-thumb-variants code. The called function can simply be a bx lr since you don't care what it does with the call.

00000000 <delay>:
   0:   b538        push    {r3, r4, r5, lr}
   2:   2400        movs    r4, #0
   4:   4d03        ldr r5, [pc, #12]   ; (14 <delay+0x14>)
   6:   4620        mov r0, r4
   8:   3401        adds    r4, #1
   a:   f7ff fffe   bl  0 <dummy>
   e:   42ac        cmp r4, r5
  10:   d1f9        bne.n   6 <delay+0x6>
  12:   bd38        pop {r3, r4, r5, pc}
  14:   004c4b40    .word   0x004c4b40

Building for the mcu cleans up the pop as after armv4t or 5t you could pop the pc to return to either mode, even though this is thumb mode only you still deal with that with these tools.

Now as shown by others, since you don't care about order just want to count you can, depending on architecture (often this is supported) count down. We are asking the compiler to not make this dead code so it has to do it in the order we asked, to be a functional representation of the C code.

void dummy ( int );
void delay(void)
{
    int i=5000000;
    while(--i) dummy(i);
}

00000000 <delay>:
   0:   b510        push    {r4, lr}
   2:   4c03        ldr r4, [pc, #12]   ; (10 <delay+0x10>)
   4:   4620        mov r0, r4
   6:   f7ff fffe   bl  0 <dummy>
   a:   3c01        subs    r4, #1
   c:   d1fa        bne.n   4 <delay+0x4>
   e:   bd10        pop {r4, pc}
  10:   004c4b3f    .word   0x004c4b3f

And now the compare went away (i-- vs --i made a difference i-- makes for more code)

With volatile:

void delay(void)
{
    volatile int i=5000000;
    while(--i) continue;
}

00000000 <delay>:
   0:   b082        sub sp, #8
   2:   4b04        ldr r3, [pc, #16]   ; (14 <delay+0x14>)
   4:   9301        str r3, [sp, #4]
   6:   9b01        ldr r3, [sp, #4]
   8:   3b01        subs    r3, #1
   a:   9301        str r3, [sp, #4]
   c:   2b00        cmp r3, #0
   e:   d1fa        bne.n   6 <delay+0x6>
  10:   b002        add sp, #8
  12:   4770        bx  lr
  14:   004c4b40    .word   0x004c4b40


void delay(void)
{
    volatile int i=5000000;
    while(i--) continue;
}

00000000 <delay>:
   0:   b082        sub sp, #8
   2:   4b04        ldr r3, [pc, #16]   ; (14 <delay+0x14>)
   4:   9301        str r3, [sp, #4]
   6:   9b01        ldr r3, [sp, #4]
   8:   1e5a        subs    r2, r3, #1
   a:   9201        str r2, [sp, #4]
   c:   2b00        cmp r3, #0
   e:   d1fa        bne.n   6 <delay+0x6>
  10:   b002        add sp, #8
  12:   4770        bx  lr
  14:   004c4b40    .word   0x004c4b40

And that doesn't take advantage of the instruction set, oh well. (Being higher or lower one count doesn't matter as this really can't/won't be a tuned loop, to tune it on a platform like this you really need to use asm and even there it is difficult to tune).

Even cleaner just do it in assembly

.globl delay
delay:
   ldr r0,=5000000
dinner:
   sub r0,#1
   bne dinner
   bx lr

00000000 <delay>:
   0:   4801        ldr r0, [pc, #4]    ; (8 <dinner+0x6>)

00000002 <dinner>:
   2:   3801        subs    r0, #1
   4:   d1fd        bne.n   2 <dinner>
   6:   4770        bx  lr
   8:   004c4b40    .word   0x004c4b40

or make it generic

.globl delay
delay:
   sub r0,#1
   bne delay
   bx lr

00000000 <delay>:
   0:   3801        subs    r0, #1
   2:   d1fe        bne.n   0 <delay>
   4:   4770        bx  lr

and then call it from C with

delay(5000000);

Lots of options, but what others didn't show is the code being optimized away and what the choices do to the code. It is quite easy to see in the compiler output using the tools what is going on and why this happened.

And there are various ways to make it or request it to not be dead code. Most people just toss in a volatile and move on. Nothing wrong with that, usually.

Upvotes: 1

Babajan
Babajan

Reputation: 382

void delay(void)
    {
        volatile int i;
        for(i=0; i<5000000 ;i++);
    }

This should work provided that Buttons_GetState() is working fine. Declared variable 'i' as volatile so that no optimization happens by compiler.

Upvotes: 0

0___________
0___________

Reputation: 67476

  1. How did you discover that the loop was skipped (maybe your button function is not working)

  2. Test it with:

void delay(volatile uint32_t del)
{
while(del--);
}


int main(void)
{
    LED_Initialize();
    Buttons_Initialize();

    while(1){
        if( 1 || Buttons_GetState() == 1){ //it skips the if checks
            LED_On(0);
            LED_On(1);
            LED_On(2);
            LED_On(3);
            delay(500000);
            LED_Off(0);
            LED_Off(1);
            LED_Off(2);
            LED_Off(3);
            delay(500000);
            }       
        }   
}

Upvotes: 0

HS2
HS2

Reputation: 179

Either specify -O0 as optimization flag in the compiler settings to avoid that the useless loop (from the compiler point of view) is optimized away. Alternatively check the MDK or BSP for a provided delay() function known to work.

Upvotes: 1

Related Questions