真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么使用VisualStudioCode+CMake+SDCC進(jìn)行C51開(kāi)發(fā)嘗試

今天就跟大家聊聊有關(guān)怎么使用Visual Studio Code + CMake + SDCC 進(jìn)行C51開(kāi)發(fā)嘗試,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

公司主營(yíng)業(yè)務(wù):網(wǎng)站建設(shè)、成都網(wǎng)站制作、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)建站推出和政免費(fèi)做網(wǎng)站回饋大家。

起因當(dāng)然是Keil 太爛了,在嘗試重新回到C51 的時(shí)候聽(tīng)說(shuō)了可用于C51 的開(kāi)源編譯器SDCC,于是就開(kāi)始嘗試Keil 以外的開(kāi)發(fā)方案。先提一下結(jié)果:失敗了。部分失敗了,雖然用SDCC 成功生成了可用的程序,但是致命的缺陷在于SDCC 沒(méi)有Debug 體驗(yàn)可言。除此之外還有一些小問(wèn)題,以下詳細(xì)敘述,留作備份。

現(xiàn)有基于SDCC 的幾個(gè)方案

1. Platform IO + Visual Studio Code

【應(yīng)有圖1】

這是我最先搜到并開(kāi)始嘗試的方案。Platform IO 相當(dāng)于一個(gè)通用的嵌入式開(kāi)發(fā)助手,主要功能是通用的“新建項(xiàng)目向?qū)?/strong>”, 工具鏈管理和統(tǒng)一的調(diào)試工具等。

開(kāi)始的體驗(yàn)非常 sweet,Platform IO 無(wú)縫集成于VS code 中,對(duì)STC C51 單片機(jī)有了實(shí)驗(yàn)性的支持,使用SDCC 編譯器,用STCGAL 作為編程工具,統(tǒng)一的Debug 功能說(shuō)實(shí)話有點(diǎn)兒不可思議,我還沒(méi)嘗試。創(chuàng)建項(xiàng)目的過(guò)程非常順滑,由Visual Studio Code 支持的編輯體驗(yàn)當(dāng)然也很不錯(cuò),不過(guò)代碼的智能補(bǔ)全和分析功能是由C/C++ 插件提供的,基于Clang,對(duì)C51 擴(kuò)展的C語(yǔ)言語(yǔ)法沒(méi)有支持,所以頭文件里的寄存器定義全部報(bào)錯(cuò),當(dāng)然也沒(méi)有相應(yīng)的智能補(bǔ)全了,這個(gè)問(wèn)題的解決方案見(jiàn)后文。

【應(yīng)有圖2】

但是就和所有企圖大包大攬的東西一樣,隨后我就發(fā)現(xiàn)了Platform IO 細(xì)節(jié)上的不講究。Platform IO 提供一個(gè)配置文件給用戶,用于調(diào)整項(xiàng)目的一些設(shè)置,比如說(shuō)引用外部的庫(kù)文件、設(shè)置路徑、添加編譯選項(xiàng)之類的,問(wèn)題是Platform IO 使用了類似Arduino 的庫(kù)組織方式,它默認(rèn)一個(gè)庫(kù)就是個(gè)具有一定文件結(jié)構(gòu)的包,其中包括了頭文件和源代碼,因此似乎沒(méi)有必要再單獨(dú)提供一個(gè)選項(xiàng)讓你配置頭文件包含目錄,而我當(dāng)時(shí)遇到的問(wèn)題是智能提示找不到SDCC 的頭文件,最簡(jiǎn)單的解決方案是在C/C++ 插件的項(xiàng)目配置里加一條路徑就行了,但是這個(gè)配置文件是受Platform IO 管理的,每次Platform IO 更新?tīng)顟B(tài)的時(shí)候手動(dòng)添加的內(nèi)容就會(huì)被清理掉,所以必須去Platform IO的配置里加路徑,然而它并沒(méi)有提供單獨(dú)配置頭文件包含目錄的功能……搜索一番后,唯一能做的似乎只有在添加編譯選項(xiàng)的時(shí)候把目錄加進(jìn)去,編譯器當(dāng)然能找到它自己的頭文件,但是我的智能提示依然不能work。

除此之外,Platform IO 的其他問(wèn)題和這個(gè)類似,都是大包大攬的工具偶爾照顧不到的細(xì)節(jié)問(wèn)題,比方說(shuō)工具鏈的版本滯后,又沒(méi)有什么簡(jiǎn)潔的方法直接升級(jí);或者是在用STC 以外的C51 單片機(jī)的時(shí)候,如果要讓Platform IO 兼容它的工作流,顯然還得付出不少時(shí)間看它的文檔,調(diào)配置。

然而既然都要花時(shí)間費(fèi)勁看文檔、調(diào)配置,為什么我不去用一個(gè)功能更強(qiáng)、更有用、更受歡迎的工具呢?說(shuō)的就是CMake。

2. QtCreator + CMake + SDCC

【應(yīng)有圖3】

CMake 是時(shí)下流行的開(kāi)源跨平臺(tái)構(gòu)建工具,主要用來(lái)管理C/C++ 項(xiàng)目的構(gòu)建任務(wù),這里有一個(gè)不錯(cuò)的CMake 基礎(chǔ)教程,來(lái)自Clion,言簡(jiǎn)意賅。顯然C51 項(xiàng)目也是C 項(xiàng)目,遵循傳統(tǒng)的編譯 - 鏈接流程,無(wú)非是目標(biāo)平臺(tái)是在單片機(jī)上裸機(jī)運(yùn)行,需要交叉編譯而已。QtCreator 則是目前來(lái)說(shuō)開(kāi)發(fā)Qt 程序體驗(yàn)最完整的IDE,畢竟是官方出品,用于開(kāi)發(fā)非Qt 的普通C/C++ 項(xiàng)目時(shí),相比VS, VS code 之類的其他IDE 環(huán)境的體驗(yàn)也是各有千秋,Qt 還是開(kāi)發(fā)桌面GUI 程序或者說(shuō)上位機(jī)程序的最優(yōu)框架之一,如果能用QtCreator 一站式解決開(kāi)發(fā)單片機(jī)和上位機(jī)程序的需求的話那就比較香了。

QtCreator 近期也集成了CMake 支持,CMake 已經(jīng)是成熟的構(gòu)建工具了,按說(shuō)不應(yīng)該出什么妖蛾子才對(duì)。結(jié)果折騰了挺長(zhǎng)時(shí)間CMake 配置不成功,因?yàn)镼tCreator 對(duì)CMake 有一些額外的、為了便于使用的配置,結(jié)果成也GUI 敗也GUI,總之我也不確定究竟是什么地方配置錯(cuò)了,我只是希望不要再讓我配置這些東西了。

于是最終,決定回歸“原生”體驗(yàn),去掉那些雜七雜八的“便利”、“統(tǒng)一化”功能,干脆的直接手寫CMake 好了。

使用Visual Studio Code + CMake

【應(yīng)有圖4】

要支持C/C++ 開(kāi)發(fā)需要先安裝C/C++ 插件,巨硬官方出品,隨后安裝同樣是巨硬出品的CMake tools 插件,或許需要重啟一次VS code 讓插件準(zhǔn)備完畢。在此之前安裝CMake 依賴的Ninjia 構(gòu)建工具,添加到PATH。

接下來(lái)新建一個(gè)文件夾作為項(xiàng)目文件夾,用VS code 打開(kāi),然后點(diǎn)F1 打開(kāi)命令面板,搜索CMake 相關(guān)的命令,運(yùn)行CMake:配置 命令,用于準(zhǔn)備好CMake 環(huán)境,如果是個(gè)完全空的新建文件夾的話運(yùn)行配置命令后會(huì)又提示找不到CMakeLists.txt,點(diǎn)一下讓它新建就好了,然后目標(biāo)類型選擇executable,這樣它會(huì)自動(dòng)準(zhǔn)備一個(gè)CMakeLists.txt 文件在文件夾下面。彈出面板選擇工具的時(shí)候選擇“未指定”,因?yàn)樗o的列表里應(yīng)該沒(méi)有SDCC。

然后一個(gè)CMake 項(xiàng)目文件就算準(zhǔn)備好了,其中的build 文件夾之后會(huì)用于存放生成的文件。

使用CMake + SDCC 編譯

首先安裝SDCC,添加到PATH。

需要編輯CMakeLists.txt 文件,允許使用SDCC 編譯項(xiàng)目,啟用智能提示。CMake 默認(rèn)使用本地環(huán)境作為目標(biāo)環(huán)境,要使用SDCC 交叉編譯,需要添加以下兩行:

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER sdcc)

第一行指定交叉編譯的目標(biāo)平臺(tái),對(duì)于單片機(jī)裸機(jī)環(huán)境就是Generic,第二行指定SDCC 為C 編譯器。至少CMake 3.18 安裝后帶有基本的SDCC 支持文件,所以用這兩行開(kāi)啟SDCC 支持后基本上不需要再添加別的東西,可以算是真實(shí)省心。然后像一般的項(xiàng)目一樣添加源文件,生成就好了。點(diǎn)擊下部狀態(tài)欄齒輪“生成”按鈕或者執(zhí)行CMake: 生成命令。

【應(yīng)有圖5】

SDCC 默認(rèn)生成的文件是.ihx 格式,位于build 子文件夾內(nèi),要給STC 單片機(jī)編程的話可能需要轉(zhuǎn)化成.hex 或.bin 格式的文件,SDCC 提供了packihx.exe 和makebin.exe 兩個(gè)命令行工具用來(lái)做這件事,格式如下:

# 生成hex:
packihx.exe blink.ihx > blink.hex
# 或生成bin:
makebin.exe blink.ihx > blink.bin

需要注意的是,這個(gè)地方對(duì)Windows 用戶有個(gè)坑,如果用powershell 命令行執(zhí)行命令的話,輸出文件的編碼存在問(wèn)題,無(wú)法正常編程,至少是無(wú)法用STC-ISP 編程。這個(gè)問(wèn)題我沒(méi)有細(xì)究,可能是powershell 輸出的文件編碼使用了UTF-16 雙字節(jié)編碼,或者說(shuō)是Unicode,而正常的hex 文件使用ASCII 編碼,因此我看到的現(xiàn)象是powershell 輸出的文件是正常文件的兩倍大小,用文本編輯器,比如Notepad3 或者Notepad++ 打開(kāi)錯(cuò)誤的hex 文件,轉(zhuǎn)換編碼到UTF-8 就可以解決問(wèn)題。

可以編輯CMakeLists.txt 實(shí)現(xiàn)讓CMake 在編譯后自動(dòng)執(zhí)行ihx 到hex 文件的轉(zhuǎn)換工作,相關(guān)代碼如下:

add_custom_command(TARGET blink
                   POST_BUILD
                   COMMAND packihx.exe ARGS $ " > blink.hex"
                   WORKING_DIRECTORY ./)

這樣自動(dòng)生成的文件沒(méi)有編碼錯(cuò)誤。

然后就可以使用STC-ISP 或者STCGAL 上傳程序文件。這一步也可以通過(guò)編輯VS code的launch.json 實(shí)現(xiàn)集成。

文后會(huì)附上blink 實(shí)例程序和CMakeLists.txt 文件

解決智能提示兼容性問(wèn)題

SDCC C51 擴(kuò)展了不少非標(biāo)準(zhǔn)C 語(yǔ)言關(guān)鍵字,基于clang 的智能提示無(wú)法理解這些東西,于是使用這些關(guān)鍵字的時(shí)候都會(huì)報(bào)錯(cuò),無(wú)法智能提示頭文件中定義的寄存器。

一種解決思路是利用條件編譯,區(qū)別智能提示運(yùn)行環(huán)境和SDCC 實(shí)際編譯環(huán)境,用空的define 取代這些關(guān)鍵字,寄存器也都用宏代替,然后在SDCC 實(shí)際編譯時(shí)使用原來(lái)C51 語(yǔ)法的寄存器定義,舉例來(lái)說(shuō):

#ifdef __SDCC
	__sfr __at (0x80) P0;    //實(shí)際有效的寄存器定義
#else
	#define __sfr    //空的關(guān)鍵字宏,消除關(guān)鍵字不兼容
	#define __at

	#define P0 (*(char*)(0x80))    //用于兼容標(biāo)準(zhǔn)C 語(yǔ)法的寄存器符號(hào),沒(méi)有實(shí)際功能
#endif

//...
	P0 = 0xf0;
	P0 |= 0x02;
//...

以上條件編譯把代碼區(qū)分到智能提示和實(shí)際編譯兩個(gè)環(huán)境,在實(shí)際編譯時(shí),SDCC 編譯器會(huì)預(yù)定義__SDCC 符號(hào),因此實(shí)際編譯時(shí)使用實(shí)際有效的寄存器定義,而在智能提示環(huán)境,用空的宏取代所有關(guān)鍵字,消除關(guān)鍵字的不兼容,然后用一個(gè)宏定義寄存器,保證寄存器名智能提示依然可以使用。這里將P0 定義為一個(gè)對(duì)char* 指針解引用的左值表達(dá)式, 因?yàn)檫@樣一來(lái)在語(yǔ)法上對(duì)P0 的賦值才是合法的,括號(hào)里的數(shù)值可以是任意不太大的整數(shù),當(dāng)然用P0 本來(lái)的值更合適。

實(shí)際使用時(shí),創(chuàng)建一個(gè)特殊的頭文件,其中的條件編譯在實(shí)際編譯時(shí)正常包含C51 頭文件,將C51 頭文件中的符號(hào)都轉(zhuǎn)寫成兼容的形式,供智能提示使用,見(jiàn)附1的代碼。

其他增強(qiáng)

  1. C51 項(xiàng)目的CMake 配置是類似的,可以使用類似項(xiàng)目模板的插件,簡(jiǎn)化新建項(xiàng)目的工作。

附1:8051_inc_error_hide.h

#ifndef 8051_INC_ERROR_HIDE_H
#define 8051_INC_ERROR_HIDE_H


/*
	To solve the problem that vs / clang dose not
	recognize those SDCC defined keywords.
*/

#ifdef __SDCC
	#include 

#else
	//keywords
	#define __interrupt
	#define __using
	#define __code
	#define __data
	#define __near
	#define __xdata
	#define __far
	#define __idata
	#define __pdata
	#define __code
	#define __bit
	#define __sfr
	#define __sfr16
	#define __sfr32
	#define __sbit
	#define __at
	#define __critical
	//#define __asm
	//#define __endasm

	//header - 8051.h registers

	#define P0 (*(char*)((0x80)))
	#define SP (*(char*)((0x81)))
	#define DPL (*(char*)((0x82)))
	#define DPH (*(char*)((0x83)))
	#define PCON (*(char*)((0x87)))
	#define TCON (*(char*)((0x88)))
	#define TMOD (*(char*)((0x89)))
	#define TL0 (*(char*)((0x8A)))
	#define TL1 (*(char*)((0x8B)))
	#define TH0 (*(char*)((0x8C)))
	#define TH1 (*(char*)((0x8D)))
	#define P1 (*(char*)((0x90)))
	#define SCON (*(char*)((0x98)))
	#define SBUF (*(char*)((0x99)))
	#define P2 (*(char*)((0xA0)))
	#define IE (*(char*)((0xA8)))
	#define P3 (*(char*)((0xB0)))
	#define IP (*(char*)((0xB8)))
	#define PSW (*(char*)((0xD0)))
	#define ACC (*(char*)((0xE0)))
	#define B (*(char*)((0xF0)))
	#define P0_0 (*(char*)((0x80)))
	#define P0_1 (*(char*)((0x81)))
	#define P0_2 (*(char*)((0x82)))
	#define P0_3 (*(char*)((0x83)))
	#define P0_4 (*(char*)((0x84)))
	#define P0_5 (*(char*)((0x85)))
	#define P0_6 (*(char*)((0x86)))
	#define P0_7 (*(char*)((0x87)))
	#define IT0 (*(char*)((0x88)))
	#define IE0 (*(char*)((0x89)))
	#define IT1 (*(char*)((0x8A)))
	#define IE1 (*(char*)((0x8B)))
	#define TR0 (*(char*)((0x8C)))
	#define TF0 (*(char*)((0x8D)))
	#define TR1 (*(char*)((0x8E)))
	#define TF1 (*(char*)((0x8F)))
	#define P1_0 (*(char*)((0x90)))
	#define P1_1 (*(char*)((0x91)))
	#define P1_2 (*(char*)((0x92)))
	#define P1_3 (*(char*)((0x93)))
	#define P1_4 (*(char*)((0x94)))
	#define P1_5 (*(char*)((0x95)))
	#define P1_6 (*(char*)((0x96)))
	#define P1_7 (*(char*)((0x97)))
	#define RI (*(char*)((0x98)))
	#define TI (*(char*)((0x99)))
	#define RB8 (*(char*)((0x9A)))
	#define TB8 (*(char*)((0x9B)))
	#define REN (*(char*)((0x9C)))
	#define SM2 (*(char*)((0x9D)))
	#define SM1 (*(char*)((0x9E)))
	#define SM0 (*(char*)((0x9F)))
	#define P2_0 (*(char*)((0xA0)))
	#define P2_1 (*(char*)((0xA1)))
	#define P2_2 (*(char*)((0xA2)))
	#define P2_3 (*(char*)((0xA3)))
	#define P2_4 (*(char*)((0xA4)))
	#define P2_5 (*(char*)((0xA5)))
	#define P2_6 (*(char*)((0xA6)))
	#define P2_7 (*(char*)((0xA7)))
	#define EX0 (*(char*)((0xA8)))
	#define ET0 (*(char*)((0xA9)))
	#define EX1 (*(char*)((0xAA)))
	#define ET1 (*(char*)((0xAB)))
	#define ES (*(char*)((0xAC)))
	#define EA (*(char*)((0xAF)))
	#define P3_0 (*(char*)((0xB0)))
	#define P3_1 (*(char*)((0xB1)))
	#define P3_2 (*(char*)((0xB2)))
	#define P3_3 (*(char*)((0xB3)))
	#define P3_4 (*(char*)((0xB4)))
	#define P3_5 (*(char*)((0xB5)))
	#define P3_6 (*(char*)((0xB6)))
	#define P3_7 (*(char*)((0xB7)))
	#define RXD (*(char*)((0xB0)))
	#define TXD (*(char*)((0xB1)))
	#define INT0 (*(char*)((0xB2)))
	#define INT1 (*(char*)((0xB3)))
	#define T0 (*(char*)((0xB4)))
	#define T1 (*(char*)((0xB5)))
	#define WR (*(char*)((0xB6)))
	#define RD (*(char*)((0xB7)))
	#define PX0 (*(char*)((0xB8)))
	#define PT0 (*(char*)((0xB9)))
	#define PX1 (*(char*)((0xBA)))
	#define PT1 (*(char*)((0xBB)))
	#define PS (*(char*)((0xBC)))
	#define P (*(char*)((0xD0)))
	#define F1 (*(char*)((0xD1)))
	#define OV (*(char*)((0xD2)))
	#define RS0 (*(char*)((0xD3)))
	#define RS1 (*(char*)((0xD4)))
	#define F0 (*(char*)((0xD5)))
	#define AC (*(char*)((0xD6)))
	#define CY (*(char*)((0xD7)))

	/* BIT definitions for bits that are not directly accessible */
	/* PCON bits */
	#define IDL             0x01
	#define PD              0x02
	#define GF0             0x04
	#define GF1             0x08
	#define SMOD            0x80

	/* TMOD bits */
	#define T0_M0           0x01
	#define T0_M1           0x02
	#define T0_CT           0x04
	#define T0_GATE         0x08
	#define T1_M0           0x10
	#define T1_M1           0x20
	#define T1_CT           0x40
	#define T1_GATE         0x80

	#define T0_MASK         0x0F
	#define T1_MASK         0xF0

	/* Interrupt numbers: address = (number * 8) + 3 */
	#define IE0_VECTOR      0       /* 0x03 external interrupt 0 */
	#define TF0_VECTOR      1       /* 0x0b timer 0 */
	#define IE1_VECTOR      2       /* 0x13 external interrupt 1 */
	#define TF1_VECTOR      3       /* 0x1b timer 1 */
	#define SI0_VECTOR      4       /* 0x23 serial port 0 */

#endif
#endif

附2:blink.c

#include "8051_inc_error_hide.h"  //-->#include 
#include 

#define LED P0_0

void delay(uint16_t t) {
	while(i--);
}

void main(void) {
	while(1) {
		LED = 0;
		delay(20000);
		LED = 1;
		delay(20000);
	}
}

附3:CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(blink LANGUAGES C)

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER sdcc)

add_executable(blink blink.c 8051_inc_error_hide.h)

add_custom_command(TARGET blink
                   POST_BUILD
                   COMMAND packihx.exe ARGS $ " > blink.hex"
                   WORKING_DIRECTORY ./)

看完上述內(nèi)容,你們對(duì)怎么使用Visual Studio Code + CMake + SDCC 進(jìn)行C51開(kāi)發(fā)嘗試有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。


分享標(biāo)題:怎么使用VisualStudioCode+CMake+SDCC進(jìn)行C51開(kāi)發(fā)嘗試
網(wǎng)頁(yè)路徑:http://weahome.cn/article/gccosd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部