標(biāo)題: 第六章 中斷的學(xué)習(xí) [打印本頁(yè)]
作者: admin 時(shí)間: 2013-7-11 19:25
標(biāo)題: 第六章 中斷的學(xué)習(xí)
中斷是單片機(jī)系統(tǒng)的重點(diǎn)中的重點(diǎn),因?yàn)橛辛酥袛�,單片機(jī)就具備了快速協(xié)調(diào)多模塊工作的能力,大家對(duì)本章節(jié)內(nèi)容要多研究,最終要完全理解并且掌握。
6.1 C語(yǔ)言的數(shù)組6.1.1 數(shù)組的基本概念我們第四章學(xué)過(guò)變量的基本類型,比如char、int等等。這種類型描述的數(shù)據(jù)是比較有限的,當(dāng)我們要處理非常大量數(shù)據(jù)的時(shí)候,就可以用到數(shù)組了,比如我們上節(jié)課的那個(gè)數(shù)碼管的真值表,我們就可以用一個(gè)數(shù)組來(lái)表達(dá)。
從概念上講,數(shù)組是具有相同數(shù)據(jù)類型的有序數(shù)據(jù)的組合,一般來(lái)講,數(shù)組定義后滿足以下三個(gè)條件。
(1)具有相同的數(shù)據(jù)類型;
(2)具有相同的名字;
(3)在存儲(chǔ)器中是被連續(xù)存放的。
比如我們上節(jié)課定義的那個(gè)數(shù)碼管真值表,如果我們把關(guān)鍵字code去掉,數(shù)組元素將被保存在RAM中,在程序中可讀可寫,同時(shí)我們也可以在中括號(hào)里邊標(biāo)明這個(gè)數(shù)組元素的個(gè)數(shù),比如:
unsigned char LedChar[16] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e};
在這個(gè)數(shù)組中的每個(gè)值都稱之為數(shù)組的一個(gè)元素,這些元素都具備相同的數(shù)據(jù)類型就是unsigned char型,他們有一個(gè)共同的名字LedChar,不管放到RAM中還是FLASH中,他們都是存放在一塊連續(xù)的存儲(chǔ)空間里的。
有一點(diǎn)要特別注意,這個(gè)數(shù)組一共有16(中括號(hào)里面的數(shù)值)個(gè)元素,但是數(shù)組的元素的表達(dá)方式下標(biāo)是從0開始,因此實(shí)際上上邊這個(gè)數(shù)組的首個(gè)元素LedChar[0]的值是0xC0,而LedChar[15]的值是0x8e,下標(biāo)從0到15一共是16個(gè)元素。
LedChar這個(gè)數(shù)組只有一個(gè)下標(biāo),我們稱之為一維數(shù)組,還有兩個(gè)下標(biāo)或者多個(gè)下標(biāo)的,我們稱之為多維數(shù)組。比如unsigned char a[2][3];表示這是一個(gè)2行3列的二維數(shù)組。在大多數(shù)情況下我們使用的是一維數(shù)組,對(duì)于初學(xué)來(lái)說(shuō),我們先來(lái)研究一維數(shù)組,多維數(shù)組遇到了再了解。
6.1.2 數(shù)組的聲明一維數(shù)組的聲明格式如下:
數(shù)據(jù)類型 數(shù)組名[數(shù)組長(zhǎng)度;
(1)數(shù)組的數(shù)據(jù)類型聲明的是該數(shù)組的每個(gè)元素的類型,即一個(gè)數(shù)組中的元素具有相同的數(shù)據(jù)類型。
(2)數(shù)組名的聲明要符合C語(yǔ)言固定的標(biāo)識(shí)符的聲明要求,只能由字母、數(shù)字、下劃線這三種符號(hào)組成,且第一個(gè)字符只能是字母或者下劃線。
(3)方括號(hào)中的數(shù)組長(zhǎng)度是一個(gè)常量或常量表達(dá)式,并且必須是正整數(shù)。
6.1.3 數(shù)組的初始化數(shù)組在進(jìn)行聲明的同時(shí)可以進(jìn)行初始化操作,格式如下:
數(shù)據(jù)類型 數(shù)組名[數(shù)組長(zhǎng)度] = {初值列表};
還是以上節(jié)課我們用的數(shù)碼管的真值表為例來(lái)講解注意事項(xiàng)。
unsigned char LedChar[16] = {
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e};
(1)初值列表里的數(shù)據(jù)之間要用逗號(hào)隔開。
(2)初值列表里的初值的數(shù)量必須小于或者等于數(shù)組長(zhǎng)度,當(dāng)小于數(shù)組長(zhǎng)度時(shí),數(shù)組的后邊沒(méi)有賦初值的元素由系統(tǒng)自動(dòng)賦值0。
(3)若給數(shù)組的所有元素賦初值,可以省略數(shù)組的長(zhǎng)度,上節(jié)課的例子中我們實(shí)際上已經(jīng)省略了數(shù)組的長(zhǎng)度。
(4)系統(tǒng)為數(shù)組分配連續(xù)的存儲(chǔ)單元的時(shí)候,數(shù)組元素的相對(duì)次序由下標(biāo)來(lái)決定,就是說(shuō)LedChar[0]、LedChar[1]... ... LedChar[15]是按照順序排下來(lái)的。
6.1.4 數(shù)組的使用和賦值在C語(yǔ)言程序中,是不能一次使用整個(gè)數(shù)組的,只能使用單個(gè)數(shù)組元素。一個(gè)數(shù)組元素相當(dāng)于一個(gè)變量,使用數(shù)組元素的時(shí)候與使用相同數(shù)據(jù)類型的變量的方法一樣。比如這個(gè)LedChar這個(gè)數(shù)組,如果沒(méi)加code關(guān)鍵字,那么它可讀可寫,我們可以寫成a = LedChar[0]這樣來(lái)把數(shù)組的一個(gè)元素的值送個(gè)a這個(gè)變量,也可以寫成LedChar[0] = a這樣把a這個(gè)變量的值送給數(shù)組的一個(gè)元素,以下三點(diǎn)要注意:
(1)引用數(shù)組的時(shí)候,那個(gè)方括號(hào)里的數(shù)字代表的是數(shù)組元素的下標(biāo),而數(shù)組初始化的時(shí)候方括號(hào)里的數(shù)字代表的是這個(gè)數(shù)組元素的個(gè)數(shù)。
(2)數(shù)組元素的方括號(hào)里的下標(biāo)可以是整型常數(shù),整型變量或者表達(dá)式,而數(shù)組初始化的時(shí)候方括號(hào)里的數(shù)字必須是常數(shù)不能是變量。
(3)數(shù)組整體賦值只可以在初始化的時(shí)候操作,功能程序只能對(duì)單個(gè)元素賦值。
6.2 if語(yǔ)句if語(yǔ)句已經(jīng)不陌生了,前邊程序我們其實(shí)已經(jīng)用過(guò)了,這里我們系統(tǒng)的介紹一下,方便后邊的深入學(xué)習(xí)。if語(yǔ)句有兩個(gè)關(guān)鍵字:if和else,把這兩個(gè)關(guān)鍵字翻譯一下就是:“如果”和“否則”。if語(yǔ)句一共有三種格式,我們分別來(lái)看。
1.if語(yǔ)句的默認(rèn)形式。
if (條件表達(dá)式)
{語(yǔ)句 1;}
其執(zhí)行過(guò)程是,if(如果)條件表達(dá)式的值為“真”,則執(zhí)行語(yǔ)句1;如果條件表達(dá)式的值為“假”,則不執(zhí)行語(yǔ)句1。真和假的概念不再贅述,參考第五章。
這里要提醒一句,C語(yǔ)言一個(gè)分號(hào)表示一句語(yǔ)句的結(jié)束,因此如果if后邊只有一條執(zhí)行語(yǔ)句的時(shí)候,可以省略大括號(hào),但是如果有多條執(zhí)行語(yǔ)句的話,必須加上大括號(hào)。
我們上節(jié)課的語(yǔ)句就很好理解了if(16 ==j) { j = 0;},如果j等于16的時(shí)候,括號(hào)里的值才是“真”,那么就執(zhí)行j=0這一句,如果j不等于16,那么里邊就為“假”,就不執(zhí)行這一句。
2.if...else語(yǔ)句
有些情況下,我們除了判斷一下if括號(hào)里的是否滿足條件,執(zhí)行相應(yīng)的語(yǔ)句,在不滿足條件的時(shí)候,我們又要執(zhí)行另外相應(yīng)的語(yǔ)句,這個(gè)時(shí)候就用到了if...else語(yǔ)句,它的基本的語(yǔ)法形式是:
if (條件表達(dá)式)
{語(yǔ)句 1;}
else
{語(yǔ)句 2;}
比如上節(jié)課的后半段程序我們也可以寫成:
P0 = LedChar[j]; //把數(shù)組里的對(duì)應(yīng)值送給P0
if(15 == j) //當(dāng)顯示到F后,歸0重新開始
{j = 0;}
else
{j++;}
這個(gè)程序大家可以改改下載進(jìn)去試試,程序邏輯大家自己動(dòng)腦分析一下,我就不解釋了。
3.if....else if語(yǔ)句
if...esle語(yǔ)句是一個(gè)二選一的語(yǔ)句,或者執(zhí)行if條件下的語(yǔ)句,或者執(zhí)行else條件下的語(yǔ)句。還有一種多選一的用法就是if...else if語(yǔ)句。他的基本語(yǔ)法格式是:
if (條件表達(dá)式1) {語(yǔ)句 1;}
else if (條件表達(dá)式2) {語(yǔ)句 2; }
else if (條件表達(dá)式3) {語(yǔ)句 3; }
... ...
else {語(yǔ)句 n;}
他的執(zhí)行過(guò)程是:依次判斷條件表達(dá)式的值,當(dāng)出現(xiàn)某個(gè)值為“真”時(shí),則執(zhí)行相對(duì)應(yīng)的語(yǔ)句,然后跳出整個(gè)if的語(yǔ)句塊,執(zhí)行“語(yǔ)句n”后邊的程序;如果所有的表達(dá)式都為“假”,則執(zhí)行“語(yǔ)句n”后,再執(zhí)行“語(yǔ)句n”后邊的程序。
if語(yǔ)句在C語(yǔ)言編程的過(guò)程中使用頻率很高,用法也簡(jiǎn)單,所以必須要熟練掌握。
6.3 switch語(yǔ)句用if....else語(yǔ)句在處理多分支的時(shí)候,分支太多就會(huì)顯得不方便,且容易出現(xiàn)if和else配對(duì)出現(xiàn)錯(cuò)誤的情況,在C語(yǔ)言中提供了另外一種多分支選擇的語(yǔ)句——switch語(yǔ)句,它的基本語(yǔ)法格式如下:
switch (表達(dá)式)
{
case 常量表達(dá)式1:執(zhí)行語(yǔ)句1;
case 常量表達(dá)式2:執(zhí)行語(yǔ)句2;
......
case 常量表達(dá)式n:執(zhí)行語(yǔ)句n;
default: 執(zhí)行語(yǔ)句n+1;
}
它的執(zhí)行過(guò)程是:首先計(jì)算“表達(dá)式”的值,然后從第一個(gè)case開始,與“常量表達(dá)式x”進(jìn)行比較,如果與當(dāng)前常量表達(dá)式的值不相等,那么就不執(zhí)行冒號(hào)后邊的程序,一旦發(fā)現(xiàn)和一個(gè)常量表達(dá)式的值相等了,那么他會(huì)執(zhí)行之后所有的,注意是所有的“執(zhí)行語(yǔ)句”,顯然這不是我們想要的結(jié)果。
在C語(yǔ)言中,有一條break語(yǔ)句,作用是跳出當(dāng)前循環(huán)語(yǔ)句,不管是for和while循環(huán),還是switch循環(huán),都可以用其搭配使用跳出循環(huán)。switch語(yǔ)句一共有n+1種可能,而我們希望要的是一條多選一的語(yǔ)句,只執(zhí)行其中一條然后直接退出該循環(huán),不再執(zhí)行下邊的任何語(yǔ)句,這個(gè)時(shí)候就需要用到break語(yǔ)句,比如我們?cè)?/font>switch表達(dá)式上加上break語(yǔ)句,如下:
switch (表達(dá)式)
{
case 常量表達(dá)式1:執(zhí)行語(yǔ)句1;break;
case 常量表達(dá)式2:執(zhí)行語(yǔ)句2;break;
......
case 常量表達(dá)式n:執(zhí)行語(yǔ)句n;break;
default:語(yǔ)句n+1;
}
加了這個(gè)break語(yǔ)句后,一旦“常量表達(dá)式x”與“表達(dá)式”相等了,那就執(zhí)行“執(zhí)行語(yǔ)句x”,執(zhí)行完畢后,由于有了break,直接跳出switch語(yǔ)句,執(zhí)行switch語(yǔ)句循環(huán)后邊的程序了,這樣就可以避免執(zhí)行不必要的語(yǔ)句。了解了這個(gè)switch語(yǔ)句,我們將會(huì)在本章程序中使用鞏固。
6.4 數(shù)碼管的動(dòng)態(tài)顯示6.4.1 動(dòng)態(tài)顯示的基本原理我們?cè)谏弦徽聦W(xué)習(xí)數(shù)碼管靜態(tài)顯示的時(shí)候說(shuō)到,74HC138只能在同一時(shí)刻導(dǎo)通一個(gè)三極管,而我們的數(shù)碼管是靠了6個(gè)三極管來(lái)控制,那我們?nèi)绾蝸?lái)讓數(shù)碼管同時(shí)顯示呢?這就用到了我們這節(jié)課的動(dòng)態(tài)顯示。
多個(gè)數(shù)碼管顯示數(shù)字的時(shí)候,我們實(shí)際上是輪流點(diǎn)亮數(shù)碼管(一個(gè)時(shí)刻內(nèi)只有一個(gè)數(shù)碼管是亮的),利用人眼的視覺暫留現(xiàn)象(也叫余輝效應(yīng)),就可以做到看起來(lái)是所有數(shù)碼管都同時(shí)亮了,這就是動(dòng)態(tài)掃描顯示的含義。
例如:我們有2個(gè)數(shù)碼管,我們要顯示“12”這個(gè)數(shù)字,讓高位的位選三極管導(dǎo)通,然后給它賦值“1”,延時(shí)一定時(shí)間后讓低位的位選三極管導(dǎo)通,然后給它賦值“2”。把這個(gè)流程以一定的速度循環(huán)運(yùn)行就可以讓數(shù)碼管顯示出“12”,由于交替速度非�?欤巳庋圩R(shí)別到的就是“12”這個(gè)數(shù)字。
那么一個(gè)數(shù)碼管需要點(diǎn)亮多長(zhǎng)時(shí)間呢?也就是說(shuō)要多長(zhǎng)時(shí)間完成一次全部數(shù)碼管的掃描呢(很明顯:整體掃描時(shí)間=單個(gè)數(shù)碼管點(diǎn)亮?xí)r間*數(shù)碼管個(gè)數(shù))?答案是:10ms以內(nèi)。當(dāng)電視機(jī)和顯示器還處在CRT(電子顯像管)時(shí)代時(shí),有一句很流行的廣告語(yǔ)——“100Hz無(wú)閃爍”,沒(méi)錯(cuò),只要刷新率大于100Hz,即刷新時(shí)間小于10ms,就可以做到無(wú)閃爍,這也就是我們的動(dòng)態(tài)掃描的硬性指標(biāo)。那么你也許會(huì)問(wèn),有最小值的限制嗎?理論上沒(méi)有,但實(shí)際上做到更快的刷新卻沒(méi)有任何進(jìn)步的意義了,因?yàn)橐呀?jīng)無(wú)閃爍了,再快也還是無(wú)閃爍,只是徒然增加CPU的負(fù)荷而已(因?yàn)?/font>1秒內(nèi)要執(zhí)行更多次的掃描程序)。所以,通常我們?cè)O(shè)計(jì)程序的時(shí)候,都是取一個(gè)接近10ms,又比較規(guī)整的值就行了。我們板子上有6個(gè)數(shù)碼管,我們下面用程序來(lái)驗(yàn)證一下數(shù)碼管動(dòng)態(tài)顯示程序。
#include <reg52.h> //包含寄存器的庫(kù)文件
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //用數(shù)組來(lái)表示數(shù)碼管真值表
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,
};
void main()
{
unsigned int counter = 0;
unsigned char j = 0;
unsigned long stopwatch = 0;
unsigned char LedNumber[6] = {0};
ENLED = 0; ADDR3 = 1;P0 = 0XFF; //74HC138和P0初始化部分
TMOD = 0x01; //設(shè)置定時(shí)器0為模式1
TH0 = 0xFC;
TL0 = 0x67; //定時(shí)值初值,定時(shí)1ms
TR0 = 1; //打開定時(shí)器0
while(1)
{
if(1 == TF0) //判斷定時(shí)器0是否溢出
{
TF0 = 0;
TH0 = 0xFC; //一旦溢出后,重新賦值
TL0 = 0x67;
counter++;
if(1000 == counter) //判斷定時(shí)器0溢出是否達(dá)到50次
{
counter = 0;
stopwatch++; //秒表數(shù)值一秒加1
LedNumber[0] = stopwatch%10;
LedNumber[1] = stopwatch/10%10;
LedNumber[2] = stopwatch/100%10;
LedNumber[3] = stopwatch/1000%10; //數(shù)碼管顯示值計(jì)算
LedNumber[4] = stopwatch/10000%10;
LedNumber[5] = stopwatch/100000%10;
}
if (0==j)
{ ADDR0=0; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[0]]; }
else if (1==j)
{ ADDR0=1; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[1]]; }
else if (2==j)
{ ADDR0=0; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[2]]; }
else if (3==j)
{ ADDR0=1; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[3]]; }
else if (4==j)
{ ADDR0=0; ADDR1=0; ADDR2=1; j++; P0=LedChar[LedNumber[4]]; }
else if (5==j)
{ ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedChar[LedNumber[5]]; }
} //數(shù)碼管動(dòng)態(tài)刷新部分
}
}
這程序,大家自己抄到Keil中,然后邊抄邊理解,最終下載到實(shí)驗(yàn)板上實(shí)驗(yàn)一下效果。其中下邊的if...else語(yǔ)句就是每1ms快速的刷新一個(gè)數(shù)碼管,這樣6個(gè)數(shù)碼管整體刷新一遍的時(shí)間就是6ms,視覺上就是6個(gè)數(shù)碼管無(wú)閃爍的同時(shí)亮起來(lái)了。
另外一個(gè)簡(jiǎn)單知識(shí)點(diǎn)這個(gè)地方也提一下,其實(shí)屬于小學(xué)三年級(jí)知識(shí),但是很多同學(xué)剛接觸C語(yǔ)言,可能遇到了也會(huì)發(fā)懵。就是在數(shù)碼管顯示值計(jì)算這個(gè)地方,相信小學(xué)我們沒(méi)學(xué)小數(shù)之前,除法運(yùn)算里邊有“被除數(shù)”、“除數(shù)”、“商”、“余數(shù)”這四個(gè)概念年。而在我們C語(yǔ)言中,“/”等同于數(shù)學(xué)里的除法運(yùn)算,而“%”等同于我們小學(xué)學(xué)的求余數(shù)運(yùn)算。如果是123456這個(gè)數(shù)字,我們要正常顯示在數(shù)碼管上,個(gè)位顯示,就是直接對(duì)10取余數(shù),這個(gè)“6”就出來(lái)了,十位數(shù)字就是先除以10,然后再對(duì)10取余數(shù),以此類推,就把6個(gè)數(shù)字全部顯示出來(lái)了。
對(duì)于多選一的動(dòng)態(tài)刷新數(shù)碼管的方式,我們?nèi)绻?font face="Times New Roman">switch會(huì)有更好的效果,大家來(lái)看一下我們用switch語(yǔ)句完成的情況。
#include <reg52.h> //包含寄存器的庫(kù)文件
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //用數(shù)組來(lái)表示數(shù)碼管真值表
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,
};
void main()
{
unsigned int counter = 0;
unsigned char j = 0;
unsigned long stopwatch =0;
unsigned char LedNumber[6]={0};
ENLED = 0; ADDR3 = 1;P0 = 0XFF; //74HC138和P0初始化部分
TMOD = 0x01; //設(shè)置定時(shí)器0為模式1
TH0 = 0xFC;
TL0 = 0x67; //定時(shí)值初值,定時(shí)1ms
TR0 = 1; //打開定時(shí)器0
while(1)
{
if(1 == TF0) //判斷定時(shí)器0是否溢出
{
TF0 = 0;
TH0 = 0xFC; //一旦溢出后,重新賦值
TL0 = 0x67;
counter++;
if(1000 == counter) //判斷定時(shí)器0溢出是否達(dá)到1000次
{
counter = 0;
stopwatch++; //秒表數(shù)值一秒加1
LedNumber[0] = stopwatch%10;
LedNumber[1] = stopwatch/10%10;
LedNumber[2] = stopwatch/100%10;
LedNumber[3] = stopwatch/1000%10; //數(shù)碼管顯示值計(jì)算
LedNumber[4] = stopwatch/10000%10;
LedNumber[5] = stopwatch/100000%10;
}
switch(j)
{
case 0: ADDR0=0; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[0]];break;
case 1: ADDR0=1; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[1]];break;
case 2: ADDR0=0; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[2]];break;
case 3: ADDR0=1; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[3]];break;
case 4: ADDR0=0; ADDR1=0; ADDR2=1; j++; P0=LedChar[LedNumber[4]];break;
case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedChar[LedNumber[5]];break;
default: break;
}
} //數(shù)碼管動(dòng)態(tài)刷新部分
}
}
大家是否能感覺到switch語(yǔ)句比if...else語(yǔ)句顯得要整齊的多?
6.4.2 數(shù)碼管消隱處理不知道細(xì)心的同學(xué)能否發(fā)現(xiàn),我們的兩次數(shù)碼管動(dòng)態(tài)刷新顯示的時(shí)候似乎并不是那么完美,第一個(gè)小問(wèn)題,大家仔細(xì)看,數(shù)碼管的不應(yīng)該顯示的段,似乎有微微的發(fā)亮,這種現(xiàn)象叫做“鬼影”,這個(gè)“鬼影”嚴(yán)重影響了我們的視覺效果,我們?cè)撊绾谓鉀Q呢?
同學(xué)們今后可能會(huì)遇到各種各樣的問(wèn)題,可能有很多我是沒(méi)有講過(guò)的問(wèn)題,遇到問(wèn)題怎么辦呢?大家要相信,你作為初學(xué)者,遇到的問(wèn)題肯定不是第一個(gè)遇到的,肯定有前輩會(huì)遇到同類問(wèn)題,他們一般會(huì)在網(wǎng)上發(fā)表各種帖子,各種討論,所以大家遇到問(wèn)題,首先解決方法就應(yīng)該形成一個(gè)到網(wǎng)上搜索的條件反射,這個(gè)問(wèn)題大家可以到網(wǎng)上搜:“數(shù)碼管消隱”或者“數(shù)碼管鬼影解決”,多找相關(guān)關(guān)鍵詞搜索,會(huì)搜索也是一種能力。
大家在網(wǎng)上搜了一下會(huì)發(fā)現(xiàn),解決這類問(wèn)題的普遍兩個(gè)方法,其中之一是延時(shí),延時(shí)之后我們?nèi)庋劬涂赡芸床坏竭@個(gè)“鬼影”了。但是延時(shí)是一個(gè)非常拙劣的手段,且不說(shuō)延時(shí)多久能讓我們看不到“鬼影”,延時(shí)后,我們的數(shù)碼管亮度會(huì)普遍降低。我們解決問(wèn)題呢,不能只知其然,不知其所以然,所以我們首先要弄懂為什么會(huì)出現(xiàn)“鬼影”。
“鬼影”的出現(xiàn),主要是因?yàn)槲覀償?shù)碼管位選和段選產(chǎn)生的瞬態(tài)所造成的。舉個(gè)簡(jiǎn)單例子,我們?cè)跀?shù)碼管動(dòng)態(tài)刷新的那部分程序中,實(shí)際上每一個(gè)數(shù)碼管點(diǎn)亮的持續(xù)時(shí)間是1ms的時(shí)間,1ms后進(jìn)行下個(gè)數(shù)碼管的切換。在進(jìn)行數(shù)碼管切換的時(shí)候,比如我們從case 5要切換到case 0的時(shí)候,case 5的位選用的是ADDR0=1; ADDR1=0; ADDR2=1;假如此刻case5也就是最高位數(shù)碼管對(duì)應(yīng)的值是0。我們要切換成的case 0的數(shù)碼管位選是ADDR0=0; ADDR1=0; ADDR2=0;而對(duì)應(yīng)的數(shù)碼管的值假如是1。
因?yàn)槲覀兊?font face="Consolas">C語(yǔ)言程序是一句一句順序往下執(zhí)行的,每一條語(yǔ)句都會(huì)占用一定的時(shí)間,即使這個(gè)時(shí)間非常非常短暫。但是當(dāng)我們把“ADDR0=1”改變成“ADDR0=0”的時(shí)候,這個(gè)瞬間存在了一個(gè)中間狀態(tài)ADDR0=0; ADDR1=0; ADDR2=1;在這個(gè)瞬間上,我們就給case 4對(duì)應(yīng)的數(shù)碼管DS5瞬間賦值了0。當(dāng)我們?nèi)繉懲炅?/font>ADDR0=0; ADDR1=0; ADDR2=0;后,這個(gè)時(shí)候,我們的P0還沒(méi)有正式賦值,而P0此刻卻保持了前一次的值,也就是在這個(gè)瞬間,我們又給case 0對(duì)應(yīng)的數(shù)碼管DS1賦值了一個(gè)0。直到我們把case 0后邊的語(yǔ)句全部完成后,我們的刷新才正式完成。而在這個(gè)刷新過(guò)程中,有2次瞬間我們給了錯(cuò)誤的數(shù)碼管賦值,雖然很弱(因?yàn)榱恋臅r(shí)間很短),但是我們還是能夠發(fā)現(xiàn)。
那弄懂了原理后,解決起來(lái)就不是困難的事情了,我們只要避開這個(gè)瞬態(tài)就可以了。不產(chǎn)生瞬態(tài)的方法是,我們?cè)谶M(jìn)行刷新的賦值語(yǔ)句期間,避免一切數(shù)碼管的賦值即可。方法有兩個(gè),一個(gè)方法是刷新之前關(guān)閉所有的段,改變好了位選后,再打開段即可;第二個(gè)方法是關(guān)閉數(shù)碼管的位,賦值過(guò)程都做好后,再重新打開即可。這個(gè)不是很難,答案我都公布一下。
關(guān)閉段:在switch(j)這句程序之前,加一句P0=0XFF;這樣就把數(shù)碼管所有的段都關(guān)閉了,當(dāng)把“ADDR”的值全部搞定后,再給P0賦對(duì)應(yīng)的值即可。
關(guān)閉位:在switch(j)這句程序之前,加上一句ENLED=1;等到把“ADDR=0; ADDR1=0; ADDR2=0; P0=LedChar[LedNumber[0]];這幾條刷新程序全部寫完后,再加上一句ENLED=0;然后再進(jìn)行break操作即可。
這個(gè)地方稍微有點(diǎn)邏輯思路在里邊,大家一定要理解深刻,深刻理解,徹底弄明白,把這個(gè)瞬態(tài)弄明白,后邊很多牽扯到此類情況的問(wèn)題,我們都可以一并搞定。
上邊的數(shù)碼管程序還有第二個(gè)問(wèn)題,大家仔細(xì)看,我們的數(shù)碼管上的數(shù)字每一秒變化一次,變化的時(shí)候,不參加變化的數(shù)碼管可能出現(xiàn)一次抖動(dòng),這個(gè)抖動(dòng)沒(méi)有什么專業(yè)的名字,我們就稱之為數(shù)碼管抖動(dòng)吧。這種數(shù)碼管抖動(dòng)是什么原因造成的呢?為何在數(shù)據(jù)改變的時(shí)候才抖動(dòng)呢?
我們來(lái)看我們的程序。我們的程序在定時(shí)到1秒的時(shí)候,執(zhí)行了“數(shù)碼管顯示值計(jì)算”這個(gè)過(guò)程,一個(gè)32位的除法運(yùn)算,實(shí)際上是比較耗費(fèi)時(shí)間的,至于這一段程序占用了多少時(shí)間,大家可以通過(guò)第四章講的Debug進(jìn)入看看這段程序運(yùn)行一共占據(jù)了多少時(shí)間。由于達(dá)到1秒的時(shí)候,程序多運(yùn)行了這么一段,導(dǎo)致了某個(gè)數(shù)碼管的點(diǎn)亮?xí)r間比其他情況下要長(zhǎng)一些,時(shí)間是1ms+程序消耗時(shí)間,于此同時(shí),其它的數(shù)碼管就熄滅了5ms+程序消耗時(shí)間,如果這個(gè)程序消耗時(shí)間非常短,那么可以忽略不計(jì),但很明顯,現(xiàn)在這段程序已經(jīng)比較長(zhǎng)了,嚴(yán)重影響我們的視覺效果了,所以我們要采取另外一種思路去解決這個(gè)問(wèn)題。
6.5 中斷的學(xué)習(xí)6.5.1 中斷的產(chǎn)生背景比如此刻我正在廚房用煤氣燒一壺水,燒開一壺水剛好需要10分鐘。我是一個(gè)主體,燒水是一個(gè)目的,而且我只能時(shí)時(shí)刻刻在這里燒水,因?yàn)橐坏┧_了,溢出來(lái)澆滅煤氣的話,有可能引發(fā)一場(chǎng)災(zāi)難。而這個(gè)時(shí)候呢,我聽到了電視里傳來(lái)《天龍八部》的主題歌,馬上就要開演了,我真想奪門而出,去看我最喜歡的電視劇。然而,聽到這個(gè)水壺發(fā)出的“咕嘟”的聲音,我清楚:除非水開了,否則我是無(wú)法享受我喜歡的電視劇的。
這里邊主體只有我一個(gè),而我要做的有兩件事情,一個(gè)是看電視,一個(gè)是燒水,而電視和燒水是兩個(gè)獨(dú)立的客體,他們是同時(shí)進(jìn)行的。其中燒水需要10分鐘,但不需要了解燒水的過(guò)程的,只需要得到水燒開的這樣一個(gè)結(jié)果就行了,提下水壺和關(guān)閉煤氣只需要幾秒的時(shí)間而已。所以我們采取的辦法就是:燒水的時(shí)候,定上一個(gè)鬧鐘,定時(shí)10分鐘,然后我就可以安心看電視了。當(dāng)10分鐘時(shí)間到了,鬧鐘響了,此刻水也燒開了,我就過(guò)去把煤氣滅掉,然后繼續(xù)回來(lái)看電視就可以了。
這個(gè)場(chǎng)景和單片機(jī)有什么關(guān)系呢?
在單片機(jī)的程序處理過(guò)程中也有很多類似的場(chǎng)景,當(dāng)單片機(jī)正在專心致志的做一件事情的時(shí)候(
如看電視),總會(huì)有一件或者多件緊迫或者不緊迫的事情發(fā)生,需要我們?nèi)リP(guān)注,有一些需要我們停下手頭的工作去馬上完成(比如水開了),只有處理完,才能回頭繼續(xù)完成剛才的工作(看電視)。如果在這個(gè)地方用上了單片機(jī)的中斷機(jī)制,不僅僅我擁有了處理意外情況的能力,而且如果我能夠充分發(fā)揮這個(gè)機(jī)制的妙用,就可以“同時(shí)”完成多個(gè)任務(wù)了。如果還是一知半解關(guān)于中斷更詳細(xì)的介紹可以看這里: http://www.torrancerestoration.com/mcuteach/234.html 6.5.2 定時(shí)器中斷應(yīng)用方法在第五章我們學(xué)過(guò)定時(shí)器,而實(shí)際上定時(shí)器一般用法都是采取中斷方式來(lái)做的,我是故意在第五章用查詢法,就是使用if(TR0 ==0)這樣的語(yǔ)句先講定時(shí)器,目的是明確告訴同學(xué)們,定時(shí)器和中斷不是一回事,定時(shí)器是單片機(jī)模塊的一個(gè)資源,確確實(shí)實(shí)存在的一個(gè)模塊,而中斷,是單片機(jī)的一種運(yùn)行機(jī)制。尤其是初學(xué)者們,很多人會(huì)誤以為定時(shí)器和中斷是一個(gè)東西,只有定時(shí)器才會(huì)觸發(fā)中斷,但實(shí)際上很多事件都會(huì)觸發(fā)中斷的,除了“燒水”,還有“有人按門鈴”,“來(lái)電話了”等等。
標(biāo)準(zhǔn)51中與中斷相關(guān)的寄存器,一共有2個(gè),其中1個(gè)是中斷使能寄存器,另外1個(gè)是中斷優(yōu)先級(jí)寄存器,這里先介紹中斷使能寄存器。隨著一些增強(qiáng)型51單片機(jī)的問(wèn)世,可能會(huì)有增加的寄存器,大家這些理解了這里所講的,其他的通過(guò)自己研讀數(shù)據(jù)手冊(cè)全部可以理解明白并且使用起來(lái)。
表6-1 IE--中斷使能寄存器(地址:A8H)
可位尋址;復(fù)位值:0x00;復(fù)位源:任何復(fù)位
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
符號(hào) | EA | -- | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
表6-2 IE--中斷使能寄存器的位描述
位 | 符號(hào) | 描述 |
| | 總中斷使能位,相當(dāng)于總開關(guān) |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
中斷使能寄存器IE控制了6個(gè)中斷使能,其中第6位暫時(shí)不用,第七位是總開關(guān),相當(dāng)于我們家里或者學(xué)生宿舍里的那個(gè)電源總閘門。而0到5位這6個(gè)相當(dāng)于每個(gè)分開關(guān)。那么也就是說(shuō),我們只要用到中斷,就要寫EA = 1這一句,打開中斷總開關(guān),然后用到哪個(gè)分中斷,再打開相對(duì)應(yīng)的位就可以了。
我們現(xiàn)在就把第五章學(xué)的定時(shí)器的程序進(jìn)行改寫,使用中斷實(shí)現(xiàn)出來(lái),把數(shù)碼管的抖動(dòng)問(wèn)題也同時(shí)一并處理掉。
#include <reg52.h> //包含寄存器的庫(kù)文件
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //用數(shù)組來(lái)表示數(shù)碼管真值表
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e,
};
unsigned char LedNumber[6] = {0}; //定義全局變量
unsigned char j = 0;
unsigned int counter = 0;
void main()
{
unsigned long stopwatch =0;
ENLED = 0; ADDR3 = 1; P0 = 0XFF; //74HC138和P0初始化部分
TMOD = 0x01; //設(shè)置定時(shí)器0為模式1
TH0 = 0xFC;
TL0 = 0x67; //定時(shí)值初值,定時(shí)1ms
TR0 = 1; //打開定時(shí)器0
EA = 1; //打開中中斷
ET0 = 1; //打開定時(shí)器0中斷
while(1)
{
if(1000 == counter) //判斷定時(shí)器0溢出是否達(dá)到1000次
{
counter = 0;
stopwatch++;
LedNumber[0] = stopwatch%10;
LedNumber[1] = stopwatch/10%10;
LedNumber[2] = stopwatch/100%10;
LedNumber[3] = stopwatch/1000%10;
LedNumber[4] = stopwatch/10000%10;
LedNumber[5] = stopwatch/100000%10;
}
}
}
void InterruptTimer0() interrupt 1 //中斷函數(shù)的特殊寫法,數(shù)字’1’為中斷入口號(hào)
{
TH0 = 0xFC; //溢出后進(jìn)入中斷重新賦值
TL0 = 0x67;
counter++; //計(jì)數(shù)值counter加1
P0 = 0xFF; //消隱
switch(j)
{
case 0: ADDR0=0; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[0]]; break;
case 1: ADDR0=1; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[1]]; break;
case 2: ADDR0=0; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[2]]; break;
case 3: ADDR0=1; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[3]]; break;
case 4: ADDR0=0; ADDR1=0; ADDR2=1; j++; P0=LedChar[LedNumber[4]]; break;
case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedChar[LedNumber[5]]; break;
default: break;
} //動(dòng)態(tài)刷新
}
大家可以先把這個(gè)程序了解明白,下載到單片機(jī)里邊實(shí)驗(yàn)一下,看看實(shí)際效果。是否可以看出來(lái),近乎完美的顯示效果經(jīng)過(guò)我們的努力終于做成功了。那下面我們還要來(lái)解析一下我們的這個(gè)程序。
在我們這個(gè)程序中,有兩個(gè)函數(shù),一個(gè)是主函數(shù),一個(gè)是中斷函數(shù)。主函數(shù)main()我們就不用說(shuō)了,重點(diǎn)強(qiáng)調(diào)一下中斷函數(shù),中斷函數(shù)的格式是固定的,首先中斷函數(shù)前邊void表示函數(shù)返回空,即中斷函數(shù)不返回任何值,函數(shù)名字是InterruptTimer0(),這個(gè)函數(shù)名字只要符合函數(shù)命名規(guī)則的前提下我們就可以隨便起,我這樣起名字是為了方便區(qū)分和記憶,而后是interrupt這個(gè)關(guān)鍵字不能錯(cuò),這個(gè)是中斷特有的關(guān)鍵字,另外后邊還有個(gè)數(shù)字1,這個(gè)數(shù)字1怎么來(lái)的呢?我們先來(lái)看一個(gè)表格。
表6-3 中斷查詢序列
這個(gè)表格同樣不需要大家記住,需要的時(shí)候過(guò)來(lái)查就可以了。我們現(xiàn)在看第二行T0中斷,它的中斷標(biāo)志是TF0,也就是當(dāng)TF0變成1的時(shí)候,就會(huì)觸發(fā)中斷。而在interrupt后邊的數(shù)字x的計(jì)算方法是 x*8+3=向量地址,T0的向量地址是000BH,那么我們可以求得x的值是1。這樣這個(gè)中斷函數(shù)名字我們就徹底明白了。
中斷函數(shù)和普通函數(shù)有個(gè)不一樣的地方,普通函數(shù)一般是在程序中調(diào)用,而中斷函數(shù)因?yàn)橛辛酥袛嗳肟�,達(dá)到中斷條件后,他會(huì)自動(dòng)進(jìn)入程序執(zhí)行。比如咱這個(gè)程序,平時(shí)一直在主程序while(1)的循環(huán)中運(yùn)行,假如程序有100行,當(dāng)運(yùn)行到了50行的時(shí)候,定時(shí)器溢出了,那么CPU就會(huì)立刻跑到中斷函數(shù)中執(zhí)行中斷程序,中斷程序運(yùn)行完畢后再自動(dòng)返回到剛才的第50行處繼續(xù)運(yùn)行下面的程序,這樣就保證了動(dòng)態(tài)刷新是固定的1ms時(shí)間,不會(huì)因?yàn)槌绦蜻\(yùn)行時(shí)間不一致的原因?qū)е聰?shù)碼管的抖動(dòng)了。
6.5.3 中斷的優(yōu)先級(jí)中斷優(yōu)先級(jí)的內(nèi)容,大家先通過(guò)我的介紹大概了解一下即可,后邊真正實(shí)際應(yīng)用的時(shí)候我們?cè)僭敿?xì)理解。
在講中斷產(chǎn)生背景的時(shí)候,我們僅僅講了看電視和燒水的例子,但是實(shí)際生活當(dāng)中還有更復(fù)雜的,比如我們正在看電視,這個(gè)時(shí)候來(lái)電話了,我們要進(jìn)入接電話的“中斷”程序當(dāng)中去,就在接電話的同時(shí),聽到了水開的聲音,水開的“中斷”也發(fā)生了,我們要放下手上的電話,先把煤氣關(guān)掉,然后再回來(lái)聽電話,最后聽完了電話再看電視,這里就產(chǎn)生了一個(gè)優(yōu)先級(jí)的問(wèn)題。
還有一種情況,我們?cè)诳措娨暤臅r(shí)候,這個(gè)時(shí)候聽到水開的聲音,水開的“中斷”發(fā)生了,我們要進(jìn)入關(guān)煤氣的“中斷”程序當(dāng)中,而在關(guān)煤氣的同時(shí),電話聲音響了,而這個(gè)時(shí)候,我們的處理方式是先把煤氣關(guān)閉,再去接聽電話,最后再看電視。
從這兩個(gè)過(guò)程中,我們可以得到一個(gè)結(jié)論,就是最最緊急的事情,一旦發(fā)生后,我們不管當(dāng)時(shí)處在哪個(gè)“程序”當(dāng)中,我們必須先去解決最最緊急的事情,解決完畢后再去解決其他事情。在我們的單片機(jī)程序當(dāng)中有時(shí)候也是這樣的,有一般緊急的中斷,有特別緊急的中斷,這取決于具體的系統(tǒng)設(shè)計(jì),這就牽扯到一個(gè)中斷優(yōu)先級(jí)和中斷嵌套的概念,在本章節(jié)我們先簡(jiǎn)單介紹一下相關(guān)寄存器,不做例程說(shuō)明。
中斷優(yōu)先級(jí)有兩種,一種是搶占優(yōu)先級(jí),一種是固有優(yōu)先級(jí),先介紹搶占優(yōu)先級(jí)。
表6-4 IP--中斷優(yōu)先級(jí)寄存器的位分配(地址:B8H)
可位尋址;復(fù)位值:0x00;復(fù)位源:任何復(fù)位
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
符號(hào) | -- | -- | PT2 | PS | PT1 | PX1 | PT0 | PX0 |
表6-5 IP--中斷優(yōu)先級(jí)寄存器的位描述(地址:B8H)
位 | 符號(hào) | 描述 |
7 | -- | 保留 |
6 | -- | 保留 |
5 | PT2 | 定時(shí)器2中斷優(yōu)先級(jí)控制位 |
4 | PS | 串口中斷優(yōu)先級(jí)控制位 |
3 | PT1 | 定時(shí)器1中斷優(yōu)先級(jí)控制位 |
2 | PX1 | 外部中斷1中斷優(yōu)先級(jí)控制位 |
1 | PT0 | 定時(shí)器0中斷優(yōu)先級(jí)控制位 |
0 | PX0 | 外部中斷0中斷優(yōu)先級(jí)控制位 |
這個(gè)寄存器的每一位,表示對(duì)應(yīng)的中斷功能的優(yōu)先級(jí),每一位的復(fù)位值都是0,當(dāng)我們把某一位設(shè)置為1的時(shí)候,這一位的優(yōu)先級(jí)就比其他位的優(yōu)先級(jí)高。比如我們?cè)O(shè)置了PT0位為1后,當(dāng)程序運(yùn)行在主循環(huán)里邊,或者任何其他中斷程序內(nèi)部的時(shí)候,一旦定時(shí)器0發(fā)生中斷,作為更高級(jí)的優(yōu)先級(jí),程序馬上就會(huì)跑到定時(shí)器0的中斷程序中運(yùn)行。同理,當(dāng)程序此刻運(yùn)行在定時(shí)器0中斷中時(shí),其他低級(jí)的中斷發(fā)生后,程序還是會(huì)繼續(xù)運(yùn)行定時(shí)器0中斷程序,直到把定時(shí)器0中的中斷程序運(yùn)行完成后,再會(huì)去相應(yīng)其他中斷程序。
我們?cè)趯I(yè)的術(shù)語(yǔ)中,當(dāng)進(jìn)入低級(jí)中斷以后,發(fā)生高級(jí)中斷,我們先進(jìn)入高級(jí)中斷運(yùn)行,處理完了高級(jí)中斷后,返回處理低級(jí)中斷,低級(jí)中斷處理完了再返回主函數(shù),這種叫做中斷嵌套。在搶占優(yōu)先級(jí)配置過(guò)程中,優(yōu)先級(jí)高的中斷是可以搶占優(yōu)先級(jí)低的中斷,形成中斷嵌套的,當(dāng)然,優(yōu)先級(jí)低的是不能搶占優(yōu)先級(jí)高的中斷的。
第二種是固有優(yōu)先級(jí),大家可能在看表6-3中斷查詢序列里就看到了有一個(gè)中斷優(yōu)先級(jí)列表,在這個(gè)列表中,中斷優(yōu)先級(jí)是從高到低排列的。但是固有優(yōu)先級(jí)和搶占優(yōu)先級(jí)不同,首先固有優(yōu)先級(jí)不會(huì)形成中斷嵌套,也就是只要當(dāng)前程序進(jìn)入中斷執(zhí)行程序了,其他任何中斷來(lái)了,都會(huì)先執(zhí)行完了當(dāng)前的中斷再回頭響應(yīng)的。
那這個(gè)固有優(yōu)先級(jí)的作用是什么呢?還有一種情況,就是當(dāng)中斷同時(shí)發(fā)生,或者是我們?cè)陂_中斷前,已經(jīng)有幾個(gè)中斷標(biāo)志位置位了,也就是說(shuō)我們可以理解為同時(shí)檢測(cè)到幾個(gè)中斷產(chǎn)生了,那么我們會(huì)先相應(yīng)表6-3中的優(yōu)先級(jí)高的中斷,處理完后再來(lái)相應(yīng)優(yōu)先級(jí)低的中斷。
6.6 作業(yè)1、掌握C語(yǔ)言的數(shù)組的概念、定義和應(yīng)用。
2、掌握if語(yǔ)句和switch語(yǔ)句的用法及區(qū)別,編程的時(shí)候能夠正確選擇使用哪個(gè)語(yǔ)句。
3、徹底理解中斷的原理和應(yīng)用方法,關(guān)閉教程自己獨(dú)立把本章節(jié)程序編寫完畢并且下載到實(shí)驗(yàn)板上實(shí)踐。
4、大家嘗試修改程序,讓我們的數(shù)碼管只顯示有效位,也就是高位的0不顯示。
5、大家改動(dòng)程序,寫一個(gè)數(shù)碼管從999999倒計(jì)時(shí)程序,并且改用定時(shí)器1的中斷來(lái)完成,通過(guò)寫這個(gè)程序,熟練掌握定時(shí)器和中斷的應(yīng)用。
上一課:第五章 定時(shí)器和數(shù)碼管
下一課:第七章 點(diǎn)陣LED的學(xué)習(xí)
作者: 周鵬 時(shí)間: 2013-9-7 12:04
很有用!我要反復(fù)的看看
作者: lmjnkj 時(shí)間: 2014-4-23 22:59
標(biāo)題: 高手請(qǐng)幫忙看看這個(gè)程序哪里有什么問(wèn)題?
本帖最后由 lmjnkj 于 2014-4-23 23:02 編輯
include<reg52.h>
#include<intrins.h>
sbit D14=P1^4;
sbit D24=P2^4;
sbit D25=P2^5;
sbit D26=P2^6;
sbit D27=P2^7;
unsigned char da_l,da_h,i;
volatile unsigned char e;
void delay();
void main()
{
TMOD=0X01;
EA=1;
ET0=1;
TH0=0xb1;
TL0=0xe0;
TR0=1;
P0=0Xff;
D27=1;
D27=0;
D14=1;
P0=0X00;
da_l=0xff;//164輸出高電平有效
//下面循環(huán)程序是將164從Q7到Q0全總部輸出為高電平
for(i=0;i<8;i++)
{
D24=0;
D25=da_l&0x80;
D24=1;
da_l<<=1;
}
P0=0XFF;
D26=1;
da_h=0xfe; //Y為低電平有效
while(1);
}
void time0(void) interrupt 1
{
TH0=0xb1;
TL0=0xe0;
e++;
if(e==50)
{
e=0;
P0=da_h;
da_h=_crol_(da_h,1);//循環(huán)移位程序
}
P0=0XFF;
}
作者: lanrumu 時(shí)間: 2015-5-28 00:12
中斷是單片機(jī)系統(tǒng)的重點(diǎn)中的重點(diǎn),
作者: Talent 時(shí)間: 2015-7-20 22:15
實(shí)在是太謝謝你了
作者: 文慧萱 時(shí)間: 2015-8-27 06:09
不錯(cuò)!
作者: 信仰b 時(shí)間: 2016-4-3 20:10
你好 我想請(qǐng)教一下 如何高位滅0啊 就是這個(gè)教程的作業(yè)4
作者: angeljiangge 時(shí)間: 2016-6-1 16:26
超級(jí)好人 學(xué)習(xí)了



作者: M陳小寶 時(shí)間: 2016-6-15 11:37
大好人 先保存 暑假再好好學(xué)習(xí)
作者: Bnuzdaxian 時(shí)間: 2016-11-18 11:41
那個(gè)if(1000==counter)判斷是否溢出50次不是很明白,有沒(méi)有大神指點(diǎn)一下
作者: 風(fēng)見草 時(shí)間: 2017-1-6 19:43
太有用了!
作者: 隨易而安 時(shí)間: 2017-3-25 19:13
難度逐漸大了。
作者: YESLASON 時(shí)間: 2017-4-26 16:11
好多中斷啊,學(xué)習(xí)了
作者: 索拉卡 時(shí)間: 2017-4-27 20:14
應(yīng)該是溢出1000次
作者: xinxin04932 時(shí)間: 2017-7-17 12:47
怎么看不到原文呢,前面幾張講得很好,先保存了
作者: xiaokevn 時(shí)間: 2017-7-19 20:28
效果是什么樣的?我是一閃一閃的呢?是這樣嗎?
作者: 溟漠瀟瀟 時(shí)間: 2017-8-3 10:24
以前就看到這個(gè)帖子了,最近在學(xué)這個(gè)。正好來(lái)看看,中斷好像比32的簡(jiǎn)單一點(diǎn)
作者: moon84523 時(shí)間: 2017-8-8 09:29
他寫錯(cuò)了,應(yīng)該是判斷是否溢出1000次,1000次相當(dāng)于1秒。
作者: 怪怪愛玩 時(shí)間: 2017-8-16 11:57
講得通俗易懂,很不錯(cuò)
作者: 沙灬漠 時(shí)間: 2017-9-8 18:27
unsigned char LedNumber[6]={0}; 各位高手能夠否講解一下,這一句中的 {0} 代表的是啥? 謝謝!
作者: wohhhde 時(shí)間: 2017-10-24 13:07
贊一個(gè)!
作者: llssll1024 時(shí)間: 2017-11-1 12:40
看得很仔細(xì)、就是忘得也挺快
作者: 皓LL 時(shí)間: 2017-11-5 21:21
關(guān)閉位時(shí),ENLED=0;74HC138就不起作用了,也起不到選取六個(gè)數(shù)碼管了?不理解。幫解釋一下
作者: sewain 時(shí)間: 2017-11-10 15:54
意思是這個(gè)數(shù)組里的每一個(gè)值都初始化為0
作者: sewain 時(shí)間: 2017-11-10 16:33
請(qǐng)教一個(gè)問(wèn)題:
在利用中斷解決“數(shù)碼管抖動(dòng)”的問(wèn)題時(shí),假如在執(zhí)行到 if(1000 == counter) 里面中間位置時(shí),LedNumber:0,1,2已經(jīng)更新,3,4,5沒(méi)有更新,這時(shí)定時(shí)器發(fā)生中斷,在中斷服務(wù)程序中去設(shè)置P0的值會(huì)不會(huì)出錯(cuò)?
作者: dpjwang 時(shí)間: 2017-11-24 13:56
學(xué)到不少知識(shí)啊,太給力了
作者: ching0330 時(shí)間: 2017-11-24 14:32
太給力了 學(xué)習(xí)很多
作者: 17314504393 時(shí)間: 2017-12-16 17:49
switch(j) { case 0: ADDR0=0; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[0]];break; case 1: ADDR0=1; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[1]];break; case 2: ADDR0=0; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[2]];break; case 3: ADDR0=1; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[3]];break; case 4: ADDR0=0; ADDR1=0; ADDR2=1; j++; P0=LedChar[LedNumber[4]];break; case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedChar[LedNumber[5]];break; default: break; }#在這里快速回復(fù)#case后面沒(méi)有常量判斷表達(dá)式那么往下是執(zhí)行哪一條語(yǔ)句
作者: 0x00ff 時(shí)間: 2018-1-30 22:44
#include <reg52.h> //包含寄存器的庫(kù)文件
unsigned char code LedChar[] = { //用數(shù)組來(lái)表示數(shù)碼管真值表
0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80};
unsigned int counter = 0;
unsigned char j = 5;
unsigned char LedNumber[6] = {0};
void main()
{
unsigned long stopwatch = 999999;
P2=0xe0;P0=0x80;P2=0x00; //74HC138和P0初始化部分
TMOD = 0x01; //設(shè)置定時(shí)器0為模式1
TH0 = 0xFC;
TL0 = 0x67; //定時(shí)值初值,定時(shí)1ms
TR0 = 1; //打開定時(shí)器0
EA = 1; //打開中中斷
ET0 = 1; //打開定時(shí)器0中斷
while(1)
{
if(1000 == counter) //判斷定時(shí)器0溢出是否達(dá)到50次
{
counter = 0;
stopwatch--; //秒表數(shù)值一秒加1
LedNumber[0] = stopwatch%10;
LedNumber[1] = stopwatch/10%10;
LedNumber[2] = stopwatch/100%10;
LedNumber[3] = stopwatch/1000%10; //數(shù)碼管顯示值計(jì)算
LedNumber[4] = stopwatch/10000%10;
LedNumber[5] = stopwatch/100000%10;
}
//數(shù)碼管動(dòng)態(tài)刷新部分
}
}
void InterruptTimer0() interrupt 1 //中斷函數(shù)的特殊寫法,數(shù)字’1’為中斷入口號(hào)
{
TH0 = 0xFC; //溢出后進(jìn)入中斷重新賦值
TL0 = 0x67;
counter++; //計(jì)數(shù)值counter加1
P0=0xff; //消隱
switch(j)
{
case 0: P2=0xe0; j=5; P0=LedChar[LedNumber[5]];P2=0x00;P2=0xc0;P0=0x01;P2=0x00;break;
case 1: P2=0xe0; j--; P0=LedChar[LedNumber[4]];P2=0x00;P2=0xc0;P0=0x02;P2=0x00;break;
case 2: P2=0xe0; j--; P0=LedChar[LedNumber[3]];P2=0x00;P2=0xc0;P0=0x04;P2=0x00;break;
case 3: P2=0xe0; j--; P0=LedChar[LedNumber[2]];P2=0x00;P2=0xc0;P0=0x08;P2=0x00;break;
case 4: P2=0xe0; j--; P0=LedChar[LedNumber[1]];P2=0x00;P2=0xc0;P0=0x10;P2=0x00;break;
case 5: P2=0xe0; j--; P0=LedChar[LedNumber[0]];P2=0x00;P2=0xc0;P0=0x20;P2=0x00;break;
default: break;
}
}
求教作業(yè)題就是那個(gè)999999,做不出來(lái)。。。
作者: 默默的保護(hù)神 時(shí)間: 2018-2-28 10:14
這個(gè)是筆誤
作者: 禹1850 時(shí)間: 2018-4-25 10:05
請(qǐng)問(wèn)有沒(méi)有開發(fā)板的原理圖?可不可以分享一下!�。�!
作者: 57185490 時(shí)間: 2018-5-3 16:15
太難了 這一章根本看不懂
作者: 57185490 時(shí)間: 2018-5-5 07:05
越看越復(fù)雜呀
這個(gè)應(yīng)該是高中的程度啦
作者: keneng 時(shí)間: 2018-6-29 10:24
繼續(xù)學(xué)習(xí)!
作者: interrupt 時(shí)間: 2018-7-23 17:45
我用定時(shí)器1時(shí),很明顯沒(méi)到1秒數(shù)字就跳了,這是為什么啊?
作者: 胡巴 時(shí)間: 2018-7-25 15:28
case 0: ADDR0=0; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[0]]; break;
case 1: ADDR0=1; ADDR1=0; ADDR2=0; j++; P0=LedChar[LedNumber[1]]; break;
case 2: ADDR0=0; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[2]]; break;
case 3: ADDR0=1; ADDR1=1; ADDR2=0; j++; P0=LedChar[LedNumber[3]]; break;
case 4: ADDR0=0; ADDR1=0; ADDR2=1; j++; P0=LedChar[LedNumber[4]]; break;
case 5: ADDR0=1; ADDR1=0; ADDR2=1; j=0; P0=LedChar[LedNumber[5]]; break;
請(qǐng)問(wèn)下這段的意思是什么
作者: sakeboy 時(shí)間: 2018-9-2 10:58
沙灬漠 發(fā)表于 2017-9-8 18:27
**** 作者被禁止或刪除 內(nèi)容自動(dòng)屏蔽 ****
按道理說(shuō)里面應(yīng)該有六組數(shù),實(shí)際就寫了第一組為0,缺少的五組數(shù)據(jù)自動(dòng)默認(rèn)為0,這里的首位0你可以更改為任意數(shù)都一樣的,代表的是下標(biāo)0的數(shù)
作者: ccyycc 時(shí)間: 2018-9-26 20:08
很有用,謝謝
作者: 小天才哥哥 時(shí)間: 2019-4-25 12:56
問(wèn)一下,怎么高位滅0
作者: 小天才哥哥 時(shí)間: 2019-5-11 23:42
數(shù)碼管怎么高位滅0啊求教
作者: 小天才哥哥 時(shí)間: 2019-5-24 20:19
怎么讓數(shù)碼管只顯示有效位啊
作者: 633699817341209 時(shí)間: 2020-2-13 03:38
講解的通俗易懂,容易理解,太棒了,謝謝您
作者: mawuxi 時(shí)間: 2020-3-10 11:34
有點(diǎn)難度
作者: b9589 時(shí)間: 2020-10-28 16:27
單片機(jī)的原理最終為實(shí)踐而服務(wù),理論結(jié)合實(shí)際很重要。
作者: QQQQ987 時(shí)間: 2024-5-26 13:11
這是那一本書出的知識(shí)點(diǎn)?
歡迎光臨 (http://www.torrancerestoration.com/bbs/) |
Powered by Discuz! X3.1 |