STM32F4-Disc1:keil MDK 版本 5 中用户定义的软件延迟不起作用
STM32F4-Disc1: user defined software delay in keil MDK version 5 not working
我正在学习嵌入式系统,我尝试实现 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。
我该如何解决这个问题?
我的调试器启动如下:
在编译器设置中指定 -O0 作为优化标志,以避免无用循环(从编译器的角度来看)被优化掉。
或者检查 MDK 或 BSP 是否提供已知有效的 delay() 函数。
你是怎么发现循环被跳过的(可能是你的按钮功能没用)
测试:
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);
}
}
}
void delay(void)
{
volatile int i;
for(i=0; i<5000000 ;i++);
}
如果 Buttons_GetState() 工作正常,这应该可以工作。将变量 'i' 声明为 volatile,这样编译器就不会进行优化。
这里的问题是这是死代码,它什么都不做,什么都不交互,所以 can/should 被优化掉了。优化器通常会这样做。
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
在 armv4t 或 5t 之后,为 mcu 构建会清除 pop,您可以将 pc 弹出到 return 任何一种模式,即使这只是拇指模式,您仍然可以使用这些工具处理它。
现在正如其他人所展示的那样,由于您不关心顺序只想计数,您可以根据体系结构(通常支持)倒数。我们要求编译器不要生成此死代码,因此它必须按照我们要求的顺序执行,成为 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
这并没有利用指令集,哦,好吧。 (更高或更低的计数并不重要,因为这真的不能't/won 调整循环,要在这样的平台上调整它,你真的需要使用 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);
很多选项,但其他人没有显示的是被优化掉的代码以及这些选项对代码的影响。使用这些工具可以很容易地在编译器输出中看到发生了什么以及为什么会发生这种情况。
并且有多种方法可以使它或要求它不是死代码。大多数人只是在波动中折腾并继续前进。通常没有错。
我正在学习嵌入式系统,我尝试实现 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。
我该如何解决这个问题?
我的调试器启动如下:
在编译器设置中指定 -O0 作为优化标志,以避免无用循环(从编译器的角度来看)被优化掉。 或者检查 MDK 或 BSP 是否提供已知有效的 delay() 函数。
你是怎么发现循环被跳过的(可能是你的按钮功能没用)
测试:
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);
}
}
}
void delay(void)
{
volatile int i;
for(i=0; i<5000000 ;i++);
}
如果 Buttons_GetState() 工作正常,这应该可以工作。将变量 'i' 声明为 volatile,这样编译器就不会进行优化。
这里的问题是这是死代码,它什么都不做,什么都不交互,所以 can/should 被优化掉了。优化器通常会这样做。
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
在 armv4t 或 5t 之后,为 mcu 构建会清除 pop,您可以将 pc 弹出到 return 任何一种模式,即使这只是拇指模式,您仍然可以使用这些工具处理它。
现在正如其他人所展示的那样,由于您不关心顺序只想计数,您可以根据体系结构(通常支持)倒数。我们要求编译器不要生成此死代码,因此它必须按照我们要求的顺序执行,成为 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
这并没有利用指令集,哦,好吧。 (更高或更低的计数并不重要,因为这真的不能't/won 调整循环,要在这样的平台上调整它,你真的需要使用 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);
很多选项,但其他人没有显示的是被优化掉的代码以及这些选项对代码的影响。使用这些工具可以很容易地在编译器输出中看到发生了什么以及为什么会发生这种情况。
并且有多种方法可以使它或要求它不是死代码。大多数人只是在波动中折腾并继续前进。通常没有错。