STM32F4-Disc1:keil MDK 版本 5 中的用户定义软件延迟不起作用

Posted

技术标签:

【中文标题】STM32F4-Disc1:keil MDK 版本 5 中的用户定义软件延迟不起作用【英文标题】:STM32F4-Disc1: user defined software delay in keil MDK version 5 not working 【发布时间】:2020-10-20 16:39:26 【问题描述】:

我正在学习嵌入式系统,并尝试实现blinky,但由于某种原因,软件延迟被跳过。当我按下按钮时,我原以为它会闪烁,但 LED 却一直亮着。

我使用的代码如下所示,

#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;

我正在使用板级支持 LED 和按钮 API。

我该如何解决这个问题?

我的调试器启动如下:

【问题讨论】:

请检查编译器是否优化了你的空循环。您可以通过查看汇编列表或调试器中的反汇编机器代码来做到这一点。 您好,我不确定如何检查,但如果这是问题,我该如何解决? 好吧,我描述了如何检查:查看程序集列表(您可能需要一个选项集)或调试器的反汇编。如果循环在那里,它应该有一些说明。 【参考方案1】:

这里的问题是这是死代码,它什么都不做,什么都没有交互,所以可以/应该优化。优化器通常会这样做。

void delay(void)

    int i;
    for(i=0; i<5000000 ;i++);

优化输出:

00000000 <delay>:
   0:   4770        bx  lr

一种方法是不优化

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

但这对于嵌入式平台来说有点残酷,所以另一个是乞求编译器对变量做一些事情,将其保存在内存中并保持最新:

void delay(void)

    volatile int i;
    for(i=0; i<5000000 ;i++);

它仍然有点难看,但会消耗一些时间:

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

双赢的方法是在编译域之外有另一个功能,让优化器工作。

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

更干净一点,燃烧一些时间但不过分,是的,请注意这是全拇指变体代码。被调用的函数可以简单地是 bx lr,因为你不关心它对调用的作用。

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

为 mcu 构建会清理弹出,因为在 armv4t 或 5t 之后,您可以弹出 pc 以返回任一模式,即使这是拇指模式,但您仍然可以使用这些工具来处理。

现在正如其他人所展示的那样,由于您不关心订单只是想计数,您可以根据架构(通常支持此功能)倒计时。我们要求编译器不要生成这个死代码,所以它必须按照我们要求的顺序执行,成为 C 代码的功能表示。

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

现在比较消失了(i-- vs--i 产生了影响,i-- 产生了更多代码)

使用易失性:

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

这并没有利用指令集,哦,好吧。 (更高或更低的计数并不重要,因为这真的不能/不会是一个调整循环,要在这样的平台上调整它,你真的需要使用 asm,即使在那里也很难调整)。

更清洁的也只是在组装中完成

.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

或者让它通用

.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

然后用 C 调用它

delay(5000000);

很多选项,但其他人没有显示的是正在优化的代码以及这些选项对代码的作用。使用工具在编译器输出中很容易看到发生了什么以及为什么会发生这种情况。

并且有多种方法可以使它或请求它不是死代码。大多数人只是在波动中折腾并继续前进。通常没有什么问题。

【讨论】:

另一种选择是使用 asm("NOP");__nop() 而不是虚拟函数。【参考方案2】:

在编译器设置中指定 -O0 作为优化标志,以避免无用的循环(从编译器的角度)被优化掉。 或者检查 MDK 或 BSP 是否提供已知可以工作的 delay() 函数。

【讨论】:

【参考方案3】:

    您是如何发现循环被跳过的(可能您的按钮功能不起作用)

    测试它:

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);
                   
           

【讨论】:

嗨,我试过这个代码。首先,我构建代码并将其加载到设备中。它不起作用。然后我使用调试器并运行代码,由于某种原因它开始工作。调试也在启动文件中开始,如原始帖子所示。我添加了一张图片以进行澄清。然后当我运行它时,代码就可以工作了。知道为什么吗? @Tea 如果您写“它不起作用”,那么请明确:什么不起作用?如何?你能指望什么?会发生什么?您可以在问题中添加这些信息。【参考方案4】:
void delay(void)
    
        volatile int i;
        for(i=0; i<5000000 ;i++);
    

如果 Buttons_GetState() 工作正常,这应该可以工作。将变量“i”声明为 volatile,以便编译器不会进行优化。

【讨论】:

以上是关于STM32F4-Disc1:keil MDK 版本 5 中的用户定义软件延迟不起作用的主要内容,如果未能解决你的问题,请参考以下文章

我的mdk5出现这个问题,怎么解决

keil 4.22 MDK 建工程时出现的重定义错误

安装Keil STM32F1到mdk5中时的时候一直显示程序未响应

下载的STM32代码,打开工程后弹出“Using an MDK Version 4 Project"

Keil MDK仿真调试STM32的时候直接进入SystemInit函数

keil mdk 5用哪个版本