我们在控制模块中所使用的处理器是cortex-m4系列中的stm32f407ve,这个处理器芯片有100个引脚,其中包含一些电源供电引脚、外部晶振引脚、swd程序烧录引脚和我们最常用的gpio功能引脚:
实际上,我们使用的gpio并不多,我们只使用了3路ad采集、uart1和uart2、i2c1、tim3和tim4的pwm输出引脚以及几个普通gpio脚(具体使用情况请参照《控制模块》)。stm32有丰富的硬件资源供我们使用,例如:ad采集、串口、i2c、spi、sdio、can、usb_otg_fs、usb_otg_hs、i2s、pwm输出、pwm采集、gpio输入输出等。在这一节里我们将完成stm32的第一个小程序:点亮led灯。
一、开发环境搭建:
我们的整个项目都会在linux系统下完成,因此后续所有章节中如无特殊说明,我们都默认在linux下搭建所有的开发环境。
首先,我们需要下载并安装在arm平台下的gcc编译工具arm-none-eabi-gcc。作者所使用的linux发行版为arch-linux,可以直接使用下面命令进行安装:
#arch-linuxpacman -s arm-none-eabi-gcc#fedorayum install arm-none-eabi-gcc#ubuntuapt-get install arm-none-eabi-gcc一般来说,其它linux的发行版都有着自己的安装软件包的方式,我们不再一一列举。此外,我们还可以直接在gnu的网站上直接下载压缩包。
我们下载linux x86_64 tarball压缩包,解压到指定目录,并将其目录加入到path环境变量中,最后执行以下命令的查看是否安装成功:
$ arm-none-eabi-gcc --versionarm-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 nowarranty; not even for merchantability or fitness for a particular purpose.其次,我们还需要使用stm32f4xx的标准开发库,我们可以到stm32官方的网站上下载。
我们需要下载f4系列的标准开发库,下载并解压之后,得到以下目录:
├── cmsis│ ├── device│ └── include└── stm32f4xx_stdperiph_driver ├── inc └── src这样我们的开发环境就准备好了,接下来就可以在这个开发环境下进行开发了。
二、点亮led灯:
使用arm gcc来编译stm32所运行的程序,我们需要编写一个makefile如下:
# output fileself_file = led.elfbin_file = led.binhex_file = led.hexinfo_file = led.infocode_file = led.code#------------------------------------------------------------------------## cross compilercc = arm-none-eabi-gccobjcopy = arm-none-eabi-objcopyobjdump = arm-none-eabi-objdumpreadelf = arm-none-eabi-readelf#------------------------------------------------------------------------## flagscflags += -std=gnu11cflags += -mthumbcflags += -wno-incompatible-pointer-typescflags += -wno-unused-but-set-variablecflags += -wno-unused-variablecflags += -mcpu=cortex-m4cflags += -mfpu=fpv4-sp-d16 -mfloat-abi=hardcflags += -dassert_param(expr)=((void)0)cflags += -d stm32f40xx -duse_stdperiph_drivercflags += -nostartfiles#------------------------------------------------------------------------## link scriptcflags += -wl,-tled.ld#------------------------------------------------------------------------## librariesstm32_libs = libs/stm32f4xx_stdperiph_drivercflags += -i$(stm32_libs)/inccflags += -ilibs/cmsis/includecflags += -ilibs/cmsis/device/st/stm32f4xx/include#------------------------------------------------------------------------## src pathsrcs = ./srccflags += -i$(srcs)cflags += -i./inc#------------------------------------------------------------------------## main boardsrc += $(srcs)/main.c#------------------------------------------------------------------------## systemsrc += ./src/system_stm32f4xx.cstartup = ./src/startup_stm32f40xx.s#------------------------------------------------------------------------## stdperiphsrc += $(stm32_libs)/src/misc.c $(stm32_libs)/src/stm32f4xx_rcc.c $(stm32_libs)/src/stm32f4xx_gpio.cstartup_obj = startup_stm32f40xx.oall:$(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 $@之后,我们还需要编写一个led.ld的链接脚本,内容如下:
/* 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) }}最后,我们编写程序源代码文件main.c,实现led亮灯功能:
#include #include 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) { }}需要说明的是,我们先来看一下led电路的原理图:
可以看到两个led分别使用的是pe0和pe1引脚,当引脚为低电平时led灯亮起,当引脚为高电平时led熄灭,因此,当我们希望led亮起只需要通过gpio_writebit()函数将pe0和pe1拉低即可。
点亮了两个led之后,我们还可以将源代码修改一下,让led变为闪烁状态:
#include #include 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++) { } }}我们在while循环中加入两个for循环让处理器执行100000次实现程序“等待一小会儿”的功能,但实际上这段时间处理器还是在运行的。
应急灯电路的工作原理
用LCR表测试无线充电系统中的线圈
2017年会爆红黑科技,这几个一定要知道(雷蛇三屏互联笔记本)
基于三菱FX3U大小物件分拣案例
CAN总线的概念/工作原理/组成/结构
基于STM32点亮LED灯
魅族PRO 6s经典评测:6个月的磨砺,缔造不一样的经典!
英特尔推新一代Horse Ridge低温量子控制芯片
如何知道AI建议是否仅出于业务需要而不是出于其他原因而被调整呢?
中国专家研制出量子通信无人机
小辣椒V9怎么样?小辣椒V9值得购买吗?小辣椒V9外观、配置、续航喜人,价格仅需1599
瑞芯微RK3568|SDK开发之Kernel编译
MEMS加速度传感器在生活中的普遍应用
基于实例的智能工艺设计系统
首日开盘大涨61.88%!安防CIS芯片龙头思特威科创板上市
生产型企业在选择机器视觉检测设备的注意事项
AI自主意识开始觉醒?
tb-gateway网关的设备接入实例
苹果手机陌陌删除的消息怎么恢复?详细教程演示
数模混合芯片scan chain问题解析