




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、 嵌入式系統(tǒng)的C語(yǔ)言 譯自C for Embedded Systems講稿劉永重譯一、C語(yǔ)言基礎(chǔ)1、什么是C? C程序語(yǔ)言最初是由Dennis Ritchie在1971年為UNIX系統(tǒng)開發(fā)并實(shí)現(xiàn)的。C的一個(gè)最大優(yōu)點(diǎn)是與任何特定的硬件或系統(tǒng)無(wú)關(guān)。這使得一個(gè)用戶寫的程序不作任何修改就能運(yùn)行在幾乎所有的機(jī)器上。 C通常被稱為中級(jí)計(jì)算機(jī)語(yǔ)言,因?yàn)樗鼘⒏呒?jí)語(yǔ)言的要素與匯編語(yǔ)言的功能結(jié)合了在一 起。 2、為什么用C? C非常靈活,而且可隨心所欲。這種自由賦予C非常強(qiáng)大的功能,有經(jīng)驗(yàn)的用戶可以掌握; C是一個(gè)相對(duì)小的語(yǔ)言,但是它經(jīng)久耐用;C有時(shí)被認(rèn)為是“高級(jí)匯編語(yǔ)言”;低級(jí)(位操作) 編程也容易實(shí)現(xiàn);松類型
2、(不象其它高級(jí)語(yǔ)言);C是結(jié)構(gòu)化編程語(yǔ)言;C允許你創(chuàng)建你腦海 中已有的任何任務(wù)。 C保留了程序員知道正在做的事情的基本體系;它只需要他們明白地表達(dá)其意圖。3、為什么不用C?文化的問題 當(dāng)考慮轉(zhuǎn)到C語(yǔ)言時(shí),我們會(huì)遇到一些共同的問題: 產(chǎn)生大而低效的代碼;標(biāo)準(zhǔn)IO程序的雍余代碼(printf,scanf,strcpy等);存貯器定位的使用:malloc(),alloc();堆棧的使用,在C中不很直接;在RAM和ROM中數(shù)據(jù)的聲明;難于寫中斷服務(wù)程序。 4、8位微控制器的ANSI C 對(duì)于嵌入式系統(tǒng),純粹的ANSI C并不方便,因?yàn)椋?嵌入式系統(tǒng)與硬件打交道。ANSI C 提供的在固定存貯空間用寄存
3、器尋址的工具非常拙劣; 5、打破一些C范例 當(dāng)在低端的8位微控制器上用C語(yǔ)言,應(yīng)想法使代碼變小。這意味著打破一些編程規(guī)則: 開/關(guān)全局中斷;使用GOTO語(yǔ)句;全局標(biāo)號(hào);全局寄存器段;指針支持。 6、嵌入式與桌面編程 嵌入式編程環(huán)境的主要特點(diǎn): 有限的RAM;有限的ROM;有限的??臻g;面向硬件編程;嚴(yán)格的定時(shí)( ISR,任務(wù), );很多不同種類的指針(far/near/rom/uni/paged/);特殊關(guān)鍵字/標(biāo)識(shí)符(,i nterrupt,tiny,)。 7、匯編與C 編譯器只是一個(gè)能干的優(yōu)秀匯編程序員。 1 寫能夠轉(zhuǎn)換為高效率匯編代碼的好的C代碼,比手工寫高效率的匯編代碼容易得多。C是終
4、極解決辦法,但其本身并未終結(jié)。 8、為什么改用C? 有很多原因用C語(yǔ)言而不用匯編: C使我們提高效益;用C寫的代碼更可靠;C代碼更容易升級(jí)和擴(kuò)展;不同平臺(tái)之間更容 易遷移;代碼容易維護(hù);文檔、書籍、第三方庫(kù)和程序都可得到。 9、C代碼結(jié)構(gòu) 如下圖所示,一個(gè)C程序基本由以下部分組成: 預(yù)處理命令、類型定義、函數(shù)原型(聲明傳給函數(shù)的函數(shù)類型和變量)、變量和函數(shù)。一個(gè)程序必須有一個(gè)main()函數(shù),每個(gè)命令行必須用分號(hào)(;)結(jié)束。 10、C函數(shù) 一個(gè)函數(shù)的結(jié)構(gòu)如下: 類型 函數(shù)名(參數(shù)) 本地變量C 語(yǔ)句 2 11、C關(guān)鍵字 1)數(shù)據(jù)類型 charshortsignedunsignedintfloa
5、tlongdouble2)修飾符conststaticvolatilerestrict3)標(biāo)識(shí)符structunionvoidenum4)選擇體ifelseswitchcasedefault5)存貯指定registertypedefautoextern6)循環(huán)體do whilefor7)跳轉(zhuǎn)gotocontinuebreakreturn8)功能指定inline9)預(yù)處理指示#include#define#undef#line#error#pragma10)條件編譯3 # if# ifdef# ifndef# elif# else# endif12、C操作符 1) 基本表達(dá)式和后綴操作符 ( )
6、 子表達(dá)式和函數(shù)調(diào)用成員 + 增加(后綴) 2) 一元操作符 數(shù)組下標(biāo)- 結(jié)構(gòu)指針結(jié)構(gòu)-減少(后綴)! 邏輯非 取補(bǔ)+ 增加(前綴)- 減少(后綴)- 一元減+ 一元加(類型) 類型強(qiáng)制3)賦值符 * 間接指針& 取地址sizeof 大小= 相等賦值+=加等于-= 減等于*= 乘等于/= 除等于%= 求余等于=右移位等于 &=按位與等于 =按位異或等于 位異或 | 位或 位右移5) 數(shù)算 / 除%求余+ 加- 減* 乘 6) 關(guān)系運(yùn)算 小于試 7)邏輯運(yùn)算 & 邏輯與8)條件運(yùn)算 ?: 條件測(cè)試 9)序列 , 逗號(hào) 二、嵌入式編程1、變量 大于=大于或等于 =相等測(cè)試!=不等測(cè)| 邏輯或變量的
7、類型決定其可帶值的類型。也就是說,為變量選擇一個(gè)類型與我們使用這個(gè)變量的方法直接相關(guān)。我們將學(xué)習(xí)C的基本類型、怎樣寫常量和聲明這些變量。 1.1選擇一個(gè)類型 “值集合”是有限的。C的整數(shù)類型不能代表所有整數(shù);它的浮點(diǎn)類型也不能代表所有浮點(diǎn)數(shù)。當(dāng)聲明一個(gè)變量并為它選擇一個(gè)類型,你應(yīng)緊記你需要的值和操作。 1.2C的基本數(shù)據(jù)類型 4 ANSI標(biāo)準(zhǔn)并沒為本地類型規(guī)定尺寸大小,但CodeWarrior規(guī)定了。C只有一些基本數(shù)據(jù)類 型: 所有數(shù)量類型(除了char)缺省都是有符號(hào)的,例如:int = signed int。 注意:INT型的大小依賴于不同的機(jī)器。13 CodeWarrior數(shù)據(jù)類型 例如
8、,按ALT+F7打開工程的通用設(shè)置,選擇“Compiler for HC08”面板并點(diǎn)擊類型尺寸。這個(gè)窗口向你顯示CodeWarrior 編譯器使用的標(biāo)準(zhǔn)類型設(shè)置。 所有基本類型可以改變,盡管這可能不是個(gè)好主意。 5 1.4數(shù)據(jù)類型的事實(shí) 代碼大小和執(zhí)行時(shí)間的最大節(jié)約可通過為變量選擇最合適的數(shù)據(jù)類型得到。 8位微控制器內(nèi)部的數(shù)據(jù)的長(zhǎng)度是8位(一字節(jié)),然而C首選的數(shù)據(jù)類型是int。 8位機(jī)處理8位數(shù)據(jù)類型比16位類型效率更高。 “int“和大數(shù)據(jù)類型只有當(dāng)所描述的數(shù)據(jù)的大小需要時(shí)才使用。當(dāng)效率非常重要時(shí),雙精度和浮點(diǎn)操作效率低,應(yīng)當(dāng)避免。 1.5選擇數(shù)據(jù)類型 8位微控制器選擇數(shù)據(jù)類型有3個(gè)規(guī)則
9、: 1) 用最可能小的類型來完成工作,大小越小占用存貯空間越少; 2) 若可能,用無(wú)符號(hào)類型; 3) 在表達(dá)式內(nèi)聲明以將數(shù)據(jù)類型減到最少需要。使用類型定義得到固定大?。?1)根據(jù)編譯器和系統(tǒng)而改變;2)移植到不同的機(jī)器代碼不變;3)當(dāng)值需要固定位時(shí) 使用。 6 打開文件:Lab1-Variables.mcp只寫了意義最少的位;寄存器用于此 目的。 每個(gè)變量剩余位用:clr,x 清除。Ma定in義函了數(shù)一內(nèi)個(gè)定數(shù)義據(jù)了類三型種的 不完同整類集型合的變量 7變量在堆棧中有一個(gè)地址。 主函數(shù)外定義了三個(gè)不同類 型的變量。 8編譯器為作用的變量保留了內(nèi)存。本例中VarA 是唯一使用的變量。 主函數(shù)外定
10、義了三個(gè)不同類型的變量。 所有聲明的全局變量均 被使用。 9在這種情況下,編譯器為所有變量保留了內(nèi)存。 根據(jù)變量大小的不同,每個(gè)加操作 用不同的方法完成。 變量聲明的內(nèi)存區(qū),每個(gè)變量有不同的大小 (1、2 和 4 字節(jié)) 2、存貯類修飾符 以下關(guān)鍵字用于聲明變量,以指定特定需要或內(nèi)存中變量存貯的相關(guān)條件。 staticvolatileconst這三個(gè)關(guān)鍵字,一起讓我們不僅可寫出好的代碼,而且可寫出緊湊的代碼。21 靜態(tài)變量 使用靜態(tài)有二個(gè)主要功能: 第一個(gè)最常用的用法是定義一個(gè)變量,在函數(shù)連續(xù)調(diào)用期間,變量不會(huì)消失。 第二個(gè)使用靜態(tài)的用法是限制變量的范圍。在模塊級(jí)定義時(shí),能被整個(gè)模塊中所有函數(shù)
11、訪問,不能被其它函數(shù)訪問。這非常重要,因?yàn)楫?dāng)嚴(yán)格限制全局變量眾所周知的問題時(shí),它讓我們獲得所有全局變量執(zhí)行性能的好處。因此,如果我們有必須被一些函數(shù)頻繁訪問的數(shù)據(jù)結(jié)構(gòu),就應(yīng)當(dāng)將函數(shù)放入同一模塊中,并將結(jié)構(gòu)聲明為靜態(tài)。這樣所有函數(shù)能夠訪問而不必通過一個(gè)訪問函數(shù)的上層,同時(shí)與數(shù)據(jù)結(jié)構(gòu)無(wú)關(guān)的代碼禁止訪問它。這一技術(shù)是一種變通方法,立即可訪問變量在小的機(jī)器上實(shí)質(zhì)上取得了足夠的性能。 聲明模塊級(jí)靜態(tài)變量(與將其設(shè)為全局相反)能取得一些其他潛在的益處。靜態(tài)變量由于定義,只能被一組特定的函數(shù)訪問。因此,編譯器和連接器能夠明智地選擇變量在存貯空間的放置。例如,對(duì)于靜態(tài)變量,編譯器/連接器也許選擇將一個(gè)模塊中所
12、有靜態(tài)變量放在連 續(xù)的區(qū)域,這樣增加了各種優(yōu)化機(jī)會(huì),例如用簡(jiǎn)單的增加或減少代替重載。相反,全局變量 10 在存貯空間的位置通常計(jì)劃于優(yōu)化編譯器的哈稀算法,這排除了可能的優(yōu)化。須著重指出, 這些變量不會(huì)存貯在堆棧中,因?yàn)樗鼈儽仨毐4嫫渲怠?下面給出一個(gè)靜態(tài)變量怎樣工作的例子: FILE1.c#include /包含文件FILE2.c/中的函數(shù) void main (void) /第一次進(jìn)入MyFunction之前,myVar=0。 MyFunction();/在FILE2.c中/第二次進(jìn)入MyFunction之前,myVar=1。 MyFunction();/在FILE2.c中FILE2.cvo
13、id MyFunction (void)/FILE2.C中定義/MyFunction函數(shù) static char myVar = 0;/本地變量 /聲明為static/盡管myVar是本地變量,但它保持了自己的值。 myVar = myVar + 1; 2.2 靜態(tài)函數(shù) 一個(gè)靜態(tài)函數(shù)只能被其所在模塊中的其它函數(shù)調(diào)用。使用靜態(tài)函數(shù)是結(jié)構(gòu)化編程的好習(xí)慣。你也許驚訝地知道靜態(tài)函數(shù)能產(chǎn)生小/快的代碼。這是可能的,因?yàn)榫幾g器在編譯時(shí)確切 地知道什么函數(shù)能調(diào)用一個(gè)給定的靜態(tài)函數(shù)。因此,函數(shù)的相關(guān)內(nèi)存區(qū)域能被調(diào)整,以致使 用調(diào)用的一個(gè)短版本或跳轉(zhuǎn)指令。潛在的改進(jìn)甚至更好,編譯器足夠聰明地用跳轉(zhuǎn)代替調(diào)用。 2
14、.3 關(guān)鍵字“static”的使用 在函數(shù)體聲明靜態(tài)的變量,在函數(shù)調(diào)用期間保持其質(zhì); 在模塊內(nèi)聲明靜態(tài)的變量,(但在函數(shù)體之外)能被模塊內(nèi)所有函數(shù)訪問; 在模塊內(nèi)聲明靜態(tài)的函數(shù),只能被模塊內(nèi)其它函數(shù)調(diào)用。 對(duì)于嵌入式系統(tǒng):封裝持續(xù)生存的數(shù)據(jù)(包裝);模塊化編碼(數(shù)據(jù)隱藏);在每個(gè)模塊中隱藏內(nèi)部處理。 2.4 可變(volatile)變量 可變變量是其值在正常程序流程以外可能改變的變量。在嵌入式系統(tǒng)中,這種情況通過兩種主要途徑發(fā)生: 通過一個(gè)中斷服務(wù)程序,或作為硬件動(dòng)作的結(jié)果。例如,通過一個(gè)串口接收到一個(gè)字符, 11 結(jié)果串口狀態(tài)寄存器更新,這完全在程序流程之外發(fā)生。很多程序員知道編譯器不會(huì)試圖
15、優(yōu)化一個(gè)volatile寄存器,而寧可每次重載它。 在嵌入式設(shè)備中,將所有外設(shè)寄存器聲明為volatile是一個(gè)好習(xí)慣。 許多編譯器供應(yīng)商經(jīng)常炫耀他們的代碼優(yōu)化,它們通常非常好,它們有些根本不明顯,但能極大地減少周期和內(nèi)存。但有時(shí)我們不想編譯器聰明和優(yōu)化一個(gè)部份,因?yàn)槲覀兇_實(shí)需要代碼那樣作。 我們?cè)鯓硬拍苓_(dá)到呢?那么,訪問定義為volatile的變量從不會(huì)被編譯器優(yōu)化。 讓我們分析一個(gè)例子,看看編譯器是怎樣處理一個(gè)volatile和一個(gè)非volatile變量 volatile volatile unsignedvoid unsigned char PORTA 0x00;unsigned cha
16、r SCS1char value; 0x16;/* PORTA/* PORTA= 00000101 */= 00000101 */main(void) POR TA = 0x05; PORTA = 0x05; SCS1; value = 10; 未使用Volatile關(guān)鍵字,編譯器將其編譯為: MOV LDASTA#5,PORTA #10 value 使用Volatile關(guān)鍵字后,編譯器將其編譯為: MOV MOV LDA LDXSTX#5,PORTA #5,PORTA SCS1 #10 value 這段代碼實(shí)際上不做任何事,但它很好地表達(dá)了優(yōu)化怎樣強(qiáng)烈地影響程序的結(jié)果。在main() 中連續(xù)
17、兩次使用語(yǔ)句:PORTA=5,這沒有意義,但讓我們假設(shè)這是正確開發(fā)程序所必須的 在這兩個(gè)語(yǔ)句之后,明顯地有一條無(wú)意義語(yǔ)句“ SCS1;”。讓我們看當(dāng)不使用volatile變量會(huì)發(fā)生什么 我們得到了優(yōu)化過的匯編代碼。重復(fù)的語(yǔ)句Port A = 5消失了只剩下一句“move # 5 to Port A”。語(yǔ)句“SCS1;”似乎什么都不做,因此聰明的編譯器將它消去了。最后,將10加載到累加器并作為值存貯。 使用volatile關(guān)鍵字聲明PORTA 和SCS1,得到的匯編代碼沒有優(yōu)化,連續(xù)兩次在Port A寫入數(shù)值5,然后將SCS1加載到累加器。最后由于累加器被使用,于是用X寄存器存貯數(shù)值10。 12
18、 好了,連續(xù)兩次用數(shù)值5寫PortA,假設(shè)這是需要這樣做,但是加載SCS1到累加器有一個(gè)很有意義的值。這是串行通口SCI需要的,讀SCS1寄存器目的是清除任何未決的標(biāo)志。無(wú)意義的語(yǔ)句“SCS1;”被翻譯為讀寄存器的的匯編語(yǔ)句,這將清除SCI中未決的標(biāo)志。 前面說過,在嵌入式設(shè)備中將所有外設(shè)寄存器聲明為volatile是一個(gè)好習(xí)慣。在分開的 頭文件中定義所有外設(shè)的名字,能使所寫代碼更友好并使遷移簡(jiǎn)化。下面這個(gè)例子用volatil e變量聲明所有寄存器,這樣做較妥當(dāng),因?yàn)槿魏芜@些寄存器能在任何時(shí)候在程序流程之外被修改。 /* MC68HC908GP20/32 OfficialPeripheral
19、Register Names */volatile volatile volatile volatile volatile volatile volatile volatile volatile volatile volatile volatile volatile25 Const變量 unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsignedunsignedchar char char char char char char char c
20、har char char charcharPORTA PORTB PORTC PORTD PORTE DDRA DDRB DDRC DDRD DDRE PTAPUE PTCPUEPTDPUE0x0000; /* 0x0001; 0x0002; 0x0003; 0x0008; Ports and data direction */0x0004; 0x0005; 0x0006; 0x0007; 0x000C; 0x000D; 0x000E;0x000F;/*Data Direction Registers */*Port pull-up enables */關(guān)鍵字“const”,C語(yǔ)言中命名最差
21、的關(guān)鍵字,并不表示恒量,而是代表“只讀”。在嵌入式系統(tǒng)中,有很大的不同,這一會(huì)應(yīng)會(huì)明白。 Const聲明可用于任何變量,它告訴編譯器將其存貯在ROM代碼。編譯器保留了那個(gè)位置程序存貯器地址。由于位于ROM中,其值不能改變。 由于它作為常量工作,必須賦一初值。如:const double PI = 3.14159265; Const 變量與明顯的常數(shù)相對(duì),很多原文要求用const變量代替明顯的常數(shù)。例如: 用const unsigned char channels = 8;代替#define CHANNELS 8 。 本方法的基本原理是在調(diào)試器內(nèi)部,你能檢查一個(gè)const變量,然而一個(gè)明顯的常數(shù)
22、不可訪問。不幸的是,在很多8位機(jī)上你將為這一好處付出極大的代價(jià)。這兩個(gè)主要代價(jià)是: 一些編譯器在RAM中創(chuàng)建一個(gè)真實(shí)的變量來支持cost變量,這是一個(gè)極大的懲罰。 一些編譯器如CodeWarrior,知道變量為const,將把變量存貯在ROM中。無(wú)論怎樣, 變量仍作為變量處理和訪問,典型地用某些變址尋址(16位)的方式。與直接尋址(8位)方式相比,這種方法通常很慢。 Const的用法: const unsigned short a;13unsigned short const unsigned unsigned short26 Const volatile 變量const a; short *
23、a;* const a; 現(xiàn)在討論一個(gè)深?yuàn)W的問題,一個(gè)變量既能是常量,又能是可變量嗎?如果是這樣,這意味什么,怎樣使用?答案是“能”。 這個(gè)修飾符應(yīng)該用于能出乎意料地改變的任何存貯器位置,因此需要volatile限定語(yǔ), 由于const該變量是只讀的。 最明顯的例子是硬件狀態(tài)寄存器,象SCI狀態(tài)寄存器SCS1。這個(gè)寄存器包含信號(hào)狀態(tài)標(biāo)志, 如發(fā)送空、發(fā)送完成、接收滿以及其它。這是一個(gè)可變寄存器由于這些標(biāo)志的改變依賴于串行 通信的狀態(tài),這也是只讀,由于標(biāo)志不能被程序直接改寫,它們只對(duì)模塊的狀態(tài)作出響應(yīng)。 這個(gè)狀態(tài)寄存器最佳聲明方法是: const volatile unsigned char S
24、CS1 0x0016 3、資源映射 3.1 訪問固定內(nèi)存位置 嵌入式系統(tǒng)通常的特點(diǎn)是需要編程者訪問一個(gè)指定的存貯器位置。 練習(xí):在某個(gè)項(xiàng)目中需要將絕對(duì)地址0xFFA處整型變量的值設(shè)為0xAA55(編譯器為純粹的ANSI編譯器)。完成這個(gè)任務(wù)的代碼是: Int * ptr; ptr = (int *)0x2FFA; *ptr = 0xAA55; 3.2 怎樣訪問I/O寄存器 在嵌入式領(lǐng)域,設(shè)備如微控制器有片上資源,應(yīng)當(dāng)被管理和訪問。很多I/O和控制寄存器位于直接頁(yè),它們應(yīng)如此聲明,因此在可能時(shí)編譯器能直接尋址。它們有定位的地址,但問 題是它們不是存儲(chǔ)器,那么怎樣訪問這些I/O寄存器呢?這是一個(gè)非
25、常重要的問題,答案比 你想的簡(jiǎn)單或者復(fù)雜。 一個(gè)普照通而有用的形式是使用如下的#define指示: #define PortA( * ( volatile unsigned char * )0x0000 )這構(gòu)成了I/O寄存器,這種情況下,Port A為地址0x0000處字符型變量。#define實(shí)際做的是每次發(fā)現(xiàn)PortA時(shí)放置一個(gè)構(gòu)件。也就是說在代碼中寫:PortA = 0x3F,實(shí)際做的就是告訴編譯器0x0000是一個(gè)volatile-unsigned-char類型的指針,它的內(nèi)容等于0x3F。 糊涂嗎?有點(diǎn)讓我們看一些其它選擇: 這樣做的一個(gè)容易的方法是在變量聲明中使用符號(hào)“”,創(chuàng)建一
26、個(gè)語(yǔ)句讀作:在地址0 x0000處創(chuàng)建一個(gè)volatile-unsigned-char型的變量PortA。 這是一個(gè)編譯器特定的語(yǔ)法,它可讀性高,但失去了兼容性。無(wú)論什么時(shí)候我們決定使用一個(gè)不同的編譯器去編譯該代碼,也許會(huì)發(fā)現(xiàn)不被識(shí)別。CodeWarrior和Cosmic包含了這個(gè)特殊語(yǔ)法。 14 CPU中的寄存器沒有內(nèi)存映射;指令集包含允許它們自修改的子集;C不提供直接訪問寄存器的工具;C編譯器允許在C代碼中使用匯編指令,如: 1)_asm AssemblyInstuction; 2) asm3) asm(AssemblyInstruction); 15修改CPU 中CCR的I位的內(nèi)容。 使
27、用匯編指示,I 位被修 改。 33 位域 在嵌入系統(tǒng)中,在一個(gè)給定的地址,一次能訪問和修改一位或幾位。 完成這個(gè)任務(wù),在C語(yǔ)言中有不同的方法達(dá)到和實(shí)現(xiàn)。 *位結(jié)構(gòu): 效率隨編譯器的不同而改變;跨編譯器和目標(biāo)不能移植。 *位類型: 不能移植(標(biāo)準(zhǔn)C語(yǔ)言中沒有);如 16$0020 0 0 0 0 1 0 0 1 當(dāng)使用時(shí)可提高代碼的效率。 *移位和掩模 可移植,適當(dāng)?shù)男剩唤?jīng)常優(yōu)化為位操作。 如果定義一個(gè)結(jié)構(gòu),但所有變量重疊在同一內(nèi)存的開始位置,你應(yīng)該使用聯(lián)合體。聯(lián)合體允許引用在聯(lián)合體中定義的以任何形式描述的數(shù)據(jù)字節(jié)。聯(lián)合體在內(nèi)存中的尺寸大小為聯(lián)合體中所列的最大類型的大小。點(diǎn)操作符用于選擇需要的
28、成員。 打開文件:Lab2-BitFields.mcp17聯(lián)合體是一個(gè)變量,不同的時(shí)間持有對(duì)象不同的類型和大小,編譯器跟蹤變 只有 PS 位被修改一條指令所寫 34 數(shù)組 C允許程序員用幾種不同方法存取數(shù)組的內(nèi)容。 Unsigned char Array=0xAA,0xBB,0xCC; 依賴于執(zhí)行,選擇最適合于該應(yīng)用的需要,將產(chǎn)生快而小的代碼。數(shù)組訪問方法: 1)硬編碼: Array0=12*UNIT_VOLTS; 編譯時(shí)決定地址,執(zhí)行速度快。 2) 變址增加 Arrayindex+=12*UNIT_VOLTS; 快速,比硬編碼靈活。3) 數(shù)組指針 *(ArrayPtr+)=12*UNIT_V
29、OLTS; 執(zhí)行速度快,可讀性差,可和循環(huán)一起使用。如下圖所示: 打開文件:Lab3-Arrays.mcp 18聯(lián)合體提供操作單一存貯區(qū)不同類型數(shù)據(jù)的方法, 程序中沒有嵌入任 硬編碼增量變址數(shù)組指針每種訪問類型都有各自的優(yōu)點(diǎn), 使用不同的寄存器完成 不同的操作。 19 35 函數(shù)指針 函數(shù)指針與數(shù)據(jù)指針一樣有用處,原因如下: 當(dāng)你想要一個(gè)額外級(jí)別的間接時(shí); 當(dāng)你想用同一段代碼依環(huán)境的不同調(diào)用不同的函數(shù)。 下面的代碼定義了一個(gè)指向函數(shù)的指針,帶了一個(gè)整型參數(shù)并返回一整數(shù): int (*function)(int); (*function)周圍的圓括符是必須的,因?yàn)槎x中的優(yōu)先關(guān)系。沒有它們,我們
30、則定義了一個(gè)函數(shù)返回一個(gè)整型指針。 例如: 函數(shù)指針初始化 函數(shù)指針現(xiàn)在指向一個(gè)不 同的函數(shù) 下面舉一個(gè)HC08QL的例子: 20 SLIC模塊僅有一個(gè)中斷;用戶必須讀SLIC中斷向量寄存器(SLCSV)來核實(shí)中斷源??赡艿慕鉀Q方案: switch 語(yǔ)句;嵌套的if語(yǔ)句;函數(shù)指針。 打開文件:Lab4-Pointers.mcp 21每次調(diào)用一個(gè)不同的函數(shù) 定義了一個(gè)函數(shù)數(shù)組。 調(diào)試(1):調(diào)試(2):22每次將執(zhí)行一個(gè)不同的函數(shù)。 1、 在函數(shù)調(diào)用處下斷點(diǎn)。2、 跟蹤進(jìn)函數(shù)(F11)。 調(diào)試(3): 打開Component-Visuallization 打開Display.vtl 然后運(yùn)行:
31、Tool什么時(shí)候使用指針: 當(dāng)使用少量函數(shù)時(shí),嵌套的IF語(yǔ)句占用空間少; Switch語(yǔ)句可讀性好,但占空間大; 當(dāng)很多函數(shù)被聲明時(shí),指針產(chǎn)生的代碼少,但它占用大量的RAM空間。36 棧指針與函數(shù)參數(shù) 棧指針支持C的關(guān)鍵特性: 在匯編程序和C編譯器中,堆棧通常用于給子程序傳遞變量; 允許使用遞歸; 是自動(dòng)變量的基礎(chǔ)。 典型地子程序?qū)研枰牟僮鲾?shù)放入累加器。堆棧相對(duì)尋址允許訪問堆棧上的數(shù)據(jù),提供直接訪問操作數(shù),排除從堆棧壓入以及彈出數(shù)值所需要的代碼和時(shí)間。 堆棧指針指令與等份的變址指令相比需要一個(gè)額外的字節(jié)和一個(gè)額外的執(zhí)行周期。例如: typedef struct 23VarA 在每個(gè)函數(shù)中被
32、修改。 unsigned char unsigned short ObjectType; void foo (unsignedID; Time;char value) volatile ObjectType instance; instance.ID = 編譯后得到: foo: value;B00B B00D B010B012A7FB 9EE701 A707 81 AIS STA AIS RTS#-3 1,SP#3 3.6.1堆棧指針尋址 堆棧指針相對(duì)尋址進(jìn)一步增強(qiáng)了C代碼的效率。有兩種類型: 8位偏移的堆棧指針相對(duì)尋址和16位偏移的堆棧指針相對(duì)尋址。它們和間址建起方式工作相似,但使用堆棧指針
33、代替H:X變址寄存器。注意當(dāng)中斷不允許時(shí)可用堆棧指針作為額外的變址寄存器。 3.6.2堆棧幀1) 幀指針 函數(shù)通常有一個(gè)包含其所有本地?cái)?shù)據(jù)的堆棧幀。編譯器并不設(shè)置一個(gè)明白的幀指針,但堆棧上的本地?cái)?shù)據(jù)和參數(shù)都根據(jù)SP寄存器訪問。 2) 入口代碼 通常入口代碼是一系列為本地變量保留空間的指令: PSHA PSHXAIS #(-s);僅當(dāng)有寄存器參數(shù) ;僅當(dāng)有寄存器參數(shù) ;為本地變量保留空間 S是函數(shù)的本地?cái)?shù)據(jù)的大小(單位:字節(jié))。沒有靜態(tài)鏈接,動(dòng)態(tài)鏈接并沒有明白地存儲(chǔ)。3) 出口代碼 出口代碼從堆棧中移除本地變量,并返回到調(diào)用者: AISRTS#(t);移除本地??臻g,包括最終的寄存器參數(shù) ;返回
34、調(diào)用者 363 HC08返回值 除函數(shù)返回一對(duì)象大于二字節(jié),函數(shù)結(jié)果都返回到寄存器中。依據(jù)返回類型,使用不同 的寄存器。如下表所示: 24返回類型 寄存器 返回大對(duì)象:函數(shù)返回大于二字節(jié)的對(duì)象均與一個(gè)附加的參數(shù)一起調(diào)用,它被傳到H:X。 這個(gè)參數(shù)是對(duì)象應(yīng)復(fù)制到的地址。 打開文件:Lab5-Arguments.mcp 聲明了四個(gè)不同類型的函數(shù),每個(gè) 函數(shù)返回一不同類型的變量。 每個(gè)函數(shù)有一個(gè)不同 類 型 的 參 數(shù) 25Char(signed或unsigned) A int(signed或unsigned) X:A 指針/數(shù)組 X:A 函數(shù)指針 X:A 被調(diào)用的函數(shù)跳轉(zhuǎn)到其 源碼所在的內(nèi)存位置。
35、全局變量賦值。 函數(shù)以 RTS 返回。26 參數(shù)在 A 寄存器中結(jié)果存貯在變量里 * A-變量堆棧中進(jìn)行的操作和返回 值存貯在 A 中 A 和X 用作參數(shù)和返回寄存器 27 ,H:X 用于返回指針.37 中斷 好, 最后一個(gè)棘手的問題深深地困擾嵌入式世界:怎樣處理中斷? 答案是簡(jiǎn)單! CodeWarrior編譯器提供了一個(gè)非ANSI的變通的方法,在源碼中直接指定中斷向量號(hào)t。表達(dá)式以interrupt關(guān)鍵字開始, 接著是中斷向量號(hào),最后是函數(shù)原型。 interrupt 17 void TBM_ISR (void) /* Timebase Module Handler*/ 你應(yīng)查HC08手冊(cè),R
36、ESET和軟中斷向處在最高和相同的優(yōu)先級(jí)。這兩個(gè)是HC08的向量0, 但CodeWarrior不得不給每個(gè)向量不同的號(hào),因此第一個(gè),RESET,將是向量0,然后SWI,向量1,依次類推直到最后TBM為向量17,以GP32為例。 中斷向量表定位: 28向量號(hào) 向量地址 向量地址大小 0 0xFFFE0xFFFF 2 1 0xFFFC0xFFFD 2 A 用作參數(shù)寄存器也用作直接尋址數(shù)組中的字節(jié)。 打開文件Lab6-Interrupts.mcp:29使用中斷修飾符聲明一個(gè)中斷服務(wù)程序。 2 0xFFFA0xFFFB 2 n 0xFFFF-(n*2) 2 使用 RTI 代替RTS函數(shù)指針3.8疊代、
37、跳轉(zhuǎn)、循環(huán)執(zhí)行無(wú)限循環(huán): While(1); For(;); Loop: goto Loop; 對(duì)于嵌入式系統(tǒng): 循環(huán)總是被基于MCU的應(yīng)用所需要。對(duì)于應(yīng)用程序第2種循環(huán)最好,因?yàn)樗粫?huì)導(dǎo)致 “always true warning”的警告。 3.9標(biāo)準(zhǔn)C庫(kù) 標(biāo)準(zhǔn)庫(kù)如stdio.h通常包含在編譯器中。Getchar() 、gets()、printf()、putchar()、puts()、scanf()、sprintf()、sscanf()等,都是這些庫(kù)中的常用函數(shù)。 #include void main(void) printf(“Hello World!n”); while(1); 30中
38、斷 向 量 存 貯 了ISR 開始的地址 當(dāng)給PC機(jī)寫這段代碼, printf()缺省的控制臺(tái)是顯示器,但HC08不需要顯示器作為片外外設(shè),如果有,哪個(gè)端口用于顯示?什么時(shí)候我們定義它?在哪兒? 在嵌入式編程中,通常printf()調(diào)用putchar()執(zhí)行打印,這假定控制臺(tái)缺省為片上串行口(SCI)。 在模擬時(shí),printf()訪問片上“虛擬”IO調(diào)用一個(gè)模擬終端通過片上SCI輸出。建議修改基礎(chǔ)庫(kù)函數(shù)putchar()和getchar() t使用任何用戶需要的輸出/輸入控制臺(tái)。 三、CodeWarrior介紹 1、C編譯器 對(duì)于現(xiàn)在的嵌入式應(yīng)用,沒必要花大量時(shí)間選擇一個(gè)C/C+交叉編譯器,但
39、你應(yīng)記住詳 細(xì)的難事。稍微詳細(xì)的資料讓我們使用一個(gè)特定的交叉編譯器容易一些,減少工程中受挫。理想情況下,應(yīng)從不考慮你的編譯器。它只是你用于將系統(tǒng)行為的算法和規(guī)則轉(zhuǎn)化為可執(zhí)行的 程序。 但是,世界不是理想的當(dāng)比較兩個(gè)或更多的交叉編譯器,以滿足你需要的硬件和軟件,你應(yīng)當(dāng)考慮些什么事情呢? 有一些好的特性形成巨大的不同: 在線匯編: 盡管C語(yǔ)言的發(fā)明已超過25年,當(dāng)開發(fā)嵌入式系統(tǒng)時(shí),使用一定數(shù)量的匯編仍然是平常的事。 中斷函數(shù):交叉編譯器另一個(gè)讓人滿意的特性是指定中斷類型。對(duì)PC平臺(tái),這一非標(biāo)準(zhǔn)關(guān)鍵字是C語(yǔ)言普遍增加的。當(dāng)用作函數(shù)聲明的一部份,它告訴編譯器這個(gè)函數(shù)是一個(gè)中斷服務(wù)程序(ISR)。編譯器
40、能產(chǎn)生額外的堆棧信息和寄存器保存以及任何ISR需要的恢復(fù)。一個(gè)好的編譯器也會(huì)阻止這種方式定義的函數(shù)被程序的其它部分調(diào)用。應(yīng)該明白,C/C+中與進(jìn)出ISR相 關(guān)的上層與匯編中是沒有差別的。 例如CodeWarrior,盡管進(jìn)一步深入理解一個(gè)處理器的中斷向表的結(jié)構(gòu)。這種情況,只需簡(jiǎn)單地向ISR 標(biāo)記添加中斷類型 (0x1E)。這使得中斷向量表自動(dòng)生成,并消除了編程人員潛在的誤解和錯(cuò)誤。 產(chǎn)生匯編語(yǔ)言:產(chǎn)生匯編語(yǔ)言的編譯器,作為匯編處理器的一部分是一個(gè)令人滿意的工 具。這一特性對(duì)手工優(yōu)化代碼很有幫助,因?yàn)槟隳苋菀椎乜吹侥愀呒?jí)語(yǔ)言程序的每一行產(chǎn)生了什么代碼,如果一個(gè)特定的函數(shù)對(duì)于給定的應(yīng)用太慢,你將能
41、夠容易地選擇函數(shù)最好的部分用匯編重寫。 標(biāo)準(zhǔn)庫(kù):當(dāng)你為通用計(jì)算機(jī)開發(fā)應(yīng)用軟件,你希望你的編譯器包含一套標(biāo)準(zhǔn)C函數(shù)庫(kù),數(shù)學(xué)庫(kù) 和C+類。它們包含各種程序如memcpy()、sin()和cout等。但由于這些庫(kù)函數(shù)不嚴(yán)格地是C或C+語(yǔ)言標(biāo)準(zhǔn)的一部分(庫(kù)標(biāo)準(zhǔn)是分開的),一個(gè)編譯器提供商可能省略它,這些省略在嵌入系統(tǒng)程序員使用的交叉編譯器提供商中是很普遍的。因些在某些情況下你不得不爭(zhēng)取得到標(biāo)準(zhǔn)庫(kù)的權(quán)利。 想一下多少次你花時(shí)間重寫那些自己的函數(shù)。因此花些時(shí)間堅(jiān)持將標(biāo)準(zhǔn)庫(kù)包含在你購(gòu)買的編 譯器中。當(dāng)然不大可能在多數(shù)嵌入式系統(tǒng)應(yīng)用中使用printf(),但直到太遲了你才意識(shí)到你需 31 要很多其它的函數(shù)。
42、如果提供了標(biāo)準(zhǔn)庫(kù),確保它們能再進(jìn)去。換句話說,那些庫(kù)中的每個(gè)函數(shù)能同時(shí)執(zhí)行多次。重 入函數(shù)能遞歸調(diào)用或多線程執(zhí)行。對(duì)于庫(kù)程序這意味著不應(yīng)使用全局變量。其內(nèi)部所有數(shù)據(jù)必 須在棧上。 起動(dòng)代碼:這是在main()前先執(zhí)行的額外的一段程序。起動(dòng)代碼通常用匯編寫成并和你建立的 可執(zhí)行代碼連接在一起。它為用高級(jí)語(yǔ)言寫的程序的執(zhí)行鋪路。 顯然,一個(gè)編譯器缺少我們提到的一些特性,也仍可是一個(gè)好的編譯器。毫無(wú)疑問,人們會(huì)爭(zhēng)論非標(biāo)準(zhǔn)特性象 “asm”和interrupt關(guān)鍵字減少了代碼的兼容性。但若你在兩個(gè)或多個(gè)差不多的交叉編譯間選擇時(shí),你可能會(huì)考慮這些事情。正好你被要求完成這些任務(wù),它們會(huì)使 你的工作容易。它
43、們的確減少編程受挫。 2、編譯器必要條件 一個(gè)好的編譯必須提供的功能有: ROM定位代碼:例如,編譯器必須給出容易寫入EPROM的的ROM定位代碼。 優(yōu)化的代碼:代碼大小和執(zhí)行時(shí)間同樣必須優(yōu)化。分枝和窺孔優(yōu)化自動(dòng)激活。 重入代碼:無(wú)全局變量用于存貯中間結(jié)果。由于結(jié)構(gòu)和或缺少棧操作(如HC05、ST7)并不 是所有的目標(biāo)系統(tǒng)有重入代碼。HC08是允許重入代碼的一個(gè)好目標(biāo)系統(tǒng)。 支持CPU的不同成員:對(duì)于M68K有用,允許激活68000、68020、68332或68881指令集。支持不同的存儲(chǔ)器模型:允許根據(jù)存貯器地圖的某些簡(jiǎn)單限制優(yōu)化產(chǎn)生的代碼。已有的存貯器模型與目標(biāo)處理器結(jié)果緊密相關(guān)。 非明顯
44、的優(yōu)化:盡管當(dāng)用匯編寫代碼時(shí),能用于決定可能的復(fù)雜的優(yōu)化。編譯器工作過程示例: 3、存貯器放置PRM文件 看了前面的代碼,尤其是當(dāng)一個(gè)從桌面編程轉(zhuǎn)到嵌入式編程,腦子里會(huì)產(chǎn)生一些基本問題 我的代碼在哪兒? 32 我的變量在哪兒? 怎樣訪問I/O寄存器? 怎樣處理中斷? 當(dāng)寫桌面應(yīng)用程序時(shí),通常不用考慮代碼放在哪里。操作系統(tǒng)負(fù)責(zé)管理內(nèi)存分配。處理嵌入式設(shè)備,你不得不充當(dāng)OS決定將代碼放在何處,至少?zèng)Q定代碼開始之處事實(shí)是連接器 為我們作了大量工作,但并是所有的事情。 為讓連接器知道我們的特定設(shè)備或目標(biāo)的存貯器分布,我們不得不指定它。定義的代碼的定位以及內(nèi)存尋址的數(shù)據(jù)段由連接器的參數(shù)文件控制。這個(gè)文件
45、由后綴 “.prm”識(shí)別。連接器的參數(shù)文件是一個(gè)ASCII文本件。對(duì)每個(gè)程序你得寫一個(gè)這樣的文件。它包含指導(dǎo)如何連接的命令。 這個(gè)文件中,SECTIONS命令塊用于定義存貯器的物理區(qū)域。在SECTIONS命令塊中,每個(gè)單獨(dú)的物理存貯器段用一個(gè)名字、一個(gè)屬性和一個(gè)地址范圍描述。 一旦定義了SECTIONS,代碼和數(shù)據(jù)段用PLACEMENT命令塊定位到存貯器中。PLACEMENT命 令塊用于將代碼和數(shù)據(jù)段定位到存貯器段。 參數(shù)文件中命令的順序沒有關(guān)系。你只應(yīng)確定SEGMENTS塊在PLACEMENT塊之前指定。若要詳細(xì)了解連接器的參數(shù)文件請(qǐng)參考Metrowerks的手冊(cè)文件“SmartLinker
46、.pdf”。 #pragma指示符33#pragma 導(dǎo)致預(yù)處理器執(zhí)行一個(gè)依賴于執(zhí)行的動(dòng)作 除非另規(guī)定一個(gè)PRAGMA聲明外,變量放在Default_RAM 的位置。地址范圍$0000至$00FF叫作直接頁(yè)、基本頁(yè)或零頁(yè)。在HC08微控制器,在直接頁(yè)的低地址部分包含I/O和控制寄存器,直接頁(yè)的高地址部分總是包含RAM。復(fù)位后,棧指針總是指向地址$00FF。直接頁(yè)非常 重要,因?yàn)樵S多CPU08指令有一個(gè)直接尋址模式(8位尋址模式),其在直接頁(yè)中訪問操作數(shù) 比擴(kuò)展尋址模式(16位尋址模式)少一個(gè)時(shí)鐘周期。更有甚者,直接尋址模式指令需要的代 碼少一字節(jié)。一些高效的指令只使用直接頁(yè)操作數(shù),它們是:BS
47、ET、BCLR、BRSET和BRCLR。MOV 指令需要一個(gè)操作數(shù)在直接頁(yè)。 只有變量明確地定義在直接頁(yè),編譯器才能利用直接尋址模式的效率高優(yōu)點(diǎn)。ANSI無(wú)標(biāo)準(zhǔn)方法做這些,編譯器通常提供不同的解決方案。CodeWarrior 使用#pragma聲明。 34 PRAGMA是一個(gè)編譯器指示。你設(shè)置pragma為需要的狀態(tài)后,那一點(diǎn)以后的所有代碼用那個(gè)設(shè)置編譯,直到你改變?cè)O(shè)置或到達(dá)文件的末尾。在每個(gè)文件的開頭,編譯器恢復(fù)到工程的設(shè)置或缺省設(shè)置。 這個(gè)pragma將聲明的變量放在直接頁(yè)段,就如前面見到的,程序員必須記住修改PRM文件,使連接器把段放在直接頁(yè)或零頁(yè)的一個(gè)地址。直接頁(yè)RAM的數(shù)量總是有限的
48、,因此只有 頻繁使用的變量應(yīng)放在直接頁(yè)。如果能用到,要為全局變量釋放更多的直接頁(yè)RAM,堆棧被 重定位在直接頁(yè)RAM之外,這不會(huì)影響堆棧指針尋址模式。 請(qǐng)看下列例子: 打開文件Lab7-DataSeg.mcp: 變量定義在段中 它的位置由連接器決定。 )35定義了一個(gè)新存貯段。 定 義 了 一 個(gè) 數(shù) 據(jù) 段 ( VarA) 位于指定的存貯位置。(即通信協(xié)議。 打開文件Lab8-CodeSeg.mcp36VarB 在缺省段。 VarA 位于 0x80 定義一個(gè)代碼段 位于指定的存貯 位置。37定義了段,函數(shù)在該段內(nèi)執(zhí)行 代碼段 function1 位于存貯器段 FunctionsROM 中,它
49、定義在EF00 與EFFF 之間。 打開文件Lab9-ConstSeg.mcp38并沒存在內(nèi)存中, 使用了直接放置其內(nèi)容的方法。若變量數(shù)組試圖在Fash 中更新,將會(huì)得到 在指定的存貯位置定義了一個(gè)常量段。 4、起動(dòng)程序 你的程序是怎樣起動(dòng)的呢?在工作站或PC上操作系統(tǒng)從磁盤上裝入程序并建立環(huán)境。在嵌入式系統(tǒng)中沒有操作系統(tǒng)?;旧?,程序員必須處理程序起動(dòng)的每個(gè)方面。 嵌入式應(yīng)用,特別是用C或C+寫的代碼,需要一個(gè)起動(dòng)模塊,在起動(dòng)main()之前配置硬件和 代碼。通常不可避免地用匯編語(yǔ)言寫成,這個(gè)模塊是處理器離開復(fù)位狀態(tài)第一個(gè)執(zhí)行的代碼。C/C+起動(dòng)代碼通常執(zhí)行下列動(dòng)作: 1) 關(guān)中斷; 2)
50、將初始化數(shù)據(jù)從ROM復(fù)制到RAM; 3) 將未初始化數(shù)據(jù)區(qū)清零; 4) 為堆棧定位空間以及初始化堆棧; 5) 創(chuàng)建并初始化堆; 6) 執(zhí)行構(gòu)造函數(shù)并初始化所有全局變量(僅C+); 7) 開中斷; 最后,起動(dòng)代碼調(diào)用main(),啟動(dòng)應(yīng)用的剩余部分。 雙擊39 5、編譯器優(yōu)化 CodeWarrior編譯器提供了幾種從C源代碼產(chǎn)生實(shí)際匯編代碼的優(yōu)化方法,這些代碼被編程到微控制器中。 “Global Optimizations”(全局優(yōu)化)設(shè)置面板設(shè)定編譯器怎樣優(yōu)化目標(biāo)代碼。所有優(yōu)化程序重新組織目標(biāo)代碼,不影響其邏輯執(zhí)行順序。 1)強(qiáng)度減弱 “Strength Reduction”(強(qiáng)度減弱)是一種
51、優(yōu)化,力爭(zhēng)用開銷小的操作代替開銷大的操作, 代價(jià)因素是執(zhí)行時(shí)間或代碼大小。 在循環(huán)內(nèi),用加法指令代替乘法指令。 下列例子將演示編譯器,基于應(yīng)用做什么,決定哪一種操作用最少代價(jià)達(dá)到同樣結(jié)果。 打開文件Lab10-Optimize1.mcp40_startup() 所 在 位起動(dòng)程序是復(fù)位后第一個(gè)執(zhí)行的, 復(fù)位 向 量 存 貯 字節(jié)乘 341 字節(jié)乘 442用MUL 指令執(zhí)行 43等 同 于 : VarA=VarA*4; 用 2 個(gè)左移指令執(zhí)行。用H:X 作為指向我們將要 2)死代碼消除 對(duì)于死代碼消除,編譯器優(yōu)化應(yīng)用程序并不為沒被使用的語(yǔ)句產(chǎn)生可執(zhí)行代碼。移除邏輯上從未執(zhí)行的語(yǔ)句或沒有被其他語(yǔ)句提到的語(yǔ)句。打開文件:Lab11-Optimize2.mcp。 循環(huán)結(jié)束后變量未被修改。 異或指令=44 賦值被跳過在循環(huán)后變量被修改。 3)死賦值 消除死賦值,編譯器移去變量在再次賦值之前沒有被使用的賦值。在下面編譯器優(yōu)化的例子, 我們將演示通過改變編譯器的優(yōu)化設(shè)置,達(dá)到改變CodeWarrior產(chǎn)生代碼的方式。打開文件Lab12-Optimize3.mcp:45 這個(gè)循環(huán)定義了 i 的值, 基于排列,
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度房屋抵押權(quán)設(shè)立合同
- 教育信息化解決方案項(xiàng)目投資合同
- 物流配送損害免責(zé)聲明
- 教育培訓(xùn)服務(wù)責(zé)任豁免協(xié)議
- 文化產(chǎn)業(yè)投資開發(fā)協(xié)議書
- 攝影工作室拍攝作品著作權(quán)歸屬聲明
- 農(nóng)業(yè)現(xiàn)代化高效節(jié)水灌溉技術(shù)推廣方案
- 企業(yè)產(chǎn)品質(zhì)量危機(jī)處理預(yù)案
- 高考文言文雙文本專練:《史記》《論語(yǔ)》
- 近期項(xiàng)目成果回顧與反思
- 2025年不停電電源(UPS)項(xiàng)目合作計(jì)劃書
- 林木采伐安全協(xié)議書范本
- 招聘技巧話術(shù)培訓(xùn)
- 2025年湖南食品藥品職業(yè)學(xué)院高職單招職業(yè)適應(yīng)性測(cè)試近5年??及鎱⒖碱}庫(kù)含答案解析
- 碳酸鈣脫硫劑項(xiàng)目可行性研究報(bào)告立項(xiàng)申請(qǐng)報(bào)告模板
- 山東省泰安市新泰市2024-2025學(xué)年(五四學(xué)制)九年級(jí)上學(xué)期1月期末道德與法治試題(含答案)
- 英語(yǔ)-遼寧省大連市2024-2025學(xué)年高三上學(xué)期期末雙基測(cè)試卷及答案
- DB3502T 160-2024 工業(yè)產(chǎn)品質(zhì)量技術(shù)幫扶和質(zhì)量安全監(jiān)管聯(lián)動(dòng)工作規(guī)范
- 燃?xì)廪r(nóng)村協(xié)管員培訓(xùn)
- 春節(jié)后復(fù)工安全教育培訓(xùn)
- 提高發(fā)票額度的合同6篇
評(píng)論
0/150
提交評(píng)論