我們?cè)诳刂颇K中所使用的處理器是Cortex-M4系列中的STM32F407VE,這個(gè)處理器芯片有100個(gè)引腳,其中包含一些電源供電引腳、外部晶振引腳、SWD程序燒錄引腳和我們最常用的GPIO功能引腳:
實(shí)際上,我們使用的GPIO并不多,我們只使用了3路AD采集、Uart1和Uart2、I2C1、Tim3和Tim4的PWM輸出引腳以及幾個(gè)普通GPIO腳(具體使用情況請(qǐng)參照《控制模塊》)。STM32有豐富的硬件資源供我們使用,例如:AD采集、串口、I2C、SPI、SDIO、CAN、USB_OTG_FS、USB_OTG_HS、I2S、PWM輸出、PWM采集、GPIO輸入輸出等。在這一節(jié)里我們將完成STM32的第一個(gè)小程序:點(diǎn)亮LED燈。
一、開(kāi)發(fā)環(huán)境搭建:
我們的整個(gè)項(xiàng)目都會(huì)在Linux系統(tǒng)下完成,因此后續(xù)所有章節(jié)中如無(wú)特殊說(shuō)明,我們都默認(rèn)在Linux下搭建所有的開(kāi)發(fā)環(huán)境。
首先,我們需要下載并安裝在Arm平臺(tái)下的gcc編譯工具arm-none-eabi-gcc。作者所使用的Linux發(fā)行版為Arch-Linux,可以直接使用下面命令進(jìn)行安裝:
#Arch-Linux
pacman -S arm-none-eabi-gcc
#Fedora
yum install arm-none-eabi-gcc
#ubuntu
apt-get install arm-none-eabi-gcc
一般來(lái)說(shuō),其它Linux的發(fā)行版都有著自己的安裝軟件包的方式,我們不再一一列舉。此外,我們還可以直接在GNU的網(wǎng)站上直接下載壓縮包。
我們下載Linux x86_64 Tarball壓縮包,解壓到指定目錄,并將其目錄加入到PATH環(huán)境變量中,最后執(zhí)行以下命令的查看是否安裝成功:
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 5.4.1 20160919 (release) [ARM/embedded-5-branch revision 240496]
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
其次,我們還需要使用STM32F4xx的標(biāo)準(zhǔn)開(kāi)發(fā)庫(kù),我們可以到STM32官方的網(wǎng)站上下載。
我們需要下載F4系列的標(biāo)準(zhǔn)開(kāi)發(fā)庫(kù),下載并解壓之后,得到以下目錄:
├── CMSIS
│ ├── Device
│ └── Include
└── STM32F4xx_StdPeriph_Driver
├── inc
└── src
這樣我們的開(kāi)發(fā)環(huán)境就準(zhǔn)備好了,接下來(lái)就可以在這個(gè)開(kāi)發(fā)環(huán)境下進(jìn)行開(kāi)發(fā)了。
二、點(diǎn)亮LED燈:
使用arm gcc來(lái)編譯STM32所運(yùn)行的程序,我們需要編寫(xiě)一個(gè)Makefile如下:
# Output files
ELF_FILE = led.elf
BIN_FILE = led.bin
HEX_FILE = led.hex
INFO_FILE = led.info
CODE_FILE = led.code
#------------------------------------------------------------------------#
# Cross Compiler
CC = arm-none-eabi-gcc
OBJCOPY = arm-none-eabi-objcopy
OBJDUMP = arm-none-eabi-objdump
READELF = arm-none-eabi-readelf
#------------------------------------------------------------------------#
# Flags
CFLAGS += -std=gnu11
CFLAGS += -mthumb
CFLAGS += -Wno-incompatible-pointer-types
CFLAGS += -Wno-unused-but-set-variable
CFLAGS += -Wno-unused-variable
CFLAGS += -mcpu=cortex-m4
CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS += -D"assert_param(expr)=((void)0)"
CFLAGS += -D STM32F40XX -DUSE_STDPERIPH_DRIVER
CFLAGS += -nostartfiles
#------------------------------------------------------------------------#
# Link script
CFLAGS += -Wl,-Tled.ld
#------------------------------------------------------------------------#
# Libraries
STM32_LIBS = libs/STM32F4xx_StdPeriph_Driver
CFLAGS += -I$(STM32_LIBS)/inc
CFLAGS += -Ilibs/CMSIS/Include
CFLAGS += -Ilibs/CMSIS/Device/ST/STM32F4xx/Include
#------------------------------------------------------------------------#
# Src Path
SRCS = ./src
CFLAGS += -I$(SRCS)
CFLAGS += -I./inc
#------------------------------------------------------------------------#
# Main Board
SRC += $(SRCS)/main.c
#------------------------------------------------------------------------#
# System
SRC += ./src/system_stm32f4xx.c
STARTUP = ./src/startup_stm32f40xx.s
#------------------------------------------------------------------------#
# StdPeriph
SRC += $(STM32_LIBS)/src/misc.c
$(STM32_LIBS)/src/stm32f4xx_rcc.c
$(STM32_LIBS)/src/stm32f4xx_gpio.c
STARTUP_OBJ = startup_stm32f40xx.o
all:$(BIN_FILE) $(HEX_FILE) $(INFO_FILE) $(CODE_FILE)
$(BIN_FILE):$(ELF_FILE)
$(OBJCOPY) -O binary $^ $@
$(HEX_FILE):$(ELF_FILE)
$(OBJCOPY) -O ihex $^ $@
$(INFO_FILE):$(ELF_FILE)
$(READELF) -a $^ > $@
$(CODE_FILE):$(ELF_FILE)
$(OBJDUMP) -S $^ > $@
$(STARTUP_OBJ):$(STARTUP)
$(CC) $(CFLAGS) $^ -c $(STARTUP)
$(ELF_FILE):$(SRC) $(STARTUP_OBJ)
$(CC) $(CFLAGS) $^ -o $@
之后,我們還需要編寫(xiě)一個(gè)led.ld的鏈接腳本,內(nèi)容如下:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of 128K RAM on AHB bus*/
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0; /* 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
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
/* Define output sections */
SECTIONS
{
.myBufBlock 0x10000000 :
{
KEEP(*(.myBufSection)) /* keep my variable even if not referenced */
} > CCMRAM
/* 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) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.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 */
_exit = .;
} >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 (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = .;
/* Initialized data sections goes into RAM, load LMA copy after code */
.data : AT ( _sidata )
{
. = 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
/* 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) }
}
最后,我們編寫(xiě)程序源代碼文件main.c,實(shí)現(xiàn)LED亮燈功能:
#include < stm32f4xx.h >
#include < stm32f4xx_conf.h >
int main(int argc, char* argv[])
{
GPIO_InitTypeDef GPIO_InitStructure = { 0 };
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_WriteBit(GPIOE, GPIO_Pin_0, 0);
GPIO_WriteBit(GPIOE, GPIO_Pin_1, 0);
while (1)
{
}
}
需要說(shuō)明的是,我們先來(lái)看一下LED電路的原理圖:
可以看到兩個(gè)LED分別使用的是PE0和PE1引腳,當(dāng)引腳為低電平時(shí)LED燈亮起,當(dāng)引腳為高電平時(shí)LED熄滅,因此,當(dāng)我們希望LED亮起只需要通過(guò)GPIO_WriteBit()函數(shù)將PE0和PE1拉低即可。
點(diǎn)亮了兩個(gè)LED之后,我們還可以將源代碼修改一下,讓LED變?yōu)殚W爍狀態(tài):
#include < stm32f4xx.h >
#include < stm32f4xx_conf.h >
int main(int argc, char* argv[])
{
GPIO_InitTypeDef GPIO_InitStructure = { 0 };
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
while (1)
{
GPIO_WriteBit(GPIOE, GPIO_Pin_0, 1);
GPIO_WriteBit(GPIOE, GPIO_Pin_1, 0);
for (int i = 0; i < 1000000; i++)
{
}
GPIO_WriteBit(GPIOE, GPIO_Pin_0, 0);
GPIO_WriteBit(GPIOE, GPIO_Pin_1, 1);
for (int i = 0; i < 1000000; i++)
{
}
}
}
我們?cè)趙hile循環(huán)中加入兩個(gè)for循環(huán)讓處理器執(zhí)行100000次實(shí)現(xiàn)程序“等待一小會(huì)兒”的功能,但實(shí)際上這段時(shí)間處理器還是在運(yùn)行的。
-
led
+關(guān)注
關(guān)注
242文章
23308瀏覽量
661597 -
led燈
+關(guān)注
關(guān)注
22文章
1592瀏覽量
108112 -
Linux
+關(guān)注
關(guān)注
87文章
11320瀏覽量
209846 -
STM32
+關(guān)注
關(guān)注
2270文章
10910瀏覽量
356582 -
GPIO
+關(guān)注
關(guān)注
16文章
1211瀏覽量
52179
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論