上面我們的敘述了一個程序的編輯、編譯和下載完整的過程,最終點亮了第一個發(fā)光二極管,不過這個程序到底是什么意思,為什么應該這么寫,可能有些人還不是很懂,下面給大家分析,并對里面涉及到的C語言知識進行講解。
為了便于講解,這里把上節(jié)課的程序一并給出:
例1 點亮第一個發(fā)光二極管程序
(1)輸入源程序(又稱為程序編輯)
#include<reg52.h>//52系列單片機頭文件
sbit D1=P1^0;//位定義單片機P1口的第1位
void main()//主函數(shù)
{
D1=0;//點亮第一個發(fā)光二極管
}
1、頭文件
程序的第一句#include<reg52.h>稱為頭文件,頭文件就是C語言中的“文件包含的”的意思。所謂“文件包含”是指在一個文件內(nèi)將另外一個文件的內(nèi)容全部包含進來。因為被包含的文件中的一些定義和命令使用的頻率很高,幾乎每個程序中都可能要用到,為了提高編程效率,減少編程人員的重得勞動,將這些定義和命令單獨組成一個文件,如reg52.h,然后用#include<reg52.h>包含進來就可以了,這個就相當于工業(yè)上的標準零件,拿來直接用就可以了。
在程序中加入頭文件通常有兩種方法,分別是#include<reg52.h>和#include“reg52.h”,注意頭文件句末不需要加“;”否者編譯器編譯時會報錯。加頭文件時< >和“”兩者是有區(qū)別的,主要如下:
當使用< >包含頭文件時,程序編譯時編譯器會首先到keil軟件的安裝文件下進行尋找,具體在keil\C51\INC這個文件下,如果沒有找到,剛編譯時會報錯。
當使用“”包含頭文件時,程序編譯時編譯器會首先到當前工程所在的文件夾下尋找,如果沒有找到,則馬上到keil軟件的安裝文件下進行尋找,如果都沒有找到,則編譯時會報錯。由于<reg52.h>在安裝文件下,所以一般寫成#include<reg52.h>,就可以了。
現(xiàn)在我們打開reg52.h文件,看里面具體內(nèi)容,可以直接在安裝文件keil\C51\INC直接找到reg52.h文件,也可以在編輯界面將鼠標箭頭放在#include<reg52.h>,然后右鍵再選擇
Open docment <reg52.h>,如圖12所示。
圖12 打開頭文件
此時會出現(xiàn)reg52.h的內(nèi)容如下:
/*--------------------------------------------------------------------------
REG52.H
Header file for generic 80C52 and 80C32 microcontroller.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __REG52_H__
#define __REG52_H__
/* BYTE Registers */
sfr P0 = 0x80;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr IE = 0xA8;
sfr IP = 0xB8;
sfr SCON = 0x98;
sfr SBUF = 0x99;
/* 8052 Extensions */
sfr T2CON = 0xC8;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;
/* BIT Registers */
/* PSW */
sbit CY = PSW^7;
sbit AC = PSW^6;
sbit F0 = PSW^5;
sbit RS1 = PSW^4;
sbit RS0 = PSW^3;
sbit OV = PSW^2;
sbit P = PSW^0; //8052 only
/* TCON */
sbit TF1 = TCON^7;
sbit TR1 = TCON^6;
sbit TF0 = TCON^5;
sbit TR0 = TCON^4;
sbit IE1 = TCON^3;
sbit IT1 = TCON^2;
sbit IE0 = TCON^1;
sbit IT0 = TCON^0;
/* IE */
sbit EA = IE^7;
sbit ET2 = IE^5; //8052 only
sbit ES = IE^4;
sbit ET1 = IE^3;
sbit EX1 = IE^2;
sbit ET0 = IE^1;
sbit EX0 = IE^0;
/* IP */
sbit PT2 = IP^5;
sbit PS = IP^4;
sbit PT1 = IP^3;
sbit PX1 = IP^2;
sbit PT0 = IP^1;
sbit PX0 = IP^0;
/* P3 */
sbit RD = P3^7;
sbit WR = P3^6;
sbit T1 = P3^5;
sbit T0 = P3^4;
sbit INT1 = P3^3;
sbit INT0 = P3^2;
sbit TXD = P3^1;
sbit RXD = P3^0;
/* SCON */
sbit SM0 = SCON^7;
sbit SM1 = SCON^6;
sbit SM2 = SCON^5;
sbit REN = SCON^4;
sbit TB8 = SCON^3;
sbit RB8 = SCON^2;
sbit TI = SCON^1;
sbit RI = SCON^0;
/* P1 */
sbit T2EX = P1^1; // 8052 only
sbit T2 = P1^0; // 8052 only
/* T2CON */
sbit TF2 = T2CON^7;
sbit EXF2 = T2CON^6;
sbit RCLK = T2CON^5;
sbit TCLK = T2CON^4;
sbit EXEN2 = T2CON^3;
sbit TR2 = T2CON^2;
sbit C_T2 = T2CON^1;
sbit CP_RL2 = T2CON^0;
#endif
該頭文件主要是對52單片機所有特殊功能寄存器的地址進行聲明。
sfr P1 = 0x90這句話表示:P1口所對應的特殊功能寄存器P1在內(nèi)存中的地址為0x80。換另一種說法就是我們對0x80處的寄存器重新取名P0,以后我們在程序中對P0進行操作,就相當于直接對0x80處的寄存器進行操作。
sfr 表示特殊功能寄存器的意思,它并非標準C 語言的關鍵字,而是Keil 為能直接訪問80C51 中的SFR 而提供了一個新的關鍵詞,其用法是:sfr 特殊功能寄存器名=地址值(注意對于頭文件里“特殊功能寄存器名”,用戶實際上也可以修改的,如P1=0x80,也可改為A1=0x80,但sfr 和地址值則不能更改,否者會編譯出錯。)
以上是對寄存器地址和定義和聲明作了解釋,大家需要牢牢記住:只有對寄存器進行聲明地址后,我們才能對其進行賦相關的值,keil軟件才能編譯通。
實際上就本程序來說,我們也可以不包含該頭文件,而加上一句話,“ sfr P1=0x90; ”也是可以的,但如果要用到P0 P1 P2 P3就需要寫四名話,這樣就會顯得很麻煩 ,只要大家能理解這個,相信大家對為什么要在些加一直頭文件的原因應該理解了,總之以后我們寫51的程序時第一句話就直接包含該頭文件就行了。
2、程序注釋
在對頭文件進行解釋后,在頭文件后面我們可以看到有“//……”符號。“//……”表示語句注釋,其作用主要是為了以后閱讀程序來方便。“//……”主要是對單句或者比較短的程序進行注釋,但如果是對一整段程序進行注釋時,這種方法就顯得非常麻煩,所以程序注釋還有第二種方法,具體的符號是/*****程序*****/,此時斜杠星號與星號斜杠之間的程序?qū)⒈徽w注釋掉。
程序被注釋掉后,編譯器在進行編譯時將不會對其進行編譯,此時相當于此處的程序被刪掉一樣。注釋的目的是為了方便他人閱讀程序,同時也方便自己一段時間后再來閱讀程序時能夠快速的理解該程序的含義。
3、位聲明
程序的第三行出現(xiàn)了”sbit D1=P1^0;”下面進行說明。
sbit表示位的意思,它也是非標準C 語言的關鍵字,編寫程序時如需操作寄存器的某一位(可位尋址的寄存器才能用)時,需定義一個位變量,此時就要要到sbit,如上面的程序中D1=P1^0,;需要注意的是,位定義時有些特殊, 用法有三種:
第一種方法:sbit 位變量名=寄存器位地址值
第二種方法:sbit 位變量名=SFR 名稱^寄存器位值(0-7)
第三種方法:sbit 位變量名=SFR 地址值^寄存器位值
如:
sbit IT0=0x88 (1)說明:0x88是IT0 的位地址值
sbit D1=P1^2 (2)說明:其中P1 必須先用sfr 定義好
sbit EA=0xA8^7 (3)說明:0xA8 就是IE寄存器的地址值
以上三種定義方法需注意的是 IT0 deng EA可由用戶隨便定義,但必須滿足C語言對變量名的定義規(guī)則。除些外其它的則必須按照上面的格式寫,如“名稱^變量位地址值”中“^”,它是由keil軟件的規(guī)定的 ,不能寫成其它的,只能這樣能才編譯通過。
這里也就是說像操作單片機某個口(如P1口),需要通過頭文件sfr P1 = 0x90對其地址進行聲明,同樣的對寄存器的某一位操作時,也需要對位地址進行聲明,此時我們就采用的是第二種表達法sbit D1=P1^0;程序中對D1的操作就相當于對P1中的位地址0Xaff進行操作。
4、主函數(shù)
格式:void main()
特點:無返回值,無參數(shù)。
無返回值,是指函數(shù)執(zhí)行完后不會返回任何值,上面void表示“空”的意思,即不返回任何值,此處也可以省略不寫。當然函數(shù)還有帶返回值的,后面具體會講到。
無參數(shù)是指該函數(shù)不還有任何參數(shù),即main后面的()沒有任何參數(shù),此時也可以在括號內(nèi)加上void ,如main(void),但通常省略不寫。后面也會講到還有參數(shù)的函數(shù),等后面講到時大家就會理解了。
函數(shù)是能夠?qū)崿F(xiàn)特定功能的代碼段,需要注意的是,一個C51源程序必須有,且只能有一個主函數(shù)main(),它是整個程序開始執(zhí)行的入口。在void main()后面必須加一對花括號,其內(nèi)部為一條一條的程序語句。如本例程:
void main()
{
{
D1=0;
}
當然這個主函數(shù)相對來說比較簡單,里面只有一條語句。
5、D1=0
這條語句位于主函數(shù)內(nèi),也是這個程序中最重要的部分。D1相當于單片機P1口的第1個引腳。通過前面的知識我們也知道,數(shù)字電路中有兩種電平,高電平和低電平,0表示低電平或者說低電壓,1表示高電平或者說高電壓。由于發(fā)光管陽極是通過限流電阻接的5V高電壓,而陰極接的P1口的第1個引腳,通過程序D1=0讓其為低電壓,所以此時程序一執(zhí)行后,該發(fā)光二極管就會發(fā)光。
6、利用總線操作法點亮發(fā)光二極管
在例1中,要實現(xiàn)讓一個管發(fā)光,我們采用了位操作法,位操作法其實就是對單片機的每一個引腳單獨操作,如例中就是先作位定義sbit D1=P1^0,然后讓D1=0,最終實現(xiàn)了功能。一個燈會點亮,相信大家也會同點多個燈,如要讓1、3、5、7燈亮時,按前面的方法程序應該寫為:
例2 位操作法實現(xiàn)1、3、5、7燈亮
#include<reg52.h>
sbit D1=P1^0;
sbit D3=P1^2;
sbit D5=P1^4;
sbit D7=P1^6;
void main()
{
D1=0;
D3=0;
D5=0;
D7=0;
}
這種方法當然可以實現(xiàn)1、3、5、7,但這種方法需要定義多個位變量,程序書寫起來相對麻煩。下面給出另一個寫法,如下:
例3 總線操作法實現(xiàn)1、3、5、7燈亮
#include<reg52.h>
void main()
{
P1=0xaa;
}
這個程序同樣可能實現(xiàn)把1、3、5、7燈點亮,也就是說程序的功能和上面位操作法寫的程序是一樣,但卻要簡單得多,這種方法就稱為總線操作法。所謂的總線操作法也就是同時操作單片機P1口的8個引腳(或者說8個I/O口),具體到程序就是通過“P1=0xaa;”實現(xiàn)的, “0x”表示16進制的意思,16進制aa轉(zhuǎn)化成2進制就是10101010,而10101010正好就是讓單片機P1口的1、3、5、7引腳輸出低電平,2、4、6、8引腳輸出高電平,此時就實現(xiàn)讓第1、3、5、7發(fā)光二極管發(fā)光。
把例3的程序下載到實驗板上的現(xiàn)象如圖13所示:
圖13 總線操作法實現(xiàn)1、3、5、7發(fā)光二極管發(fā)光
在上面三個例子中,我們用兩種方法(位操作法和總線操作法)實現(xiàn)點亮發(fā)光二極管的目的,目的是達到了,但如若仔細推敲兩個程序,實際上是不完善的的。讓我們看例三,程序第一句為頭文件,是對52單片機一些特殊功能寄存器相關的聲明,然后第二句進入主程序執(zhí)行“P1=0xaa;”,點亮1、3、5、7發(fā)光二極管,但執(zhí)行完這句后單片機又試干嘛呢?這之后我們沒有寫相關程序讓單片機執(zhí)行什么事情,所以程序執(zhí)行到這里有可能出錯(這里只是說有可能,因為每個編譯器不一樣),通過反復的實驗,我們得出keil軟件一般這里會回到主程序的第一句開始執(zhí)行,也就是說又開始執(zhí)行“P1=0xaa;”,周而復始,一直這樣的執(zhí)行下去,1、3、5、7發(fā)光二極管就一直會亮下去。但從程序開發(fā)角度來說,我們說這里就必須做相應的處理,明確告訴下一步程序要做什么事情。比如說我們就是要讓主程序周而復始,反復的執(zhí)行,此時我們就可以利用C語言中的while語句來實現(xiàn),下面先講下while語句。
格式:
While (表達式)
{
語句(內(nèi)部也可為空)
}
特點:
{
語句(內(nèi)部也可為空)
}
特點:
先判斷表達式,后執(zhí)行語句。
原則:若表達式為不是0,即為真,則執(zhí)行語句,如為0 ,即為假就跳出while 語句。
注意:while語句內(nèi)部的語句可以為空,即什么也沒有,此時就寫成while(1){};此時{}可以不寫,即寫成while(1);意思就是永遠為真,執(zhí)行空語句,相當于程序永遠停止在這里。
了解了while語句后,我們就可以把例三的程序進完善了,程序如例四。
例4:完整點亮發(fā)光二極管的程序
#include<reg52.h>
void main()
{
While(1)
{
P1=0xaa;
}
}
While語句就介紹到這,下面總結(jié)一下,單片機軟件的學習就是通過我們寫程序,控制單片機外部引腳輸出“1”(高電平+5V),和“0”(低電平0V),從而達到對各種外設的控制。單片機學習來并不難,只要大家有信心,肯花功夫,學起來也是很容易的。