无法在STM32F4微控制器上闪烁LED
Can't blink LED on STM32F4 microcontroller
我想确保我已经正确设置了我的第一个嵌入式软件项目,因此我试图使我的 NUCLEO-F411RE 板(STM32F411RE 微控制器)上的 LED 闪烁。我没有使用任何 IDE,因为我想从头开始做所有事情。我的项目结构如下
├── build
│ ├── Buggy.bin
│ ├── Buggy.dis
│ ├── Buggy.elf
│ ├── Buggy.hex
│ ├── Buggy.map
│ ├── main.o
│ ├── startup_stm32f411xe.o
│ ├── stm32f4xx_it.o
│ └── system_stm32f4xx.o
├── lib
│ ├── cmsis
│ │ ├── include
│ │ │ ├── arm_common_tables.h
│ │ │ ├── arm_const_structs.h
│ │ │ ├── arm_math.h
│ │ │ ├── core_cm0.h
│ │ │ ├── core_cm0plus.h
│ │ │ ├── core_cm3.h
│ │ │ ├── core_cm4.h
│ │ │ ├── core_cm7.h
│ │ │ ├── core_cmFunc.h
│ │ │ ├── core_cmInstr.h
│ │ │ ├── core_cmSimd.h
│ │ │ ├── core_sc000.h
│ │ │ └── core_sc300.h
│ │ └── stm32f4xx
│ │ ├── stm32f4xx.h
│ │ └── system_stm32f4xx.h
│ ├── Makefile
│ └── STM32F4xx_StdPeriph_Driver
│ ├── include
│ │ ├── misc.h
│ │ ├── stm32f4xx_adc.h
│ │ ├── stm32f4xx_crc.h
│ │ ├── stm32f4xx_dbgmcu.h
│ │ ├── stm32f4xx_dma.h
│ │ ├── stm32f4xx_exti.h
│ │ ├── stm32f4xx_flash.h
│ │ ├── stm32f4xx_flash_ramfunc.h
│ │ ├── stm32f4xx_gpio.h
│ │ ├── stm32f4xx_i2c.h
│ │ ├── stm32f4xx_iwdg.h
│ │ ├── stm32f4xx_pwr.h
│ │ ├── stm32f4xx_rcc.h
│ │ ├── stm32f4xx_rtc.h
│ │ ├── stm32f4xx_sdio.h
│ │ ├── stm32f4xx_spi.h
│ │ ├── stm32f4xx_syscfg.h
│ │ ├── stm32f4xx_tim.h
│ │ ├── stm32f4xx_usart.h
│ │ └── stm32f4xx_wwdg.h
│ ├── libstdperiph.a
│ ├── Makefile
│ └── src
│ ├── misc.c
│ ├── misc.o
│ ├── stm32f4xx_adc.c
│ ├── stm32f4xx_adc.o
│ ├── stm32f4xx_crc.c
│ ├── stm32f4xx_crc.o
│ ├── stm32f4xx_dbgmcu.c
│ ├── stm32f4xx_dbgmcu.o
│ ├── stm32f4xx_dma.c
│ ├── stm32f4xx_dma.o
│ ├── stm32f4xx_exti.c
│ ├── stm32f4xx_exti.o
│ ├── stm32f4xx_flash.c
│ ├── stm32f4xx_flash.o
│ ├── stm32f4xx_flash_ramfunc.c
│ ├── stm32f4xx_flash_ramfunc.o
│ ├── stm32f4xx_gpio.c
│ ├── stm32f4xx_gpio.o
│ ├── stm32f4xx_i2c.c
│ ├── stm32f4xx_i2c.o
│ ├── stm32f4xx_iwdg.c
│ ├── stm32f4xx_iwdg.o
│ ├── stm32f4xx_pwr.c
│ ├── stm32f4xx_pwr.o
│ ├── stm32f4xx_rcc.c
│ ├── stm32f4xx_rcc.o
│ ├── stm32f4xx_rtc.c
│ ├── stm32f4xx_rtc.o
│ ├── stm32f4xx_sdio.c
│ ├── stm32f4xx_sdio.o
│ ├── stm32f4xx_spi.c
│ ├── stm32f4xx_spi.o
│ ├── stm32f4xx_syscfg.c
│ ├── stm32f4xx_syscfg.o
│ ├── stm32f4xx_tim.c
│ ├── stm32f4xx_tim.o
│ ├── stm32f4xx_usart.c
│ ├── stm32f4xx_usart.o
│ ├── stm32f4xx_wwdg.c
│ └── stm32f4xx_wwdg.o
├── main.c
├── main.h
├── Makefile
├── startup_stm32f411xe.s
├── stm32f4xx_conf.h
├── stm32f4xx_flash.ld
├── stm32f4xx_it.c
├── stm32f4xx_it.h
└── system_stm32f4xx.c
我正在使用 STD_Periph 库来处理与实际硬件和 CMSIS 的接口,以对实际处理器核心和外围设备进行硬件抽象(我认为)。
main.c 源文件应包含使电路板上的 LED 每秒闪烁的代码。
#include "stm32f4xx.h"
void TimingDelay_Decrement(void);
static __IO uint32_t uwTimingDelay;
static void Delay(__IO uint32_t nTime);
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
while (1) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
Delay(1000);
}
}
void Delay(__IO uint32_t nTime)
{
uwTimingDelay = nTime;
while(uwTimingDelay != 0x00) {
uwTimingDelay--;
}
}
void TimingDelay_Decrement(void)
{
if (uwTimingDelay != 0x00)
{
uwTimingDelay--;
}
}
当我使用 Makefile 和 make
命令构建我的项目时,一切正常。然后我 运行 make flash,也成功完成。但是,none 个 LED 每秒都在闪烁。我不知道问题出在哪里,尤其是没有错误信息,所以我很难调试。我觉得它与 Makefile 或链接描述文件有关,所以我将在下面包含它们。
生成文件:
# STM32F4-Discovery Makefile
C_SRC=$(wildcard *.c) \
$(wildcard src/*.c)
# Add assembly source files here or use $(wildcard *.s) for all .s files
S_SRC = $(wildcard *.s)
# Project name
PROJ_NAME = Buggy
OUTPATH = build
BINPATH = /usr/bin/
OUTPATH := $(abspath $(OUTPATH))
BASEDIR := $(abspath ./)
MKDIR_P = mkdir -p
###################################################
# Check for valid float argument
# NOTE that you have to run make clean after
# changing these as hardfloat and softfloat are not
# binary compatible
ifneq ($(FLOAT_TYPE), hard)
ifneq ($(FLOAT_TYPE), soft)
#override FLOAT_TYPE = hard
override FLOAT_TYPE = soft
endif
endif
###################################################
AS=$(BINPATH)arm-none-eabi-as
CC=$(BINPATH)arm-none-eabi-gcc
LD=$(BINPATH)arm-none-eabi-gcc
OBJCOPY=$(BINPATH)arm-none-eabi-objcopy
OBJDUMP=$(BINPATH)arm-none-eabi-objdump
SIZE=$(BINPATH)arm-none-eabi-size
LINKER_SCRIPT = stm32f4xx_flash.ld
CPU = -mcpu=cortex-m4 -mthumb
CFLAGS = $(CPU) -c -std=gnu99 -g -O2 -Wall
LDFLAGS = $(CPU) -mlittle-endian -mthumb-interwork -Wl,--gc-sections,-Map=$(OUTPATH)/$(PROJ_NAME).map,--cref --specs=nano.specs
ifeq ($(FLOAT_TYPE), hard)
CFLAGS += -fsingle-precision-constant -Wdouble-promotion
CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
else
CFLAGS += -msoft-float
endif
# Default to STM32F411xE if no device is passed
ifeq ($(DEVICE_DEF), )
DEVICE_DEF = STM32F411xE
endif
CFLAGS += -D$(DEVICE_DEF)
vpath %.a lib
# Includes
INCLUDE_PATHS = -I$(BASEDIR)/lib/cmsis/stm32f4xx -I$(BASEDIR)/lib/cmsis/include -I$(BASEDIR)
INCLUDE_PATHS += -I$(BASEDIR)/lib/STM32F4xx_StdPeriph_Driver/include
# Library paths
LIBPATHS = -L$(BASEDIR)/lib/STM32F4xx_StdPeriph_Driver
# Libraries to link
LIBS = -lstdperiph -lc -lgcc -lnosys
OBJS = $(C_SRC:.c=.o)
OBJS += $(S_SRC:.s=.o)
###################################################
.PHONY: lib proj
all: dir lib proj
$(SIZE) $(OUTPATH)/$(PROJ_NAME).elf
lib:
$(MAKE) -C lib FLOAT_TYPE=$(FLOAT_TYPE) BINPATH=$(BINPATH) DEVICE_DEF=$(DEVICE_DEF) BASEDIR=$(BASEDIR)
proj: $(OUTPATH)/$(PROJ_NAME).elf
.s.o:
$(AS) $(CPU) -o $(addprefix $(OUTPATH)/, $@) $<
.c.o:
$(CC) $(CFLAGS) -std=gnu99 $(INCLUDE_PATHS) -o $(addprefix $(OUTPATH)/, $@) $<
$(OUTPATH)/$(PROJ_NAME).elf: $(OBJS)
$(LD) $(LDFLAGS) -T$(LINKER_SCRIPT) $(LIBPATHS) -o $@ $(addprefix $(OUTPATH)/, $^) $(LIBS) $(LD_SYS_LIBS)
$(OBJCOPY) -O ihex $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).hex
$(OBJCOPY) -O binary $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).bin
$(OBJDUMP) -S --disassemble $(OUTPATH)/$(PROJ_NAME).elf > $(OUTPATH)/$(PROJ_NAME).dis
dir:
$(MKDIR_P) $(OUTPATH)
clean:
rm -f $(OUTPATH)/*.o
rm -f $(OUTPATH)/$(PROJ_NAME).elf
rm -f $(OUTPATH)/$(PROJ_NAME).hex
rm -f $(OUTPATH)/$(PROJ_NAME).bin
rm -f $(OUTPATH)/$(PROJ_NAME).dis
rm -f $(OUTPATH)/$(PROJ_NAME).map
# Remove the following line if you don't want to clean the Libraries as well
$(MAKE) clean -C lib
flash:
st-flash --reset write $(OUTPATH)/$(PROJ_NAME).bin 0x08000000
链接描述文件:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;; /* required amount of heap */
_Min_Stack_Size = 0x400;; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(4);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
编辑:
我已经安装了 cubeMX 并使用了它们生成的代码。然而,闪烁的 LED 仍然不可见。
这是我在 main.c 文件中的主要方法:
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1) {
// write pin state
// NOTE: You can in turn use HAL_GPIO_TogglePin
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
// synchronous delay for 500 ms
HAL_Delay(500);
}
}
我假设生成的代码工作正常,所以错误要么在我的 main.c 中,要么只是硬件问题。
如果这是您的第一个项目 - 不要使用 SPL(标准外设库)。 STM 不再支持它,并且没有用于较新的 STM32 uC 的库。安装 cubeMX 并使用此工具项目生成的启动。
在您修改后的代码中,您需要
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
而不是 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
您没有切换到正确的 I/O。来自 Nucleo-F411RE 用户手册:
User LD2: the green LED is a user LED connected to Arduino signal D13 corresponding to STM32 I/O PA5 (pin 21) or PB13 (pin 34) depending on the STM32 target.
D13 heare 是指 Arduino 连接器 D13 引脚 - 该名称是为了与 Arduino Shields 兼容,与 STM32 GPIO 引脚名称无关。在您的情况下,它是 PA5(Table 用户手册中的 16)。
您的(原始)延迟函数存在根本性缺陷。忙循环延迟将根据处理器的时钟速率、使用的编译器甚至使用的编译器选项而有所不同。但更重要的是,因为从 1000 开始递减不会花费很长的时间,因此 "flashing" 会太快而无法用人眼感知,甚至可能超过 LED 本身的 on/off 时间。
您应该改为使用硬件定时器或时钟源。所有 Cortex-M 设备都有一个 SYSCLK,默认情况下以系统时钟速率除以 8 运行。例如:
void delay_millisec(unsigned ms )
{
unsigned ticks = (ms * (SystemCoreClock/ 8)) / 1000 ;
SysTick->LOAD = ticks;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
SysTick->CTRL = 0;
}
然后:
while (1)
{
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
delay_millisec(500);
}
将产生 1Hz 的闪烁率。
一个更复杂的解决方案是让 SYSCLK ISR 以 1ms 的间隔增加一个滴答计数器,并让延迟函数计算经过的滴答间隔。例如,这就是默认 HAL_delay()
实现的工作方式。
根据评论...这是汇编代码程序的 10 行(更像是 20,或更多取决于您如何计算)。
使用STM32F411RE的NUCLEO-F411RE
.cpu cortex-m7
.syntax unified
.thumb
stacktop: .word 0x20001000
.word reset
.thumb_func
reset:
/*
Address offset: 0x30
Reset value: 0x0000 0000
*/
ldr r0,=0x40023830
ldr r1,=0x00000001
str r1,[r0]
/*
Address offset: 0x00
Reset value: 0xA800 0000 for port A
*/
ldr r0,=0x40020000
ldr r1,=0xA8000400
str r1,[r0]
add r0,#0x18
ldr r1,=0x00000020
ldr r2,=0x00200000
d0:
str r1,[r0]
mov r3,#0x00100000
d1:
subs r3,#1
bne d1
str r2,[r0]
mov r3,#0x00100000
d2:
subs r3,#1
bne d2
b d0
我使用重置值而不是读取-修改-写入来保存一些 instructions/locations。
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m7 flash.s -o flash.o
arm-none-eabi-ld -Ttext=0x08000000 flash.o -o flash.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000008000000
arm-none-eabi-objdump -d flash.elf > flash.list
arm-none-eabi-objcopy -O binary flash.elf flash.bin
_start 没关系可以添加两行以使该评论消失。
flash.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <stacktop>:
8000000: 20001000 .word 0x20001000
8000004: 08000009 .word 0x08000009
08000008 <reset>:
8000008: 480b ldr r0, [pc, #44] ; (8000038 <d2+0x6>)
800000a: f04f 0101 mov.w r1, #1
800000e: 6001 str r1, [r0, #0]
8000010: 480a ldr r0, [pc, #40] ; (800003c <d2+0xa>)
8000012: 490b ldr r1, [pc, #44] ; (8000040 <d2+0xe>)
8000014: 6001 str r1, [r0, #0]
8000016: f100 0018 add.w r0, r0, #24
800001a: f04f 0120 mov.w r1, #32
800001e: f44f 1200 mov.w r2, #2097152 ; 0x200000
08000022 <d0>:
8000022: 6001 str r1, [r0, #0]
8000024: f44f 1380 mov.w r3, #1048576 ; 0x100000
08000028 <d1>:
8000028: 3b01 subs r3, #1
800002a: d1fd bne.n 8000028 <d1>
800002c: 6002 str r2, [r0, #0]
800002e: f44f 1380 mov.w r3, #1048576 ; 0x100000
08000032 <d2>:
8000032: 3b01 subs r3, #1
8000034: d1fd bne.n 8000032 <d2>
8000036: e7f4 b.n 8000022 <d0>
8000038: 40023830 .word 0x40023830
800003c: 40020000 .word 0x40020000
8000040: a8000400 .word 0xa8000400
复制 flash.bin 到你的卡上,led 应该像我的一样闪烁。
根据文档,LED 在 Arduino D1 上,也就是 PA5 上,D13 连接到 F411RE 和 F401RE NUCLEO 板。 (在其他使用此 PCB 的 NUCLEO 产品上为 PB13)。如果您只读到文档说 PB13 或 PA5,那么至少先试一试。正如评论或答案中所述,您的 gpio 状态变化之间的延迟需要足够大,以便人眼看到,因此在其中放置一个适当大小的延迟。
mov r3,#0x00100000
更改这些 r3 行(如果它抱怨将其更改为 ldr r3,#0xwhatever_you_want)以查看下一个版本的闪烁率变化
mov r3,#0x00400000
我想确保我已经正确设置了我的第一个嵌入式软件项目,因此我试图使我的 NUCLEO-F411RE 板(STM32F411RE 微控制器)上的 LED 闪烁。我没有使用任何 IDE,因为我想从头开始做所有事情。我的项目结构如下
├── build
│ ├── Buggy.bin
│ ├── Buggy.dis
│ ├── Buggy.elf
│ ├── Buggy.hex
│ ├── Buggy.map
│ ├── main.o
│ ├── startup_stm32f411xe.o
│ ├── stm32f4xx_it.o
│ └── system_stm32f4xx.o
├── lib
│ ├── cmsis
│ │ ├── include
│ │ │ ├── arm_common_tables.h
│ │ │ ├── arm_const_structs.h
│ │ │ ├── arm_math.h
│ │ │ ├── core_cm0.h
│ │ │ ├── core_cm0plus.h
│ │ │ ├── core_cm3.h
│ │ │ ├── core_cm4.h
│ │ │ ├── core_cm7.h
│ │ │ ├── core_cmFunc.h
│ │ │ ├── core_cmInstr.h
│ │ │ ├── core_cmSimd.h
│ │ │ ├── core_sc000.h
│ │ │ └── core_sc300.h
│ │ └── stm32f4xx
│ │ ├── stm32f4xx.h
│ │ └── system_stm32f4xx.h
│ ├── Makefile
│ └── STM32F4xx_StdPeriph_Driver
│ ├── include
│ │ ├── misc.h
│ │ ├── stm32f4xx_adc.h
│ │ ├── stm32f4xx_crc.h
│ │ ├── stm32f4xx_dbgmcu.h
│ │ ├── stm32f4xx_dma.h
│ │ ├── stm32f4xx_exti.h
│ │ ├── stm32f4xx_flash.h
│ │ ├── stm32f4xx_flash_ramfunc.h
│ │ ├── stm32f4xx_gpio.h
│ │ ├── stm32f4xx_i2c.h
│ │ ├── stm32f4xx_iwdg.h
│ │ ├── stm32f4xx_pwr.h
│ │ ├── stm32f4xx_rcc.h
│ │ ├── stm32f4xx_rtc.h
│ │ ├── stm32f4xx_sdio.h
│ │ ├── stm32f4xx_spi.h
│ │ ├── stm32f4xx_syscfg.h
│ │ ├── stm32f4xx_tim.h
│ │ ├── stm32f4xx_usart.h
│ │ └── stm32f4xx_wwdg.h
│ ├── libstdperiph.a
│ ├── Makefile
│ └── src
│ ├── misc.c
│ ├── misc.o
│ ├── stm32f4xx_adc.c
│ ├── stm32f4xx_adc.o
│ ├── stm32f4xx_crc.c
│ ├── stm32f4xx_crc.o
│ ├── stm32f4xx_dbgmcu.c
│ ├── stm32f4xx_dbgmcu.o
│ ├── stm32f4xx_dma.c
│ ├── stm32f4xx_dma.o
│ ├── stm32f4xx_exti.c
│ ├── stm32f4xx_exti.o
│ ├── stm32f4xx_flash.c
│ ├── stm32f4xx_flash.o
│ ├── stm32f4xx_flash_ramfunc.c
│ ├── stm32f4xx_flash_ramfunc.o
│ ├── stm32f4xx_gpio.c
│ ├── stm32f4xx_gpio.o
│ ├── stm32f4xx_i2c.c
│ ├── stm32f4xx_i2c.o
│ ├── stm32f4xx_iwdg.c
│ ├── stm32f4xx_iwdg.o
│ ├── stm32f4xx_pwr.c
│ ├── stm32f4xx_pwr.o
│ ├── stm32f4xx_rcc.c
│ ├── stm32f4xx_rcc.o
│ ├── stm32f4xx_rtc.c
│ ├── stm32f4xx_rtc.o
│ ├── stm32f4xx_sdio.c
│ ├── stm32f4xx_sdio.o
│ ├── stm32f4xx_spi.c
│ ├── stm32f4xx_spi.o
│ ├── stm32f4xx_syscfg.c
│ ├── stm32f4xx_syscfg.o
│ ├── stm32f4xx_tim.c
│ ├── stm32f4xx_tim.o
│ ├── stm32f4xx_usart.c
│ ├── stm32f4xx_usart.o
│ ├── stm32f4xx_wwdg.c
│ └── stm32f4xx_wwdg.o
├── main.c
├── main.h
├── Makefile
├── startup_stm32f411xe.s
├── stm32f4xx_conf.h
├── stm32f4xx_flash.ld
├── stm32f4xx_it.c
├── stm32f4xx_it.h
└── system_stm32f4xx.c
我正在使用 STD_Periph 库来处理与实际硬件和 CMSIS 的接口,以对实际处理器核心和外围设备进行硬件抽象(我认为)。
main.c 源文件应包含使电路板上的 LED 每秒闪烁的代码。
#include "stm32f4xx.h"
void TimingDelay_Decrement(void);
static __IO uint32_t uwTimingDelay;
static void Delay(__IO uint32_t nTime);
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
while (1) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
Delay(1000);
}
}
void Delay(__IO uint32_t nTime)
{
uwTimingDelay = nTime;
while(uwTimingDelay != 0x00) {
uwTimingDelay--;
}
}
void TimingDelay_Decrement(void)
{
if (uwTimingDelay != 0x00)
{
uwTimingDelay--;
}
}
当我使用 Makefile 和 make
命令构建我的项目时,一切正常。然后我 运行 make flash,也成功完成。但是,none 个 LED 每秒都在闪烁。我不知道问题出在哪里,尤其是没有错误信息,所以我很难调试。我觉得它与 Makefile 或链接描述文件有关,所以我将在下面包含它们。
生成文件:
# STM32F4-Discovery Makefile
C_SRC=$(wildcard *.c) \
$(wildcard src/*.c)
# Add assembly source files here or use $(wildcard *.s) for all .s files
S_SRC = $(wildcard *.s)
# Project name
PROJ_NAME = Buggy
OUTPATH = build
BINPATH = /usr/bin/
OUTPATH := $(abspath $(OUTPATH))
BASEDIR := $(abspath ./)
MKDIR_P = mkdir -p
###################################################
# Check for valid float argument
# NOTE that you have to run make clean after
# changing these as hardfloat and softfloat are not
# binary compatible
ifneq ($(FLOAT_TYPE), hard)
ifneq ($(FLOAT_TYPE), soft)
#override FLOAT_TYPE = hard
override FLOAT_TYPE = soft
endif
endif
###################################################
AS=$(BINPATH)arm-none-eabi-as
CC=$(BINPATH)arm-none-eabi-gcc
LD=$(BINPATH)arm-none-eabi-gcc
OBJCOPY=$(BINPATH)arm-none-eabi-objcopy
OBJDUMP=$(BINPATH)arm-none-eabi-objdump
SIZE=$(BINPATH)arm-none-eabi-size
LINKER_SCRIPT = stm32f4xx_flash.ld
CPU = -mcpu=cortex-m4 -mthumb
CFLAGS = $(CPU) -c -std=gnu99 -g -O2 -Wall
LDFLAGS = $(CPU) -mlittle-endian -mthumb-interwork -Wl,--gc-sections,-Map=$(OUTPATH)/$(PROJ_NAME).map,--cref --specs=nano.specs
ifeq ($(FLOAT_TYPE), hard)
CFLAGS += -fsingle-precision-constant -Wdouble-promotion
CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
else
CFLAGS += -msoft-float
endif
# Default to STM32F411xE if no device is passed
ifeq ($(DEVICE_DEF), )
DEVICE_DEF = STM32F411xE
endif
CFLAGS += -D$(DEVICE_DEF)
vpath %.a lib
# Includes
INCLUDE_PATHS = -I$(BASEDIR)/lib/cmsis/stm32f4xx -I$(BASEDIR)/lib/cmsis/include -I$(BASEDIR)
INCLUDE_PATHS += -I$(BASEDIR)/lib/STM32F4xx_StdPeriph_Driver/include
# Library paths
LIBPATHS = -L$(BASEDIR)/lib/STM32F4xx_StdPeriph_Driver
# Libraries to link
LIBS = -lstdperiph -lc -lgcc -lnosys
OBJS = $(C_SRC:.c=.o)
OBJS += $(S_SRC:.s=.o)
###################################################
.PHONY: lib proj
all: dir lib proj
$(SIZE) $(OUTPATH)/$(PROJ_NAME).elf
lib:
$(MAKE) -C lib FLOAT_TYPE=$(FLOAT_TYPE) BINPATH=$(BINPATH) DEVICE_DEF=$(DEVICE_DEF) BASEDIR=$(BASEDIR)
proj: $(OUTPATH)/$(PROJ_NAME).elf
.s.o:
$(AS) $(CPU) -o $(addprefix $(OUTPATH)/, $@) $<
.c.o:
$(CC) $(CFLAGS) -std=gnu99 $(INCLUDE_PATHS) -o $(addprefix $(OUTPATH)/, $@) $<
$(OUTPATH)/$(PROJ_NAME).elf: $(OBJS)
$(LD) $(LDFLAGS) -T$(LINKER_SCRIPT) $(LIBPATHS) -o $@ $(addprefix $(OUTPATH)/, $^) $(LIBS) $(LD_SYS_LIBS)
$(OBJCOPY) -O ihex $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).hex
$(OBJCOPY) -O binary $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).bin
$(OBJDUMP) -S --disassemble $(OUTPATH)/$(PROJ_NAME).elf > $(OUTPATH)/$(PROJ_NAME).dis
dir:
$(MKDIR_P) $(OUTPATH)
clean:
rm -f $(OUTPATH)/*.o
rm -f $(OUTPATH)/$(PROJ_NAME).elf
rm -f $(OUTPATH)/$(PROJ_NAME).hex
rm -f $(OUTPATH)/$(PROJ_NAME).bin
rm -f $(OUTPATH)/$(PROJ_NAME).dis
rm -f $(OUTPATH)/$(PROJ_NAME).map
# Remove the following line if you don't want to clean the Libraries as well
$(MAKE) clean -C lib
flash:
st-flash --reset write $(OUTPATH)/$(PROJ_NAME).bin 0x08000000
链接描述文件:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;; /* required amount of heap */
_Min_Stack_Size = 0x400;; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(4);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
编辑:
我已经安装了 cubeMX 并使用了它们生成的代码。然而,闪烁的 LED 仍然不可见。
这是我在 main.c 文件中的主要方法:
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1) {
// write pin state
// NOTE: You can in turn use HAL_GPIO_TogglePin
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
// synchronous delay for 500 ms
HAL_Delay(500);
}
}
我假设生成的代码工作正常,所以错误要么在我的 main.c 中,要么只是硬件问题。
如果这是您的第一个项目 - 不要使用 SPL(标准外设库)。 STM 不再支持它,并且没有用于较新的 STM32 uC 的库。安装 cubeMX 并使用此工具项目生成的启动。
在您修改后的代码中,您需要
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
而不是 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
您没有切换到正确的 I/O。来自 Nucleo-F411RE 用户手册:
User LD2: the green LED is a user LED connected to Arduino signal D13 corresponding to STM32 I/O PA5 (pin 21) or PB13 (pin 34) depending on the STM32 target.
D13 heare 是指 Arduino 连接器 D13 引脚 - 该名称是为了与 Arduino Shields 兼容,与 STM32 GPIO 引脚名称无关。在您的情况下,它是 PA5(Table 用户手册中的 16)。
您的(原始)延迟函数存在根本性缺陷。忙循环延迟将根据处理器的时钟速率、使用的编译器甚至使用的编译器选项而有所不同。但更重要的是,因为从 1000 开始递减不会花费很长的时间,因此 "flashing" 会太快而无法用人眼感知,甚至可能超过 LED 本身的 on/off 时间。
您应该改为使用硬件定时器或时钟源。所有 Cortex-M 设备都有一个 SYSCLK,默认情况下以系统时钟速率除以 8 运行。例如:
void delay_millisec(unsigned ms )
{
unsigned ticks = (ms * (SystemCoreClock/ 8)) / 1000 ;
SysTick->LOAD = ticks;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
SysTick->CTRL = 0;
}
然后:
while (1)
{
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
delay_millisec(500);
}
将产生 1Hz 的闪烁率。
一个更复杂的解决方案是让 SYSCLK ISR 以 1ms 的间隔增加一个滴答计数器,并让延迟函数计算经过的滴答间隔。例如,这就是默认 HAL_delay()
实现的工作方式。
根据评论...这是汇编代码程序的 10 行(更像是 20,或更多取决于您如何计算)。
使用STM32F411RE的NUCLEO-F411RE
.cpu cortex-m7
.syntax unified
.thumb
stacktop: .word 0x20001000
.word reset
.thumb_func
reset:
/*
Address offset: 0x30
Reset value: 0x0000 0000
*/
ldr r0,=0x40023830
ldr r1,=0x00000001
str r1,[r0]
/*
Address offset: 0x00
Reset value: 0xA800 0000 for port A
*/
ldr r0,=0x40020000
ldr r1,=0xA8000400
str r1,[r0]
add r0,#0x18
ldr r1,=0x00000020
ldr r2,=0x00200000
d0:
str r1,[r0]
mov r3,#0x00100000
d1:
subs r3,#1
bne d1
str r2,[r0]
mov r3,#0x00100000
d2:
subs r3,#1
bne d2
b d0
我使用重置值而不是读取-修改-写入来保存一些 instructions/locations。
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m7 flash.s -o flash.o
arm-none-eabi-ld -Ttext=0x08000000 flash.o -o flash.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000008000000
arm-none-eabi-objdump -d flash.elf > flash.list
arm-none-eabi-objcopy -O binary flash.elf flash.bin
_start 没关系可以添加两行以使该评论消失。
flash.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <stacktop>:
8000000: 20001000 .word 0x20001000
8000004: 08000009 .word 0x08000009
08000008 <reset>:
8000008: 480b ldr r0, [pc, #44] ; (8000038 <d2+0x6>)
800000a: f04f 0101 mov.w r1, #1
800000e: 6001 str r1, [r0, #0]
8000010: 480a ldr r0, [pc, #40] ; (800003c <d2+0xa>)
8000012: 490b ldr r1, [pc, #44] ; (8000040 <d2+0xe>)
8000014: 6001 str r1, [r0, #0]
8000016: f100 0018 add.w r0, r0, #24
800001a: f04f 0120 mov.w r1, #32
800001e: f44f 1200 mov.w r2, #2097152 ; 0x200000
08000022 <d0>:
8000022: 6001 str r1, [r0, #0]
8000024: f44f 1380 mov.w r3, #1048576 ; 0x100000
08000028 <d1>:
8000028: 3b01 subs r3, #1
800002a: d1fd bne.n 8000028 <d1>
800002c: 6002 str r2, [r0, #0]
800002e: f44f 1380 mov.w r3, #1048576 ; 0x100000
08000032 <d2>:
8000032: 3b01 subs r3, #1
8000034: d1fd bne.n 8000032 <d2>
8000036: e7f4 b.n 8000022 <d0>
8000038: 40023830 .word 0x40023830
800003c: 40020000 .word 0x40020000
8000040: a8000400 .word 0xa8000400
复制 flash.bin 到你的卡上,led 应该像我的一样闪烁。
根据文档,LED 在 Arduino D1 上,也就是 PA5 上,D13 连接到 F411RE 和 F401RE NUCLEO 板。 (在其他使用此 PCB 的 NUCLEO 产品上为 PB13)。如果您只读到文档说 PB13 或 PA5,那么至少先试一试。正如评论或答案中所述,您的 gpio 状态变化之间的延迟需要足够大,以便人眼看到,因此在其中放置一个适当大小的延迟。
mov r3,#0x00100000
更改这些 r3 行(如果它抱怨将其更改为 ldr r3,#0xwhatever_you_want)以查看下一个版本的闪烁率变化
mov r3,#0x00400000