在程序運(yùn)行過程中,其值不能被改變的量稱之為常量。常量分為不同的類型,有整型常量如1、2、3、100;浮點(diǎn)型常量3.14、0.56、-4.8;字符型常量?a?、?b?、?0?;字符串常量“a”、“abc”、“1234”、“1234abcd”等。
細(xì)心的同學(xué)會發(fā)現(xiàn),整型和浮點(diǎn)型常量我們直接寫的數(shù)字,而字符型常量用單引號來表示一個字符,用雙引號來表示一個字符串,尤其大家要注意?a?和“a”是不一樣的,這個等會我們要詳細(xì)介紹。
常量一般有兩種表現(xiàn)形式:
比如,我們可以把3.14取名為 PI(即π)。再比如,我們上節(jié)課的串口程序,我們用的波特率是9600,如果用符號常量來進(jìn)行提前聲明的話,那我們要修改成其它速率的話,就不用在程序中找9600修改了,直接修改聲明處就可以了,兩種方法舉例說明。用 const 聲明。比如我們在程序開始位置定義一個符號常量 BAUD。
定義形式是:
const 類型 符號常量名字=常量值;
如
const unsigned int BAUD = 9600; /*注意結(jié)尾有個分號*/
我們就可以在程序中直接把9600改成 BAUD,這樣我們?nèi)绻牟ㄌ芈实脑?,直接在程序開頭位置改一下這個值就可以了。用預(yù)處理命令 #define 來完成,預(yù)處理命令我們先來認(rèn)識 #define。
定義形式是:
#define 符號常量名 常量值
如
#define BAUD 9600 /*注意結(jié)尾沒有分號*/
這樣定義以后,只要在程序中出現(xiàn) BAUD 的話,意思就是完全替代了后邊的9600這個數(shù)字。
不知大家是否記得,我們之前定義數(shù)碼管真值表的時候,用了一個 code 關(guān)鍵字。
unsigned char code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
我們當(dāng)時說加了 code 之后,這個真值表的數(shù)據(jù)只能被使用,不能被改變,如果我們直接寫 LedChar[0] = 1;這樣就錯了。實(shí)際上 code 這個關(guān)鍵字是51單片機(jī)特有的,如果是其它類型的單片機(jī)我們只需要寫成 const unsigned char LedChar[]={}就可以了,自動保存到 FLASH 里,而51單片機(jī)只用 const 而不加 code 的話,這個數(shù)組會保存到 RAM 中,而不會保存到 FLASH 中,鑒于此,在51這個體系下,const 反倒變得不那么重要了,它的作用被 code 取代了,這里大家知道這么回事即可。
我們來對各種類型的常量做進(jìn)一步說明。
整型常量和浮點(diǎn)型常量就沒多少可說的了,之前我們應(yīng)用的都很熟練了,整型直接寫數(shù)字就是十進(jìn)制如128,前邊 0x 開頭的表示是十六進(jìn)制 0x80,浮點(diǎn)型直接寫帶小數(shù)點(diǎn)的數(shù)據(jù)就可以了。
字符型常量是由一對單引號括起來的單個字符。它分為兩種形式,一種是普通字符,一種是轉(zhuǎn)義字符。
普通字符就是那些我們可以直接書寫直接看到的有形的字符,比如阿拉伯?dāng)?shù)字0~9,英文字符 A~z,以及標(biāo)點(diǎn)符號等。它們都是 ASCII 碼表中的字符,而它們在單片機(jī)中都占用一個字節(jié)的空間,其值就是對應(yīng)的 ASCII 碼值。比如?a?的值是97,?A?的值是65,?0?的值是48,如果定義一個變量 unsigned char a = ?a?,那么變量 a 的值就是97。
除了上述這些字符之外,還有一些特殊字符,它們一些是無形的,像回車符、換行符這些都是看不到的,還有一些像?\”這類字符它們已經(jīng)有特殊用途了,想象一下如果寫 '''覺得編譯器會怎么去解釋呢。針對這些特殊符號,為了可以讓它們正常進(jìn)入到我們的程序代碼中,C 語言就規(guī)定了轉(zhuǎn)義字符,它是以反斜杠()開頭的特定字符序列,讓它們來表示這些特殊字符,比如我們用 \n 來代表換行。我們用一個簡單表格來說明一下常用的轉(zhuǎn)義字符的意思,如表12-2所示。
表 12-2 常用轉(zhuǎn)義字符及含義
字符形式 | 含義 |
---|---|
\n | 換行 |
\t | 橫向跳格(相當(dāng)于 Tab) |
\v | 豎向跳格 |
\b | 退格 |
\r | 光標(biāo)移到行首 |
\|反斜杠字符?\? | |
\? | 單引號字符 |
\” | 雙引號字符 |
\f | 走紙換頁 |
\0 | 空值 |
表格不需要大家記住,用到了,過來查就可以了。
字符串常量是用雙引號括起來的字符序列,一般我們都稱之字符串。如“a”、“1234”、“welcome to www.kingst.org”等都是字符串常量。字符串常量在內(nèi)存中按順序逐個存儲字符串中的字符的 ASCII 碼值,并且特別注意,最后還有一個字符?\0?,?\0?字符的 ASCII 碼值是0,它是字符串結(jié)束標(biāo)志,在寫字符串的時候,這個?\0?是隱藏的,我們看不到,但是實(shí)際卻是存在的。所以“a”就比?a?多了一個 ?\0?,“a”的就占了2個字節(jié),而 ?a?只占一個字節(jié)。
還有一個地方要注意, 就是字符串中的空格, 也是一個字符,比如 “welcome to www.kingst.org ”一共占了26個字節(jié)的空間。其中21個字母,2個?.?,2個 ? ?(空格字符)以及一個?\0?。
為了對比字符串、字符數(shù)組、常量數(shù)組的區(qū)別,我們寫個了簡單的演示程序,定義了4個數(shù)組分別是:
unsigned char array1[] = "1-Hello!\r\n";
unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', '\r', '\n'};
unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};
unsigned char array4[] = "4-Hello!\r\n";
在串口調(diào)試助手下,發(fā)送十六進(jìn)制的1、2、3、4,使用字符形式顯示的話,會分別往電腦上送這4個數(shù)組中對應(yīng)的那個數(shù)組。我們只是在起始位置做了區(qū)分,其它均沒有區(qū)別。大家可以比較一下效果。
此外還要說明一點(diǎn),數(shù)組1和數(shù)組4,數(shù)組1我們是發(fā)完整的字符串,而數(shù)組4我們僅僅發(fā)送數(shù)組中的字符,沒有發(fā)結(jié)束符號。串口調(diào)試助手用字符形式顯示是沒有區(qū)別的,但是大家如果改用十六進(jìn)制顯示,大家會發(fā)現(xiàn)數(shù)組1比數(shù)組4多了一個字節(jié)? \0 ?的 ASCII 值00。
#include <reg52.h>
bit cmdArrived = 0; //命令到達(dá)標(biāo)志,即接收到上位機(jī)下發(fā)的命令
unsigned char cmdIndex = 0; //命令索引,即與上位機(jī)約定好的數(shù)組編號
unsigned char cntTxd = 0; //串口發(fā)送計數(shù)器
unsigned char *ptrTxd; //串口發(fā)送指針
unsigned char array1[] = "1-Hello!\r\n";
unsigned char array2[] = {'2', '-', 'H', 'e', 'l', 'l', 'o', '!', '\r', '\n'};
unsigned char array3[] = {51, 45, 72, 101, 108, 108, 111, 33, 13, 10};
unsigned char array4[] = "4-Hello!\r\n";
void ConfigUART(unsigned int baud);
void main(){
EA = 1; //開總中斷
ConfigUART(9600); //配置波特率為 9600
while (1){
if (cmdArrived){
cmdArrived = 0;
switch (cmdIndex){
case 1:
ptrTxd = array1; //數(shù)組1的首地址賦值給發(fā)送指針
cntTxd = sizeof(array1); //數(shù)組1的長度賦值給發(fā)送計數(shù)器
TI = 1; //手動方式啟動發(fā)送中斷,處理數(shù)據(jù)發(fā)送
break;
case 2:
ptrTxd = array2;
cntTxd = sizeof(array2);
TI = 1;
break;
case 3:
ptrTxd = array3;
cntTxd = sizeof(array3);
TI = 1;
break;
case 4:
ptrTxd = array4;
cntTxd = sizeof(array4) - 1; //字符串實(shí)際長度為數(shù)組長度減1
TI = 1;
break;
default:
break;
}
}
}
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud){
SCON = 0x50; //配置串口為模式1
TMOD &= 0x0F; //清零 T1 的控制位
TMOD |= 0x20; //配置 T1 為模式2
TH1 = 256 - (11059200/12/32)/baud; //計算 T1 重載值
TL1 = TH1; //初值等于重載值
ET1 = 0; //禁止 T1 中斷
ES = 1; //使能串口中斷
TR1 = 1; //啟動 T1
}
/* UART 中斷服務(wù)函數(shù) */
void InterruptUART() interrupt 4{
if (RI){ //接收到字節(jié)
RI = 0; //清零接收中斷標(biāo)志位
cmdIndex = SBUF; //接收到的數(shù)據(jù)保存到命令索引中
cmdArrived = 1; //設(shè)置命令到達(dá)標(biāo)志
}
if (TI){ //字節(jié)發(fā)送完畢
TI = 0; //清零發(fā)送中斷標(biāo)志位
if (cntTxd > 0){ //有待發(fā)送數(shù)據(jù)時,繼續(xù)發(fā)送后續(xù)字節(jié)
SBUF = *ptrTxd; //發(fā)出指針指向的數(shù)據(jù)
cntTxd--; //發(fā)送計數(shù)器遞減
ptrTxd++; //發(fā)送指針遞增
}
}
}