1. C51 單片機 局部變數 問題
這個問題也困擾過我,比如定時器中斷里設置一個變數x,不設置初值,然後程序里放個x++。
結果就是那個x真的一直在加。
不過我沒有去深究過,個人理解是,如果系統不缺空間的話,單片機就不會去清除空餘的單元。
是不是這樣我不清楚,但是分析如下:
我們自己用匯編寫程序的時候,直接對RAM操作,如果不去清0,裡面的數是永遠不會變的。這說明,單片機本身是沒有這種操作的。
如果說C語言編譯過程中有加這種功能,也很難想像會在每個工作周期中不斷的去檢查哪些單元閑置的,並且對其清零。因為這樣要佔用很多CPU時間。
個人猜測,注意,是猜測!應該是當不得不要把某個物理單元劃分給新的變數時,才會真的把舊變數清除掉。否則,哪怕是局部變數,對應的物理單元應該是一直不變的。
(以上有錯,是我一開始寫的,請看下面的分析為准)
一邊寫一邊仔細回憶一下,大概知道了:
如果你寫了一個函數,裡面有5個變數。並且,這個函數你不去調用它。那麼編譯後你會發現程序多佔用了5個位元組的RAM。如果你調用它,有些情況下(程序特別長,變數特別多時)你會發現,程序里使用的RAM沒增加!(多說明一下,為什麼不調用它反而要佔用空間呢?因為系統不知道這5個空間什麼時候可以釋放,那麼定義了以後只好一直保留著。而如果你調用它了,什麼時候有用什麼時候沒用是固定的,那麼系統編譯時就可以在該變數不被使用時把空間分配給其他變數,有點「分時復用」的意思。而且,如果你寫了太多函數,裡面有足夠多的變數,然後這些函數統統都永不調用,編譯時就會出錯,提示你系統RAM不足)
這至少說明兩點,
一、如果你定義了一個局部變數,那麼系統就會一直留著空間給它以備使用。那麼那個物理單元隨時都是為「那一個」變數准備著的,它的值並不會清零。
二、當你程序使用了太多變數時,那麼系統編譯時會自動調節,在適當的時候釋放物理單元給「不同的變數」使用,也就是同一個物理單元要被不同的局部變數用,這種時候才會出現你說的情況,那就是局部變數會被清零。
實際上平時寫的程序都太短了,根本沒用使用完系統的RAM,那麼編譯時自然不會做這個操作。
同樣可以得出一個結論,正是因為單片機的RAM有限,才會導致局部變數不清除。對於電腦來說,空間近乎無限的,隨時隨地你都可以分配空間給變數用。但是51單片機就那麼128位元組,必須詳細策劃好如何分配,所以局部變數才有了自己固定的地盤。
說到這里,我相信你的問題我已經找到答案了,雖然都是我推測的,但是十之八九不會錯。歡迎討論!
哦,至於初始值為0,這個沒啥啊,單片機復位以後,RAM就是0
書上沒寫,但是實際用都是這樣
2. 看圖解說51單片機P0口是怎麼輸出地址和數據的請先看我的分析
打個比方,你在P口輸出12345678地址,74hc373打開,此時外部存儲器地址數據都是12345678,就是說,地址12345678中存著12345678的數據,然後74hc373鎖存,地址為保留12345678,然後改P輸出口,這時候寫的是數據,寫00000000,此時就是地址12345678中存著00000000的數據,完成一次存儲,然後進行下一次存儲,打開鎖存器74hc373的同時把P口改為下一次需要的地址。重復上次操作,就行了。
3. 要求用51單片機控制8個LED亮滅的原理圖+程序+解說
本來以為這個程序很簡單的,沒想到寫了快三個小時。哎學藝不精啊。貼出來給你研究吧。我不想做過多的解釋了,我是按我理解的你出的題目做的,可能和你的本意不是很一樣,
1、依次亮,依次滅:從一個燈亮到全亮,再到全滅,每次改變一個燈亮滅
2、奇偶號燈間隔亮滅:隔一個燈亮一個燈亮燈時間為1s,沒有燈全滅的時候
3、依次閃爍、切換時間為3秒,閃爍時間為2秒,我理解的是,沒三秒鍾有一個燈在閃爍,其中有一秒鍾是滅燈狀態
程序中使用了P1口與8個發光二極體相連,具體電路圖你網路一下吧,還有使用了一個按鍵,該按鍵與P3.7相連,低電平為按下狀態。
程序如下:
#include <reg52.h>
#include <intrins.h>
/*變數聲明:
i、j、k都是記錄計時器溢出次數的變數,
stat是記錄當前顯示狀態的變數,由按鍵key控制
temp是狀態2中保護P1口狀態的變數*/
unsigned char i=0,j=0,k=0,stat=0,temp;
bit flag=1; //狀態1處於滅燈還是亮燈狀態的變數,1為依次亮燈,0為依次亮燈
sbit key=P3^7; //按鍵控制
void init(); //初始化函數
void delay(unsigned int N); //延時函數
void keyscan(); //鍵盤掃描函數
void main()
{
init();
while (1)
{
switch (stat)
{
case 0: //0方案
if(i==20&&flag)
{
i=0;
P1=P1<<1; //依次亮燈
temp=P1;
if(temp==0)
{
flag=0;
}
}
if(i==20&&!flag)
{
i=0;
if(P1==0xff)
{
flag=1;
P1=0xfe;
}
if(!flag)
{
P1=P1<<1; //依次滅燈
temp=P1;
P1=temp+1;
}
}
break;
case 1: //2方案
if(i==20)
{
i=0;
P1=~P1; //去反後亮燈狀態為滅燈,P1初值取0x55或0xaa,奇偶交替亮燈
}
break;
case 2: //3方案
if(j==60)
{
P1=temp;
P1=_crol_(P1,1);
temp=P1; //保護P1口亮燈狀態
k=0;
j=0;
}
//閃爍部分,應該可以優化
if(k<5)
{
P1=0xff;
}
else if(k>=5&&k<10)
{
P1=temp;
}
else if(k>=10&&k<15)
{
P1=0xff;
}
else if(k>=15&&k<20)
{
P1=temp;
}
else if(k>=20&&k<25)
{
P1=0xff;
}
else if(k>=30&&k<35)
{
P1=temp;
}
else if(k>=35&&k<40)
{
P1=0xff;
}
//-----------------------------------
break;
}
keyscan();
}
}
void init()
{
TH0=0x3c; //定時器賦初值定時時間50ms
TL0=0xB0;
TMOD=0x01; //設置定時器工作方式為方式1
EA=1; //開總中斷
ET0=1; //開中斷允許位
TR0=1; //定時器計數
P1=0xfe; //這里假設led燈與P1口相連並且
//低電平有效
}
void delay(unsigned int N)
{
int i,j;
for (i=0;i<N;i++);
for (j=0;j<110;j++);
}
void keyscan()
{
if(!key)
{
delay(10); //消抖
if(!key); //確認有鍵按下
stat++;
if(stat==3)
{
stat=0;
}
//右鍵按下復位
i=0;
j=0;
k=0;
TH0=0x3c;
TL0=0xB0;
switch (stat)
{
case 0:
P1=0xfe;
flag=1;
stat=0;
break;
case 1:
P1=0x55;
break;
case 2:
P1=0xfe;
temp=P1;
break;
}
//-----------------------------------
while(!key) //此循環中的函數和主函數中的顯示函數是同一個
//用於長按鍵的顯示,可以去掉,去掉長按鍵不會正常顯示,松開按鍵後正常
{
switch (stat)
{
case 0:
if(i==20&&flag)
{
i=0;
P1=P1<<1;
temp=P1;
if(temp==0)
{
flag=0;
}
}
if(i==20&&!flag)
{
i=0;
if(P1==0xff)
{
flag=1;
P1=0xfe;
}
if(!flag)
{
P1=P1<<1;
temp=P1;
P1=temp+1;
}
}
break;
case 1:
if(i==20)
{
i=0;
P1=~P1;
}
break;
case 2:
if(j==60)
{
P1=temp;
P1=_crol_(P1,1);
temp=P1;
k=0;
j=0;
}
if(k<5)
{
P1=0xff;
}
else if(k>=5&&k<10)
{
P1=temp;
}
else if(k>=10&&k<15)
{
P1=0xff;
}
else if(k>=15&&k<20)
{
P1=temp;
}
else if(k>=20&&k<25)
{
P1=0xff;
}
else if(k>=30&&k<35)
{
P1=temp;
}
else if(k>=35&&k<40)
{
P1=0xff;
}
break;
}
}
}
}
void timer0() interrupt 1
{
TH0=0x3c;
TL0=0xB0; //溢出後重新賦初值
//定時器中斷時間為50ms
i++; //20次中斷時間為1s
j++; //40次中斷時間為2s
k++; //60次中斷時間為3s
}
有什麼不懂的可以網路Hi我
4. 單片機什麼是用鍵值的方式解決按鍵掃描問題,簡單解說下 單解說下
左邊的圖,每個按鍵對應於一個IO口,按下按鍵時相應的IO口被拉到低電平,其IO寄存器位回讀為「0」。因此根據回讀到的哪個寄存器位為0就能知道哪個鍵被按下。這種方式適用於規模較小的鍵盤。
右邊的圖是常見的行列掃描接法。當單個行掃描管腳拉低後,回讀列掃描管腳的狀態,即可知道該行有哪幾個按鍵被按下。逐一拉低各個行掃描管腳並回讀列管腳狀態,即可獲得整個矩陣鍵盤的按鍵狀態。這種方式適用於規模較大的鍵盤,有效節省珍貴的IO口。
5. 急求單片機流水燈程序及詳解
你好! 給你兩個份實例 基本可以搞定啦 !
一。。。。流水燈實例
1. 基礎知識:定址方式是尋找、確定參與操作的數據的地址的方式。8051單片機的定址方式包括寄存器定址、直接定址、寄存器間接定址、立即定址、變址定址和位定址7種定址方式。
2. 硬體電路(等級不夠還不能傳圖片哈)
3. 軟體程序設計:
ORG 0000H ;偽指令,指定程序從0000H開始存放
LJMP MAIN; 跳轉指令,程序跳轉到MAIN處
ORG 0100H ;偽指令,指定以下程序從0100H開始存放
MAIN:
MOV SP,#60H ;給堆棧指針賦初值
MOV P1,#0FFH ;給P1賦初值,LED全滅
;以下為查表程序
MOV DPTR,#LED_TABLE
LIGHT:
MOV R7,#42
LOOP:
MOV A,#42
SUBB A,R7
MOVC A,@A+DPTR
MOV P1,A ;輸出顯示
LCALL DELAY ;調延時子程序
DJNZ R7,LOOP
SJMP LIGHT ;跳轉,程序繼續
DELAY:
MOV R7,#10H
DELAY0:
MOV R6,#7FH
DELAY1:
MOV R5,#7FH
DJNZ R5,$
DJNZ R6,DELAY1
DJNZ R7,DELAY0
RET
;表格數據
LED_TABLE:
DB 0FFH ;全部熄滅
DB 0FEH. , 0FDH , 0FBH , 0F7H , 0EFH , 0DFH , 0BFH, 07FH ;依次逐個點亮
DB 0FEH. , 0FCH , 0F8H , 0F0H , 0E0H , 0C0H , 080H, 000H ; 依次逐個疊加
DB 080H. , 0C0H , 0E0H , 0F0H , 0F8H , 0FCH , 0FEH, 0FFH ;依次逐個遞減
DB 07EH. , 0BDH , 0DBH , 0E7H , 0E7H , 0DBH , 0BDH, 07EH ;兩邊靠攏後分開
DB 07EH. , 03CH , 01BH , 000H , 000H , 018H , 03CH, 07EH ;從兩邊疊加後遞減
DB 000H ;全部點亮
END
4. 運行結果
程序運行後,將依次循環出現8隻LED依次逐個點亮 、依次逐個疊加、依次逐個遞減、從兩邊靠攏後分開、從兩邊疊加後遞減的流水燈效果。
5. 技巧總結
查表指令可用於復雜代碼轉換顯示,通過查表指令可以實現復雜的顯示效果,並可以減少程序代碼。
二 。。。。用單片機控制的LED流水燈設計(電路、程序全部給出)
1.引言
當今時代是一個新技術層出不窮的時代,在電子領域尤其是自動化智能控制領域,傳統的分立元件或數字邏輯電路構成的控制系統,正以前所未見的速度被單片機智能控制系統所取代。單片機具有體積小、功能強、成本低、應用面廣等優點,可以說,智能控制與自動控制的核心就是單片機。目前,一個學習與應用單片機的高潮正在工廠、學校及企事業單位大規模地興起。學習單片機的最有效方法就是理論與實踐並重,本文筆者用AT89C51單片機自製了一款簡易的流水燈,重點介紹了其軟體編程方法,以期給單片機初學者以啟發,更快地成為單片機領域的優秀人才。
2.硬體組成
按照單片機系統擴展與系統配置狀況,單片機應用系統可分為最小系統、最小功耗系統及典型系統等。AT89C51單片機是美國ATMEL公司生產的低電壓、高性能CMOS
8位單片機,具有豐富的內部資源:4kB快閃記憶體、128BRAM、32根I/O口線、2個16位定時/計數器、5個向量兩級中斷結構、2個全雙工的串列口,具有4.25~5.50V的電壓工作范圍和0~24MHz工作頻率,使用AT89C51單片機時無須外擴存儲器。因此,本流水燈實際上就是一個帶有八個發光二極體的單片機最小應用系統,即為由發光二極體、晶振、復位、電源等電路和必要的軟體組成的單個單片機。其具體硬體組成如圖1所示。
圖1 流水燈硬體原理圖
從原理圖中可以看出,如果要讓接在P1.0口的LED1亮起來,那麼只要把P1.0口的電平變為低電平就可以了;相反,
如果要接在P1.0口的LED1熄滅,就要把P1.0口的電平變為高電平;同理,接在P1.1~P1.7口的其他7個LED的點亮和熄滅的方法同LED1。因此,要實現流水燈功能,我們只要將發光二極體LED1~LED8依次點亮、熄滅,8隻LED燈便會一亮一暗的做流水燈了。在此我們還應注意一點,由於人眼的視覺暫留效應以及單片機執行每條指令的時間很短,我們在控制二極體亮滅的時候應該延時一段時間,否則我們就看不到「流水」效果了。
3.軟體編程
單片機的應用系統由硬體和軟體組成,上述硬體原理圖搭建完成上電之後,我們還不能看到流水燈循環點亮的現象,我們還需要告訴單片機怎麼來進行工作,即編寫程序控制單片機管腳電平的高低變化,來實現發光二極體的一亮一滅。軟體編程是單片機應用系統中的一個重要的組成部分,是單片機學習的重點和難點。下面我們以最簡單的流水燈控制功能即實現8個LED燈的循環點亮,來介紹實現流水燈控制的幾種軟體編程方法。
3.1位控法
這是一種比較笨但又最易理解的方法,採用順序程序結構,用位指令控制P1口的每一個位輸出高低電平,從而來控制相應LED燈的亮滅。程序如下:
ORG 0000H ;單片機上電後從0000H地址執行
AJMP START ;跳轉到主程序存放地址處
ORG 0030H ;設置主程序開始地址
START:MOV SP,#60H ;設置堆棧起始地址為60H
CLR P1.0 ;P1.0輸出低電平,使LED1點亮
ACALL DELAY ;調用延時子程序
SETB P1.0 ;P1.0輸出高電平,使LED1熄滅
CLR P1.1 ;P1.1輸出低電平,使LED2點亮
ACALL DELAY ;調用延時子程序
SETB P1.1 ;P1.1輸出高電平,使LED2熄滅
CLR P1.2 ;P1.2輸出低電平,使LED3點亮
ACALL DELAY ;調用延時子程序
SETB P1.2 ;P1.2輸出高電平,使LED3熄滅
CLR P1.3 ;P1.3輸出低電平,使LED4點亮
ACALL DELAY ;調用延時子程序
SETB P1.3 ;P1.3輸出高電平,使LED4熄滅
CLR P1.4 ;P1.4輸出低電平,使LED5點亮
ACALL DELAY ;調用延時子程序
SETB P1.4 ;P1.4輸出高電平,使LED5熄滅
CLR P1.5 ;P1.5輸出低電平,使LED6點亮
ACALL DELAY ;調用延時子程序
SETB P1.5 ;P1.5輸出高電平,使LED6熄滅
CLR P1.6 ;P1.6輸出低電平,使LED7點亮
ACALL DELAY ;調用延時子程序
SETB P1.6 ;P1.6輸出高電平,使LED7熄滅
CLR P1.7 ;P1.7輸出低電平,使LED8點亮
ACALL DELAY ;調用延時子程序
SETB P1.7 ;P1.7輸出高電平,使LED8熄滅
ACALL DELAY ;調用延時子程序
AJMP START ;8個LED流了一遍後返回到標號START處再循環
DELAY: ;延時子程序
MOV R0,#255 ;延時一段時間
D1: MOV R1,#255
DJNZ R1,$
DJNZ R0,D1
RET ;子程序返回
END ;程序結束
3.2循環移位法
在上個程序中我們是逐個控制P1埠的每個位來實現的,因此程序顯得有點復雜,下面我們利用循環移位指令,採用循環程序結構進行編程。我們在程序一開始就給P1口送一個數,這個數本身就讓P1.0先低,其他位為高,然後延時一段時間,再讓這個數據向高位移動,然後再輸出至P1口,這樣就實現「流水」效果啦。由於8051系列單片機的指令中只有對累加器ACC中數據左移或右移的指令,因此實際編程中我們應把需移動的數據先放到ACC中,讓其移動,然後將ACC移動後的數據再轉送到P1口,這樣同樣可以實現「流水」效果。具體編程如下所示,程序結構確實簡單了很多。
ORG 0000H ;單片機上電後從0000H地址執行
AJMP START ;跳轉到主程序存放地址處
ORG 0030H ;設置主程序開始地址
START:MOV SP,#60H ;設置堆棧起始地址為60H
MOV A,#0FEH ;ACC中先裝入LED1亮的數據(二進制的11111110)
MOV P1,A ;將ACC的數據送P1口
MOV R0,#7 ;將數據再移動7次就完成一個8位流水過程
LOOP: RL A ;將ACC中的數據左移一位
MOV P1,A ;把ACC移動過的數據送p1口顯示
ACALL DELAY ;調用延時子程序
DJNZ R0,LOOP ;沒有移動夠7次繼續移動
AJMP START ;移動完7次後跳到開始重來,以達到循環流動效果
DELAY: ;延時子程序
MOV R0,#255 ;延時一段時間
D1: MOV R1,#255
DJNZ R1,$
DJNZ R0,D1
RET ;子程序返回
END ;程序結束
3.3查表法
上面的兩個程序都是比較簡單的流水燈程序,「流水」花樣只能實現單一的「從左到右」流方式。運用查表法所編寫的流水燈程序,能夠實現任意方式流水,而且流水花樣無限,只要更改流水花樣數據表的流水數據就可以隨意添加或改變流水花樣,真正實現隨心所欲的流水燈效果。我們首先把要顯示流水花樣的數據建在一個以TAB為標號的數據表中,然後通過查表指令「MOVC A,@A+DPTR」把數據取到累加器A中,然後再送到P1口進行顯示。具體源程序如下,TAB標號處的數據表可以根據實現效果的要求任意修改。
ORG 0000H ;單片機上電後從0000H地址執行
AJMP START ;跳轉到主程序存放地址處
ORG 0030H ;設置主程序開始地址
START:MOV SP,#60H ;設置堆棧起始地址為60H
MOV DPTR,# TAB ;流水花樣表首地址送DPTR
LOOP: CLR A ;累加器清零
MOVC A,@A+DPTR ;取數據表中的值
CJNE A,#0FFH,SHOW;檢查流水結束標志
AJMP START ;所有花樣流完,則從頭開始重復流
SHOW: MOV P1,A ;將數據送到P1口
ACALL DELAY ;調用延時子程序
INC DPTR ;取數據表指針指向下一數據
AJMP LOOP ;繼續查表取數據
DELAY: ;延時子程序
MOV R0,#255 ;延時一段時間
D1: MOV R1,#255
DJNZ R1,$
DJNZ R0,D1
RET ;子程序返回
TAB: ;下面是流水花樣數據表,用戶可據要求任意編寫
DB 11111110B ;二進製表示的流水花樣數據,從低到高左移
DB 11111101B
DB 11111011B
DB 11110111B
DB 11101111B
DB 11011111B
DB 10111111B
DB 01111111B
DB 01111111B ;二進製表示的流水花樣數據,從高到低右移
DB 10111111B
DB 11011111B
DB 11101111B
DB 11110111B
DB 11111011B
DB 11111101B
DB 11111110B
DB 0FEH,0FDH,0FBH,0F7H ;十六進製表示的流水花樣數據
DB 0EFH,0DFH,0BFH,7FH
DB 7FH,0BFH,0DFH,0EFH
DB 0F7H,0FBH,0FDH,0FEH
……
DB 0FFH ;流水花樣結束標志0FFH
END ;程序結束
4.結語
當上述程序之一編寫好以後,我們需要使用編譯軟體對其編譯,得到單片機所能識別的二進制代碼,然後再用編程器將二進制代碼燒寫到AT89C51單片機中,最後連接好電路通電,我們就看到LED1~LED8的「流水」效果了。本文所給程序實現的功能比較簡單,旨在拋磚引玉,用戶可以自己在此基礎上擴展更復雜的流水燈控制,比如鍵盤控制流水花樣、控制流水燈顯示數字或圖案等等。
希望能幫上你
6. 單片機中ES和RI及TI的概念有點糾結,求助!
意思差不多了。
ES是串列口中斷允許位。=1允許串列口中斷,=0,禁止串列口中斷。