最近剛完成本身8266的小項目,已經發布在github上,有興趣的朋友能夠看一下html
github地址:esp-ujnpython
1. 經過MQTT協議與服務器交互git
2. 內置HTTP服務器,支持經過瀏覽器進行參數配置github
咱們在編譯8266代碼時可使用項目中的gen_misc.sh(Windows下爲gen_misc.bat)腳本,選擇合適的參數後就會在sdk/bin/文件夾中生成可燒錄的文件,如eagle.flash.bin,eagle.irom0text.bin。 但這樣存在的問題是每次編譯時都須要選擇一遍編譯參數,因此通常會使用make命令進行編譯,如:json
make COMPILE=gcc BOOT=none APP=0 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=4
這是由於gen_misc.sh的做用僅僅是供用戶選擇編譯參數,最終的編譯過程是經過make命令依據Makefile文件中定義的若干規則來進行的。接下來經過以下幾個方面來探討整個編譯流程瀏覽器
SDK中Makefile文件以樹形結構組織。整體上分爲3類:主文件,項目配置文件,庫配置文件。服務器
|--sdk/ |----Makefile |----project/ |------Makefile |------user/ |--------Makefile |------json/ |--------Makefile
如上圖所示架構
日常開發過程當中,通常咱們只須要關注項目配置文件與庫配置文件便可。若有時爲了程序的模塊化,須要將不一樣的功能模塊編譯成獨立的庫。這時須要修改項目配置文件,並建立對應的庫配置文件。例如咱們須要添加一個json庫。這時就須要:app
須要在兩個Makefile中作出的改動以下:模塊化
#sdk/project/json/Makefile GEN_LIBS = libjson.a #庫名 #sdk/project/Makefile SUBDIRS = user \ json #庫目錄 COMPONENTS_eagle.app.v6 = user/libuser.a \ json/libjson.a #庫路徑
對於Non-FOTA模式,編譯完成後會在sdk/bin/目錄下生成eagle.flash.bin與eagle.irom0text.bin。顯然這兩個文件並非編譯器的直接產物,通常來講編譯器會經過咱們的代碼生成一個可執行程序。那麼這兩個文件是從何而來的呢?實際上這兩個文件是編譯後生成的可執行文件的一部分。可執行文件被拆解成了多個部分,而後拼湊出了這兩個文件供咱們燒錄。咱們的代碼通過編譯後會生成一個elf文件,它的路徑在sdk/project/.output/eagle/debug/image/eagle.app.v6.out。這個一個標準的elf文件,可使用readelf命令查看它的一些信息。
#readelf -h ELF 頭: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 類別: ELF32 數據: 2 補碼,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 類型: EXEC (可執行文件) 系統架構: Tensilica Xtensa Processor 版本: 0x1 入口點地址: 0x40100004 程序頭起點: 52 (bytes into file) Start of section headers: 549292 (bytes into file) 標誌: 0x300 本頭的大小: 52 (字節) 程序頭大小: 32 (字節) Number of program headers: 5 節頭大小: 40 (字節) 節頭數量: 19 字符串表索引節頭: 16 #readelf -S 共有 19 個節頭,從偏移量 0x861ac 開始: 節頭: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .data PROGBITS 3ffe8000 0000e0 000804 00 WA 0 0 16 [ 2] .rodata PROGBITS 3ffe8810 0008f0 0015d0 00 A 0 0 16 [ 3] .bss NOBITS 3ffe9de0 001ec0 006f18 00 WA 0 0 16 [ 4] .irom0.text PROGBITS 40210000 0092c0 038b44 00 AX 0 0 16 [ 5] .text PROGBITS 40100000 001ec0 0073fc 00 AX 0 0 4 [ 6] .xtensa.info NOTE 00000000 041e04 000038 00 0 0 1 [ 7] .comment PROGBITS 00000000 041e3c 001bbd 00 0 0 1 [ 8] .debug_frame PROGBITS 00000000 0439fc 00211c 00 0 0 4 [ 9] .debug_info PROGBITS 00000000 045b18 014bec 00 0 0 1 [10] .debug_abbrev PROGBITS 00000000 05a704 003f8a 00 0 0 1
將可執行文件eagle.app.v6.out轉變爲可燒錄文件的過程定義在sdk/Makefile,也就是在主文件中。大致流程以下:
#將.text、.data、.rodata和.irom0.text節的數據轉存到文件 objcopy --only-section .text -O binary eagle.app.v6.out eagle.app.v6.text.bin objcopy --only-section .data -O binary eagle.app.v6.out eagle.app.v6.data.bin objcopy --only-section .rodata -O binary eagle.app.v6.out eagle.app.v6.rodata.bin objcopy --only-section .irom0.text -O binary eagle.app.v6.out eagle.app.v6.irom0text.bin #經過eagle.app.v6.text.bin、eagle.app.v6.data.bin和eagle.app.v6.rodata.bin生成eagle.app.flash.bin python sdk/tools/gen_appbin.py eagle.app.v6.text.bin eagle.app.v6.data.bin eagle.app.v6.rodata.bin #將最後生成的可燒錄文件放到sdk/bin/目錄下 mv eagle.app.flash.bin sdk/bin/eagle.flash.bin mv eagle.app.v6.irom0text.bin sdk/bin/eagle.irom0text.bin
經過上邊readelf -S獲取到的節區表信息能夠看到,實際在內存中出現的節只有.text、.data、.bss、.rodata和.irom0.text。
.text + .data + .rodata => eagle.flash.bin
.irom0.text => eagle.irom0text.bin
經過比較這幾個節的大小與燒錄文件大小的關係能夠獲得相同的結果(eagle.flash.bin文件中除了包含程序節數據,還有少許的配置數據)。.bss節雖然在內存中出現可是在程序初始化時會被整個清零,因此沒必要出如今燒錄文件中。這幾個節包含的數據內容以下:
節名 | 做用 |
.text | 存放代碼 |
.data | 存放已初始化的全局變量 |
.bss | 存放未初始化的全局變量 |
.rodata | 存放只讀數據 |
.irom0.text | 存放標註有ICACHE_FLASH_ATTR的代碼或ICACHE_RODATA_ATTR的變量 |
在前邊已經提到,咱們寫的代碼最終會編譯爲一個elf格式的可執行文件(eagle.app.v6.out),接下咱們經過具體Makefile文件中的代碼對整個編譯的執行過程進行分析。以前講到sdk/Makefile爲主文件,也就是全部編譯時用到的邏輯都在其中定義。咱們整個的編譯流程中須要按順序產生以下幾類目標:二進制目標文件、庫文件、elf文件、燒錄文件。那麼如何經過一個主Makefile來完成這些工做呢,這裏須要先看一下其他兩類起配置做用的Makefile。這兩類Makefile的最後都會有以下代碼:
PDIR := ../$(PDIR) sinclude $(PDIR)Makefile
它的做用是包含本身父文件夾中的Makefile文件,那麼最後主Makefile文件中的內容會被包含到庫配置文件與項目配置文件中。在項目配置文件中,它的做用是產生elf可執行文件與燒錄文件,在庫配置文件中,它的做用是產生靜態連接庫。主Makefile中,最主要的顯式規則以下,經過這兩條規則產生了全部咱們須要的文件。
...
314 all: .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
... 324 .subdirs: 325 @set -e; $(foreach d, $(SUBDIRS), $(MAKE) -C $(d);)
這兩條規則的目標都是僞目標,並不會產生任何文件,咱們所須要的全部文件都是目標all的依賴文件。
$(OBJS) |
二進制目標文件 |
$(OLIBS) |
靜態連接庫 |
$(OIMAGES) |
elf可執行文件 |
$(OBINS) |
燒錄文件 |
$(SPECIAL_MKTARGETS) |
一直爲空 |
第一個依賴文件.subdirs是一個僞目標,也就是每次編譯時都會先執行.subdirs中定義的操做,也就是遍歷全部含Makefile文件的子文件夾,執行make命令。經過這種方式產生的結果就是make工具的當前路徑發生了改變。拿上邊列出的項目結構爲例,咱們一次編譯過程可分爲
這時若是執行了sdk/project/json/Makefile,那麼此時make工具的當前路徑變爲了sdk/project/json/。此時sdk/project/json/Makefile對上層Makefile進行包含後再次構建目標all。通常來講sdk/project/json/中不會再有包含Makefile的子文件夾,那麼此時目標all的第一個依賴項.subdirs會馬上返回,而後再對其他的依賴項進行構建。
還有一點須要說明的是目標all的依賴項並非全都有值的,好比$(SPECIAL_MKTARGETS)的值就一直爲空,表示不存在此依賴項。繼續拿上邊的項目結構舉例:
make當前路徑 | $(OBJS) |
$(OLIBS) |
$(OIMAGES) |
$(OBINS) |
sdk/project/ | 空 | 空 | eagle.app.v6.out | eagle.app.v6.bin |
sdk/project/json/ | json.o | libjson.a | 空 | 空 |
根據make當前路徑的不一樣,目標all有不一樣的依賴項,而後再根據主Makefile中定義隱式規則對依賴項進行構建,即完成了整個項目的構建過程。
有的朋友可能對Makefile的語法不熟悉,這裏推薦一個網站
https://www.gnu.org/s/make/manual/make.html
官方的教程很詳細