專注差異化嵌入式產(chǎn)品解決方案 給智能產(chǎn)品定制注入靈魂給予生命
提供開發(fā)工具、應(yīng)用測試 完善的開發(fā)代碼案例庫分享
從全面的產(chǎn)品導(dǎo)入到強(qiáng)大技術(shù)支援服務(wù) 全程貼心伴隨服務(wù),創(chuàng)造無限潛能!
十年專注單片機(jī)方案開發(fā)的方案公司英銳恩,分享C語言的點(diǎn)滴。英銳恩現(xiàn)提供服務(wù)產(chǎn)品涉及主控芯片:8位單片機(jī)、16位單片機(jī)、32位單片機(jī)及各類運(yùn)算放大器等。
由于ICD2是在線仿真所以回占用部分芯片資源,但連接器并不知道什么地址已經(jīng)被ICD2占用,如果碰巧分配了被ICD2占用的資源,回浪費(fèi)大家的調(diào)試時(shí)間,那么如何讓連接器知道什么地址不能分配呢?
請參考以下方法
適用條件:在MPLAB IDE 環(huán)境集成C編譯器 + ICD2 調(diào)試程序
1.使用 HITECH C + ICD2 PROJECT->BUILD OPTIONS->PROJECT->PICC Global 選擇compile for ICD 以下這段話表達(dá)的意思也不很貼切:光在中斷入口和出口處為了保護(hù)和恢復(fù)這些中間臨時(shí)變量就需要大量的開銷,嚴(yán)重影響中斷服務(wù)的效率。
其實(shí),在PICC中,對中斷中使用的臨時(shí)變量,為了防止重入或覆蓋,干脆就是中斷專用。
在我們的PICC編程中,臨時(shí)變量(或者說局部變量),都有其生存期,在生存期結(jié)束后,可被覆蓋。但中斷函數(shù)中使用到的臨時(shí)變量卻與一般概念上的臨時(shí)變量有很大的區(qū)別。中斷函數(shù)中使用到的臨時(shí)變量和中斷函數(shù)中不使用到的臨時(shí)變量不能占用同一地址。換言之,中斷和非中斷的臨時(shí)變量地址不得重疊。看一下下面的例程:(16F877A) #include "pic.h"
unsigned char i,j,k;
void fn1(void) { unsigned char s1[40]; s1[0]=1; }
void interrupt SAMPLE (void) { unsigned char s2[50]; s2[0]=1; fn1();
}
void main(void) {
}
說明:i,j,k-----三個(gè)全局變量 s1[40]----40個(gè)中斷調(diào)用使用的臨時(shí)變量 s2[50]----50個(gè)中斷中使用的臨時(shí)變量 保護(hù)W、STATUS和PCLATH用去三個(gè)內(nèi)存變量 從上面程序中可以看出,BANK0資源已經(jīng)耗盡。50+40+3+3=96。由于中斷使用了93個(gè)RAM,這樣中斷以外就沒有可以使用的臨時(shí)變量了。也就是說中斷使用的臨時(shí)變量將不會被重入。
再看一下下面的例子: #include "pic.h"
unsigned char i,j,k;
void fn1(void) { unsigned char s1[20]; s1[0]=1; }
void fn2(void) { unsigned char s3[20]; s3[0]=1; }
void interrupt SAMPLE (void) { unsigned char s2[50]; s2[0]=1; fn1();
}
void main(void) { fn2(); } 說明:i,j,k-----三個(gè)全局變量 s1[20]----20個(gè)中斷調(diào)用使用的臨時(shí)變量 s2[50]----50個(gè)中斷中使用的臨時(shí)變量 s3[20]----20個(gè)非中斷使用的臨時(shí)變量 保護(hù)W、STATUS和PCLATH用去三個(gè)內(nèi)存變量 從上面的例子中也可以看出,中斷使用的臨時(shí)變量和中斷外使用的臨時(shí)變量不可重疊。 eyuge2兄所說的PICC自動保護(hù)現(xiàn)場,其實(shí)就是PICC對中斷中使用的臨時(shí)變量實(shí)施“禁入”。也就是,中斷內(nèi)可實(shí)施中斷內(nèi)臨時(shí)變量覆蓋;中斷外的臨時(shí)變量也可進(jìn)行覆蓋;但中斷內(nèi)外之間的臨時(shí)變量不可相互覆蓋。 當(dāng)然為了防止重入,中斷中使用的函數(shù)不可在中斷外被調(diào)用。(PICC可自動實(shí)施重入檢查。)
大家來看一下具體的例子,用PIC16F887A編譯下列程序。
第一次,中斷處理函數(shù)什么也不做。 #include "pic.h" unsigned int m,n; unsigned char i,j,k,l; void interrupt SAMPLE (void) {
}
void main(void) {
}
因?yàn)椴贿M(jìn)行操作,中斷函數(shù)不需要進(jìn)行現(xiàn)場保護(hù),編譯后結(jié)果是直接返回。
0004 0009 retfie
第二次, 中斷處理函數(shù)增加一條對全局變量的操作。 void interrupt SAMPLE (void) { i++; } 編譯結(jié)果是: 0004 00F0 movwf saved_w 0005 0803 movf 3,w 0006 0183 clrf 3 0007 00A8 movwf saved_status 0008 0183 clrf 3 0009 0AA0 incf _i 000A 0828 movf saved_status,w 000B 0083 movwf 3 000C 0EF0 swapf saved_w 000D 0E70 swapf saved_w,w 000E 0009 retfie
從結(jié)果看,只保護(hù)W和STATUS,不對全局變量進(jìn)行現(xiàn)場保護(hù)。
最后來看一個(gè)中斷處理函數(shù)中調(diào)用一個(gè)函數(shù)fn()的例子。在fn()函數(shù)中涉及全局變量和局部變量。
void fn(void) { char temp; temp=i+j; i=temp++; } void interrupt SAMPLE (void) { fn(); }
0004 int_entry 0004 00F0 movwf saved_w 0005 0803 movf 3,w 0006 0183 clrf 3 0007 00A9 movwf saved_status 0008 080A movf 10,w 0009 00AA movwf saved_pclath 000A 018A clrf 10 000B 120A ISR BCF PCLATH,0X4 000C 118A BCFPCLATH,0X3 000D 27F9 CALL fn 000E 120A BCF PCLATH,0X4 000F 118A BCF PCLATH,0X3 0010 082A movf saved_pclath,w 0011 008A movwf 10 0012 int_restore 0012 0829 movf saved_status,w 0013 0083 movwf 3 0014 0EF0 swapf saved_w 0015 0E70 swapf saved_w,w 0016 0009 retfie
在第三個(gè)例子中,由于涉及到跨頁的問題,中斷函數(shù)為了能正確返回中斷發(fā)生處地址,對PCLATH進(jìn)行保護(hù)。
從以上我們可以看出,無論是在中斷處理中使用的變量,還是在中斷處理中調(diào)用其它函數(shù)而使用的變量,中斷函數(shù)都不會對其實(shí)行自動保護(hù)。
當(dāng)然中斷前的地址入棧是屬于硬件功能,不需要軟件中作出相應(yīng)操作。 PICC與指針 1:指針?biāo)囊兀ㄟ@部分主要從網(wǎng)上搜索到的,還不錯(cuò)):指針的類型指針?biāo)赶虻念愋椭羔樀闹祷蛘呓兄羔標(biāo)赶虻膬?nèi)存區(qū)指針本身所占據(jù)的內(nèi)存區(qū) 1 指針的類型。 從語法的角度看,你只要把指針聲明語句里的指針名字去掉,剩下的部分就 是這個(gè)指針的類型。這是指針本身所具有的類型。讓我們看看例一中各個(gè)指針的 類型: (1)int *ptr; //指針的類型是int * (2)char *ptr; //指針的類型是char * (3)int **ptr; //指針的類型是 int ** (4)int (*ptr)[3]; //指針的類型是 int(*)[3] (5)int *(*ptr)[4]; //指針的類型是 int *(*)[4] 怎么樣?找出指針的類型的方法是不是很簡單?
1 .2指針?biāo)赶虻念愋汀?當(dāng)你通過指針來訪問指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念愋蜎Q定了編譯 器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來看待。 從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的指針聲明符 *去掉,剩下的就是指針?biāo)赶虻念愋?。例如?(1)int *ptr; //指針?biāo)赶虻念愋褪莍nt (2)char *ptr; //指針?biāo)赶虻牡念愋褪莄har (3)int **ptr; //指針?biāo)赶虻牡念愋褪?int * (4)int (*ptr)[3]; //指針?biāo)赶虻牡念愋褪?int()[3] (5)int *(*ptr)[4]; //指針?biāo)赶虻牡念愋褪?int *()[4] 在指針的算術(shù)運(yùn)算中,指針?biāo)赶虻念愋陀泻艽蟮淖饔谩?指針的類型(即指針本身的類型)和指針?biāo)赶虻念愋褪莾蓚€(gè)概念。當(dāng)你對C越 來越熟悉時(shí),你會發(fā)現(xiàn),把與指針攪和在一起的"類型"這個(gè)概念分成"指針的 類型"和"指針?biāo)赶虻念愋?quot;兩個(gè)概念,是精通指針的關(guān)鍵點(diǎn)之一。
1.3 指針的值,或者叫指針?biāo)赶虻膬?nèi)存區(qū)或地址。 指針的值是指針本身存儲的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是 一個(gè)一般的數(shù)值。在32位程序里,所有類型的指針的值都是一個(gè)32位整數(shù),因?yàn)?32位程序里內(nèi)存地址全都是32位長。 指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開始,長度為si zeof(指針?biāo)赶虻念愋?的一片內(nèi)存區(qū)。以后,我們說一個(gè)指針的值是XX,就相 當(dāng)于說該指針指向了以XX為首地址的一片內(nèi)存區(qū)域;我們說一個(gè)指針指向了某塊 內(nèi)存區(qū)域,就相當(dāng)于說該指針的值是這塊內(nèi)存區(qū)域的首地址。 指針?biāo)赶虻膬?nèi)存區(qū)和指針?biāo)赶虻念愋褪莾蓚€(gè)完全不同的概念。在例一中 ,指針?biāo)赶虻念愋鸵呀?jīng)有了,但由于指針還未初始化,所以它所指向的內(nèi)存區(qū) 是不存在的,或者說是無意義的。 以后,每遇到一個(gè)指針,都應(yīng)該問問:這個(gè)指針的類型是什么?指針指向的 類型是什么?該指針指向了哪里? 1.4 指針本身所占據(jù)的內(nèi)存區(qū)。 指針本身占了多大的內(nèi)存?你只要用函數(shù)sizeof(指針的類型)測一下就知道 了。在32位平臺里,指針本身占據(jù)了4個(gè)字節(jié)的長度。 指針本身占據(jù)的內(nèi)存這個(gè)概念在判斷一個(gè)指針表達(dá)式是否是左值時(shí)很有用。(注釋:在picc18里,指針占用了兩個(gè)字節(jié))
下面就picc18列舉個(gè)實(shí)例看下面程序
int data[10]={1,2,4,5,6,7,8}; int lenth1,lenth2,lenth3; int *ptr1,*ptr2; main() { ptr1=&data[0]; ptr2=&data[1]; lenth1=ptr2-ptr1; lenth2=(int)ptr2-(int)ptr1; lenth3=*ptr2-*ptr1; } 在watch窗口可以看到lenth1 為1,而lenth2為2,可以這樣解釋:在lenth1=ptr2-ptr1;這條語句中兩個(gè)INT類型指針變量相減得一個(gè)(這在c中是允許的)非指針的數(shù),這個(gè)數(shù)的代表如下:如果這兩個(gè)指針指向的內(nèi)型為INT型,那么這個(gè)數(shù)代表兩個(gè)指針之間相隔多少個(gè)INT型變量,顯然在以上程序中,data[0],和data[1]之間相隔了1個(gè)INT型變量而在 lenth2=(int)ptr2-(int)ptr1;實(shí)際上是求data[1]和data[0]之間占用多少個(gè)內(nèi)存空間,注意(int)ptr是ptr的值(不是ptr所指向的數(shù)的值) lenth3=*ptr2-*ptr1;相信稍微懂c的人都知道是在求ptr所指向的兩個(gè)值之間的差,和lenth3=data[1]-data[0]等效大家不防試試lenth4=(char*)ptr2-(char*)ptr1;看看等于多少,
2: 指針函數(shù)和函數(shù)指針有什么區(qū)別
2.1,這兩個(gè)概念都是簡稱,指針函數(shù)是指帶指針的函數(shù),即本質(zhì)是一個(gè)函數(shù)。我們知道函數(shù)都又返回類型(如果不返回值,則為無值型),只不過指針函數(shù)返回類型是某一類型的指針。其定義格式如下所示:
返回類型標(biāo)識符 *返回名稱(形式參數(shù)表) { 函數(shù)體 } 返回類型可以是任何基本類型和復(fù)合類型。返回指針的函數(shù)的用途十分廣泛。事實(shí)上,每一個(gè)函數(shù),即使它不帶有返回某種類型的指針,它本身都有一個(gè)入口地址,該地址相當(dāng)于一個(gè)指針。比如函數(shù)返回一個(gè)整型值,實(shí)際上也相當(dāng)于返回一個(gè)指針變量的值,不過這時(shí)的變量是函數(shù)本身而已,而整個(gè)函數(shù)相當(dāng)于一個(gè)“變量”。例如下面一個(gè)返回指針函數(shù)的例子: char data[10]; char* test(void);
main() { char *ptr; ptr=test(); }
char* test(void) { char *p; p=data; return p; } 注意:該程序在picc18中調(diào)試
2.2,“函數(shù)指針”是指向函數(shù)的指針變量,因而“函數(shù)指針”本身首先應(yīng)是指針變量,只不過該指針變量指向函數(shù)。這正如用指針變量可指向整型變量、字符型、數(shù)組一樣,這里是指向函數(shù)。如前所述,C在編譯時(shí),每一個(gè)函數(shù)都有一個(gè)入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂?。有了指向函?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可引用其他類型變量一樣,在這些概念上一致的。函數(shù)指針有兩個(gè)用途:調(diào)用函數(shù)和做函數(shù)的參數(shù)。函數(shù)指針的說明方法為: 數(shù)據(jù)類型標(biāo)志符 (*指針變量名)(參數(shù));注:函數(shù)括號中的參數(shù)可有可無,視情況而定。 下面的程序說明了函數(shù)指針調(diào)用函數(shù)的方法: char max(char x,char y); char min(char x,char y); char (*ptr)(char,char); char a=2,b=3,c; main() { ptr=max; c=(*ptr)(a,b); ptr=min; c=(*ptr)(a,b); } char max(char x,char y) { return x>=y?x:y; } char min(char x,char y) { return x<=y?x:y; } 注意:該程序在picc18中調(diào)試在pic的
c程序編寫中,函數(shù)指針不是很常用,如果大家有興趣看看UCOS的pic18的移植版本,就可以發(fā)現(xiàn)ucos中是用函數(shù)指針傳遞任務(wù)程序的入口地址的。
3:結(jié)構(gòu)聯(lián)合與指針先看下面的一個(gè)簡單的舉例 typedef struct datas { char datah; char datal; struct datas *next; } data; //a是一個(gè)結(jié)構(gòu)變量 data a,b; data *ptr1,*ptr2; main() { ptr1=&a; ptr1->datah =1; ptr1->datal =2; ptr2=&b; ptr1->datah =3; ptr1->datal =4; ptr1->next=ptr2; ptr2->next=0; } 數(shù)據(jù)a,b是一個(gè)結(jié)構(gòu)型變量,ptr則是指向結(jié)構(gòu)型變量的指針,在c語言里,通過形如ptr->x的形式來訪問結(jié)構(gòu)或者指針的成員。在結(jié)構(gòu)變量中定義了一個(gè)指針struct datas *next; 這是在指針鏈中常用到的,ptr1->next=ptr2; ptr2->next=0;實(shí)際上已經(jīng)建立了一條簡單的指針鏈,當(dāng)然建立指針鏈用這種初始化的方法不夠簡單,但是上面的程序只是為了說明指針和結(jié)構(gòu)而已。在單片機(jī)的c語言程序中,聯(lián)合和結(jié)構(gòu)是經(jīng)常用在一起的下面在舉一個(gè)簡單的列子: typedef struct { char datah; char datal; } data; typedef union {
data twpbyte; int bytes;
} piccdata;
piccdata adres[10]; piccdata *ptr1,*ptr2; char lenth1,lenth2; main() { ptr2=adres; ptr1=adres; ptr1++; lenth1=ptr1-ptr2; lenth2=(char)ptr1-(char)ptr2;
} 在以上程序中,lenth2為2,而lenth1為1,道理和第一個(gè)列子一樣,只是應(yīng)該注意的是在piccdata型變量中占用的空間為2個(gè)字節(jié)而不是4個(gè)字節(jié)?。?!另:結(jié)構(gòu)與聯(lián)合是pic應(yīng)用的一個(gè)好東西,這點(diǎn)HOTpower曾經(jīng)有一很經(jīng)典的文章,在此列中,只要稍微加以改動,就可以對一個(gè)16位變量即可以整體訪問,也可分為高八位訪問和低八位訪問。這在ad轉(zhuǎn)換中是很有用的。
4: const與指針 const是一個(gè)C語言的關(guān)鍵字,它限定一個(gè)變量不允許被改變。 如果const關(guān)鍵字不涉及到指針,我們很好理解,下面是涉及到指針的情況: int b = 500; const int* a = &b; [1] int const *a = &b; [2] Int* const a = &b; [3] const int* const a = &b; [4] 如果const位于星號的左側(cè),則const就是用來修飾指針?biāo)赶虻淖兞?,即指針指向?yàn)槌A?;如果const位于星號的右側(cè),const就是修飾指針本身,即指針本身是常量。因此,[1]和[2]的情況相同,都是指針?biāo)赶虻膬?nèi)容為常量(const放在變量聲明符的位置無關(guān)),這種情況下不允許對內(nèi)容進(jìn)行更改操作,如不能*a = 3 ;[3]為指針本身是常量,而指針?biāo)赶虻膬?nèi)容不是常量,這種情況下不能對指針本身進(jìn)行更改操作,如a++是錯(cuò)誤的;[4]為指針本身和指向的內(nèi)容均為常量。有了上面的基礎(chǔ),對于在picc18中的c語言應(yīng)用就可以開始了,在單片機(jī)編程中,經(jīng)常會用到查表程序等,通常把大量的數(shù)據(jù)放入rom中,下面是一個(gè)簡單的列子
const int a[8]={1,2,3,-3,3,5,6,7}; const int *ptr; main() { ptr=a; ptr++; } 顯然ptr是一個(gè)指向常量的指針,ptr指向的數(shù)是不可變的,但是ptr本身是可變的,我們可以通過ptr來訪問定義在rom中的數(shù)組a[8];
歡迎補(bǔ)充和指正,謝謝!