[英]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,它也成功完成。 但是,沒有一個 LED 每秒閃爍一次。 我不知道問題是什么,特別是沒有錯誤消息,所以我很難調試。 我感覺它與 Makefile 或 linker 腳本有關,所以我將它們包含在下面。
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
Linker 腳本:
/* 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 中,要么只是硬件問題。
您沒有切換正確的 I/O。 從 Nucleo-F411RE 用戶手冊:
用戶 LD2:綠色 LED 是連接到 Arduino 信號 D13 的用戶 LED,對應於 STM32 I/O PA5(引腳 21)或 PB13(引腳 34),具體取決於 STM32 目標。
D13 是指 Arduino 連接器 D13 引腳 - 該名稱是為了與 Arduino Shields 兼容,與 STM32 GPIO 引腳名稱無關。 在您的情況下,它是 PA5(用戶手冊中的表 16)。
您的(原始)延遲 function 存在根本缺陷。 繁忙循環延遲將根據處理器的時鍾速率、使用的編譯器甚至使用的編譯器選項而有所不同。 但更特別的是,因為從 1000 遞減不會花費明顯的時間,因此“閃爍”會太快而無法用人眼感知,甚至可能超過 LED 本身的開/關時間。
您應該改用硬件定時器或時鍾源。 所有 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 flash 速率。
更復雜的解決方案是讓 SYSCLK ISR 以 1 毫秒的間隔增加一個滴答計數器,並讓延遲 function 計數經過的滴答間隔。 例如,默認的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
我使用重置值而不是讀取-修改-寫入來保存一些指令/位置。
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 沒關系可以添加兩行以使該注釋 go 消失。
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 是連接 F411RE 和 F401RE NUCLEO 板的 D13 的位置。 (使用此 PCB 的其他 NUCLEO 產品上是 PB13)。 如果您只閱讀到文檔說 PB13 或 PA5,那么至少先嘗試一個,然后再嘗試另一個。 正如評論或答案中所涵蓋的那樣,您的延遲需要在 gpio state 變化之間足夠大,以便人眼看到,所以在其中放置一個合適的延遲。
mov r3,#0x00100000
更改這些 r3 行(如果它抱怨將其更改為 ldr r3,#0xwhatever_you_want)以查看下一個構建時的閃爍率變化,例如
mov r3,#0x00400000
如果這是您的第一個項目 - 不要使用 SPL(標准外設庫)。 STM 不再支持它,並且沒有用於更新的 STM32 uC 的庫。 安裝 cubeMX 並使用此工具項目生成的啟動。
在您修改后的代碼中,您需要
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
而不是HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.