本帖最后由 沒有你 于 2020-6-21 14:28 編輯
開發STC單片機最常用的平臺是keil,也可以用IAR for 8051,但是這兩種平臺都只能在Windows平臺上開發。如果要跨平臺開發STC單片機的話,比如windows、linux和MacOS,甚至在樹莓派上,那么可以使用SDCC編譯器。SDCC的特點是免費、開源和跨平臺,這個是keil和IAR沒法比的。網上對SDCC的資料總結不是很詳細,沒有提供一個完整的makefile模板。有一款開源下載工具stcflash可以燒錄STC單片機,因為stcflash是用python實現的,所以很容易做到跨平臺。不過,原作者已經多年未更新stcflash了,導致stcflash也只支持一些古董級別類似STC89C51類的單片機的燒錄。我在此基礎上添加了對STC8全系列(STC8A、STC8F、STC8H、STC8G、STC8C)、STC15系列的型號識別和燒錄支持。下圖是在vscode終端用stcflash燒錄STC8A8K64S4A12的過程:
下圖是在樹莓派4B上對STC8H8K64U的燒錄過程:
這樣,無論在windows、linux,MacOS還是樹莓派,只要有安裝python環境,都可以正常燒錄STC新系列的單片機了。編輯代碼可以采用VScode,VScode擁有強大的插件支持,堪稱宇宙第一編輯器。VScode的特點也是免費、開源和跨平臺。組合VScode+SDCC+stcflash,都是免費、開源和跨平臺,解決了代碼的編輯、編譯和下載的問題。本次對SDCC的介紹分為三部分,第一部分是SDCC的介紹,第二部分是提供一個工程模板和Makefile的實例,第三部分是使用改進開源stcflash對STC8系列單片機的燒錄。下面是第一部分的內容。
一、SDCC簡介
SDCC是一款免費開源的編譯器,它支持標準(ANSI C89 / ISO C90,ISO C99,ISO C11 /ISO C17)C編譯。SDCC是由Sandeep Dutta為8位單片機設計的編譯器,支持MCS51(8051,8052,8031,8032等)、STM8、PIC、DS80C390,HC08,Z80等十幾種架構的單片機的編譯。SDCC還是一款跨平臺的編譯器,支持windows、linux和Mac OS,同一個代碼工程可以在不同平臺編譯和調試。
二、SDCC組成
SDCC主要由可執行文件和庫組成,其中,可執行文件由如下構成:
- 1、sdcc compiler
- 這個是編譯器,可以單純編譯代碼,但是沒有對生成文件進行鏈接
- 2、sdcpp prepocessor
- 這個是預處理器,可以對頭文件和相關宏進行預處理
- 3、sdas assemblers and sdld linker
- 這個是匯編語言處理器和鏈接器,可以編譯匯編語言,并且鏈接所有的生成文件,生成ihx格式的燒錄文件。
- 4、sdbinutils utilties(sdar,sdranlib,sdnm,sdobjcopy)
- 這個主要是由GUN Binutils分離出來的,比如可以用sdobject進行反匯編。
- 5、ucsim simulator
- 這是軟件仿真器
- 6、sdcdb debugger
- 這個是代碼調試器
- 7、Packihx
- 這個工具可以把ihx后綴的文件轉換成hex后綴的文件
- 8、makebin
- 這個工具可以將ihx文件轉換成bin文件
- 9、SDCC run-time libraries
復制代碼
庫由如下構成:
- 1、dbuf library
- 2、Boost C++ libraries
復制代碼
三、SDCC支持的基本數據類型
SDCC支持多種數據類型,如下圖所示:
編譯器也允許在函數的任何地方內聯匯編代碼,另外,也可以調用在匯編中開發的例程。
四、SDCC的下載和安裝 1、源碼安裝 如果想要自己編譯的話,可以直接下載源碼編譯安裝。官方給源碼,主要是為了兼容多個平臺,比如x86、ARM v6和ARM v8等。只要在對應平臺編譯源碼,就能產生相應的可執行二進制文件。 對linux而言,源碼的安裝包括三個步驟:配置(configure)、編譯(make)和安裝(make install)。configure文件是一個可執行的腳本文件,它有很多選項,在待安裝的源碼目錄下使用命令./configure –help可以輸出詳細的選項列表。其中--prefix選項是配置安裝目錄,如果不配置該選項,安裝后可執行文件默認放在/usr /local/bin,庫文件默認放在/usr/local/lib,配置文件默認放在/usr/local/etc,其它的資源文件放在/usr /local/share。可以通過./configure --prefix=目錄,來指定安裝的路徑。 以Ubuntu16.4為例,下載源碼后解壓,然后進入源碼目錄,打開終端輸入- ./configure --disable-pic14-port --disable-pic16-port --prefix=/opt/SDCC/
復制代碼 這時configure文件會檢測系統是否符合安裝的條件,可能會出現一些錯誤: - 1、缺少bison
- configure: error: Cannot find required program bison
- 解決方法:sudo apt-get install flex bison
- 2、找不到找不到adjacency_list.hpp
- configure: error: boost library not found (boost/graph/adjacency_list.hpp).
- 解決方法:sudo apt-get install libboost-date-time-dev libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-iostreams-dev
- 3、沒有PIC14、PIC16設備
- failed for device/lib/pic14
- 解決方法:./configure --disable-pic14-port --disable-pic16-port
復制代碼 如果條件符合,再輸入:make,即可編譯源碼。編譯源碼的過程中,可能出現: - 1、fatal error: zlib.h:
- 解決方法: sudo apt-get install zlib1g-dev
復制代碼等待編譯完成后,輸入:sudo make install,即可安裝SDCC到Ubuntu中,而且安裝目錄就在指定的/usr/SDCC。 如果是自己指定的目錄,需要手動添加環境變量,按照本例子,輸入: 在最后添加- export PATH=/opt/SDCC/bin:$PATH
復制代碼 然后再執行 使之生效。
卸載SDCC也比較簡單,如果是按照上面的那種,直接刪除/usr/SDCC文件夾即可。如果沒有指定prefix,也可以在源碼目錄輸入(可能有些源碼不帶uninstall),實現自動卸載程序。 2、二進制文件包安裝 如果想方便的話,可以直接下載官方對應各個平臺編譯好的二進制文件包,注意根據不同平臺的選擇下載對應的二進制文件。比如window系統,是下載exe后綴的文件,還有注意32位系統還是64位系統。Linux平臺的話,比如Ubuntu,可以在終端輸入:sudo apt-get install sdcc,系統會自動下載安裝SDCC及其組件。不過,這種方式安裝的SDCC可能不是最新版本。SDCC官方也提供樹莓派可以直接使用的二進制包,想想直接樹莓派編譯開發8051單片機也是一件比較有趣的操作! 3、查看SDCC版本 在終端輸入 可以查看sdcc的版本信息。目前我下載使用的SDCC版本為4.0.2版本。 五、SDCC的使用 1、生成文件解析 SDCC支持命令行操作,最常用的命令就是sdcc sourcefile.c,這個命令是編譯并且鏈接生成文件,生成的文件有如下幾種: - sourcefile.asm - 由編譯器創建的匯編文件
- sourcefile.lst - 由匯編器創建的匯編鏈接文件
- sourcefile.rst - 由鏈接編輯器創建的具有鏈接信息更新的匯編鏈接文件
- sourcefile.sym - 由匯編器創建的源文件鏈接標識
- sourcefile.rel - 由匯編器產生的目標文件,可以作鏈接編輯器的輸入
- sourcefile.map - 由鏈接器創建的加載模塊的內存映射
- sourcefile.mem - 帶有內存使用情況的文件
- sourcefile.ihx - 以Intel hex forma格式的加載模塊
復制代碼 2、多個源文件編譯 如果只有一個源文件,可以直接用sdcc xxx.c命令編譯生成ihx文件。對于多個源文件,可以分別編譯源文件產生目標文件,最后將所有目標文件鏈接起來就行。 假設現在同一個目錄下有三個源文件:foo1.c、foo2.c和foolmain.c,其中main函數在foolmain.c里。有兩種方法編譯: - sdcc -c foo1.c
- sdcc -c foo2.c
- sdcc -c foomain.c
- sdcc foomain.rel foo1.rel foo2.rel
復制代碼解釋:sdcc -c foo1.c告訴SDCC編譯但是不鏈接文件,編譯后產生目標文件foo1.rel。sdcc foomain.rel foo1.rel foo2.rel則是告訴SDCC鏈接foomain.rel需要依賴foo1.rel和foo2.rel,最后可以生成ihx文件 - sdcc -c foo1.c
- sdcc -c foo2.c
- sdcc foomain.c foo1.rel foo2.rel
復制代碼解釋:這個和上面的步驟是等價的。sdcc foomain.c foo1.rel foo2.rel告訴SDCC編譯和鏈接foomain.c需要依賴foo1.rel foo2.rel。 3、生成hex文件 所有源文件編譯鏈接完成后,會產生ihx后綴的文件,這個文件無法支持用STC-ISP燒錄單片機,所以需要將ihx轉成hex文件。首先找到SDCC的安裝目錄下的bin目錄,復制packihx文件到源文件目錄下,然后在終端輸入packihx xxx.ihx > xxx.hex,然后就可以生成hex文件了。 4、其他選項 SDCC還有許多其他命令可以選擇,比如: - sdcc --code-loc 0x2000 source.c,可以指定程序從0x2000開始存放
- sdcc --opt-code-speed source.c,可以讓SDCC對代碼的運行速度做優化
- Sdcc --model-large source.c,可以指定單片機的數據存儲模型為large。
- sdcc -mmcs51 source.c可以選擇目標處理器為MCS51,這個也是SDCC的默認選項,可以不用指定。但是如果要編譯STM8單片機的源文件,則要sdcc -mstm8 source.c,指定STM8。為目標處理器。
復制代碼 此外,還有許多其他選項,有興趣的可以查看官方說明手冊,鏈接: http://sdcc.sourceforge.net/doc/sdccman.pdf六、存儲類型 MCS51內部存儲類型如下圖所示: 1、__data/__near 這個是默認的存儲類型,聲明變量被分配到8051內存存儲器RAM的可直接尋址的地址空間data里。訪問此類型變量也是速度最快的 使用如下: - __data unsigned char value;
復制代碼 2、__xdata/__far 這種存儲類型聲明的變量被分配到外部存儲器RAM(xdata)中去,這個是large數據存儲模型單片機的默認地址分配(意思就是如果指定-model-large數據存儲模型,定義變量就算不加__xdata,變量也會被自動分配到外部存儲器RAM(xdata)中) 使用如下: - __xdata unsigned char value;
復制代碼 3、__idata 這種存儲器類型的聲明被分配到8051內部存儲器RAM的間接尋址空間(idata) 使用如下: - __idata unsigned char value;
復制代碼4、__pdata 這種存儲類型聲明的變量被分配到外部存儲器RAM(xdata)的前256字節空間去,這個是medium數據存儲模型單片機的默認地址分配。 使用如下: - __pdata unsigned char value;
復制代碼 5、__code 這種存儲類型聲明的變量被分配到代碼存儲區,這種類型的變量是只讀的,常用__code來聲明一些常量,比如查表數據等。 使用如下: - __code unsigned char value;
復制代碼
6、__bit 這種存儲類型聲明的變量被分配到8051存儲器的可尋址區域。8051內核有16字節的可尋址RAM(0x20-02F),總共可以提供16x8=128bits尋址位。 使用如下: 7、__sfr / __sfr16 / __sfr32 / __sbit 這種存儲類型通常用于特殊功能寄存器,可以以位、字節和字訪問。比如: - __sfr __at (0x80) P0 //端口P0
- _sbit __at (0x80+1) //端口P0_1
復制代碼
其中,__at表示指派一個絕對地址 七、絕對地址 通常使用__at來指派一個絕對地址,可以指定一個變量的RAM存儲地址。比如:
- __xdata __ar (0x2000) unsigned char value //指定變量value存放在外部RAM的0x2000到0x2001地址。
- __code __at (0x3000) char name[5] = "SDCC";//字符串SDCC存放0x3000的ROM空間
復制代碼
八、指針 SDCC編譯器用*字符支持變量指針的聲明,SDCC指針可用在所有標準C中可用的操作中。但是,因為8051和派生系列的獨特結構,SDCC編譯器提供兩個類型的指針:通用指針和存儲器指針。一些指針聲明的例子如下: - __xdata unsigned char * __data p;//內部ram的指針p指向外部ram的數據對象
- __data unsigned char * __xdata p;//外部ram的指針p指向內部ram的數據對象
- __xdata unsigned char * __code p;//rom的指針p指向外部ram的數據對象
- __code unsigned char * __code p;//rom的指針p指向rom的數據對象
- usigned char * __xdata p;//位于xdata空間的普通指針
- unsigned char * p;//位于默認存儲空間的普通指針
- char (* __data fp) (void);//位于data空間的函數指針
復制代碼
九、中斷服務例程 SDCC編譯器支持在C語言源程序直接編寫8051單片機的中斷服務例程,需要用到關鍵字__interrupt,中斷函數的定義格式如下:
- void 函數名 (void) __interrupt (n) __using(n)
復制代碼其中,第一個n表示中斷號,第二個n表示使用寄存器組。 其中,不同中斷的入口地址計算如下圖所示: __using(n)可以指定中斷服務例程所使用的寄存器組,單片機進入中斷函數會先保存當前寄存器組的內容,如果指定其他的寄存器組,可以不保存當前的寄存器組的內容,直接使用指定寄存器組,可以提高效率。 如果中斷服務例程沒有指定一個寄存器組(沒有__using)或者使用__using(0),編譯器將保存所有被使用的寄存器到堆棧的入口之上并且在退出時恢復。然而,如果中斷服務例程調用其他函數,整個寄存器組被保存到堆棧中。該方案對使用非常少的寄存器的小中斷服務例程是非常有利的。 使用時,除了定義中斷服務函數,還需要在main函數的源文件聲明該中斷服務例程,這樣才能正常進入中斷 十、使用內嵌匯編代碼 SDCC支持兩種內嵌匯編代碼格式: - 第一種:__asm __endasm
- 第二種:__asm__("inline_assembler_code")
復制代碼其中,第二種格式需要SDCC 3.2.0版本及以上才支持。兩種格式使用示例如下所示: - 第一種示例:
- __asm
- ; This is a comment
- label:
- nop
- __endasm
- 第二種示例:
- __asm__("; This is a comment\nlabel:\n\tnop");
復制代碼第二種格式需要使用\n來換行,沒有第一種直觀,一般推薦使用第一種方式內嵌匯編代碼。 可以使用內嵌匯編來實現nop(空操作)的功能,一般用于延時等,可以如下定義: - #define DELAY \
- __asm \
- nop \
- nop \
- _endasm
復制代碼下一部分我會基于一個實際工程模板介紹makefile的編寫,通過makefile管理工程多個源文件、頭文件和宏。
|