一開始學習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