一、閃爍燈如圖1 所示為一簡單單片機系統原理圖:在 P1.0 端口上接一個發光二極管 L1,使 L1 在不停地一亮一滅,一亮一滅的時間間隔為 0.2 秒。
延時程序的設計方法,作為單片機的指令的執行的時間是很短,數量大微秒級,因此,我們要求的閃爍時間間隔為 0.2 秒,相對于微秒來說,相差太大,所以我們在執行某一指令時,插入延時程序,來達到我們的要求,但這樣的延時程 序是如何設計呢?下面具體介紹其原理:如圖 4.1.1 所示的石英晶體為 12MHz,因此,1 個機器周期為 1 微秒,機器周期 微秒如圖 1 所示,當 P1.0 端口輸出高電平,即 P1.0=1 時,根據發光二極管的單向導電性可知,這時發光二極管 L1 熄滅;當P1.0 端口輸出低電平,即 P1.0=0 時,發光二極管 L1 亮;我們可以使用 SETB P1.0指令使 P1.0端口輸出高電平,使用 CLR P1.0 指令使 P1.0 端口輸出低電平。
C 語言源程序 - #include <AT89X51.H>
- sbit L1=P1^0;
- void delay02s(void) //延時 0.2 秒子程序
- {
- unsigned char i,j,k;
- for(i=20;i>0;i--)
- for(j=20;j>0;j--)
- for(k=248;k>0;k--);
- }
- void main(void)
- {
- while(1)
- {
- L1=0;
- delay02s();
- L1=1;
- delay02s();
- }
復制代碼
匯編源程序 - ORG 0
- START: CLR P1.0
- LCALL DELAY
- SETB P1.0
- LCALL DELAY
- LJMP START
- DELAY: MOV R5,#20 ;延時子程序,延時 0.2 秒
- D1: MOV R6,#20
- D2: MOV R7,#248
- DJNZ R7,$
- DJNZ R6,D2
- DJNZ R5,D1
- RET
- END
復制代碼
二、多路開關狀態指示如圖 3 所示,AT89S51 單片機的 P1.0-P1.3 接四個發光二極管 L1-L4, P1.4-P1.7 接了四個開關 K1-K4,編程將開關的狀態反映到發光二極管上。 (開關閉合,對應的燈亮,開關斷開,對應的燈滅)。 對于開關狀態檢測,相對單片機來說,是輸入關系,我們可輪流檢測每個開關狀 態,根據每個開關的狀態讓相應的發光二極管指示,可以采用 JB P1.X,REL 或 JNB P1.X,REL 指令來完成;也可以一次性檢測四路開關狀態,然后讓其指 示,可以采用 MOV A,P1 指令一次把 P1 端口的狀態全部讀入,然后取高 4 位的狀態來指示。 方法1(匯編源程序) ORG 00H START: MOV A,P1
ANL A,#0F0H RR A RR A RR A RR A ORL A,#0F0H MOV P1,A SJMP START END
方法1(C語言程序) #INClude <AT89X51.H> unsigned char temp;
void main(void) { while(1) { temp=P1>>4; temp=temp | 0xf0; P1=temp; } } 方法2(匯編源程序) ORG 00H START: JB P1.4,NEXT1 CLR P1.0 SJMP NEX1
NEXT1: SETB P1.0 NEX1: JB P1.5,NEXT2 CLR P1.1 SJMP NEX2 NEXT2: SETB P1.1 NEX2: JB P1.6,NEXT3 CLR P1.2 SJMP NEX3 NEXT3: SETB P1.2 NEX3: JB P1.7,NEXT4 CLR P1.3 SJMP NEX4 NEXT4: SETB P1.3 NEX4: SJMP START END 方法2(C 語言源程序)  #INClude <AT89X51.H>
void main(void) { while(1) { if(P1_4==0) { P1_0=0; } Else { P1_0=1; } if(P1_5==0) { P1_1=0; } else  { P1_1=1; } if(P1_6==0) { P1_2=0; } else { P1_2=1; } if(P1_7==0) { P1_3=0; } else 三、廣告燈的設計利用取表的方法,使端口 P1 做單一燈的變化:左移 2 次,右移 2 次,閃爍 2 次 (延時的時間 0.2 秒)。 利用 MOV DPTR,#DATA16 的指令來使數據指針寄存器指到表的開 頭。 利用 MOVC A,@A+DPTR 的指令,根據累加器的值再加上 DPTR 的 值,就可以使程序計數器 PC 指到表格內所要取出的數據。因此,只要把控制碼建成一個表,而利用 MOVC A,@A+DPTR 做取碼的操作,  就可方便地處理一些復雜的控制動作,取表過程如下圖所示: 匯編源程序 ORG 0 START: MOV DPTR,#TABLE LOOP: CLR A MOVC A,@A+DPTR CJNE A,#01H,LOOP1 JMP START LOOP1: MOV P1,A MOV R3,#20 LCALL DELAY INC DPTR JMP LOOP DELAY: MOV R4,#20 D1: MOV R5,#248  DJNZ R5,$ DJNZ R4,D1 DJNZ R3,DELAY R RET T ABLE: DB 0FEH,0FDH,0FBH,0F7H DB 0EFH,0DFH,0BFH,07FH DB 0FEH,0FDH,0FBH,0F7H DB 0EFH,0DFH,0BFH,07FH DB 07FH,0BFH,0DFH,0EFH DB 0F7H,0FBH,0FDH,0FEH DB 07FH,0BFH,0DFH,0EFH DB 0F7H,0FBH,0FDH,0FEH DB 00H, 0FFH,00H, 0FFH DB 01H END C 語言源程序 #INClude <AT89X51.H> unsigned char code table[]={0xfe,0xfd,0xfb,0xf7, 0xef,0xdf,0xbf,0x7f,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f, 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef, 0xf7,0xfb,0xfd,0xfe,0x00,0xff,0x00,0xff,0x01}; unsigned char i; void delay(void) { unsigned char m,n,s; for(m=20;m>0;m--) for(n=20;n>0;n--) for(s=248;s>0;s--); } void main(void) { while(1) { if(table[ i]!=0x01) { P1=table[ i]; i++; delay(); } else { i=0; } } }
四、 00-59 秒計時器如下圖8所示,在 AT89S51 單片機的 P0 和 P2 端口分別接有兩個共陰數碼管,P0 口驅動顯示秒的時間的十位,而 P2 口驅動顯示秒的時間的個位。在設計過程中我們用一個存儲單元作為秒計數單元,當一秒鐘到來時,就讓秒計數單元加 1,當秒計數達到 60 時,就自動返回到 0,重新秒計數。 對于秒計數單元中的數據要把它十位數和個位數分開,方法仍采用對 10 整除和對 10 求余。 匯編源程序 Second EQU 30H ORG 0000H  START: MOV Second, #00H NEXT: MOV A, Second MOV B,#10 DIV AB MOV DPTR,#TABLE MOVC A,@A+DPTR MOV P0,A MOV A,B MOVC A,@A+DPTR MOV P2,A LCALL DELY1S INC Second MOV A,Second CJNE A,#60,NEXT  LJMP START  DELY1S: MOV R5,#100 D2: MOV R6,#20 D1: MOV R7,#248 DJNZ R7,$ DJNZ R6,D1 DJNZ R5,D2 RET TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH END
C 語言源程序 #include <AT89X51.H> unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char Second; void delay1s(void) { unsigned char i,j,k; for(k=100;k>0;k--) for(i=20;i>0;i--) for(j=248;j>0;j--); } void main(void) { Second=0; P0=table[Second/10]; P2=table[Second%10]; while(1) { delay1s(); Second++; if(Second==60) { Second=0; } P0=table[Second/10]; P2=table[Second%10]; } }

五、動態數碼顯示技術如圖 9 所示,P0 端口接動態數碼管的字形碼筆段,P2 端口接動態數碼管的數位選擇端,P1.7接 一個開關,當開關接高電平時,顯示“12345”字樣;當開關接低電平時,顯示“HELLO”字樣。 動態掃描方法:動態接口采用各數碼管循環輪流顯示的方法,當循環顯示頻率較高時,利用人眼的暫留特性,看不出閃爍顯示現象,這種顯示需要一個接口完成字形碼的輸出(字形選擇),另一接口完成各數碼管的輪流點亮(數位選擇)。 在進行數碼顯示的時候,要對顯示單元開辟 8 個顯示緩沖區,每個顯示緩沖區裝有顯示的不同數據即可。對于顯示的字形碼數據我們采用查表方法來完成。
匯編源程序  ORG 0000H START: JB P1.7,DIR1 MOV DPTR,#TABLE1 SJMP DIR DIR1: MOV DPTR,#TABLE2 DIR: MOV R0,#00H MOV R1,#01H NEXT: MOV A,R0  MOVC A,@A+DPTR MOV P0,A MOV A,R1 MOV P2,A LCALL DAY INC R0 RL A MOV R1,A CJNE R1,#0DFH,NEXT SJMP START DAY: MOV R6,#4 D1: MOV R7,#248 DJNZ R7,$ DJNZ R6,D1  RET TABLE1: DB 06H,5BH,4FH,66H,6DH TABLE2: DB 78H,79H,38H,38H,3FH END
C 語言源程序 - #include <AT89X51.H>
- Unsigned char code table1[ ]={0x06,0x5b,0x4f,0x66,0x6d};
- Unsigned char code table2[]={0x78,0x79,0x38,0x38,0x3f};
- Unsigned char i;
- Unsigned char a,b;
- Unsigned char temp;
- void main(void)
- {
- while(1)
- {
- temp=0xfe;
- for(i=0;i<5;i++)
- {
- if(P1_7==1)
- {
- P0=table1[ i];
- }
- else
- {
- P0=table2[ i];
- }
- P2=temp;
- a=temp<<(i+1);
- b=temp>>(7-i);
- temp=a|b;
- for(a=4;a>0;a--)
- for(b=248;b>0;b--);
- }
- }
復制代碼
六、4×4 矩陣式鍵盤識別技術如圖 11 所示,用 AT89S51 的并行口 P1 接 4×4 矩陣鍵盤,以 P1.0-P1.3作輸入線,以 P1.4-P1.7 作輸出線;在數碼管上顯示每個按鍵的“0-F”序。對應的按鍵的序號排列如圖12 所示每個按鍵有它的行值和列值 ,行值和列值的組合就是識別這個按鍵的編碼。矩陣的行線和列線分別通過兩并行接口和 CPU 通信。每個按鍵的狀態同樣需變成數字量“0”和“1”,開關的一端(列線)通過電阻接 VCC,而接地是通過程序輸出數字“0”實現的。鍵盤處理程序的任務是:確定有無鍵按下,判斷哪一個鍵按下,鍵的功能是什么;  還要消除按鍵在閉合或斷開時的抖動。兩個并行口中,一個輸出掃描碼,使按鍵逐行動態接地,另一個并行口輸入按鍵狀態,由行掃描值和回饋信號共同形成鍵編碼而識別按鍵,通過軟件查表,查出該鍵的 功能。  
匯編源程序 - KEYBUF EQU 30H
- ORG 00H
- START: MOV KEYBUF,#2
- WAIT:
- MOV P3,#0FFH
- CLR P3.4
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY1
- LCALL DELY10MS
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY1
- MOV A,P3
- ANL A,#0FH
- CJNE A,#0EH,NK1
- MOV KEYBUF,#0
- LJMP DK1
- NK1: CJNE A,#0DH,NK2
- MOV KEYBUF,#1
- LJMP DK1
- NK2: CJNE A,#0BH,NK3
- MOV KEYBUF,#2
- LJMP DK1
- NK3: CJNE A,#07H,NK4
- MOV KEYBUF,#3
- LJMP DK1
- NK4: NOP
- DK1:
- MOV A,KEYBUF
- MOV DPTR,#TABLE
- MOVC A,@A+DPTR
- MOV P0,A
- DK1A: MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JNZ DK1A
- NOKEY1:
- MOV P3,#0FFH
- CLR P3.5
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY2
- LCALL DELY10MS
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY2
- MOV A,P3
- ANL A,#0FH
- CJNE A,#0EH,NK5
- MOV KEYBUF,#4
- LJMP DK2
- NK5: CJNE A,#0DH,NK6
- MOV KEYBUF,#5
- LJMP DK2
- NK6: CJNE A,#0BH,NK7
- MOV KEYBUF,#6
- LJMP DK2
- NK7: CJNE A,#07H,NK8
- MOV KEYBUF,#7
- LJMP DK2
- NK8: NOP
- DK2:
- MOV A,KEYBUF
- MOV DPTR,#TABLE
- MOVC A,@A+DPTR
- MOV P0,A
- DK2A: MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JNZ DK2A
- NOKEY2:
- MOV P3,#0FFH
- CLR P3.6
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY3
- LCALL DELY10MS
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY3
- MOV A,P3
- ANL A,#0FH
- CJNE A,#0EH,NK9
- MOV KEYBUF,#8
- LJMP DK3
- NK9: CJNE A,#0DH,NK10
- MOV KEYBUF,#9
- LJMP DK3
- NK10: CJNE A,#0BH,NK11
- MOV KEYBUF,#10
- LJMP DK3
- NK11: CJNE A,#07H,NK12
- MOV KEYBUF,#11
- LJMP DK3
- NK12: NOP
- DK3:
- MOV A,KEYBUF
- MOV DPTR,#TABLE
- MOVC A,@A+DPTR
- MOV P0,A
- DK3A: MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JNZ DK3A
- NOKEY3:
- MOV P3,#0FFH
- CLR P3.7
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY4
- LCALL DELY10MS
- MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JZ NOKEY4
- MOV A,P3
- ANL A,#0FH
- CJNE A,#0EH,NK13
- MOV KEYBUF,#12
- LJMP DK4
- NK13: CJNE A,#0DH,NK14
- MOV KEYBUF,#13
- LJMP DK4
- NK14: CJNE A,#0BH,NK15
- MOV KEYBUF,#14
- LJMP DK4
- NK15: CJNE A,#07H,NK16
- MOV KEYBUF,#15
- LJMP DK4
- NK16: NOP
- DK4:
- MOV A,KEYBUF
- MOV DPTR,#TABLE
- MOVC A,@A+DPTR
- MOV P0,A
- DK4A: MOV A,P3
- ANL A,#0FH
- XRL A,#0FH
- JNZ DK4A
- NOKEY4:
- LJMP WAIT
- DELY10MS:
- MOV R6,#10
- D1: MOV R7,#248
- DJNZ R7,$
- DJNZ R6,D1
- RET
- TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H
- DB 7FH,6FH,77H,7CH,39H,5EH,79H,71H
- END
復制代碼
C 語言源程序 - #INClude <AT89X51.H>
- unsigned char code table[]={0x3f,0x06,0x5b,0x4f,
- 0x66,0x6d,0x7d,0x07,
- 0x7f,0x6f,0x77,0x7c,
- 0x39,0x5e,0x79,0x71};
- unsigned char temp;
- unsigned char key;
- unsigned char i,j;
- void main(void)
- {
- while(1)
- {
- P3=0xff;
- P3_4=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=50;i>0;i--)
- for(j=200;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=7;
- break;
- case 0x0d:
- key=8;
- break;
- case 0x0b:
- key=9;
- break;
- case 0x07:
- key=10;
- break;
- }
- temp=P3;
- P1_0=~P1_0;
- P0=table[key];
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- }
- }
- P3=0xff;
- P3_5=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=50;i>0;i--)
- for(j=200;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=4;
- break;
- case 0x0d:
- key=5;
- break;
- case 0x0b:
- key=6;
- break;
- case 0x07:
- key=11;
- break;
- }
- temp=P3;
- P1_0=~P1_0;
- P0=table[key];
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- }
- }
- P3=0xff;
- P3_6=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=50;i>0;i--)
- for(j=200;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=1;
- break;
- case 0x0d:
- key=2;
- break;
- case 0x0b:
- key=3;
- break;
- case 0x07:
- key=12;
- break;
- }
- temp=P3;
- P1_0=~P1_0;
- P0=table[key];
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- }
- }
- P3=0xff;
- P3_7=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=50;i>0;i--)
- for(j=200;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=0;
- break;
- case 0x0d:
- key=13;
- break;
- case 0x0b:
- key=14;
- break;
- case 0x07:
- key=15;
- break;
- }
- temp=P3;
- P1_0=~P1_0;
- P0=table[key];
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- }
- }
- }
- }
復制代碼
七、按鍵識別方法每按下一次開關 SP1,計數值加 1,通過AT89S51 單片機的 P1 端口的 P1.0 到 P1.3 顯示出其二進制計數值。 *程序設計方法:作為一個按鍵從沒有按下到按下以及釋放是一個完整的過程,也就是說,當我們按下一個按鍵時,總希望某個命令只執行一次,而在按鍵按下的 過程中,不要有干擾進來,因為,在按下的過程中,一旦有干擾過來,可能造成誤觸發過程,這并不是我們所想要的。因此在按鍵按下的時候,要把我們手上的干擾信號以及按鍵的機械接觸等干擾信號給濾除掉,一般情況下,我們可以采用電容來濾除掉這些干擾信號,但實際上,會增加硬件成本及硬件電路的體積,這是我們不希望,總得有個辦法解決這個問題,因此我們可以采用軟件濾波的方法去除這些干擾信號,一般情況下,一個按鍵按下的時候,總是在按下的時刻存在著一定的干擾信號,按下之后就基本上進入了穩定的狀態。具體的一個按鍵從按下到釋放的全過程的信號圖如上圖所示:從圖中可以看出,我們在程序設計時,從按鍵被識別按下之后,延時 5ms 以上,從而避開了干擾信號區域,我們再來檢測一次,看按鍵是否真得已經按下,若真得已經按下,這時肯定輸出為低電平,若這時檢測到的是高電平,證明剛才是由于干擾信號引起的誤觸發,CPU 就認為是誤觸發信號而舍棄這次的按鍵識別過程。從而提高了系統的可靠性。 由于要求每按下一次,命令被執行一次,直到下一次再按下的時候,再執行一次命令,因此從按鍵被識別出來之后,我們就可以執行這次的命令,所以要有一個等待按鍵釋放的過程,顯然釋放的過程,就是使其恢復成高電平狀態。
1. 匯編源程序
ORG 0000H START: MOV R1,#00H ;初始化 R1 為 0,表示從 0 開始計數 MOV A, R1 ; CPL A ;取反指令 MOV P1,A ;送出 P1 端口由發光二極管顯示 REL: JB P3.7,REL ;判斷 SP1 是否按下 LCALL DELAY10MS ;若按下,則延時 10ms 左右 JB P3.7,REL ;再判斷 SP1 是否真得按下 INC R1 ;若確實按下,則進行按鍵處理,使 MOV A,R1 ;計數內容加 1,并送出 P1 端口由 CPL A ;發光二極管顯示 MOV P1,A ; JNB P3.7,$ ;等待 SP1 釋放 SJMP REL ;繼續對 K1 按鍵掃描 DELAY10MS: MOV R6,#20 ;延時 10ms 子程序 L1: MOV R7,#248 DJNZ R7,$ DJNZ R6,L1 RET END
2. C 語言源程序
#include <AT89X51.H> unsigned char count; void delay10ms(void) { unsigned char i,j; for(i=20;i>0;i--) for(j=248;j>0;j--); } void main(void) { while(1) { if(P3_7==0) { delay10ms(); if(P3_7==0) { count++; if(count==16) { count=0; } P1=~count; while(P3_7==0); } } } }
八、數字鐘(1. 開機時,顯示 12:00:00 的時間開始計時; (2. P0.0/AD0 控制“秒”的調整,每按一次加 1 秒; (3. P0.1/AD1 控制“分”的調整,每按一次加 1 分;  (4. P0.2/AD2 控制“時”的調整,每按一次加 1 個小時
6. 匯編源程序 SECOND EQU 30H MINITE EQU 31H HOUR EQU 32H HOURK BIT P0.0 MINITEK BIT P0.1 SECONDK BIT P0.2 DISPBUF EQU 40H DISPBIT EQU 48H T2SCNTA EQU 49H T2SCNTB EQU 4AH TEMP EQU 4BH ORG 00H LJMP START ORG 0BH LJMP INT_T0 START: MOV SECOND,#00H MOV MINITE,#00H MOV HOUR,#12 MOV DISPBIT,#00H MOV T2SCNTA,#00H MOV T2SCNTB,#00H MOV TEMP,#0FEH LCALL DISP MOV TMOD,#01H MOV TH0,#(65536-2000) / 256 MOV TL0,#(65536-2000) MOD 256 SETB TR0 SETB ET0 SETB EA WT: JB SECONDK,NK1 LCALL DELY10MS JB SECONDK,NK1 INC SECOND MOV A,SECOND CJNE A,#60,NS60 MOV SECOND,#00H NS60: LCALL DISP JNB SECONDK,$ NK1: JB MINITEK,NK2 LCALL DELY10MS JB MINITEK,NK2 INC MINITE MOV A,MINITE CJNE A,#60,NM60 MOV MINITE,#00H NM60: LCALL DISP JNB MINITEK,$ NK2: JB HOURK,NK3 LCALL DELY10MS JB HOURK,NK3 INC HOUR MOV A,HOUR CJNE A,#24,NH24 MOV HOUR,#00H NH24: LCALL DISP JNB HOURK,$ NK3: LJMP WT DELY10MS: MOV R6,#10 D1: MOV R7,#248 DJNZ R7,$ DJNZ R6,D1 RET DISP: MOV A,#DISPBUF ADD A,#8 DEC A MOV R1,A MOV A,HOUR MOV B,#10 DIV AB MOV @R1,A DEC R1 MOV A,B MOV @R1,A DEC R1 MOV A,#10 MOV@R1,A DEC R1 MOV A,MINITE MOV B,#10 DIV AB MOV @R1,A DEC R1 MOV A,B MOV @R1,A DEC R1 MOV A,#10 MOV@R1,A DEC R1 MOV A,SECOND MOV B,#10 DIV AB MOV @R1,A DEC R1 MOV A,B MOV @R1,A DEC R1 RET INT_T0: MOV TH0,#(65536-2000) / 256 MOV TL0,#(65536-2000) MOD 256 MOV A,#DISPBUF ADD A,DISPBIT MOV R0,A MOV A,@R0 MOV DPTR,#TABLE MOVC A,@A+DPTR MOV P1,A MOV A,DISPBIT MOV DPTR,#TAB MOVC A,@A+DPTR MOV P3,A INC DISPBIT MOV A,DISPBIT CJNE A,#08H,KNA MOV DISPBIT,#00H KNA: INC T2SCNTA MOV A,T2SCNTA CJNE A,#100,DONE MOV T2SCNTA,#00H INC T2SCNTB MOV A,T2SCNTB CJNE A,#05H,DONE MOV T2SCNTB,#00H INC SECOND MOV A,SECOND CJNE A,#60,NEXT MOV SECOND,#00H INC MINITE MOV A,MINITE CJNE A,#60,NEXT MOV MINITE,#00H INC HOUR MOV A,HOUR CJNE A,#24,NEXT MOV HOUR,#00H NEXT: LCALL DISP DONE: RETI TABLE: DB 3FH,06H,5BH,4FH,66H,6DH, 7DH,07H,7FH,6FH,40H TAB: DB 0FEH,0FDH,0FBH,0F7H,0EFH, 0DFH,0BFH,07FH END
7. C 語言源程序 #INClude <AT89X51.H> unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00}; unsigned char dispbitcode[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; unsigned char dispbuf[8]={0,0,16,0,0,16,0,0}; unsigned char dispbitcnt; unsigned char second; unsigned char minite; unsigned char hour; unsigned int tcnt; unsigned char mstcnt; unsigned char i,j; void main(void) { TMOD=0x02; TH0=0x06; TL0=0x06; TR0=1; ET0=1; EA=1; while(1) { if(P0_0==0) { for(i=5;i>0;i--) for(j=248;j>0;j--); if(P0_0==0) { second++; if(second==60) { second=0; } dispbuf[0]=second%10; dispbuf[1]=second/10; while(P0_0==0); } } if(P0_1==0) { for(i=5;i>0;i--) for(j=248;j>0;j--); if(P0_1==0) { minite++; if(minite==60) { minite=0; } dispbuf[3]=minite%10; dispbuf[4]=minite/10; while(P0_1==0); } } if(P0_2==0) { for(i=5;i>0;i--) for(j=248;j>0;j--); if(P0_2==0) { hour++; if(hour==24) { hour=0; } dispbuf[6]=hour%10; dispbuf[7]=hour/10; while(P0_2==0); } } } } void t0(void) interrupt 1 using 0 { mstcnt++; if(mstcnt==8) { mstcnt=0; P1=dispcode[dispbuf[dispbitcnt]]; P3=dispbitcode[dispbitcnt]; dispbitcnt++; if(dispbitcnt==8) { dispbitcnt=0; } } tcnt++; if(tcnt==4000) {
tcnt=0; second++; if(second==60) { second=0; minite++; if(minite==60) { minite=0; hour++; if(hour==24) { hour=0; } } } dispbuf[0]=second%10; dispbuf[1]=second/10; dispbuf[3]=minite%10; dispbuf[4]=minite/10; dispbuf[6]=hour%10; dispbuf[7]=hour/10; } } }
九. ADC0809A/D 轉換器基本應用技術 1 基本知識ADC0809 是帶有 8 位 A/D 轉換器、8 路多路開關以及微處理機兼容的控制邏輯的 CMOS組件。它是逐次逼近式 A/D 轉換器,可以和單片機直接接口。 2 ADC0809 的內部邏輯結構圖9-1
由上圖可知,ADC0809 由一個 8 路模擬開關、一個地址鎖存與譯碼器、一個 A/D轉換器和一個三態輸出鎖存器組成。多路開關可選通 8 個模擬通道,允許 8 路模擬量分時輸入,共用 A/D 轉換器進行轉換。三態輸出鎖器用于鎖存 A/D 轉換完的數字量,當 OE 端為高電平時,才可以從三態輸出鎖存器取走轉換完的數據。 3 引腳結構 IN0-IN7:8 條模擬量輸入通道 ADC0809 對輸入模擬量要求:信號單極性,電壓范圍是 0-5V,若信號太小,必須進行放大;輸入的模擬量在轉換過程中應該保持不變,如若模擬量變化太快,則需在輸入前增加采樣保持電路。 地址輸入和控制線:4 條 ALE 為地址鎖存允許輸入線,高電平有效。當 ALE 線為高電平時,地址鎖存與譯 碼器將 A,B,C 三條地址線的地址信號進行鎖存,經譯碼后被選中的通道的模擬 量進轉換器進行轉換。A,B 和 C 為地址輸入線,用于選通 IN0-IN7 上的一路模 擬量輸入。通道選擇表如下表所示。 C | B | A | 選擇的通道 | 0 | 0 | 0 | IN0 | 0 | 0 | 1 | IN1 | 0 | 1 | 0 | IN2 | 0 | 1 | 1 | IN3 | 1 | 0 | 0 | IN4 | 1 | 0 | 1 | IN5 | 1 | 1 | 0 | IN6 | 1 | 1 | 1 | IN7 |
數字量輸出及控制線:11 條 ST 為轉換啟動信號。當 ST 上跳沿時,所有內部寄存器清零;下跳沿時,開始進 行 A/D 轉換;在轉換期間,ST 應保持低電平。EOC 為轉換結束信號。當 EOC 為高 電平時,表明轉換結束;否則,表明正在進行 A/D 轉換。OE 為輸出允許信號, 用于控制三條輸出鎖存器向單片機輸出轉換得到的數據。OE=1,輸出轉換得到 的數據;OE=0,輸出數據線呈高阻狀態。D7-D0 為數字量輸出線。 CLK 為時鐘輸入信號線。因 ADC0809 的內部沒有時鐘電路,所需時鐘信號必須由 外界提供,通常使用頻率為 500KHZ,VREF(+),VREF(-)為參考電壓輸入。 4 ADC0809 應用說明 (1). ADC0809 內部帶有輸出鎖存器,可以與 AT89S51 單片機直接相連。 (2). 初始化時,使 ST 和 OE 信號全為低電平。 (3). 送要轉換的哪一通道的地址到 A,B,C 端口上。 (4). 在 ST 端給出一個至少有 100ns 寬的正脈沖信號。 (5). 是否轉換完畢,我們根據 EOC 信號來判斷。 (6). 當 EOC 變為高電平時,這時給 OE 為高電平,轉換的數據就輸出給單片機了。 5 實驗任務 如下圖所示,從 ADC0809 的通道 IN3 輸入 0-5V 之間的模擬量,通過 ADC0809 轉換成數字量在數碼管上以十進制形成顯示出來。ADC0809 的 VREF 接+5V電壓。 6 電路原理圖
5. 系統板上硬件連線 (1). 把“單片機系統板”區域中的 P1 端口的 P1.0-P1.7 用 8 芯排線連接到“動態數碼顯示”區域中的 A B C D E F G H 端口上,作為數碼管的筆段驅動。 (2). 把“單片機系統板”區域中的 P2 端口的 P2.0-P2.7 用 8 芯排線連接到“動態數碼顯示”區域中的 S1 S2 S3 S4 S5 S6 S7 S8 端口上,作為數碼管的位段選擇。 (3). 把“單片機系統板”區域中的 P0 端口的 P0.0-P0.7 用 8 芯排線連接到“模數轉換模塊”區域中的 D0D1D2D3D4D5D6D7 端口上,A/D 轉換完畢的數據輸入到單片機的 P0 端口 (4). 把“模數轉換模塊”區域中的 VREF 端子用導線連接到“電源模塊”區域中的 VCC 端子上; (5). 把“模數轉換模塊”區域中的 A2A1A0 端子用導線連接到“單片機系統”區域中的 P3.4 P3.5 P3.6 端子上; (6). 把“模數轉換模塊”區域中的 ST 端子用導線連接到“單片機系統” 區域中的 P3.0 端子上; (7). 把“模數轉換模塊”區域中的 OE 端子用導線連接到“單片機系統”區域中的 P3.1 端子上; (8).把“模數轉換模塊”區域中的 EOC 端子用導線連接到“單片機系統”區域中的 P3.2 端子上; (9). 把“模數轉換模塊”區域中的 CLK 端子用導線連接到“分頻模塊”區域中的 /4 端子上; (10). 把“分頻模塊”區域中的 CK IN 端子用導線連接到“單片機系統”區域中的 ALE 端子上; (11). 把“模數轉換模塊”區域中的 IN3 端子用導線連接到“三路可調壓模塊”區域中的 VR1 端子上; 6. 程序設計內容 (1). 進行 A/D 轉換時,采用查詢 EOC 的標志信號來檢測 A/D 轉換是否完畢,若完畢則把數據通過 P0 端口讀入,經過數據處理之后在數碼管上顯示。 (2). 進行 A/D 轉換之前,要啟動轉換的方法:ABC=110 選擇第三通道ST=0,ST=1,ST=0 產生啟動轉換的正脈沖信號
7. 匯編源程序 - CH EQU 30H
- DPCNT EQU 31H
- DPBUF EQU 33H
- GDATA EQU 32H
- ST BIT P3.0
- OE BIT P3.1
- EOC BIT P3.2
- ORG 00H
- LJMP START
- ORG 0BH
- LJMP T0X
- ORG 30H
- START: MOV CH,#0BCH
- MOV DPCNT,#00H
- MOV R1,#DPCNT
- MOV R7,#5
- MOV A,#10
- MOV R0,#DPBUF
- LOP: MOV @R0,A
- INC R0
- DJNZ R7,LOP
- MOV @R0,#00H
- INC R0
- MOV @R0,#00H
- INC R0
- MOV @R0,#00H
- MOV TMOD,#01H
- MOV TH0,#(65536-4000)/256
- MOV TL0,#(65536-4000)
- MOD 256
- SETB TR0
- SETB ET0
- SETB EA
- WT: CLR ST
- SETB ST
- CLR ST
- WAIT: JNB EOC,WAIT
- SETB OE
- MOV GDATA,P0
- CLR OE
- MOV A,GDATA
- MOV B,#100
- DIV AB
- MOV 33H,A
- MOV A,B
- MOV B,#10
- DIV AB
- MOV 34H,A
- MOV 35H,B
- SJMP WT
- T0X: NOP
- MOV TH0,#(65536-4000)/256
- MOV TL0,#(65536-4000) MOD 256
- MOV DPTR,#DPCD
- MOV A,DPCNT
- ADD A,#DPBUF
- MOV R0,A
- MOV A,@R0
- MOVC A,@A+DPTR
- MOV P1,A
- MOV DPTR,#DPBT
- MOV A,DPCNT
- MOVC A,@A+DPTR
- MOV P2,A
- INC DPCNT
- MOV A,DPCNT
- CJNE A,#8,NEXT
- MOV DPCNT,#00H
- NEXT: RETI
- DPCD: DB 3FH,06H,5BH,4FH,66H
- DB 6DH,7DH,07H,7FH,6FH,00H
- DPBT: DB 0FEH,0FDH,0FBH,0F7H
- DB 0EFH,0DFH,0BFH,07FH
- END
復制代碼
8. C 語言源程序 - #INClude <AT89X52.H>
- unsigned char code dispbitcode[]={0xfe,0xfd,0xfb,0xf7,
- 0xef,0xdf,0xbf,0x7f};
- unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,0x66,
- 0x6d,0x7d,0x07,0x7f,0x6f,0x00};
- unsigned char dispbuf[8]={10,10,10,10,10,0,0,0};
- unsigned char dispcount;
- sbit ST=P3^0;
- sbit OE=P3^1;
- sbit EOC=P3^2;
- unsigned char channel=0xbc;//IN3
- unsigned char getdata;
- void main(void)
- {
- TMOD=0x01;
- TH0=(65536-4000)/256;
- TL0=(65536-4000)%256;
- TR0=1;
- ET0=1;
- EA=1;
- P3=channel;
- while(1)
- {
- ST=0;
- ST=1;
- ST=0;
- while(EOC==0);
- OE=1;
- getdata=P0;
- OE=0;
- dispbuf[2]=getdata/100;
- getdata=getdata%10;
- dispbuf[1]=getdata/10;
- dispbuf[0]=getdata%10;
- }
- }
- void t0(void) interrupt 1 using 0
- {
- TH0=(65536-4000)/256;
- TL0=(65536-4000)%256;
- P1=dispcode[dispbuf[dispcount]];
- P2=dispbitcode[dispcount];
- dispcount++;
- if(dispcount==8)
- {
- dispcount=0;
- }
- }
復制代碼
3
十、DS18B20 數字溫度計使用1.DS18B20 基本知識 DS18B20 數字溫度計是 DALLAS 公司生產的 1-Wire,即單總線器件,具有線路簡單,體積小的特點。因此用它來組成一個測溫系統,具有線路簡單,在一根通信線,可以掛很多這樣的數字溫度計,十分方便。 1、DS18B20 產品的特點 (1)、只要求一個端口即可實現通信。 (2)、在 DS18B20 中的每個器件上都有獨一無二的序列號。 (3)、實際應用中不需要外部任何元器件即可實現測溫。 (4)、測量溫度范圍在-55。C 到+125。C 之間。 (5)、數字溫度計的分辨率用戶可以從 9 位到 12 位選擇。 (6)、內部有溫度上、下限告警設置。 2、DS18B20 的引腳介紹 TO-92 封裝的 DS18B20 的引腳排列見圖 1,其引腳功能描述見表 1。(底視圖)圖 1
由于 DS18B20 采用的是 1-Wire 總線協議方式,即在一根數據線實現數據的雙向傳輸,而對 AT89S51 單片機來說,硬件上并不支持單總線協議,因此,我們必須采用軟件的方法來模擬單總線的協議時序來完成對 DS18B20 芯片的訪問。
由于 DS18B20 是在一根 I/O 線上讀寫數據,因此,對讀寫的數據位有著嚴格的時序要求。DS18B20 有嚴格的通信協議來保證各位數據傳輸的正確性和完整性。該協議定義了幾種信號的時序:初始化時序、讀時序、寫時序。所有時序都是將主機作為主設備,單總線器件作為從設備。而每一次命令和數據的傳輸都是從主機主動啟動寫時序開始,如果要求單總線器件回送數據,在進行寫命令后,主機需啟動讀時序完成數據接收。數據和命令的傳輸都是低位在先。  DS18B20 的復位時序
 DS18B20 的讀時序對于DS18B20 的讀時序分為讀 0時序和讀 1 時序兩個過程。對于 DS18B20的讀時隙是從主機把單總線拉低之后,在 15 秒之內就得釋放單總線,以讓 DS18B20 把數據傳輸到單總線上。DS18B20 在完成一個讀時序過程,至少需要 60us 才能完成。

對于 DS18B20 的寫時序仍然分為寫 0 時序和寫 1 時序兩個過程。對于DS18B20 寫 0 時序和寫 1 時序的要求不同,當要寫 0 時序時,單總線要被拉低至少 60us,保證 DS18B20 能夠在 15us 到 45us 之間能夠正確地采樣 IO 總線上的“0”電平,當要寫 1 時序時,單總線被拉低之后,在 15us 之內就得釋放單總線。
4. 實驗任務 用一片 DS18B20 構成測溫系統,測量的溫度精度達到 0.1 度,測量的溫度的范圍在-20 度到+100 度之間,用 8 位數碼管顯示出來。 5. 電路原理圖
7. C 語言源程序 - #INClude <AT89X52.H>
- #INClude <INTRINS.h>
- unsigned char code displaybit[]={0xfe,0xfd,0xfb,0xf7,
- 0xef,0xdf,0xbf,0x7f};
- unsigned char code displaycode[]={0x3f,0x06,0x5b,0x4f,
- 0x66,0x6d,0x7d,0x07,
- 0x7f,0x6f,0x77,0x7c,
- 0x39,0x5e,0x79,0x71,0x00,0x40};
- unsigned char code dotcode[32]={0,3,6,9,12,16,19,22,
- 25,28,31,34,38,41,44,48,
- 50,53,56,59,63,66,69,72,
- 75,78,81,84,88,91,94,97};
- unsigned char displaycount;
- unsigned char displaybuf[8]={16,16,16,16,16,16,16,16};
- unsigned char timecount;
- unsigned char readdata[8];
- sbit DQ=P3^7;
- bit sflag;
- bit resetpulse(void)
- {
- unsigned char i;
- DQ=0;
- for(i=255;i>0;i--);
- DQ=1;
- for(i=60;i>0;i--);
- RETurn(DQ);
- for(i=200;i>0;i--);
- }
- void writecommandtods18b20(unsigned char command)
- {
- unsigned char i;
- unsigned char j;
- for(i=0;i<8;i++)
- {
- if((command & 0x01)==0)
- {
- DQ=0;
- for(j=35;j>0;j--);
- DQ=1;
- }
- else
- {
- DQ=0;
- for(j=2;j>0;j--);
- DQ=1;
- for(j=33;j>0;j--);
- }
- command=_cror_(command,1);
- }
- }
- Unsigned char readdatafromds18b20(void)
- Unsigned char i;
- Unsigned char j;
- Unsigned char temp;
- temp=0;
- for(i=0;i<8;i++)
- {
- temp=_cror_(temp,1);
- DQ=0;
- _nop_();
- _nop_();
- DQ=1;
- for(j=10;j>0;j--);
- if(DQ==1)
- {
- temp=temp | 0x80;
- }
- else
- {
- temp=temp | 0x00;
- }
- for(j=200;j>0;j--);
- }
- RETurn(temp);
- }
- void main(void)
- {
- TMOD=0x01;
- TH0=(65536-4000)/256;
- TL0=(65536-4000)%256;
- ET0=1;
- EA=1;
- while(resetpulse());
- writecommandtods18b20(0xcc);
- writecommandtods18b20(0x44);
- TR0=1;
- while(1)
- {
- ;
- }
- }
- void t0(void) interrupt 1 using 0
- {
- unsigned char x;
- unsigned int result;
- TH0=(65536-4000)/256;
- TL0=(65536-4000)%256;
- if(displaycount==2)
- {
- P0=displaycode[displaybuf[displaycount]] | 0x80;
- }
- else
- {
- P0=displaycode[displaybuf[displaycount]];
- }
- P2=displaybit[displaycount];
- displaycount++;
- if(displaycount==8)
- {
- displaycount=0;
- }
- timecount++;
- if(timecount==150)
- {
- timecount=0;
- while(resetpulse());
- writecommandtods18b20(0xcc);
- writecommandtods18b20(0xbe);
- readdata[0]=readdatafromds18b20();
- readdata[1]=readdatafromds18b20();
- for(x=0;x<8;x++)
- {
- displaybuf[x]=16;
- }
- sflag=0;
- if((readdata[1] & 0xf8)!=0x00)
- {
- sflag=1;
- readdata[1]=~readdata[1];
- readdata[0]=~readdata[0];
- result=readdata[0]+1;
- readdata[0]=result;
- if(result>255)
- {
- readdata[1]++;
- }
- }
- readdata[1]=readdata[1]<<4;
- readdata[1]=readdata[1] & 0x70;
- x=readdata[0];
- x=x>>4;
- x=x & 0x0f;
- readdata[1]=readdata[1] | x;
- x=2;
- result=readdata[1];
- while(result/10)
- {
- displaybuf[x]=result%10;
- result=result/10;
- x++;
- }
- displaybuf[x]=result;
- if(sflag==1)
- {
- displaybuf[x+1]=17;
- }
- x=readdata[0] & 0x0f;
- x=x<<1;
- displaybuf[0]=(dotcode[x])%10;
- displaybuf[1]=(dotcode[x])/10;
- while(resetpulse());
- writecommandtods18b20(0xcc);
- writecommandtods18b20(0x44);
- }
- }
復制代碼
11、 4×4 鍵盤及 8 位數碼管顯示構成的電子密碼鎖用 4×4 組成 0-9 數字鍵及確認鍵。
用 8 位數碼管組成顯示電路提示信息,當輸入密碼時,只顯示“8.”,當密碼位數輸入完畢按下確認鍵時,對輸入的密碼與設定的密碼進行比較,若密碼正確,則門開,此處用 LED 發光二極管亮一秒鐘做為提示,同時發出“叮咚”聲;若密碼不正確,禁止按鍵輸入 3 秒,同時發出“嘀、嘀” 報警聲;若在 3 秒之內仍有按鍵按下,則禁止按鍵輸入 3 秒被重新禁止。 4. 程序設計內容 (1). 4×4 行列式鍵盤識別技術:有關這方面內容前面已經討論過,這里不再重復。 (2). 8 位數碼顯示,初始化時,顯示“P ”,接著輸入最大 6 位數的密碼,當密碼輸入完后,按下確認鍵,進行密碼比較,然后給出相應的信息。在輸入密碼過程中,顯示器只顯示“8.”。當數字輸入超過 6個時,給出報警信息。在密碼輸入過程中,若輸入錯誤,可以利用“DEL”鍵刪除剛才輸入的錯誤的數字。 (3). 4×4 行列式鍵盤的按鍵功能分布圖如圖 4.33.2 所示:
5. C 語言源程序 - #include <AT89X52.H>
- unsigned char ps[]={1,2,3,4,5};
- unsigned char code dispbit[]={0xfe,0xfd,0xfb,0xf7,
- 0xef,0xdf,0xbf,0x7f};
- unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,0x66,
- 0x6d,0x7d,0x07,0x7f,0x6f,
- 0x77,0x7c,0x39,0x5e,0x79,0x71,
- 0x00,0x40,0x73,0xff};
- unsigned char dispbuf[8]={18,16,16,16,16,16,16,16};
- unsigned char dispcount;
- unsigned char flashcount;
- unsigned char temp;
- unsigned char key;
- unsigned char keycount;
- unsigned char pslen=5;
- unsigned char getps[6];
- bit keyoverflag;
- bit errorflag;
- bit rightflag;
- unsigned int second3;
- unsigned int aa,bb;
- unsigned int cc;
- bit okflag;
- bit alarmflag;
- bit hibitflag;
- unsigned char oka,okb;
- void main(void)
- {
- unsigned char i,j;
- TMOD=0x01;
- TH0=(65536-500)/256;
- TL0=(65536-500)%256;
- TR0=1;
- ET0=1;
- EA=1;
- while(1)
- {
- P3=0xff;
- P3_4=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=10;i>0;i--)
- for(j=248;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=7;
- break;
- case 0x0d:
- key=8;
- break;
- case 0x0b:
- key=9;
- break;
- case 0x07:
- key=10;
- break;
- }
- temp=P3;
- P1_1=~P1_1;
- if((key>=0) && (key<10))
- {
- if(keycount<6)
- {
- getps[keycount]=key;
- dispbuf[keycount+2]=19;
- }
- keycount++;
- if(keycount==6)
- {
- keycount=6;
- }
- else if(keycount>6)
- {
- keycount=6;
- keyoverflag=1;//key overflow
- }
- }
- else if(key==12)//delete key
- {
- if(keycount>0)
- {
- keycount--;
- getps[keycount]=0;
- dispbuf[keycount+2]=16;
- }
- else
- {
- keyoverflag=1;
- }
- }
- else if(key==15)//enter key
- {
- if(keycount!=pslen)
- {
- errorflag=1;
- rightflag=0;
- second3=0;
- }
- else
- {
- for(i=0;i<keycount;i++)
- {
- if(getps[ i]!=ps[ i])
- {
- i=keycount;
- errorflag=1;
- rightflag=0;
- second3=0;
- goto a;
- }
- }
- errorflag=0;
- rightflag=1;
- a: i=keycount;
- }
- }
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- keyoverflag=0;//?????????
- }
- }
- P3=0xff;
- P3_5=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=10;i>0;i--)
- for(j=248;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=4;
- break;
- case 0x0d:
- key=5;
- break;
- case 0x0b:
- key=6;
- break;
- case 0x07:
- key=11;
- break;
- }
- temp=P3;
- P1_1=~P1_1;
- if((key>=0) && (key<10))
- {
- if(keycount<6)
- {
- getps[keycount]=key;
- dispbuf[keycount+2]=19;
- }
- keycount++;
- if(keycount==6)
- {
- keycount=6;
- }
- else if(keycount>6)
- {
- keycount=6;
- keyoverflag=1;//key overflow
- }
- }
- else if(key==12)//delete key
- {
- if(keycount>0)
- {
- keycount--;
- getps[keycount]=0;
- dispbuf[keycount+2]=16;
- }
- else
- {
- keyoverflag=1;
- }
- }
- else if(key==15)//enter key
- {
- if(keycount!=pslen)
- {
- errorflag=1;
- rightflag=0;
- second3=0;
- }
- else
- {
- for(i=0;i<keycount;i++)
- {
- if(getps[ i]!=ps[ i])
- {
- i=keycount;
- errorflag=1;
- rightflag=0;
- second3=0;
- goto a4;
- }
- }
- errorflag=0;
- rightflag=1;
- a4: i=keycount;
- }
- }
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- keyoverflag=0;//?????????
- }
- }
- P3=0xff;
- P3_6=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=10;i>0;i--)
- for(j=248;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=1;
- break;
- case 0x0d:
- key=2;
- break;
- case 0x0b:
- key=3;
- break;
- case 0x07:
- key=12;
- break;
- }
- temp=P3;
- P1_1=~P1_1;
- if((key>=0) && (key<10))
- {
- if(keycount<6)
- {
- getps[keycount]=key;
- dispbuf[keycount+2]=19;
- }
- keycount++;
- if(keycount==6)
- {
- keycount=6;
- }
- else if(keycount>6)
- {
- keycount=6;
- keyoverflag=1;//key overflow
- }
- }
- else if(key==12)//delete key
- {
- if(keycount>0)
- {
- keycount--;
- getps[keycount]=0;
- dispbuf[keycount+2]=16;
- }
- else
- {
- keyoverflag=1;
- }
- }
- else if(key==15)//enter key
- {
- if(keycount!=pslen)
- {
- errorflag=1;
- rightflag=0;
- second3=0;
- }
- else
- {
- for(i=0;i<keycount;i++)
- {
- if(getps[ i]!=ps[ i])
- {
- i=keycount;
- errorflag=1;
- rightflag=0;
- second3=0;
- goto a3;
- }
- }
- errorflag=0;
- rightflag=1;
- a3: i=keycount;
- }
- }
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- keyoverflag=0;//?????????
- }
- }
- P3=0xff;
- P3_7=0;
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- for(i=10;i>0;i--)
- for(j=248;j>0;j--);
- temp=P3;
- temp=temp & 0x0f;
- if (temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- switch(temp)
- {
- case 0x0e:
- key=0;
- break;
- case 0x0d:
- key=13;
- break;
- case 0x0b;
- key=14;
- break;
- case 0x07:
- key=15;
- break;
- }
- temp=P3;
- P1_1=~P1_1;
- if((key>=0) && (key<10))
- {
- if(keycount<6)
- {
- getps[keycount]=key;
- dispbuf[keycount+2]=19;
- }
- keycount++;
- if(keycount==6)
- {
- keycount=6;
- }
- else if(keycount>6)
- {
- keycount=6;
- keyoverflag=1;//key overflow
- }
- }
- else if(key==12)//delete key
- {
- if(keycount>0)
- {
- keycount--;
- getps[keycount]=0;
- dispbuf[keycount+2]=16;
- }
- else
- {
- keyoverflag=1;
- }
- }
- else if(key==15)//enter key
- {
- if(keycount!=pslen)
- {
- errorflag=1;
- rightflag=0;
- second3=0;
- }
- else
- {
- for(i=0;i<keycount;i++)
- {
- if(getps[ i]!=ps[ i])
- {
- i=keycount;
- errorflag=1;
- rightflag=0;
- second3=0;
- goto a2;
- }
- }
- errorflag=0;
- rightflag=1;
- a2: i=keycount;
- }
- }
- temp=temp & 0x0f;
- while(temp!=0x0f)
- {
- temp=P3;
- temp=temp & 0x0f;
- }
- keyoverflag=0;//?????????
- }
- }
- }
- }
- void t0(void) interrupt 1 using 0
- {
- TH0=(65536-500)/256;
- TL0=(65536-500)%256;
- flashcount++;
- if(flashcount==8)
- {
- flashcount=0;
- P0=dispcode[dispbuf[dispcount]];
- P2=dispbit[dispcount];
- dispcount++;
- if(dispcount==8)
- {
- dispcount=0;
- }
- }
- if((errorflag==1) && (rightflag==0))
- {
- bb++;
- if(bb==800)
- {
- bb=0;
- alarmflag=~alarmflag;
- }
- if(alarmflag==1)//sound alarm signal
- {
- P1_7=~P1_7;
- }
- aa++;
- if(aa==800)//light alarm signal
- {
- aa=0;
- P1_0=~P1_0;
- }
- second3++;
- if(second3==6400)
- {
- second3=0;
- errorflag=0;
- rightflag=0;
- alarmflag=0;
- bb=0;
- aa=0;
- }
- }
- else if((errorflag==0) && (rightflag==1))
- {
- P1_0=0;
- cc++;
- if(cc<1000)
- {
- okflag=1;
- }
- else if(cc<2000)
- {
- okflag=0;
- }
- else
- {
- errorflag=0;
- rightflag=0;
- P1_7=1;
- cc=0;
- oka=0;
- okb=0;
- okflag=0;
- P1_0=1;
- }
- if(okflag==1)
- {
- oka++;
- if(oka==2)
- {
- oka=0;
- P1_7=~P1_7;
- }
- }
- else
- {
- okb++;
- if(okb==3)
- {
- okb=0;
- P1_7=~P1_7;
- }
- }
- }
- if(keyoverflag==1)
- {
- P1_7=~P1_7;
- }
- }
復制代碼
12. 6 位數顯頻率計數器1. 實驗任務 利用 AT89S51 單片機的 T0、T1 的定時計數器功能,來完成對輸入的信號進行頻率計數,計數的頻率結果通過 8 位動態數碼管顯示出來。要求能夠對0-250KHZ 的信號頻率進行準確計數,計數誤差不超過±1HZ。 3. 程序設計內容 (1). 定時/計數器 T0 和 T1 的工作方式設置,由圖可知,T0 是工作在計數狀態下,對輸入的頻率信號進行計數,但對工作在計數狀態下的T0,最大計數值為 fOSC/24,由于 fOSC=12MHz,因此:T0 的最大計數頻率為250KHz。對于頻率的概念就是在一秒只數脈沖的個數,即為頻率值。 所以 T1 工作在定時狀態下,每定時 1 秒中到,就停止 T0 的計數,而從 T0 的計數單元中讀取計數的數值,然后進行數據處理。送到數碼管顯示出來。 (2). T1 工作在定時狀態下,最大定時時間為 65ms,達不到 1 秒的定時,所 以采用定時 50ms,共定時 20 次,即可完成 1 秒的定時功能。 
. C 語言源程序 - #include <AT89X52.H>
- unsigned char code dispbit[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
- unsigned char code dispcode[]={0x3f,0x06,0x5b,0x4f,0x66,
- 0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x40};
- unsigned char dispbuf[8]={0,0,0,0,0,0,10,10};
- unsigned char temp[8];
- unsigned char dispcount;
- unsigned char T0count;
- unsigned char timecount;
- bit flag;
- unsigned long x;
- void main(void)
- {
- unsigned char i;
- TMOD=0x15;
- TH0=0;
- TL0=0;
- TH1=(65536-4000)/256;
- TL1=(65536-4000)%256;
- TR1=1;
- TR0=1;
- ET0=1;
- ET1=1;
- EA=1;
- while(1)
- {
- if(flag==1)
- {
- flag=0;
- x=T0count*65536+TH0*256+TL0;
- for(i=0;i<8;i++)
- {
- temp[ i]=0;
- }
- i=0;
- while(x/10)
- {
- temp[ i]=x%10;
- x=x/10;
- i++;
- }
- temp[ i]=x;
- for(i=0;i<6;i++)
- {
- dispbuf[ i]=temp[ i];
- }
- timecount=0;
- T0count=0;
- TH0=0;
- TL0=0;
- TR0=1;
- }
- }
- }
- void t0(void) interrupt 1 using 0
- {
- T0count++;
- }
- void t1(void) interrupt 3 using 0
- {
- TH1=(65536-4000)/256;
- TL1=(65536-4000)%256;
- timecount++;
- if(timecount==250)
- {
- TR0=0;
- timecount=0;
- flag=1;
- }
- P0=dispcode[dispbuf[dispcount]];
- P2=dispbit[dispcount];
- dispcount++;
- if(dispcount==8)
- {
- dispcount=0;
- }
- }
復制代碼
13、LCD驅動程序
根據原理圖設計LCD1602液晶顯示電路的驅動程序: - /********************************
- FILE NAME: LCD1602.c
- CHIP TYPE: AT89C51
- CLOCK FREQUENCY: 12MHZ
- IDE: VSMStudio
- COMPILER: IAR for 8051
- TIME: 7/ 2012
- ********************************/
- #include "ioAT89C51.h"
- #include "intrinsics.h"
- // Define P3 pins
- #define DATA_BUS (P0);數據總線P0
- #define RS (P2_bit.P2_0);P2.0
- #define RW (P2_bit.P2_1) ;P2.1
- #define E (P2_bit.P2_2);P2.2
- // Define new types
- typedef unsigned char uchar;無符號字符型
- typedef unsigned int uint;無符號整型
- // Function Prototypes
- void check_busy(void);檢查忙信號
- void write_command(uchar com);寫指令
- void write_data(uchar data);寫數據
- void LCD_init(void);LCD初始化
- void string(uchar ad ,uchar *s);
- void lcd_test(void);測試函數
- void delay(uint);延時函數
- void main(void);主函數
- { LCD_init();
- while(1)
- { string(0x80,"Have a nice day!");
- string(0xC0," Proteus VSM");
- delay(100);
- write_command(0x01);
- delay(100);
- }
- }
- /*******************************************
- LCD1602 Driver mapped as IO peripheral
- *******************************************/
- // Delay,延時
- void delay(uint j)
- { uchar i = 60;
- for(; j>0; j--)
- { while(--i);
- i = 59;
- while(--i);
- i = 60;
- }
- }
- // Test the Busy bit,測試忙位BF
- void check_busy(void)
- { do
- { DATA_BUS = 0xff;
- E = 0;
- RS = 0;
- RW = 1;
- E = 1;高電平
- __no_operation();
- } while(DATA_BUS & 0x80);
- E = 0;
- }
- // Write a command,寫指令
- void write_command(uchar com)
- { check_busy();
- E = 0;
- RS = 0;
- RW = 0;
- DATA_BUS = com;
- E = 1;
- __no_operation();
- E = 0;下降沿
- delay(1);
- }
- // Write Data,寫數據
- void write_data(uchar data)
- { check_busy();
- E = 0;
- RS = 1;
- RW = 0;
- DATA_BUS = data;
- E = 1;
- __no_operation();
- E = 0;
- delay(1);
- }
- // Initialize LCD controller
- void LCD_init(void)
- { write_command(0x38); // 8-bits, 2 lines, 7x5 dots
- write_command(0x0C); // no cursor, no blink, //enable display
- write_command(0x06); // auto-INCrement on
- write_command(0x01); // clear screen
- delay(1);
- }
- // Display a string
- void string(uchar ad, uchar *s)
- { write_command(ad);
- while(*s>0)
- { write_data(*s++);
- delay(100);
- }
- }
復制代碼
- ; LCD Display Driver Demo.
- ; Timing code assumes 1.2MHz Clock
- ;LCD Registers addresses
- LCD_CMD_WR EQU 00H
- LCD_DATA_WR EQU 01H
- LCD_BUSY_RD EQU 02H
- LCD_DATA_RD EQU 03H
- ;LCD Commands
- LCD_CLS EQU 01
- LCD_HOME EQU 02
- LCD_SETMODE EQU 04
- LCD_SETVISIBLE EQU 08
- LCD_SHIFT EQU 16
- LCD_SETFUNCTION EQU 32
- LCD_SETCGADDR EQU 64
- LCD_SETDDADDR EQU 128
- ;Reset vector
- ORG 0000h
- JMP start;//Start of the program
- ORG 0100h
- string1a:DB ' !! A M A Z I N G !! '
- DB 0
- string1b:DB '!! A M A Z I N G !! '
- DB 0
- string2:DB ' A virtual LM032L... '
- DB 0
- string3:DB ' driven by a virtual '
- DB 0
- string4:DB ' 8051 processor!'
- DB 0
- start: MOV A,#038h; 8位2行,5*7點陣
- CALL wrcmd
- loop: MOV A,#LCD_SETVISIBLE+6 ;Make the display & blink visible:開顯示,光標不閃爍
- CALL wrcmd
- MOV R7,#2
- loop2: MOV DPTR,#string1a
- CALL wrstr
- MOV DPTR,#200
- CALL wtms
- MOV A,#LCD_CLS ;Clear screen
- CALL wrcmd
- MOV DPTR,#string1b
- CALL wrstr
- MOV DPTR,#200
- CALL wtms
- MOV A, #LCD_CLS ;Clear screen
- CALL wrcmd
- DJNZ R7,loop2
- MOV DPTR,#string1a
- CALL wrstr
- MOV DPTR,#400
- CALL wtms
- MOV A,#LCD_SETDDADDR+64
- CALL wrcmd
- MOV DPTR,#string2
- CALL wrslow
- MOV DPTR,#200
- CALL wtms
- MOV A,#LCD_CLS ;Clear screen
- CALL wrcmd
- MOV DPTR,#string3
- CALL wrslow
- MOV A,#LCD_SETDDADDR+64
- CALL wrcmd
- MOV DPTR,#string4
- CALL wrslow
- MOV A,#LCD_SETVISIBLE+7 ;Show the //blink cursor as well.
- CALL wrcmd
- MOV DPTR,#2000
- CALL wtms
- MOV A,#LCD_CLS ;Clear screen
- CALL wrcmd
- JMP loop
- ;Sub routine to write null terminated string at DPTR in program ram.
- wrstr: MOV R0,#LCD_DATA_WR
- wrstr1: CLR A
- MOVC A,@A+DPTR
- JZ wrstr2
- MOVX @R0,A
- CALL wtbusy
- INC DPTR
- PUSH DPL
- PUSH DPH
- POP DPH
- POP DPL
- JMP wrstr1
- wrstr2: RET
- ;Sub routine to write null terminated string at DPTR in program ram. Slowly
- wrslow: MOV R0,#LCD_DATA_WR
- wrslw1: CLR A
- MOVc A,@A+DPTR
- JZ wrslw2
- MOVX @R0,A
- CALL wtbusy
- INC DPTR
- PUSH DPL
- PUSH DPH
- MOV DPTR,#100
- CALL wtms
- POP DPH
- POP DPL
- JMP wrslw1
- wrslw2: RET
- ;Sub routine to write command:
- wrcmd: MOV R0,#LCD_CMD_WR
- MOVX @R0,A
- JMP wtbusy
- ;Sub routine to write character:
- wrchar: MOV R0,#LCD_DATA_WR
- MOVX @R0,A
- ;Subroutine to wait for busy clear
- wtbusy: MOV R1,#LCD_BUSY_RD
- MOVX A,@r1
- JB ACC.7,wtbusy
- RET
- ;Wait for number of seconds in A
- wtsec: PUSH ACC
- CALL wtms
- POP ACC
- DEC A
- JNZ wtsec
- RET
- ;Wait for number of milliseconds in DPTR
- wtms: XRL DPL,#0FFh ;Can't do DEC DPTR, so do the loop by forming 2's complement
- XRL DPH,#0FFh;and INCrementing instead.
- INC DPTR
- wtms1:MOV TL0,#09Ch ;100 ticks before overflow = 1ms at 1.2MHz Clock
- MOV TH0,#0FFh
- MOV TMOD,#1 ;Timer 0 mode 1
- SETB TCON.4 ;Timer 0 runs
- wtms2: JNB TCON.5,wtms2
- CLR TCON.4 ;Timer 0 stops
- CLR TCON.5
- INC DPTR
- MOV A,DPL
- ORL A,DPH
- JNZ wtms1
- RET
- END
復制代碼
完整的Word格式文檔51黑下載地址:
51單片機匯編語言及C語言經典實例.doc
(2.68 MB, 下載次數: 53)
2018-11-5 20:23 上傳
點擊文件名下載附件
|