一开始学习51单片机就是用的MDK这个IDE软件,IDE软件虽然看起来直观好像更加容易入门(因为有界面看起来很形象),但是实际上IDE却是向我们这些入门人员隐藏了背后真实存在的过程,让我们以为编译就是点一下一个按键就完成了。直到使用了大半年的STM32芯片,我觉得不能一直依赖IDE软件,所以打算试试在Linux下开发STM32,首先需要一个 linux下STM32的编译器查了一下,度娘告诉我 arm-none-eabi-gcc编译器是可以编译STM32的代码的,所以需要现在Linux(ubantu)下安装好arm-none-eabi-gcc这里就不写怎么安装了。然后我们开始开发一个STM32的LED程序,可能在MDK软件下,没人不会但是在Linux下没接触过了就无从下手了。我也是第一次尝试学习所以记录下。
因为在MDK上开发过STM32所以我知道需要下面几个文件
core_cm3.c
core_cm3.h
system_stm32f10x.c
stm32f10x.h
stm32f10x_conf.h
system_stm32f10x.h
startup_stm32f103xb.s
所以把他们从以前的工程中考出来,放到linux下的目录下备用,这里需要注意是,启动文件startup_stm32f103xb.s是和MDK下不同的需要在ST官网上去下载。
然后需要的了解的就是linux下的编译命令
arm-none-eabi-gcc -c xxx.c -o name 这个命令就是将一个c文件编译生成name名称的.o文件。但是这里还需要用到一些编译标志暂时不用清楚作用,先打通流程最终的编译命令就是
arm-none-eabi-gcc -o “”name“” -c -W -Wall -g -I $(INC) -lm -lc -mcpu=cortex-m3 -mthumb -D STM32F10X_HD -D USE_STDPERIPH_DRIVER -O1 -std=gnu11
然后是将上面的 c 文件挨个编译一下,产生对应的.o文件。
其中编译 core-m3这个文件时遇到报错,查询到是两个函数中需要修改如下。具体原因是因为对应的编译器的汇编语句不同。
修改前 uint32_t __STREXB(uint8_t value, uint8_t *addr) { uint32_t result=0; __ASM volatile ("strexb %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) ); return(result); } uint32_t __STREXH(uint16_t value, uint16_t *addr) { uint32_t result=0; __ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r" (value) ); return(result); } 修改后 uint32_t __STREXB(uint8_t value, uint8_t *addr) { uint32_t result=0; __ASM volatile ("strexb %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) ); return(result); } uint32_t __STREXH(uint16_t value, uint16_t *addr) { uint32_t result=0; __ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r" (value) ); return(result); }
最后再来补充一个控制LED闪灯的驱动程序,使用过STM32单片机的一定不难理解底下的这段代码。就是控制GPIOA_PIN8高低电平切换。
#include "stm32f10x.h" int main(void) { int i,j; RCC->APB2ENR = 0x00000004; GPIOA->CRH = 0x00000003; while(1) { GPIOA->BSRR=(0x00000001<<8); for(j=0;j<720000;j++); GPIOA->BSRR=(0x00000001<<24); for(i=0;i<720000;i++); } return 0; }
最后得编译上面的代码,得到main.c 对应的.o文件。如下
看着这些.o文件是不是挺面熟,在MDK工程的obj文件夹下有这一堆文件,然后后面需要用这些文件最终生成HEX文件。前回避了Makefile到这里连接脚本是必须的了,不过好处是可以暂时不用自己写,直接用ST提供的脚本随便一个同系列的芯片的链接脚本吧内存定义那一部分改成对应芯片的FLASH和RAM大小就行。
然后执行下面的命令 注意-T是指指定连接脚本就是STM32F103X6_FLASH.ld
arm-none-eabi-gcc system_stm32f10x.o startup_stm32f103xb.o core_cm3.o main.o -T STM32F103X6_FLASH.ld -o led.elf -mthumb -mcpu=cortex-m3 -W -Wall -lm -lc
这时我遇到了这样一个问题
官方连接文件中有一段和现有的arm-none-eabi-gcc版本不兼容导致的 exit undefined的问题错误码
gcc 链接出错exit.c:(.text.exit+0x16): undefined reference to `_exit'这个链接给出了解决方法和原因说明(https://blog.csdn.net/weixin_30224379/article/details/95334513)
我选择在链接脚本中增加_exit 符号提供给链接器,然后再次执行链接命令目录下得到led.elf文件,到这里就算是大功告成了,接下来如果你只需要执行两条简单的命令就可以得到对应的可以烧录的文件
arm-none-eabi-objcopy led.elf led.bin -Obinary 得到bin文件
arm-none-eabi-objcopy led.elf led.hex -Oihex 得到hex文件
最终我在Windows下使用串口烧录软件将,程序下载到板子上,灯欢快的闪起来了。所以这也算是我的第一个Linux下的ARM程序开发,后来我学着写了一个Makefile测试了下可以用,但是吃相难看放到最后,当做回忆。。。。。
1 TOP = $(shell pwd) 2 COMPLIT = arm-none-eabi 3 TARGET := led 4 ASM := $(COMPLIT)-as 5 CC := $(COMPLIT)-gcc 6 LD := $(COMPLIT)-ld 7 OBJCOPY = $(COMPLIT)-objcopy 8 OBJDUMP = $(COMPLIT)-objdump 9 OBJ = system_stm32f10x.o startup_stm32f103xb.o core_cm3.o main.o 10 SRC = $(TOP)/src/ 11 INC = $(TOP)/inc/ 12 FLAG := -W -Wall -g -I $(INC) -lm -lc -mcpu=cortex-m3 -mthumb -D STM32F10X_HD -D USE_STDPERIPH_DRIVER -O1 -std=gnu11 13 14 all: $(OBJ) 15 $(CC) $(OBJ) -T STM32F103X6_FLASH.ld -o $(TARGET).elf -mthumb -mcpu=cortex-m3 -W -Wall -lm -lc 16 $(OBJCOPY) $(TARGET).elf $(TARGET).bin -Obinary 17 $(OBJCOPY) $(TARGET).elf $(TARGET).hex -Oihex 18 main.o: 19 $(CC) -o main.o -c $(FLAG) $(SRC)main.c 20 startup_stm32f103xb.o: startup_stm32f103xb.s $(INC)system_stm32f10x.h 21 $(ASM) -o startup_stm32f103xb.o startup_stm32f103xb.s 22 system_stm32f10x.o: 23 $(CC) -o system_stm32f10x.o -c $(FLAG) $(SRC)system_stm32f10x.c 24 core_cm3.o: 25 $(CC) -o core_cm3.o -c $(FLAG) $(SRC)core_cm3.c 26 27 28 clean: 29 rm *.o