走在馬路上的時候,經常會看到馬路兩側有一些LED點陣的廣告牌,這些廣告牌看起來絢爛奪目,非常吸引人,而且還會變化很多種不同的顯示方式。本章就會學習到點陣LED的控制方式,同時也會學習C語言變量的進階知識——變量的作用域和存儲類別。
7.1變量的作用域
所謂的作用域就是指變量起作用的范圍,也是變量的有效范圍。變量按他的作用域可以分為局部變量和全局變量。
7.1.1局部變量
在一個函數內部聲明的變量是內部變量,它只在本函數內有效,在本函數以外是不能使用的,這樣的變量就是局部變量。此外,函數的形參也是局部變量,形參會在后面講解函數的時候再詳細解釋。
比如第6章程序中定義的unsigned long sec這個變量,它是定義在main函數內部的,所以只能由main函數使用,中斷函數就不能使用這個變量。同理,如果在中斷函數內部定義的變量,在main函數中也是不能使用的。
7.1.2全局變量
在函數外聲明的變量就是全局變量。一個源程序文件可以包含一個或者多個函數,全局變量的作用范圍是從它開始聲明的位置一直到程序結束。
比如第6章程序中定義的unsigned char LedBuff[6]這個數組,它的作用域就是從開始定義的位置一直到程序結束,不管是main函數,還是中斷函數InterruptTimer0,都可以直接使用這個數組。
局部變量只有在聲明它的函數范圍內可以使用,而全局變量可以被作用域內的所有的函數使用。所以在一個函數內既可以使用本函數內聲明的局部變量,也可以使用全局變量。從編程規范上講,一個程序文件內所有的全局變量都應定義在文件的開頭部分,在文件中所有函數之前。
由于C語言函數只有一個返回值,但是卻經常會希望一個函數可以提供或影響多個結果值,這時就可以利用全局變量來實現。但是考慮到全局變量的一些特征,應該限制全局變量的使用,過多使用全局變量也會帶來一些問題。
1、全局變量可以被作用域內所有的函數直接引用,可以增加函數間數據聯系的途徑,但同時加強了函數模塊之間的數據聯系,使這些函數的獨立性降低,對其中任何一個函數的修改都可能會影響到其它函數的執行結果,函數之間過于緊密的聯系不利于程序的維護。
2、全局變量的應用會降低函數的通用性,函數在執行的時候過多依賴于全局變量,不利于函數的重復利用。目前編寫的程序還都比較簡單,就一個.c文件,但以后要學到一個程序中有多個.c文件,當一個函數被另外一個.c文件調用的時候,必須將這個全局變量的變量值一起移植,而全局變量不只被一個函數調用,這樣會引起一些不可預見的后果。
3、過多使用全局變量會降低程序的清晰度,使程序的可讀性下降。在各個函數執行的時候都可能改變全局變量值,往往難以清楚的判斷出每個時刻各個全局變量的值。
4、定義全局變量會永久占用單片機的內存單元,而局部變量只有進入定義局部變量的函數時才會占用內存單元,函數退出后會自動釋放所占用的內存。所以大量的全局變量會額外增加內存消耗。
綜上所述之原因,在編程規范上有一條原則,就是盡量減少全局變量的使用,能用局部變量代替的就不用全局變量。
還有一種特殊情況,在看別人程序的時候請注意,C語言是允許局部變量和全局變量同名的,它們定義后在內存中占有不同的內存單元。如果在同一源文件中,全局變量和局部變量同名,在局部變量作用域范圍內,只有局部變量有效,全局變量不起作用,也就是說局部變量具有更高優先級。但是從編程規范上講,是要避免全局變量與局部變量重名的,從而避免不必要的誤解和誤操作。
7.2變量的存儲類別
變量的存儲類別分為自動、靜態、寄存器和外部這四種。其中后兩種暫不介紹,主要介紹自動變量和靜態變量這兩種。
函數中的局部變量,如果不加static這個關鍵字來修飾,都屬于自動變量,也叫做動態存儲變量。這種存儲類別的變量,在調用該函數的時候系統會給他們分配存儲空間,在函數調用結束后會自動釋放這些存儲空間。動態存儲變量的關鍵字是auto,但是這個關鍵字是可以省略的。
與動態變量對應的就是靜態變量。首先,全局變量均是靜態變量,此外,還有一種特殊的局部變量也是靜態變量。即在定義局部變量時前邊加上static這個關鍵字,加上這個關鍵字的變量就稱之為靜態局部變量,它的特點是,在整個生存期中只賦一次初值,在第一次執行該函數時,它的值就是給定的那個初值,而之后在該函數所有的執行次數中,它的值都是上一次函數執行結束后的值,即它可以保持前次的執行結果。
有這樣一種情況,某個變量只在一個函數中使用,但是卻想在函數多次調用期間保持住這個變量的值而不丟失,也就是說在該函數的本次調用中該變量值的改變要依賴與上一次調用函數時的值,而不能每次都從初值開始。如果使用局部動態變量的話,每次進入函數后上一次的值就丟失了,它每次都從初值開始,如果定義成全局變量的話,又違背了上面提到的盡量減少全局變量的使用這條原則,那么此時,局部靜態變量就是最好的解決方案了。
比如第6章最后的例程中有一個控制數碼管動態掃描顯示用的索引變量i和實現秒定時的計數變量cnt,當時就是定義成了全局變量,現在就可以改成局部靜態變量來試試。
#include
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //數碼管顯示字符轉換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數碼管顯示緩沖區,初值0xFF確保啟動時都不亮
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char flag1s = 0; //1秒定時標志
void main()
{
unsigned long sec = 0; //記錄經過的秒數
EA = 1; //使能總中斷
ENLED = 0; //使能U3,選擇控制數碼管
ADDR3 = 1; //因為需要動態改變ADDR0-2的值,所以不需要再初始化了
TMOD = 0x01; //設置T0為模式1
TH0 = 0xFC; //為T0賦初值0xFC67,定時1ms
TL0 = 0x67;
ET0 = 1; //使能T0中斷
TR0 = 1; //啟動T0
while (1)
{
if (flag1s == 1) //判斷1秒定時標志
{
flag1s = 0; //1秒定時標志清零
sec++; //秒計數自加1
//以下代碼將sec按十進制位從低到高依次提取并轉為數碼管顯示字符
LedBuff[0] = LedChar[sec%10];
LedBuff[1] = LedChar[sec/10%10];
LedBuff[2] = LedChar[sec/100%10];
LedBuff[3] = LedChar[sec/1000%10];
LedBuff[4] = LedChar[sec/10000%10];
LedBuff[5] = LedChar[sec/100000%10];
}
}
}
/* 定時器0中斷服務函數 */
void InterruptTimer0() interrupt 1
{
static unsigned char i = 0; //動態掃描的索引,定義為局部靜態變量
static unsigned int cnt = 0; //記錄T0中斷次數,定義為局部靜態變量
TH0 = 0xFC; //重新加載初值
TL0 = 0x67;
cnt++; //中斷次數計數值加1
if (cnt >= 1000) //中斷1000次即1秒
{
cnt = 0; //清零計數值以重新開始下1秒計時
flag1s = 1; //設置1秒定時標志為1
}
//以下代碼完成數碼管動態掃描刷新
P0 = 0xFF; //顯示消隱
switch (i)
{
case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
default: break;
}
}
注意看程序中中斷函數里的局部變量i,為其加上了static關鍵字來修飾,就成為了靜態局部變量。它的初始化i = 0操作只進行一次,程序執行代碼中會進行i++等操作,那么下次再進入中斷函數的時候,i會保持上次中斷函數執行完畢后的值。如果去掉static這個關鍵字,那么每次進入中斷函數后,i都會被初始化成0,可以自己修改程序看一下實際效果是否和理論相符。
審核編輯 黃宇
-
led
+關注
關注
243文章
24458瀏覽量
688089
發布評論請先 登錄
第8章 函數進階與按鍵(8.1 8.2)
第7章 變量進階與點陣LED(7.5 7.6)
FZH367 具有獨立自動呼吸功能的LED(12×16)點陣驅動芯片 原廠技術支持
原廠 FZH365 具有獨立自動呼吸功能的LED(12×12)點陣驅動芯片
原廠 FZH364 一款8×8點陣恒流LED驅動芯片
第2章 點亮你的LED(2.3 2.4)
第2章 點亮你的LED
Texas Instruments TPS92402 LED點陣控制器數據手冊
Texas Instruments TPS92401 LED點陣控制器數據手冊

第7章 變量進階與點陣LED(7.1 7.2)
評論