山東大學(xué)飛思卡爾單片機教學(xué)第6章C語言程序設(shè)計課件_第1頁
山東大學(xué)飛思卡爾單片機教學(xué)第6章C語言程序設(shè)計課件_第2頁
山東大學(xué)飛思卡爾單片機教學(xué)第6章C語言程序設(shè)計課件_第3頁
山東大學(xué)飛思卡爾單片機教學(xué)第6章C語言程序設(shè)計課件_第4頁
山東大學(xué)飛思卡爾單片機教學(xué)第6章C語言程序設(shè)計課件_第5頁
已閱讀5頁,還剩81頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

關(guān)于實驗安排

10周和11周的周二晚上在興隆山校區(qū)計算中心1號和3號機房(1、2班1號機房,3、4班2號機房,17:30前到)開放實驗:11周、12周和13周在千佛山校區(qū)機械廠教學(xué)車間樓207(1、2班星期六全天;3、4班星期天全天:如有特殊事情者,可靈活選擇;上午9:00——下午17:00)實驗后撰寫實驗報告集成開發(fā)環(huán)境先用熟4.6版本;然后會用5.0版本關(guān)于實驗安排10周和11周的周二晚上在興隆山校區(qū)計算中心1本周實驗安排

實驗4:掌握最基本C語言程序的調(diào)試及運行方法

1.建立一個C語言工程,并按要求編寫求兩個數(shù)最大值的C語言程序,編譯并排錯;

2.使用全速調(diào)試,并觀察程序運行結(jié)果;

3.使用單步調(diào)試,并觀察數(shù)據(jù)區(qū)、寄存器區(qū)、存儲器區(qū)的數(shù)據(jù)變化。實驗5:使用仿真方法,編寫并調(diào)試復(fù)雜的C語言程序

1.自定義一個包含10個無符號int型的數(shù)組,數(shù)組元素分別是10,9,8,7,6,5,4,3,2,1,0。使用冒泡排序法對該數(shù)組元素進行從小到大排序,調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化,程序要有注釋。本周實驗安排實驗4:掌握最基本C語言程序的調(diào)試及運行方法本周實驗安排

2.基于上個程序,把排序好的數(shù)組進行數(shù)字濾波:去除最大的3個和最小的3個,剩下的求平均,調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化,程序要有注釋。

3.課本195頁例2、例3,課本196頁例4

調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化。本周實驗安排 2.基于上個程序,把排序好的數(shù)組進行數(shù)字濾波第六章C語言程序設(shè)計

§6-1C語言程序設(shè)計基礎(chǔ)§6-2MC9S12的C編程與ANSIC的不同§6-3C程序設(shè)計舉例內(nèi)容提要:第六章C語言程序設(shè)計§6-1C語言程序設(shè)計基礎(chǔ)內(nèi)容提單片機編程語言

匯編語言

優(yōu)點:執(zhí)行效率高,時序控制精確

C語言

優(yōu)點:①程序開發(fā)速度快,提高開發(fā)工作效率;

②軟件調(diào)試直觀;

③可維護性好;

④可移植性好(與匯編語言相比較)。單片機C語言

單片機C語言和標(biāo)準(zhǔn)C語言的用法基本相同,但是單片機C語言是針對于資源少的MCU,它的編程方法和編程手段與PC機上使用C語言還是有很大差別。§6-1C語言程序設(shè)計基礎(chǔ)單片機編程語言

匯編語言

優(yōu)點:執(zhí)行效率高多文件C語言工程的頭文件當(dāng)代碼量非常大時,程序員一般會把代碼分成幾個部分,然后寫入到多個文件中。這些文件一般分為C文件和H文件。作為一般規(guī)則,以下內(nèi)容應(yīng)該放到H文件中:預(yù)處理和宏定義結(jié)構(gòu)、聯(lián)合和枚舉聲明typedef聲明外部函數(shù)聲明全局變量聲明如果定義的變量僅僅被一個C文件所私有,那么不可放到H文件中。多文件C語言工程的頭文件其它C語言基礎(chǔ)知識參見補充課件:“補充(自己看)_C語言基礎(chǔ).PPT”其它C語言基礎(chǔ)知識參見補充課件:CodeWarrior數(shù)據(jù)類型§6-2MC9S12的C編程與ANSIC的不同CodeWarrior數(shù)據(jù)類型§6-2MC9S12的C編自定義CodeWarrior數(shù)據(jù)類型在CodeWarrior環(huán)境下按ALT+F7打開工程的通用設(shè)置,選擇“CompilerforHC12”面板然后點按鈕“TypeSizes”。建議不要隨意更改數(shù)據(jù)類型的設(shè)置自定義CodeWarrior數(shù)據(jù)類型在CodeWarrio數(shù)據(jù)類型的事實代碼大小和執(zhí)行時間的最大節(jié)約可通過為變量選擇最合適的數(shù)據(jù)類型而得到。16位微控制器內(nèi)部的數(shù)據(jù)長度是16位(2字節(jié)),所以首選的數(shù)據(jù)類型是“int”。16位機處理16位數(shù)據(jù)比8位數(shù)據(jù)的效率更高。對于9S12單片機來說,16位數(shù)據(jù)類型效率最高,因此在使用中,盡量選擇int類型數(shù)據(jù)。數(shù)據(jù)類型的事實代碼大小和執(zhí)行時間的最大節(jié)約可通過為變量選擇存貯類修飾符以下關(guān)鍵字用于聲明變量,以指定特定需要或內(nèi)存中變量存貯的相關(guān)條件。static(靜態(tài)變量)volatile(可變變量)const(只讀變量)這三個關(guān)鍵字,一起讓我們不僅可寫出好的代碼,而且可寫出緊湊的代碼。存貯類修飾符以下關(guān)鍵字用于聲明變量,以指定特定需要或內(nèi)存中變static在函數(shù)體聲明的靜態(tài)變量,在函數(shù)調(diào)用期間保持其值;在模塊內(nèi)聲明靜態(tài)的變量(但在函數(shù)體之外),能被模塊內(nèi)所有函數(shù)訪問;在模塊內(nèi)聲明靜態(tài)的函數(shù),只能被模塊內(nèi)其它函數(shù)調(diào)用。如:staticintfunc(intx,inty);當(dāng)一個C語言工程由多個程序員進行編寫時,就不用擔(dān)心自己所定義的函數(shù)與別人定義的重名了。如果在函數(shù)名和函數(shù)類型前加extern,則表示此函數(shù)是外部函數(shù),可以被其他文件中的函數(shù)調(diào)用。如:externintfunc(intx,inty)。static在函數(shù)體聲明的靜態(tài)變量,在函數(shù)調(diào)用期間保持其值;File1.c#include<File2.h> /*包含文件FILE2.c中的函數(shù)*/voidmain(void){ MyFunction(); /*第一次進入MyFunction之前,myVar=0*/ MyFunction(); /*第二次進入MyFunction之前,myVar=1*/}File2.cvoidMyFunction(void){ staticcharmyVar=0;/*本地變量聲明為static*/ myVar=myVar+1; /*myVar是本地變量,但它保持了自己的值*/}File1.c可變(volatile)變量volatile的本意是“易變的”,

就是該變量會以編譯器無法預(yù)知的方式發(fā)生變化,請編譯器不要做優(yōu)化(所有的編譯器的優(yōu)化均假設(shè)編譯器知道變量的變化規(guī)律)。也就是說是其值在正常程序流程以外可能改變的變量。在嵌入式系統(tǒng)中,這種情況通過兩種主要途徑發(fā)生:一是中斷服務(wù)程序,二是硬件動作。例如,通過串口接收到一個字符,然后串口狀態(tài)寄存器的值會發(fā)生改變,這完全在程序流程之外發(fā)生。在嵌入式設(shè)備中,最好將所有外設(shè)寄存器聲明為volatile??勺儯╲olatile)變量volatile的本意是“易變例子由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優(yōu)化。比如:

static

int

i=0;

int

main(void)

{

...

while

(1)

{

if

(i)

dosomething();

}

}

/*

Interrupt

service

routine.

*/

void

ISR_2(void)

{

i=1;

}

例子由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用dosomething函數(shù),但是,由于編譯器判斷在main函數(shù)里面沒有修改過i,因此可能只執(zhí)行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導(dǎo)致dosomething永遠(yuǎn)也不會被調(diào)用。如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優(yōu)化(肯定執(zhí)行)。此例中i也應(yīng)該如此說明。

程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用doConst變量

關(guān)鍵字“const”,C語言中命名最差的關(guān)鍵字,并不表示恒量,而是代表“只讀”。在嵌入式系統(tǒng)中,有很大的不同,這一會應(yīng)會明白。const是C++中最常用的幾個關(guān)鍵字之一,對確保程序的完整性有著重要的意義。它在調(diào)用者和被調(diào)用者之間傳遞這樣一種信息:Baby,我不會改變你的,放心吧。Const聲明可用于任何變量,它告訴編譯器將其存貯在ROM代碼。編譯器保留了那個位置程序存貯器地址。由于位于ROM中,其值不能改變。Const變量

關(guān)鍵字“const”,C語言中命名最差的關(guān)鍵Const的用法:constunsignedshorta;unsignedshortconsta;constunsignedshort*a;unsignedshort*consta;Const的用法:Constvolatile變量現(xiàn)在討論一個深奧的問題,一個變量既能是常量,又能是可變量嗎?如果是這樣,這意味什么,怎樣使用?答案是“能”。這個修飾符應(yīng)該用于能出乎意料地改變的任何存貯器位置,因此需要volatile限定語,由于const該變量是只讀的。Constvolatile變量現(xiàn)在討論一個深奧的問題,最明顯的例子是硬件狀態(tài)寄存器,象SCI狀態(tài)寄存器SCS1。這個寄存器包含信號狀態(tài)標(biāo)志,如發(fā)送空、發(fā)送完成、接收滿以及其它。這是一個可變寄存器由于這些標(biāo)志的改變依賴于串行通信的狀態(tài),這也是只讀,由于標(biāo)志不能被程序直接改寫,它們只對模塊的狀態(tài)作出響應(yīng)。這個狀態(tài)寄存器最佳聲明方法是:#defineSCS1(*(constvolatileunsignedchar*)0x0016)最明顯的例子是硬件狀態(tài)寄存器,象SCI狀態(tài)寄存器SCS1。這訪問固定內(nèi)存位置

與PC機不同,嵌入式系統(tǒng)通常的特點是需要編程者訪問一個指定的存貯器位置。練習(xí):在某個項目中需要將絕對地址0x2FFA處整型變量的值設(shè)為0xAA55(編譯器為純粹的ANSI編譯器)。完成這個任務(wù)的代碼是:int*ptr;ptr=(int*)0x2FFA;*ptr=0xAA55;訪問固定內(nèi)存位置

與PC機不同,嵌入式系統(tǒng)通常的特點是需要編訪問I/O寄存器一個普通而有用的形式是使用如下的#define指示:#definePortA(*(volatileunsignedchar*)0x0000)這種情況下,PortA為地址0x0000處字符型變量。#define實際做的是每次發(fā)現(xiàn)PortA時放置一個構(gòu)件。也就是說在代碼中寫:PortA=0x3F,實際做的就是告訴編譯器0x0000是一個volatile-unsigned-char類型的指針,它的內(nèi)容等于0x3F。訪問I/O寄存器一個普通而有用的形式是使用如下的#defi數(shù)組C語言允許程序員用幾種不同方法存取數(shù)組的內(nèi)容。選擇最適合的方法來存取,可以提高代碼的效率。 數(shù)組的訪問方法有:硬編碼:

Array[0]=12*UNIT_VOLTS;

編譯時決定地址,執(zhí)行速度快。變址增加

Array[index++]=12*UNIT_VOLTS;

快速,比硬編碼靈活。數(shù)組指針 *(ArrayPtr++)=12*UNIT_VOLTS;

執(zhí)行速度快,可讀性差,可和循環(huán)一起使用。數(shù)組C語言允許程序員用幾種不同方法存取數(shù)組的內(nèi)容。選擇最適合C語言與匯編語言的混合編程在絕大多數(shù)場合采用C語言編程可完成預(yù)期的目的,但是對一些特殊情況進行編程時要結(jié)合匯編語言。匯編語言具有直接和硬件打交道、執(zhí)行代碼的效率高等特點,可以做到C語言所不能做到的一些事情:一個程序中的關(guān)鍵部分對執(zhí)行速度有很高要求,實時性強。用匯編編程可以更有效的利用CPU的寄存器和指令集,用其產(chǎn)生的代碼比用編譯器產(chǎn)生的代碼運行更快。對特定硬件接口的訪問。例如對I/O端口的訪問,或者用于禁止、啟用中斷系統(tǒng)。因此,選用C語言編程時,還需要夾雜一些匯編程序,通過這種混合編程的方法將C語言和匯編語言的優(yōu)點結(jié)合起來,這已經(jīng)成為MCU開發(fā)最流行的編程方法。C語言與匯編語言的混合編程在絕大多數(shù)場合采用C語言編程可完C中使用匯編目前大多數(shù)MCU系統(tǒng),在C語言中使用匯編語言有兩種情況:一種是匯編程序部分和C程序部分為不同模塊或不同的文件,通常由C程序調(diào)用匯編程序模塊的變量和函數(shù)(也可稱為子程序或過程);另一種是嵌入式匯編,即在C語言程序中嵌入一段匯編語言程序。C語言中嵌入?yún)R編語句:_asmAssemblyInstuction;asm(AssemblyInstruction);asm{ AssemblyInstruction AssemblyInstruction}C中使用匯編目前大多數(shù)MCU系統(tǒng),在C語言中使用匯編語言有C中調(diào)用匯編指令構(gòu)成的子程序:當(dāng)匯編程序和C程序為不同模塊時,程序一般可分為若干個C程序模塊和匯編程序模塊,C程序模塊通常是程序的主體框架,而匯編程序模塊通常是若用C語言實現(xiàn)則效率不高的函數(shù)組成,也可以是已經(jīng)成熟的、沒有必要再轉(zhuǎn)化成C語言的匯編子程序。在這種混合編程技術(shù)中,關(guān)鍵是參數(shù)的傳遞和函數(shù)的返回值。它們必須有完整的約定,否則數(shù)據(jù)的交換就可能出錯。定義匯編子程序,定義格式如下:_子程序名 代碼

……rtsC中調(diào)用匯編指令構(gòu)成的子程序:注意:第一,在子程序名前加‘_’;第二,在C代碼中調(diào)用匯編子程序時可直接調(diào)用:子程序名();第三,在子程序中不能使用映象寄存器的宏定義,只能用它們的直接地址;第四,匯編子程序的編寫時,對于使用過的寄存器需要進行保護。在匯編中使用C語言在匯編中也可以調(diào)用C代碼中的變量與子程序。使用C代碼中定義的變量:在變量名前加‘_’或‘%’,例如unsignedcharPortA;asm(LDAA%PortA)調(diào)用C代碼中定義的函數(shù):在函數(shù)名前加‘_’,例如:function1();asm(BRA_function1);注意:C語言的中斷CodeWarrior編譯器提供了一個非ANSI的變通的方法,在源碼中直接指定中斷向量號t。表達(dá)式以interrupt關(guān)鍵字開始,接著是中斷向量號,最后是函數(shù)原型。interrupt7voidRTI_ISR(void) { /*RTI_ISRFunctionCodes*/ }9S12所有的中斷源共分成55個優(yōu)先級。處在最高優(yōu)先級的中斷是復(fù)位中斷,它的中斷向量號為0,第二位的是時鐘監(jiān)視器復(fù)位中斷,它的中斷向量號為1,以此類推。C語言的中斷CodeWarrior編譯器提供了一個非ANS標(biāo)準(zhǔn)C庫標(biāo)準(zhǔn)庫如stdio.h通常包含在編譯器中。Getchar()、gets()、printf()、putchar()、puts()、scanf()、sprintf()、sscanf()等,都是這些庫中的常用函數(shù)。

當(dāng)給PC機寫這段代碼,printf()缺省的控制臺是顯示器,但HC12沒有顯示器作為片外外設(shè),如果這樣調(diào)用庫函數(shù),哪個端口用于顯示?什么時候我們定義它?在哪兒?在嵌入式編程中,通常printf()調(diào)用putchar()執(zhí)行打印,這假定控制臺缺省為片上串行口(SCI)。如果需要使用這個功能,則需要修改基礎(chǔ)庫函數(shù)putchar()才能正確輸出。

標(biāo)準(zhǔn)C庫標(biāo)準(zhǔn)庫如stdio.h通常包含在編譯器中。GetcC語言入口在MC9S12的C程序中,程序的入口在什么地方?程序是怎樣起動的呢?在工作站或PC上操作系統(tǒng)從磁盤上裝入程序并建立環(huán)境。而在嵌入式系統(tǒng)中沒有操作系統(tǒng)。對程序員來說,程序員必須處理程序起動的每個方面。嵌入式應(yīng)用,特別是用C或C++寫的代碼,需要一個起動模塊,在起動main()之前配置硬件和代碼。通常不可避免地用匯編語言寫成,這個模塊是處理器離開復(fù)位狀態(tài)第一個執(zhí)行的代碼。C語言入口在MC9S12的C程序中,程序的入口在什么地方?C起動代碼通常執(zhí)行下列動作(1)關(guān)中斷;(2)將初始化數(shù)據(jù)從ROM復(fù)制到RAM;(3)將未初始化數(shù)據(jù)區(qū)清零;(4)為堆棧定位空間以及初始化堆棧;(5)創(chuàng)建并初始化堆;(6)執(zhí)行構(gòu)造函數(shù)并初始化所有全局變量(僅C++);(7)開中斷;C起動代碼通常執(zhí)行下列動作(1)關(guān)中斷;最后,起動代碼調(diào)用main(),啟動應(yīng)用的剩余部分。當(dāng)然針對于MC9S12來說,啟動代碼可以不必這么麻煩??梢灾怀跏蓟?,初始化硬件然后直接跳轉(zhuǎn)到main函數(shù),下面是一個啟動代碼的例子最后,起動代碼調(diào)用main(),啟動應(yīng)用的剩余部分。當(dāng)然針對void_Startup(){asm{LDS0x1000JSRHardInitJSRmain}}程序員可以自己編寫啟動代碼,可以將應(yīng)用程序中的硬件初始化程序都放在啟動代碼里面,這樣就可以比較專注地在應(yīng)用程序中做要做的事情。void_Startup()程序的鏈接與定位對于MC9S12單片機來說,由于RAM,EEPROM,寄存器可以重新映射,而且不同型號的單片機內(nèi)部FLASH大小也不一樣,這樣程序編譯后存放在什么位置,成了我們必須告訴鏈接器的內(nèi)容,因為鏈接器不知道你用的單片機RAM放在什么地方,ROM放在什么地方。為了方便管理,CodeWarrior自動生成一個.prm文件,此文件用來管理程序的定位。但是該文件并不一定與用戶的實際情況相符,因此在鏈接前,必須自己核對該文件是否與實際的硬件相符。看書的192頁。程序的鏈接與定位對于MC9S12單片機來說,由于RAM,E§6-3C程序設(shè)計舉例§6-3C程序設(shè)計舉例例1對從RAM中0x1000地址開始的10個無符號字節(jié)型數(shù)進行由小到大排序程序分析:可以使用C語言把一個unsignedchar類型的數(shù)組存儲到從0x1000開始的內(nèi)存空間中,數(shù)組的大小為10個字節(jié),內(nèi)容由程序員自定義。由小到大的排序使用冒泡法完成。unsignedchardata[10]@0x1000,i,j,temp; /*定義數(shù)組*/voidmain(void){ for(i=0;i<10;i++)/*初始化數(shù)組*/ {data[i]=20-i;} for(i=0;i<9;i++)/*使用冒泡法排序*/ for(j=0;j<9;j++) {if(data[j]>data[j+1]) {temp=data[j]; data[j]=data[j+1]; data[j+1]=temp;}} for(;;){}};注:若在函數(shù)內(nèi)定義變量,須在函數(shù)體開始處定義例1對從RAM中0x1000地址開始的10個無符號字節(jié)型數(shù)例2求1+2+…+100的和然后分成低、高字節(jié)存放在0x1000和0x1001地址單元中程序分析:求1+2+…100可以用100次循環(huán)來實現(xiàn),因為求出來的和不可以用一個字節(jié)表示,所以必須分成高低字節(jié)存儲。下面先給一個錯誤的程序。unsignedintsum@0x1000=0,i=0;voidmain(void){ for(;i<=100;i++) sum+=i; for(;;){}}例2求1+2+…+100的和然后分成低、高字節(jié)存放在0x1下面給出正確的程序:unsignedcharsum_lowbyte@0x1000=0,sum_highbyte@0x1001=0,i=0;unsignedintsum;voidmain(void){ for(;i<=100;i++) sum+=i; sum_lowbyte=sum&0xff; sum_highbyte=(sum&0xff00)>>8; for(;;){}}下面給出正確的程序:例3產(chǎn)生一個隨機數(shù),然后返回結(jié)果程序分析:產(chǎn)生隨機數(shù)可以調(diào)用一個函數(shù)rand(),使用它可以返回一個0到32767之間的隨機數(shù)。然后使用switch-case進行處理。注意使用rand()函數(shù),必須包含標(biāo)準(zhǔn)庫stdlib.h。此頭文件在CodeWarrior安裝目錄“\lib\HC12c\include”目錄中。#include<stdlib.h>inti,j;voidmain(void){ i=rand(); i=i/10000; switch(i) { case0:j=1000;break; case1:j=2000;break; case2:j=3000;break; case3:j=4000;break; default:j=5000; } for(;;){}}例3產(chǎn)生一個隨機數(shù),然后返回結(jié)果例4C語言嵌入?yún)R編語句有時在C語言中嵌入?yún)R編語句是很有用的。比如在時序需要嚴(yán)格控制的場合,使用匯編語言加入幾條“NOP”指令,顯然能夠改變程序運行的時序;在需要對較大數(shù)據(jù)表操作的場合,使用匯編語言查表顯然有更高的效率。假設(shè)程序中存在兩個無符號整型數(shù)據(jù)表,需要做的工作是把兩個表中對應(yīng)元素相乘,再把結(jié)果相加并存儲。即實現(xiàn)如下公式:其中n為數(shù)組的大小,Xi和Yi分別為數(shù)組元素。在9S12指令集中,有一條指令EMACS可以實現(xiàn)乘加操作,我們可以使用這條指令來完成以上的計算。

例4C語言嵌入?yún)R編語句有時在C語言中嵌入?yún)R編語句是很有程序代碼和詳細(xì)注釋如下:#include<hidef.h>#include<mc9s12dg128.h>#pragmaLINK_INFODERIVATIVE"mc9s12dg128b"#include"main_asm.h"unsignedintdata1[4];unsignedintdata2[4];/*定義兩個數(shù)組*/unsignedint*tbl1,*tbl2;/*指向數(shù)組的指針*/unsignedchari; /*計數(shù)變量*/unsignedlongsum;/*保存結(jié)果*/程序代碼和詳細(xì)注釋如下:voidmain(void){ EnableInterrupts; asm_main(); result=0; tbl1=data1;/*取得數(shù)組指針*/ tbl2=data2; for(i=0;i<4;i++)/*給數(shù)組初值*/ { data1[i]=i+1; data2[i]=i+1; }for(i=0;i<4;i++){ asm{ LDXtbl1;X中保存第一數(shù)組地址

LDYtbl2;Y中保存第二數(shù)組地址

EMACSsum;乘加操作*/} tbl1+=1;/*地址自加*/ tbl2+=1;} for(;;){}}voidmain(void)謝謝!謝謝!關(guān)于實驗安排

10周和11周的周二晚上在興隆山校區(qū)計算中心1號和3號機房(1、2班1號機房,3、4班2號機房,17:30前到)開放實驗:11周、12周和13周在千佛山校區(qū)機械廠教學(xué)車間樓207(1、2班星期六全天;3、4班星期天全天:如有特殊事情者,可靈活選擇;上午9:00——下午17:00)實驗后撰寫實驗報告集成開發(fā)環(huán)境先用熟4.6版本;然后會用5.0版本關(guān)于實驗安排10周和11周的周二晚上在興隆山校區(qū)計算中心1本周實驗安排

實驗4:掌握最基本C語言程序的調(diào)試及運行方法

1.建立一個C語言工程,并按要求編寫求兩個數(shù)最大值的C語言程序,編譯并排錯;

2.使用全速調(diào)試,并觀察程序運行結(jié)果;

3.使用單步調(diào)試,并觀察數(shù)據(jù)區(qū)、寄存器區(qū)、存儲器區(qū)的數(shù)據(jù)變化。實驗5:使用仿真方法,編寫并調(diào)試復(fù)雜的C語言程序

1.自定義一個包含10個無符號int型的數(shù)組,數(shù)組元素分別是10,9,8,7,6,5,4,3,2,1,0。使用冒泡排序法對該數(shù)組元素進行從小到大排序,調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化,程序要有注釋。本周實驗安排實驗4:掌握最基本C語言程序的調(diào)試及運行方法本周實驗安排

2.基于上個程序,把排序好的數(shù)組進行數(shù)字濾波:去除最大的3個和最小的3個,剩下的求平均,調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化,程序要有注釋。

3.課本195頁例2、例3,課本196頁例4

調(diào)試并觀察存儲器區(qū)數(shù)據(jù)變化。本周實驗安排 2.基于上個程序,把排序好的數(shù)組進行數(shù)字濾波第六章C語言程序設(shè)計

§6-1C語言程序設(shè)計基礎(chǔ)§6-2MC9S12的C編程與ANSIC的不同§6-3C程序設(shè)計舉例內(nèi)容提要:第六章C語言程序設(shè)計§6-1C語言程序設(shè)計基礎(chǔ)內(nèi)容提單片機編程語言

匯編語言

優(yōu)點:執(zhí)行效率高,時序控制精確

C語言

優(yōu)點:①程序開發(fā)速度快,提高開發(fā)工作效率;

②軟件調(diào)試直觀;

③可維護性好;

④可移植性好(與匯編語言相比較)。單片機C語言

單片機C語言和標(biāo)準(zhǔn)C語言的用法基本相同,但是單片機C語言是針對于資源少的MCU,它的編程方法和編程手段與PC機上使用C語言還是有很大差別?!?-1C語言程序設(shè)計基礎(chǔ)單片機編程語言

匯編語言

優(yōu)點:執(zhí)行效率高多文件C語言工程的頭文件當(dāng)代碼量非常大時,程序員一般會把代碼分成幾個部分,然后寫入到多個文件中。這些文件一般分為C文件和H文件。作為一般規(guī)則,以下內(nèi)容應(yīng)該放到H文件中:預(yù)處理和宏定義結(jié)構(gòu)、聯(lián)合和枚舉聲明typedef聲明外部函數(shù)聲明全局變量聲明如果定義的變量僅僅被一個C文件所私有,那么不可放到H文件中。多文件C語言工程的頭文件其它C語言基礎(chǔ)知識參見補充課件:“補充(自己看)_C語言基礎(chǔ).PPT”其它C語言基礎(chǔ)知識參見補充課件:CodeWarrior數(shù)據(jù)類型§6-2MC9S12的C編程與ANSIC的不同CodeWarrior數(shù)據(jù)類型§6-2MC9S12的C編自定義CodeWarrior數(shù)據(jù)類型在CodeWarrior環(huán)境下按ALT+F7打開工程的通用設(shè)置,選擇“CompilerforHC12”面板然后點按鈕“TypeSizes”。建議不要隨意更改數(shù)據(jù)類型的設(shè)置自定義CodeWarrior數(shù)據(jù)類型在CodeWarrio數(shù)據(jù)類型的事實代碼大小和執(zhí)行時間的最大節(jié)約可通過為變量選擇最合適的數(shù)據(jù)類型而得到。16位微控制器內(nèi)部的數(shù)據(jù)長度是16位(2字節(jié)),所以首選的數(shù)據(jù)類型是“int”。16位機處理16位數(shù)據(jù)比8位數(shù)據(jù)的效率更高。對于9S12單片機來說,16位數(shù)據(jù)類型效率最高,因此在使用中,盡量選擇int類型數(shù)據(jù)。數(shù)據(jù)類型的事實代碼大小和執(zhí)行時間的最大節(jié)約可通過為變量選擇存貯類修飾符以下關(guān)鍵字用于聲明變量,以指定特定需要或內(nèi)存中變量存貯的相關(guān)條件。static(靜態(tài)變量)volatile(可變變量)const(只讀變量)這三個關(guān)鍵字,一起讓我們不僅可寫出好的代碼,而且可寫出緊湊的代碼。存貯類修飾符以下關(guān)鍵字用于聲明變量,以指定特定需要或內(nèi)存中變static在函數(shù)體聲明的靜態(tài)變量,在函數(shù)調(diào)用期間保持其值;在模塊內(nèi)聲明靜態(tài)的變量(但在函數(shù)體之外),能被模塊內(nèi)所有函數(shù)訪問;在模塊內(nèi)聲明靜態(tài)的函數(shù),只能被模塊內(nèi)其它函數(shù)調(diào)用。如:staticintfunc(intx,inty);當(dāng)一個C語言工程由多個程序員進行編寫時,就不用擔(dān)心自己所定義的函數(shù)與別人定義的重名了。如果在函數(shù)名和函數(shù)類型前加extern,則表示此函數(shù)是外部函數(shù),可以被其他文件中的函數(shù)調(diào)用。如:externintfunc(intx,inty)。static在函數(shù)體聲明的靜態(tài)變量,在函數(shù)調(diào)用期間保持其值;File1.c#include<File2.h> /*包含文件FILE2.c中的函數(shù)*/voidmain(void){ MyFunction(); /*第一次進入MyFunction之前,myVar=0*/ MyFunction(); /*第二次進入MyFunction之前,myVar=1*/}File2.cvoidMyFunction(void){ staticcharmyVar=0;/*本地變量聲明為static*/ myVar=myVar+1; /*myVar是本地變量,但它保持了自己的值*/}File1.c可變(volatile)變量volatile的本意是“易變的”,

就是該變量會以編譯器無法預(yù)知的方式發(fā)生變化,請編譯器不要做優(yōu)化(所有的編譯器的優(yōu)化均假設(shè)編譯器知道變量的變化規(guī)律)。也就是說是其值在正常程序流程以外可能改變的變量。在嵌入式系統(tǒng)中,這種情況通過兩種主要途徑發(fā)生:一是中斷服務(wù)程序,二是硬件動作。例如,通過串口接收到一個字符,然后串口狀態(tài)寄存器的值會發(fā)生改變,這完全在程序流程之外發(fā)生。在嵌入式設(shè)備中,最好將所有外設(shè)寄存器聲明為volatile??勺儯╲olatile)變量volatile的本意是“易變例子由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優(yōu)化。比如:

static

int

i=0;

int

main(void)

{

...

while

(1)

{

if

(i)

dosomething();

}

}

/*

Interrupt

service

routine.

*/

void

ISR_2(void)

{

i=1;

}

例子由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用dosomething函數(shù),但是,由于編譯器判斷在main函數(shù)里面沒有修改過i,因此可能只執(zhí)行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導(dǎo)致dosomething永遠(yuǎn)也不會被調(diào)用。如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優(yōu)化(肯定執(zhí)行)。此例中i也應(yīng)該如此說明。

程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用doConst變量

關(guān)鍵字“const”,C語言中命名最差的關(guān)鍵字,并不表示恒量,而是代表“只讀”。在嵌入式系統(tǒng)中,有很大的不同,這一會應(yīng)會明白。const是C++中最常用的幾個關(guān)鍵字之一,對確保程序的完整性有著重要的意義。它在調(diào)用者和被調(diào)用者之間傳遞這樣一種信息:Baby,我不會改變你的,放心吧。Const聲明可用于任何變量,它告訴編譯器將其存貯在ROM代碼。編譯器保留了那個位置程序存貯器地址。由于位于ROM中,其值不能改變。Const變量

關(guān)鍵字“const”,C語言中命名最差的關(guān)鍵Const的用法:constunsignedshorta;unsignedshortconsta;constunsignedshort*a;unsignedshort*consta;Const的用法:Constvolatile變量現(xiàn)在討論一個深奧的問題,一個變量既能是常量,又能是可變量嗎?如果是這樣,這意味什么,怎樣使用?答案是“能”。這個修飾符應(yīng)該用于能出乎意料地改變的任何存貯器位置,因此需要volatile限定語,由于const該變量是只讀的。Constvolatile變量現(xiàn)在討論一個深奧的問題,最明顯的例子是硬件狀態(tài)寄存器,象SCI狀態(tài)寄存器SCS1。這個寄存器包含信號狀態(tài)標(biāo)志,如發(fā)送空、發(fā)送完成、接收滿以及其它。這是一個可變寄存器由于這些標(biāo)志的改變依賴于串行通信的狀態(tài),這也是只讀,由于標(biāo)志不能被程序直接改寫,它們只對模塊的狀態(tài)作出響應(yīng)。這個狀態(tài)寄存器最佳聲明方法是:#defineSCS1(*(constvolatileunsignedchar*)0x0016)最明顯的例子是硬件狀態(tài)寄存器,象SCI狀態(tài)寄存器SCS1。這訪問固定內(nèi)存位置

與PC機不同,嵌入式系統(tǒng)通常的特點是需要編程者訪問一個指定的存貯器位置。練習(xí):在某個項目中需要將絕對地址0x2FFA處整型變量的值設(shè)為0xAA55(編譯器為純粹的ANSI編譯器)。完成這個任務(wù)的代碼是:int*ptr;ptr=(int*)0x2FFA;*ptr=0xAA55;訪問固定內(nèi)存位置

與PC機不同,嵌入式系統(tǒng)通常的特點是需要編訪問I/O寄存器一個普通而有用的形式是使用如下的#define指示:#definePortA(*(volatileunsignedchar*)0x0000)這種情況下,PortA為地址0x0000處字符型變量。#define實際做的是每次發(fā)現(xiàn)PortA時放置一個構(gòu)件。也就是說在代碼中寫:PortA=0x3F,實際做的就是告訴編譯器0x0000是一個volatile-unsigned-char類型的指針,它的內(nèi)容等于0x3F。訪問I/O寄存器一個普通而有用的形式是使用如下的#defi數(shù)組C語言允許程序員用幾種不同方法存取數(shù)組的內(nèi)容。選擇最適合的方法來存取,可以提高代碼的效率。 數(shù)組的訪問方法有:硬編碼:

Array[0]=12*UNIT_VOLTS;

編譯時決定地址,執(zhí)行速度快。變址增加

Array[index++]=12*UNIT_VOLTS;

快速,比硬編碼靈活。數(shù)組指針 *(ArrayPtr++)=12*UNIT_VOLTS;

執(zhí)行速度快,可讀性差,可和循環(huán)一起使用。數(shù)組C語言允許程序員用幾種不同方法存取數(shù)組的內(nèi)容。選擇最適合C語言與匯編語言的混合編程在絕大多數(shù)場合采用C語言編程可完成預(yù)期的目的,但是對一些特殊情況進行編程時要結(jié)合匯編語言。匯編語言具有直接和硬件打交道、執(zhí)行代碼的效率高等特點,可以做到C語言所不能做到的一些事情:一個程序中的關(guān)鍵部分對執(zhí)行速度有很高要求,實時性強。用匯編編程可以更有效的利用CPU的寄存器和指令集,用其產(chǎn)生的代碼比用編譯器產(chǎn)生的代碼運行更快。對特定硬件接口的訪問。例如對I/O端口的訪問,或者用于禁止、啟用中斷系統(tǒng)。因此,選用C語言編程時,還需要夾雜一些匯編程序,通過這種混合編程的方法將C語言和匯編語言的優(yōu)點結(jié)合起來,這已經(jīng)成為MCU開發(fā)最流行的編程方法。C語言與匯編語言的混合編程在絕大多數(shù)場合采用C語言編程可完C中使用匯編目前大多數(shù)MCU系統(tǒng),在C語言中使用匯編語言有兩種情況:一種是匯編程序部分和C程序部分為不同模塊或不同的文件,通常由C程序調(diào)用匯編程序模塊的變量和函數(shù)(也可稱為子程序或過程);另一種是嵌入式匯編,即在C語言程序中嵌入一段匯編語言程序。C語言中嵌入?yún)R編語句:_asmAssemblyInstuction;asm(AssemblyInstruction);asm{ AssemblyInstruction AssemblyInstruction}C中使用匯編目前大多數(shù)MCU系統(tǒng),在C語言中使用匯編語言有C中調(diào)用匯編指令構(gòu)成的子程序:當(dāng)匯編程序和C程序為不同模塊時,程序一般可分為若干個C程序模塊和匯編程序模塊,C程序模塊通常是程序的主體框架,而匯編程序模塊通常是若用C語言實現(xiàn)則效率不高的函數(shù)組成,也可以是已經(jīng)成熟的、沒有必要再轉(zhuǎn)化成C語言的匯編子程序。在這種混合編程技術(shù)中,關(guān)鍵是參數(shù)的傳遞和函數(shù)的返回值。它們必須有完整的約定,否則數(shù)據(jù)的交換就可能出錯。定義匯編子程序,定義格式如下:_子程序名 代碼

……rtsC中調(diào)用匯編指令構(gòu)成的子程序:注意:第一,在子程序名前加‘_’;第二,在C代碼中調(diào)用匯編子程序時可直接調(diào)用:子程序名();第三,在子程序中不能使用映象寄存器的宏定義,只能用它們的直接地址;第四,匯編子程序的編寫時,對于使用過的寄存器需要進行保護。在匯編中使用C語言在匯編中也可以調(diào)用C代碼中的變量與子程序。使用C代碼中定義的變量:在變量名前加‘_’或‘%’,例如unsignedcharPortA;asm(LDAA%PortA)調(diào)用C代碼中定義的函數(shù):在函數(shù)名前加‘_’,例如:function1();asm(BRA_function1);注意:C語言的中斷CodeWarrior編譯器提供了一個非ANSI的變通的方法,在源碼中直接指定中斷向量號t。表達(dá)式以interrupt關(guān)鍵字開始,接著是中斷向量號,最后是函數(shù)原型。interrupt7voidRTI_ISR(void) { /*RTI_ISRFunctionCodes*/ }9S12所有的中斷源共分成55個優(yōu)先級。處在最高優(yōu)先級的中斷是復(fù)位中斷,它的中斷向量號為0,第二位的是時鐘監(jiān)視器復(fù)位中斷,它的中斷向量號為1,以此類推。C語言的中斷CodeWarrior編譯器提供了一個非ANS標(biāo)準(zhǔn)C庫標(biāo)準(zhǔn)庫如stdio.h通常包含在編譯器中。Getchar()、gets()、printf()、putchar()、puts()、scanf()、sprintf()、sscanf()等,都是這些庫中的常用函數(shù)。

當(dāng)給PC機寫這段代碼,printf()缺省的控制臺是顯示器,但HC12沒有顯示器作為片外外設(shè),如果這樣調(diào)用庫函數(shù),哪個端口用于顯示?什么時候我們定義它?在哪兒?在嵌入式編程中,通常printf()調(diào)用putchar()執(zhí)行打印,這假定控制臺缺省為片上串行口(SCI)。如果需要使用這個功能,則需要修改基礎(chǔ)庫函數(shù)putchar()才能正確輸出。

標(biāo)準(zhǔn)C庫標(biāo)準(zhǔn)庫如stdio.h通常包含在編譯器中。GetcC語言入口在MC9S12的C程序中,程序的入口在什么地方?程序是怎樣起動的呢?在工作站或PC上操作系統(tǒng)從磁盤上裝入程序并建立環(huán)境。而在嵌入式系統(tǒng)中沒有操作系統(tǒng)。對程序員來說,程序員必須處理程序起動的每個方面。嵌入式應(yīng)用,特別是用C或C++寫的代碼,需要一個起動模塊,在起動main()之前配置硬件和代碼。通常不可避免地用匯編語言寫成,這個模塊是處理器離開復(fù)位狀態(tài)第一個執(zhí)行的代碼。C語言入口在MC9S12的C程序中,程序的入口在什么地方?C起動代碼通常執(zhí)行下列動作(1)關(guān)中斷;(2)將初始化數(shù)據(jù)從ROM復(fù)制到RAM;(3)將未初始化數(shù)據(jù)區(qū)清零;(4)為堆棧定位空間以及初始化堆棧;(5)創(chuàng)建并初始化堆;(6)執(zhí)行構(gòu)造函數(shù)并初始化所有全局變量(僅C++);(7)開中斷;C起動代碼通常執(zhí)行下列動作(1)關(guān)中斷;最后,起動代碼調(diào)用main(),啟動應(yīng)用的剩余部分。當(dāng)然針對于MC9S12來說,啟動代碼可以不必這么麻煩??梢灾怀跏蓟?,初始化硬件然后直接跳轉(zhuǎn)到main函數(shù),下面是一個啟動代碼的例子最后,起動代碼調(diào)用main(),啟動應(yīng)用的剩余部分。當(dāng)然針對void_Startup(){asm{LDS0x1000JSRHardInitJSRmain}}程序員可以自己編寫啟動代碼,可以將應(yīng)用程序中的硬件初始化程序都放在啟動代碼里面,這樣就可以比較專注地在應(yīng)用程序中做要做的事情。void_Startup()程序的鏈接與定位對于MC9S12單片機來說,由于RAM,EEPROM,寄存器可以重新映射,而且不同型號的單片機內(nèi)部FLASH大小也不一樣,這樣程序編譯后存放在什么位置,成了我們必須告訴鏈接器的內(nèi)容,因為鏈接器不知道你用的單片機RAM放在什么地方,ROM放在什么地方。為了方便管理,CodeWarrior自動生成一個.prm文件,此文件用來管理程序的定位。但是該文件并不一定與用戶的實際情況相符,因此在鏈接前,必須自己核對該文件是否與實際的硬件相符??磿?92頁。程序的鏈接與定位對于MC9S12單片機來說,由于RAM,E§6-3C程序設(shè)計舉例§6-3C程序設(shè)計舉例例1對從RAM中0x1000地址開始的10個無符號字節(jié)型數(shù)進行由小到大排序程序分析:可以使用C語言把一個unsignedchar類型的數(shù)組存儲到從0x1000開始的內(nèi)存空間中,數(shù)組的大小為10個字節(jié),內(nèi)容由程序員自定義。由小到大的排序使用冒泡法完成。unsignedchardata[10]@0x1000,i,j,temp; /*定義數(shù)組*/voidmain(void){ for(i=0;i<10;i++)/*初始化數(shù)組*/ {data[i]=20-i;} for(i=0;i<9;i++)/*使用冒泡法排序*/ for(j=0;j<9;j++) {if(data[j]>data[j+1])

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論