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 中的用户定义软件延迟不起作用的主要内容,如果未能解决你的问题,请参考以下文章
安装Keil STM32F1到mdk5中时的时候一直显示程序未响应
下载的STM32代码,打开工程后弹出“Using an MDK Version 4 Project"