導語:本期是本次單片機學習知識點的最終回,我們會列出前三回一起方便讀者回顧學習。本次主要知識點為單片機串口通信、接口和實際案例實踐——單片機音樂程序設計的學習。單片機對于初學者來說確實很難理解,不少學過單片機的同學或電子愛好者,甚至在畢業時仍舊是一無所獲。基于此,電子發燒友網將整合《單片機學習知識點全攻略》,共分為四個系列,以饗讀者,敬請期待!此系列對于業內電子工程師也有收藏和參考價值。
?
參閱相關系列
??? 單片機學習知識點全攻略(三)
?
系列四
22:單片機串行口通信程序設計
23:LED數碼管靜態顯示接口與編
24:動態掃描顯示接口電路及程序
25:單片機鍵盤接口程序設計
26:單片機矩陣式鍵盤接口技術及
27:關于單片機的一些基本概念
28:實際案例實踐——單片機音樂程序設計
22、單片機串行口通信程序設計
1.串行口方式0應用編程 8051單片機串行口方式0為移位寄存器方式,外接一個串入并出的移位寄存器,就能擴展一個并行口。
《單片機串行口通信程序設計硬件連接圖》
例:用8051單片機串行口外接CD4094擴展8位并行輸出口,如圖所示,8位并行口的各位都接一個發光二極管,要求發光管呈流水燈狀態。 串行口方式0的數據傳送可采用中斷方式,也可采用查詢方式,無論哪種方式,都要借助于TI或RI標志。串行發送時,能靠TI置位(發完一幀數據后)引起中斷申請,在中斷服務程序中發送下一幀數據,或者通過查詢TI的狀態,只要TI為0就繼續查詢,TI為1就結束查詢,發送下一幀數據。在串行接收時,則由RI引起中斷或對RI查詢來確定何時接收下一幀數據。無論采用什么方式,在開始通信之前,都要先對控制寄存器SCON進行初始化。在方式0中將,將00H送SCON就能了。
-----------------單片機串行口通信程序設計列子--------------------------
ORG 2000H
START: MOV SCON,#00H ;置串行口工作方式0
MOV A,#80H ;最高位燈先亮
CLR P1.0 ;關閉并行輸出(避象傳輸過程中,各LED的“暗紅”現象)
OUT0: MOV SBUF,A ;開始串行輸出
OUT1: JNB TI,OUT1 ;輸出完否
CLR TI ;完了,清TI標志,以備下次發送
SETB P1.0 ;打開并行口輸出
ACALL DELAY ;延時一段時間
RR A ;循環右移
CLR P1.0 ;關閉并行輸出
JMP OUT0 ;循環
說明:DELAY延時子程序能用前面我們講P1口流水燈時用的延時子程序,這里就不給出了。
二、串行口異步通信
org 0000H
AJMP START
ORG 30H
START:
mov SP,#5fh ;
mov TMOD,#20h ;T1: 工作模式2
mov PCON,#80h ;SMOD=1
mov TH1,#0FDH ;初始化波特率(參見表)
mov SCON,#50h ;Standard UART settings
MOV R0,#0AAH ;準備送出的數
SETB REN ;允許接收
SETB TR1 ;T1開始工作
WAIT:
MOV A,R0
CPL A
MOV R0,A
MOV SBUF,A
LCALL DELAY
JBC TI,WAIT1 ;如果TI等于1,則清TI并轉WAIT1
AJMP WAIT
WAIT1: JBC RI,READ ;如果RI等于1,則清RI并轉READ
AJMP WAIT1
READ:
MOV A,SBUF ;將取得的數送P1口
MOV P1,A
LJMP WAIT
DELAY: ;延時子程序
MOV R7,#0ffH
DJNZ R7,$
RET
END
將程序編譯通過,寫入芯片,插入實驗板,用通讀電纜將實驗板與主機的串行口相連就能實驗了。上面的程序功能很簡單,就是每隔一段時間向主機輪流送數55H和AAH,并把主機送去的數送到P1口。能在PC端用串行口精靈來做實驗。串行口精靈在我主頁上有下載。運行串行口精靈后,按主界面上的“設置參數”按鈕進入“設置參數”對話框,按下面的參數進行設置。注意,我的機器上用的是串行口2,如果你不是串行口2,請自行更改串行口的設置。
設置完后,按確定返回主界面,注意右邊有一個下拉列表,應當選中“按16進制”。然后按“開始發送”、“開始接收”就能了。按此設置,實驗板上應當有兩只燈亮,6只燈滅。大家能自行更改設置參數中的發送字符如55,00,FF等等,觀察燈的亮滅,并分析原因,也能在主界面上更改下拉列表中的“按16進制”為“按10進制”或“按ASCII字符”來觀察現象,并仔細分析。這對于大家理解16進制、10進制、ASCII字符也是很有好處的。程序本身很簡單,又有注釋,這里就不詳加說明了。
三、上述程序的中斷版本
org 0000H
AJMP START
org 0023h
AJMP SERIAL ;
ORG 30H
START:
mov SP,#5fh ;
mov TMOD,#20h ;T1: 工作模式2
mov PCON,#80h ;SMOD=1
mov TH1,#0FDH ;初始化波特率(參見表)
mov SCON,#50h ;Standard UART settings
MOV R0,#0AAH ;準備送出的數
SETB REN ;允許接收
SETB TR1 ;T1開始工作
SETB EA ;開總中斷
SETB ES ;開串行口中斷
SJMP $
SERIAL:
MOV A,SBUF
MOV P1,A
CLR RI
RETI
END
本程序沒有寫入發送程序,大家能自行添加。
23、LED數碼管靜態顯示接口與編程
在單片機系統中,常常用LED數碼數碼管顯示器來顯示各種數字或符號。由于它具有顯示清晰、亮度高、使用電壓低、壽命長的特點,因此使用非常廣泛。
引言:還記得我們小時候玩的“火柴棒游戲”嗎,幾根火柴棒組合起來,能拼成各種各樣的圖形,LED數碼管顯示器實際上也是這么一個東西。
八段LED數碼管顯示器
《單片機靜態顯示接口》
八段LED數碼管顯示器由8個發光二極管組成。基中7個長條形的發光管排列成“日”字形,另一個賀點形的發光管在數碼管顯示器的右下角作為顯示小數點用,它能顯示各種數字及部份英文字母。LED數碼管顯示器有兩種不一樣的形式:一種是8個發光二極管的陽極都連在一起的,稱之為共陽極LED數碼管顯示器;另一種是8個發光二極管的陰極都連在一起的,稱之為共陰極LED數碼管顯示器。如下圖所示。`
共陰和共陽結構的LED數碼管顯示器各筆劃段名和安排位置是相同的。當二極管導通時,對應的筆劃段發亮,由發亮的筆劃段組合而顯示的各種字符。8個筆劃段hgfedcba對應于一個字節(8位)的D7 D6 D5 D4 D3 D2 D1 D0,于是用8位二進制碼就能表示欲顯示字符的字形代碼。例如,對于共陰LED數碼管顯示器,當公共陰極接地(為零電平),而陽極hgfedcba各段為0111011時,數碼管顯示器顯示“P”字符,即對于共陰極LED數碼管顯示器,“P”字符的字形碼是73H。如果是共陽LED數碼管顯示器,公共陽極接高電平,顯示“P”字符的字形代碼應為10001100(8CH)。這里必須注意的是:很多產品為方便接線,常不按規則的辦法去對應字段與位的關系,這個時候字形碼就必須根據接線來自行設計了,后面我們會給出一個例程。
在單片機應用系統中,數碼管顯示器顯示常用兩種辦法:靜態顯示和動態掃描顯示。所謂靜態顯示,就是每一個數碼管顯示器都要占用單獨的具有鎖存功能的I/O接口用于筆劃段字形代碼。這樣單片機只要把要顯示的字形代碼發送到接口電路,就不用管它了,直到要顯示新的數據時,再發送新的字形碼,因此,使用這種辦法單片機中CPU的開銷小。能供給單獨鎖存的I/O接口電路很多,這里以常用的串并轉換電路74LS164為例,介紹一種常用靜態顯示電路,以使大家對靜態顯示有一定的了解。
MCS-51單片機串行口方式押為移們寄存器方式,外接6片74LS164作為6位LED數碼管顯示器的靜態顯示接口,把8031的RXD作為數據輸出線,TXD作為移位時鐘脈沖。74LS164為TTL單向8位移位寄存器,可實現串行輸入,并行輸出。其中A、B(第1、2腳)為串行數據輸入端,2個管腳按邏輯與運算規律輸入信號,公一個輸入信號時可并接。T(第8腳)為時鐘輸入端,可連接到串行口的TXD端。每一個時鐘信號的上升沿加到T端時,移位寄存器移一位,8個時鐘脈沖過后,8位二進制數全部移入74LS164中。R(第9腳)為復位端,當R=0時,移位寄存器各位復0,只有當R=1時,時鐘脈沖才起作用。Q1…Q8(第3-6和10-13管腳)并行輸出端分別接LED數碼管顯示器的hg---a各段對應的管腳上。關于74LS164還能作如下的介紹:所謂時鐘脈沖端,其實就是需要高、低、高、低的脈沖,不管這個脈沖是怎么來的,比如,我們用根電線,一端接T,一端用手拿著,分別接高電平、低電平,那也是給出時鐘脈沖,在74LS164獲得時鐘脈沖的瞬間(再講清楚點,是在脈沖的沿),如果數據輸入端(第1,2管腳)是高電平,則就會有一個1進入到74LS164的內部,如果數據輸入端是低電平,則就會有一個0進入其內部。在給出了8個脈沖后,最先進入74LS164的第一個數據到達了最高位,然后再來一個脈沖會有什么發生呢?再來一個脈沖,第一個脈沖就會從最高位移出,就象車站排隊買票,欄桿就那么長,要從后面進去一本人,前面必須要從前面走出去一本人才行。
搞清了這一點,下面讓我們來看電路,6片7LS164首尾相串,而時鐘端則接在一起,這樣,當輸入8個脈沖時,從單片機RXD端輸出的數據就進入到了第一片74LS164中了,而當第二個8個脈沖到來后,這個數據就進入了第二片74LS164,而新的數據則進入了第一片74LS164,這樣,當第六個8個脈沖完成后,首次送出的數據被送到了最左面的164中,其他數據依次出現在第一、二、三、四、五片74LS164中。有個問題,在第一個脈沖到來時,除了第一片74LS164中接收數據外,其他各片在干嗎呢?它們也在接收數據,因為它們的時鐘端都是被接在一起的,可是數據還沒有送到其他各片呢,它們在接收什么數據呢?。。。。。。其實所謂數據不過是一種說法而已,實際就是電平的高低,當第一個脈沖到來時,第一片164固然是從單片機接收數據了,而其它各片也接到前一片的Q8上,而Q8是一根電線,在數字電路中它只可能有兩種狀態:低電平或高電平,也就是“0”和“1”。所以它的下一片74LS164也相當于是在接收數據啊。只是接收的全部是0或1而已。這個問題放在這兒說明,可能有朋友不屑一顧,而有的朋友可能還是不清楚,這實際上涉及到數的本質的問題,如果不懂的,請仔細思考,并找一些數字電路的數,理解164的工作原理,再來看這個問題,或者去看看我的另一篇文章《27課:關于單片機的一些基本概念》的文章。務必搞懂,搞懂了這一點,你的級別就高過開始學習者,可謂入門者了。
入口:把要顯示的數分別放在顯示緩沖區60H-65H共6個單元中,并且分別對應各個數碼管LED0-LED5。
出口:將預置在顯示緩沖區中的6個數成對應的顯示字形碼,然后輸出到數碼管顯示器中顯示。
單片機led顯示程序如下:
DISP: MOV SCON,#00H ;初始化串行口方式0
MOV R1,#06H ;顯示6位數
MOV R0,#65H ;60H-65H為顯示緩沖區
MOV DPTR,#SEGTAB ;字形表的入口地址
LOOP:
MOV A,@R0 ;取最高位的待顯示數據
MOVC A,@A+DPTR ;查表獲取字形碼
MOV SBUF,A ;送串行口顯示
DELAY: JNB TI,DELAY ;等待發送完畢
CLR TI ;清發送標志
DEC R0 ;指針下移一位,準備取下一個待顯示數
DJNZ R1,LOOP ;直到6個數據全顯示完。
RET
SETTAB: ;字形表,前面有介紹,以后我們再介紹字形表的制作。
DB 03H 9FH 27H 0DH 99H 49H 41H 1FH 01H 09H 0FFH
; 0 1 2 3 4 5 6 7 8 9 消隱碼
單片機顯示測試用主程序
ORG 0000H
AJMP START
ORG 30H
START: MOV SP,#6FH
MOV 65H,#0
MOV 64H,#1
MOV 63H,#2
MOV 62H,#3
MOV 61H,#4
MOV 60H,#5
LCALL DISP
SJMP $
如果按圖示數碼管排列,則以上主程序將顯示的是543210,想想看,如果要顯示012345該怎樣送數?
下面我們來分析一下字形表的制作問題。先就上述“標準”的圖形來看吧。寫出數據位和字形的對應關系并列一個表如下(設為共陽型,也就是對應的輸出位為0時筆段亮)
如何,字形表會做了吧,就是這樣列個表格,根據要求(0亮或1亮)寫出對應位的0和1,就成了。做個練習,寫出A-F的字形碼吧。
如果為了接線方便而打亂了接線的次序,那么字形表又該如何接呢?也很簡單,一樣地列表啊。以新實驗板為例,共陽型。接線如下:
P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0
C E H D G F A B
則字形碼如下所示:
;0 00101000 28H
;1 01111110 7EH
;2 10100100 0A4H
;3 01100100 64H
;4 01110010 72H
;5 01100001 61H
;6 00100001 21H
;7 01111100 7CH
;8 00100000 20H
;9 01100000 60H
作為練習,大家寫出A-F的字形代碼。
24、動態掃描顯示接口電路及程序
在單片機系統中動態掃描顯示接口是單片機中應用最為廣泛的一種顯示方式之一。其接口電路是把所有顯示器的8個筆劃段a-h同名端連在一起,而每一個顯示器的公共極COM是各自獨立地受I/O線控制。CPU向字段輸出口送出字形碼時,所有顯示器接收到相同的字形碼,但究竟是那個顯示器亮,則取決于COM端,而這一端是由I/O控制的,所以我們就能自行決定何時顯示哪一位了。而所謂動態掃描就是指我們采用分時的辦法,輪流控制各個顯示器的COM端,使各個顯示器輪流點亮。在http://www.51hei.com還有很多關于單片機顯示接口的文章,大家可以參考一下
在輪流點亮掃描過程中,每位顯示器的點亮時間是極為短暫的(約1ms),但由于人的視覺暫留現象及發光二極管的余輝效應,盡管實際上各位顯示器并非同時點亮,但只要掃描的速度足夠快,給人的印象就是一組穩定的顯示數據,不會有閃爍感。
下圖所示就是我們的單片機實驗板上的動態掃描接口。由89c51的P0口能灌入較大的電流,所以我們采用共陽的數碼管,并且不用限流電阻,而只是用兩只1N4004進行降壓后給數碼管供電,這里僅用了兩只,實際上還能擴充。它們的公共端則由PNP型三極管8550控制,顯然,如果8550導通,則對應的數碼管就能亮,而如果8550截止,則對應的數碼管就不可能亮,8550是由P2.7,P2.6控制的。這樣我們就能通過控制P27、P26達到控制某個數碼管亮或滅的目的。
下面的這個單片機程序,就是用實驗板上的數碼管顯示0和1。
FIRST EQU P2.7 ;第一位數碼管的位控制
SECOND EQU P2.6 ;第二位數碼管的位控制
DISPBUFF EQU 5AH ;顯示緩沖區為5AH和5BH
ORG 0000H
AJMP START
ORG 30H
START:
MOV SP,#5FH ;設置堆棧
MOV P1,#0FFH
MOV P0,#0FFH
MOV P2,#0FFH ;初始化,所顯示器,LED滅
MOV DISPBUFF,#0 ;第一位顯示0
MOV DISPBUFF+1,#1 ;第二握顯示1
LOOP:
LCALL DISP ;調用顯示程序
AJMP LOOP
;主程序到此結束
DISP:
PUSH ACC ;ACC入棧
PUSH PSW ;PSW入棧
MOV A,DISPBUFF ;取第一個待顯示數
MOV DPTR,#DISPTAB ;字形表首地址
MOVC A,@A+DPTR ;取字形碼
MOV P0,A ;將字形碼送P0位(段口)
CLR FIRST ;開第一位顯示器位口
LCALL DELAY ;延時1毫秒
SETB FIRST ;關閉第一位顯示器(開始準備第二位的數據)
MOV A,DISPBUFF+1 ;取顯示緩沖區的第二位
MOV DPTR,#DISPTAB
MOVC A,@A+DPTR
MOV P0,A ;將第二個字形碼送P0口
CLR SECOND ;開第二位顯示器
LCALL DELAY ;延時
SETB SECOND ;關第二位顯示
POP PSW
POP ACC
RET
DELAY: ;延時1毫秒
PUSH PSW
SETB RS0
MOV R7,#50
D1: MOV R6,#10
D2: DJNZ R6,$
DJNZ R7,D1
POP PSW
RET
DISPTAB:DB 28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60H
END
從上面的單片機例程中能看出,動態掃描顯示必須由CPU持續地調用顯示程序,才能保證持續持續的顯示。
上面的這個程序能實現數字的顯示,但不太實用,為什么呢?這里僅是顯示兩個數字,并沒有做其他的工作,因此,兩個數碼管輪流顯示1毫秒,沒有問題,實際的工作中,當然不可能只顯示兩個數字,還是要做其他的事情的,這樣在二次調用顯示程序之間的時間間隔就不一不定了,如果時間間隔比較長,就會使顯示不連續。而實際工作中是很難保證所有工作都能在很短時間內完成的。況且這個顯示程序也有點“浪費”,每個數碼管顯示都要占用1個毫秒的時間,這在很多合是不允許的,怎么辦呢?我們能借助于定時器,定時時間一到,產生中斷,點亮一個數碼管,然后馬上返回,這個數碼管就會一直亮到下一次定時時間到,而不用調用延時程序了,這段時間能留給主程序干其他的事。到下一次定時時間到則顯示下一個數碼管,這樣就很少浪費了。
Counter EQU 59H ;計數器,顯示程序通過它得知現正顯示哪個數碼管
FIRST EQU P2.7 ;第一位數碼管的位控制
SECOND EQU P2.6 ;第二位數碼管的位控制
DISPBUFF EQU 5AH ;顯示緩沖區為5AH和5BH
ORG 0000H
AJMP START
ORG 000BH ;定時器T0的入口
AJMP DISP ;顯示程序
ORG 30H
START:
MOV SP,#5FH ;設置堆棧
MOV P1,#0FFH
MOV P0,#0FFH
MOV P2,#0FFH ;初始化,所顯示器,LED滅
MOV TMOD,#00000001B ;定時器T0工作于模式1(16位定時/計數模式)
MOV TH0,#HIGH(65536-2000)
MOV TL0,#LOW(65536-2000)
SETB TR0
SETB EA
SETB ET0
MOV Counter,#0 ;計數器初始化
MOV DISPBUFF,#0 ;第一位始終顯示0
MOV A,#0
LOOP:
MOV DISPBUFF+1,A ;第二位輪流顯示0-9
INC A
LCALL DELAY
CJNE A,#10,LOOP
MOV A,#0
AJMP LOOP ;在此中間能按排任意程序,這里僅作示范。
;主程序到此結束
DISP: ;定時器T0的中斷響應程序
PUSH ACC ;ACC入棧
PUSH PSW ;PSW入棧
MOV TH0,#HIGH(65536-2000) ;定時時間為2000個周期,約2170微秒(11.0592M)
MOV TL0,#LOW(65536-2000)
SETB FIRST
SETB SECOND ;關顯示
MOV A,#DISPBUFF ;顯示緩沖區首地址
ADD A,Counter
MOV R0,A
MOV A,@R0 ;根據計數器的值取對應的顯示緩沖區的值
MOV DPTR,#DISPTAB ;字形表首地址
MOVC A,@A+DPTR ;取字形碼
MOV P0,A ;將字形碼送P0位(段口)
MOV A,Counter ;取計數器的值
JZ DISPFIRST ;如果是0則顯示第一位
CLR SECOND ;不然顯示第二位
AJMP DISPNEXT
DISPFIRST:
CLR FIRST ;顯示第一位
DISPNEXT:
INC Counter ;計數器加1
MOV A,Counter
DEC A ;如果計數器計到2,則讓它回0
DEC A
JZ RSTCOUNT
AJMP DISPEXIT
RSTCOUNT:
MOV Counter,#0 ;計數器的值只能是0或1
DISPEXIT:
POP PSW
POP ACC
RETI
DELAY: ;延時130毫秒
PUSH PSW
SETB RS0
MOV R7,#255
D1: MOV R6,#255
D2: NOP
NOP
NOP
NOP
DJNZ R6,D2
DJNZ R7,D1
POP PSW
RET
DISPTAB:DB 28H,7EH,0a4H,64H,72H,61H,21H,7CH,20H,60H
END
從上面的單片機程序能看出,動態顯示和靜態顯示相比,程序稍有點復雜,不過,這是值得的。這個程序有一定的通用性,只要改變端口的值及計數器的值就能顯示更多位數了。下面給出顯示程序的流程圖。
《動態掃描程序框圖》
25、單片機鍵盤接口程序設計
鍵盤是由若干按鈕組成的開關矩陣,它是單片機系統中最常用的輸入設備,用戶能通過鍵盤向計算機輸入指令、地址和數據。一般單片機系統中采和非編碼鍵盤,非編碼鍵盤是由軟件來識別鍵盤上的閉合鍵,它具有結構簡單,使用靈活等特點,因此被廣泛應用于單片機系統。
按鈕開關的抖動問題
組成鍵盤的按鈕有觸點式和非觸點式兩種,單片機中應用的一般是由機械觸點組成的。在下圖中,當開
《鍵盤結構圖》
圖1
圖2
關S未被按下時,P1。0輸入為高電平,S閉合后,P1。0輸入為低電平。由于按鈕是機械觸點,當機械觸點斷開、閉合時,會有抖動動,P1。0輸入端的波形如圖2所示。這種抖動對于人來說是感覺不到的,但對計算機來說,則是完全能感應到的,因為計算機處理的速度是在微秒級,而機械抖動的時間至少是毫秒級,對計算機而言,這已是一個“漫長”的時間了。前面我們講到中斷時曾有個問題,就是說按鈕有時靈,有時不靈,其實就是這個原因,你只按了一次按鈕,可是計算機卻已執行了多次中斷的過程,如果執行的次數正好是奇數次,那么結果正如你所料,如果執行的次數是偶數次,那就不對了。
為使CPU能正確地讀出P1口的狀態,對每一次按鈕只作一次響應,就必須考慮如何去除抖動,常用的去抖動的辦法有兩種:硬件辦法和軟件辦法。單片機中常用軟件法,因此,對于硬件辦法我們不介紹。軟件法其實很簡單,就是在單片機獲得P1。0口為低的信息后,不是立即認定S1已被按下,而是延時10毫秒或更長一些時間后再次檢測P1。0口,如果仍為低,說明S1的確按下了,這實際上是避開了按鈕按下時的抖動時間。而在檢測到按鈕釋放后(P1。0為高)再延時5-10個毫秒,消除后沿的抖動,然后再對鍵值處理。不過一般情況下,我們常常不對按鈕釋放的后沿進行處理,實踐證明,也能滿足一定的要求。當然,實際應用中,對按鈕的要求也是千差萬別,要根據不一樣的需要來編制處理程序,但以上是消除鍵抖動的原則。
鍵盤與單片機的連接
《鍵盤連接》
圖3
《單片機與鍵盤接口圖》
圖4
1、通過1/0口連接。將每個按鈕的一端接到單片機的I/O口,另一端接地,這是最簡單的辦法,如圖3所示是實驗板上按鈕的接法,四個按鈕分別接到P3.2 、P3.3、P3.4和P3.5。對于這種鍵各程序能采用持續查詢的辦法,功能就是:檢測是否有鍵閉合,如有鍵閉合,則去除鍵抖動,判斷鍵號并轉入對應的鍵處理。下面給出一個例程。其功能很簡單,四個鍵定義如下:
P3.2:開始,按此鍵則燈開始流動(由上而下)
P3.3:停止,按此鍵則停止流動,所有燈為暗
P3.4:上,按此鍵則燈由上向下流動
P3.5:下,按此鍵則燈由下向上流動
UpDown EQU 00H ;上下行標志
StartEnd EQU 01H ;起動及停止標志
LAMPCODE EQU 21H ;存放流動的數據代碼
ORG 0000H
AJMP MAIN
ORG 30H
MAIN:
MOV SP,#5FH
MOV P1,#0FFH
CLR UpDown ;啟動時處于向上的狀態
CLR StartEnd ;啟動時處于停止狀態
MOV LAMPCODE,#0FEH ;單燈流動的代碼
LOOP:
ACALL KEY ;調用鍵盤程序
JNB F0,LNEXT ;如果無鍵按下,則繼續
ACALL KEYPROC ;不然調用鍵盤處理程序
LNEXT:
ACALL LAMP ;調用燈顯示程序
AJMP LOOP ;反復循環,主程序到此結束
DELAY:
MOV R7,#100
D1: MOV R6,#100
DJNZ R6,$
DJNZ R7,D1
RET
;----------------------------------------延時程序,鍵盤處理中調用
KEYPROC:
MOV A,B ;從B寄存器中獲取鍵值
JB ACC.2,KeyStart ;分析鍵的代碼,某位被按下,則該位為1(因為在鍵盤程序中已取反)
JB ACC.3,KeyOver
JB ACC.4,KeyUp
JB ACC.5,KeyDown
AJMP KEY_RET
KeyStart:
SETB StartEnd ;第一個鍵按下后的處理
AJMP KEY_RET
KeyOver:
CLR StartEnd ;第二個鍵按下后的處理
AJMP KEY_RET
KeyUp: SETB UpDown ;第三個鍵按下后的處理
AJMP KEY_RET
KeyDown:
CLR UpDown ;第四個鍵按下后的處理
KEY_RET:RET
KEY:
CLR F0 ;清F0,表示無鍵按下。
ORL P3,#00111100B ;將P3口的接有鍵的四位置1
MOV A,P3 ;取P3的值
ORL A,#11000011B ;將其余4位置1
CPL A ;取反
JZ K_RET ;如果為0則一定無鍵按下
ACALL DELAY ;不然延時去鍵抖
ORL P3,#00111100B
MOV A,P3
ORL A,#11000011B
CPL A
JZ K_RET
MOV B,A ;確實有鍵按下,將鍵值存入B中
SETB F0 ;設置有鍵按下的標志
K_RET:
ORL P3,#00111100B ;此處循環等待鍵的釋放
MOV A,P3
ORL A,#11000011B
CPL A
JZ K_RET1 ;直到讀取的數據取反后為0說明鍵釋放了,才從鍵盤處理程序中返回
AJMP K_RET
K_RET1:
RET
D500MS: ;流水燈的延遲時間
PUSH PSW
SETB RS0
MOV R7,#200
D51: MOV R6,#250
D52: NOP
NOP
NOP
NOP
DJNZ R6,D52
DJNZ R7,D51
POP PSW
RET
LAMP:
JB StartEnd,LampStart ;如果StartEnd=1,則啟動
MOV P1,#0FFH
AJMP LAMPRET ;不然關閉所有顯示,返回
LampStart:
JB UpDown,LAMPUP ;如果UpDown=1,則向上流動
MOV A,LAMPCODE
RL A ;實際就是左移位而已
MOV LAMPCODE,A
MOV P1,A
LCALL D500MS
AJMP LAMPRET
LAMPUP:
MOV A,LAMPCODE
RR A ;向下流動實際就是右移
MOV LAMPCODE,A
MOV P1,A
LCALL D500MS
LAMPRET:
RET
END
以上程序功能很簡單,但它演示了一個單片機鍵盤處理程序的基本思路,程序本身很簡單,也不很實用,實際工作中還會有好多要考慮的因素,比如主循環每次都調用燈的循環程序,會造成按鈕反應“遲鈍”,而如果一直按著鍵不放,則燈不會再流動,一直要到松開手為止,等等,大家能仔細考慮一下這些問題,再想想有什么好的解決辦法。
2、采用中斷方式:如圖4所示。各個按鈕都接到一個與非上,當有任何一個按鈕按下時,都會使與門輸出為低電平,從而引起單片機的中斷,它的好處是不用在主程序中持續地循環查詢,如果有鍵按下,單片機再去做對應的處理
26、矩陣式鍵盤接口技術及程序設計
在單片機系統中鍵盤中按鈕數量較多時,為了減少I/O口的占用,常常將按鈕排列成矩陣形式,如圖1所示。在矩陣式鍵盤中,每條水平線和垂直線在交叉處不直接連通,而是通過一個按鈕加以連接。這樣,一個端口(如P1口)就能組成4*4=16個按鈕,比之直接將端口線用于鍵盤多出了一倍,而且線數越多,區別越明顯,比如再多加一條線就能組成20鍵的鍵盤,而直接用端口線則只能多出一鍵(9鍵)。由此可見,在需要的鍵數比較多時,采用矩陣法來做鍵盤是合理的。
《單片機矩陣式鍵盤接口技術及編程接口圖》
矩陣式結構的鍵盤顯然比直接法要復雜一些,識別也要復雜一些,上圖中,列線通過電阻接正電源,并將行線所接的單片機的I/O口作為輸出端,而列線所接的I/O口則作為輸入。這樣,當按鈕沒有按下時,所有的輸出端都是高電平,代表無鍵按下。行線輸出是低電平,一旦有鍵按下,則輸入線就會被拉低,這樣,通過讀入輸入線的狀態就可得知是否有鍵按下了。具體的識別及編程辦法如下所述。
矩陣式鍵盤的按鈕識別辦法
確定矩陣式鍵盤上何鍵被按下介紹一種“行掃描法”。
行掃描法 行掃描法又稱為逐行(或列)掃描查詢法,是一種最常用的按鈕識別辦法,如上圖所示鍵盤,介紹過程如下。
判斷鍵盤中有無鍵按下 將全部行線Y0-Y3置低電平,然后檢測列線的狀態。只要有一列的電平為低,則表示鍵盤中有鍵被按下,而且閉合的鍵位于低電平線與4根行線相交叉的4個按鈕之中。若所有列線均為高電平,則鍵盤中無鍵按下。
判斷閉合鍵所在的位置 在確認有鍵按下后,即可進入確定具體閉合鍵的過程。其辦法是:依次將行線置為低電平,即在置某根行線為低電平時,其它線為高電平。在確定某根行線位置為低電平后,再逐行檢測各列線的電平狀態。若某列為低,則該列線與置為低電平的行線交叉處的按鈕就是閉合的按鈕。
下面給出一個具體的例程:
圖仍如上所示。8031單片機的P1口用作鍵盤I/O口,鍵盤的列線接到P1口的低4位,鍵盤的行線接到P1口的高4位。列線P1.0-P1.3分別接有4個上拉電阻到正電源+5V,并把列線P1.0-P1.3設置為輸入線,行線P1.4-P.17設置為輸出線。4根行線和4根列線形成16個相交點。
檢測當前是否有鍵被按下。檢測的辦法是P1.4-P1.7輸出全“0”,讀取P1.0-P1.3的狀態,若P1.0-P1.3為全“1”,則無鍵閉合,不然有鍵閉合。
去除鍵抖動。當檢測到有鍵按下后,延時一段時間再做下一步的檢測判斷。
若有鍵被按下,應識別出是哪一個鍵閉合。辦法是對鍵盤的行線進行掃描。P1.4-P1.7按下述4種組合依次輸出:
P1.7 1 1 1 0
P1.6 1 1 0 1
P1.5 1 0 1 1
P1.4 0 1 1 1
在每組行輸出時讀取P1.0-P1.3,若全為“1”,則表示為“0”這一行沒有鍵閉合,不然有鍵閉合。由此得到閉合鍵的行值和列值,然后可采用計算法或查表法將閉合鍵的行值和列值轉換成所定義的鍵值
為了保證鍵每閉合一次CPU僅作一次處理,必須卻除鍵釋放時的抖動。
《單片機矩陣式鍵盤接口技術及編程》
鍵盤掃描程序:
從以上分析得到單片機鍵盤掃描程序的流程圖如圖2所示。程序如下
SCAN: MOV P1,#0FH
MOV A,P1
ANL A,#0FH
CJNE A,#0FH,NEXT1
SJMP NEXT3
NEXT1: ACALL D20MS
MOV A,#0EFH
NEXT2: MOV R1,A
MOV P1,A
MOV A,P1
ANL A,#0FH
CJNE A,#0FH,KCODE;
MOV A,R1
SETB C
RLC A
JC NEXT2
NEXT3: MOV R0,#00H
RET
KCODE: MOV B,#0FBH
NEXT4: RRC A
INC B
JC NEXT4
MOV A,R1
SWAP A
NEXT5: RRC A
INC B
INC B
INC B
INC B
JC NEXT5
NEXT6: MOV A,P1
ANL A,#0FH
CJNE A,#0FH,NEXT6
MOV R0,#0FFH
RET
鍵盤處理程序就作這么一個簡單的介紹,實際上,鍵盤、顯示處理是很復雜的,它一般占到一個應用程序的大部份代碼,可見其重要性,但說到,這種復雜并不來自于單片機的本身,而是來自于操作者的習慣等等問題,因此,在編寫鍵盤處理程序之前,最好先把它從邏輯上理清,然后用適當的算法表示出來,最后再去寫代碼,這樣,才能快速有效地寫好代碼。
27、關于單片機的一些基本概念
隨著電子技術的迅速發展,計算機已深入地滲透到我們的生活中,許多電子愛好者開始學習單片機知識,但單片機的內容比較抽象,相對電子愛好者已熟悉的模擬電路、數字電路,單片機中有一些新的概念,這些概念非常基本以至于一般作者不屑去談,教材自然也不會很深入地講解這些概念,但這些內容又是學習中必須要理解的,下面就結合本人的學習、教學經驗,對這些最基本概念作一說明,希望對自學者有所幫助。
一、總線:我們知道,一個電路總是由元器件通過電線連接而成的,在模擬電路中,連線并不成為一個問題,因為各器件間一般是串行關系,各器件之間的連線并不很多,但計算機電路卻不一樣,它是以微處理器為核心,各器件都要與微處理器相連,各器件之間的工作必須相互協調,所以就需要的連線就很多了,如果仍如同模擬電路一樣,在各微處理器和各器件間單獨連線,則線的數量將多得驚人,所以在微處理機中引入了總線的概念,各個器件共同享用連線,所有器件的8根數據線全部接到8根公用的線上,即相當于各個器件并聯起來,但僅這樣還不行,如果有兩個器件同時送出數據,一個為0,一個為1,那么,接收方接收到的究竟是什么呢?這種情況是不允許的,所以要通過控制線進行控制,使器件分時工作,任何時候只能有一個器件發送數據(能有多個器件同時接收)。器件的數據線也就被稱為數據總線,器件所有的控制線被稱為控制總線。在單片機內部或者外部存儲器及其它器件中有存儲單元,這些存儲單元要被分配地址,才能使用,分配地址當然也是以電信號的形式給出的,由于存儲單元比較多,所以,用于地址分配的線也較多,這些線被稱為地址總線。
二、數據、地址、指令:之所以將這三者放在一起,是因為這三者的本質都是一樣的──數字,或者說都是一串‘0’和‘1’組成的序列。換言之,地址、指令也都是數據。指令:由單片機芯片的設計者規定的一種數字,它與我們常用的指令助記符有著嚴格的一一對應關系,不能由單片機的開發者更改。地址:是尋找單片機內部、外部的存儲單元、輸入輸出口的依據,內部單元的地址值已由芯片設計者規定好,不可更改,外部的單元能由單片機開發者自行決定,但有一些地址單元是一定要有的(詳見程序的執行過程)。數據:這是由微處理機處理的對象,在各種不一樣的應用電路中各不相同,一般而言,被處理的數據可能有這么幾種情況:
1·地址(如MOV DPTR,#1000H),即地址1000H送入DPTR。
2·方式字或控制字(如MOV TMOD,#3),3即是控制字。
3·常數(如MOV TH0,#10H)10H即定時常數。
4·實際輸出值(如P1口接彩燈,要燈全亮,則執行指令:MOV P1,#0FFH,要燈全暗,則執行指令:MOV P1,#00H)這里0FFH和00H都是實際輸出值。又如用于LED的字形碼,也是實際輸出的值。
理解了地址、指令的本質,就不難理解程序運行過程中為什么會跑飛,會把數據當成指令來執行了。
三、P0口、P2口和P3的第二功能使用辦法 開始學習時一般對P0口、P2口和P3口的第二功能使用辦法迷惑不解,認為第二功能和原功能之間要有一個切換的過程,或者說要有一條指令,事實上,各端口的第二功能完全是自動的,不需要用指令來轉換。如P3.6、P3.7分別是WR、RD信號,當微片理機外接RAM或有外部I/O口時,它們被用作第二功能,不能作為通用I/O口使用,只要一微處理機一執行到MOVX指令,就會有對應的信號從P3.6或P3.7送出,不需要事先用指令說明。事實上‘不能作為通用I/O口使用’也并不是‘不能’而是(使用者)‘不會’將其作為通用I/O口使用。你完全能在指令中按排一條SETB P3.7的指令,并且當單片機執行到這條指令時,也會使P3.7變為高電平,但使用者不會這么去做,因為這常常這會導致系統的崩潰(即死機)。
四、程序的執行過程 單片機在通電復位后8051內的程序計數器(PC)中的值為‘0000’,所以程序總是從‘0000’單元開始執行,也就是說:在系統的ROM中一定要存在‘0000’這個單元,并且在‘0000’單元中存放的一定是一條指令。
五、堆棧 堆棧是一個區域,是用來存放數據的,這個區域本身沒有任何特殊之處,就是內部RAM的一部份,特殊的是它存放和取用數據的方式,即所謂的‘先進后出,后進先出’,并且堆棧有特殊的數據傳輸指令,即‘PUSH’和‘POP’,有一個特殊的專為其服務的單元,即堆棧指針SP,每當執一次PUSH指令時,SP就(在原來值的基礎上)自動加1,每當執行一次POP指令,SP就(在原來值的基礎上)自動減1。由于SP中的值能用指令加以改變,所以只要在程序開始階段更改了SP的值,就能把堆棧設置在規定的內存單元中,如在程序開始時,用一條MOV SP,#5FH指令,就時把堆棧設置在從內存單元60H開始的單元中。一般程序的開頭總有這么一條設置堆棧指針的指令,因為開機時,SP的初始值為07H,這樣就使堆棧從08H單元開始往后,而08H到1FH這個區域正是8031的第二、三、四工作寄存器區,經常要被使用,這會造成數據的渾亂。不一樣作者編寫程序時,初始化堆棧指令也不完全相同,這是作者的習慣問題。當設置好堆棧區后,并不意味著該區域成為一種專用內存,它還是能象普通內存區域一樣使用,只是一般情況下編程者不會把它當成普通內存用了。
六、單片機的開發過程 這里所說的開發過程并不是一般書中所說的從任務分析開始,我們假設已設計并制作好硬件,下面就是編寫軟件的工作。在編寫軟件之前,首先要確定一些常數、地址,事實上這些常數、地址在設計階段已被直接或間接地確定下來了。如當某器件的連線設計好后,其地址也就被確定了,當器件的功能被確定下來后,其控制字也就被確定了。然后用文本編緝器(如EDIT、CCED等)編寫軟件,編寫好后,用編譯器對源程序文件編譯,查錯,直到沒有語法錯誤,除了極簡單的程序外,一般應用仿真機對軟件進行調試,直到程序運行正確為止。運行正確后,就能寫片(將程序固化在EPROM中)。在源程序被編譯后,生成了擴展名為HEX的目標文件,一般編程器能夠識別這種格式的文件,只要將此文件調入即可寫片。在此,為使大家對整個過程有個認識,舉一例說明:
表1
ORG 0000H
LJMP START
ORG 040H
START:
MOV SP,#5FH ;設堆棧
LOOP:
NOP
LJMP LOOP ;循環
END ;結束
表2
:03000000020040BB
:0700400075815F000200431F
表3
02 00 40 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 75 81 5F 00 02 00 43
表1為源程序,表2是匯編后得到的HEX文件,表3是由HEX文件轉換成的目標文件,也就是最終寫入EPROM的文件,它由編程器轉換得到,也能由HEXBIN一類的程序轉換得到。學過手工匯編者應當不難找出表3與表1的一一對應關系,值得注意的是從02 00 40后開始的一長串‘FF’,直到75 81,這是由于偽指令:ORG 040H造成的結果。
七、仿真、仿真機 仿真是單片機開發過程中非常重要的一個環節,除了一些極簡單的任務,一般產品開發過程中都要進行仿真,仿真的主要目的是進行軟件調試,當然借助仿真機,也能進行一些硬件排錯。一塊單片機應用電路板包括單片機部份及為達到使用目的而設計的應用電路,仿真就是利用仿真機來代替應用電路板(稱目標機)的單片機部份,對應用電路部份進行測試、調試。仿真有CPU仿真和ROM仿真兩種,所謂CPU仿真是指用仿真機代替目標機的CPU,由仿真機向目標機的應用電路部份供給各種信號、數據,進行調試的辦法。這種仿真能通過單步運行、連續運行等多種辦法來運行程序,并能觀察到單片機內部的變化,便于改正程序中的錯誤。所謂ROM仿真,就是用仿真機代替目標機的ROM,目標機的CPU工作時,從仿真機中讀取程序,并執行。這種仿真其實就是將仿真機當成一片EPROM,只是省去了擦片、寫片的麻煩,并沒有多少調試手段可言。常常這是二種不一樣類型的仿真機,也就是說,一臺仿真機不能既做CPU仿真,又做ROM仿真。可能的情況下,當然以CPU仿真好。以上是本人對單片機的理解,如有不對之處,請諸位大俠多多指點。發表您的高論。
28、單片機音樂程序設計
利用單片機(或單板機)奏樂大概是無線電愛好者感興趣的問題之一。本文從單片機的基本發間實驗出發,談談音樂程序的設計原理,并給出具體實例,以供參考。
單片機的基本發音實驗
我們知道,聲音的頻譜范圍約在幾十到幾千赫茲,若能利用程序來控制單處機某個口線的“高”電平或低電平,則在該口線上就能產生一定頻率的矩形波,接上喇叭就能發出一定頻率的聲音,若再利用延時程序控制“高”“低”電平的持續時間,就能改變輸出頻率,從而改變音調。
例如,要產生200HZ的音頻信號,按圖1接入喇叭(若屬臨時實驗,也可將喇叭直接接在P1口線上),實驗程序為:
其中子程序DEL為延時子程序,當R3為1時,延時時間約為20us,R3中存放延時常數,對200HZ音頻,其周期為1/200秒,即5ms。這樣,當P1.4的高電平或低電平的持續時間為2.5ms,即R3的時間常數取2500/20=125(7DH)時,就能發出200HZ的音調。將上述程序鍵入學習機,并持續修改R3的常數能感到音調的變化。樂曲中,每一音符對應著確定的頻率,表1給出C調時各音符頻率及其對應的時間常數。讀者能根據表1所供給的常數,將其16進制代碼送入R3,反復練習體會。根據表1能奏出音符。僅這還不夠,要準確奏出一首曲子,必須準確地控制樂曲節奏,即一音符的持續時間。
音符的節拍我們能用定時器T0來控制,送入不一樣的初值,就能產生不一樣的定時時間。便如某歌曲的節奏為每分鐘94拍,即一拍為0.64秒。其它節拍與時間的對應關系見表2。
但時,由于T0的最大定時時間只能為131毫秒,因此不可能直接用改變T0的時間初值來實現不一樣節拍。我們能用T0來產生10毫秒的時間基準,然后設置一個中斷計數器,通過判別中斷計數器的值來控制節拍時間的長短。表2中也給出了各種節拍所對應的時間常數。例如對1/4拍音符,定時時間為0.16秒,對應的時間常數為16(即10H);對3拍音符,定時時間為1.92秒,對應時間長數為192(即C0H)。
我們將每一音符的時間常數和其對應的節拍常數作為一組,按次序將樂曲中的所有常數排列成一個表,然后由查表程序依次取出,產生音符并控制節奏,就能實現演奏效果。此外,結束符和體止符能分別用代碼00H和FFH來表示,若查表結果為00H,則表示曲子終了;若查表結果為FFH,則產生對應的停頓效果。為了產生手彈的節奏感,在某些音符(例如兩個相同音符)音插入一個時間單位的頻率略有不一樣的音符。
下面給出程序序清單,可直接在TD-III型學習機上演奏,對其它不一樣型號的學習機,只需對應地改變一下地址即可。本程序演奏的是民歌“八月桂花遍地開”,C調,節奏為94拍/分。讀者也能自行找出一首歌,按表1和表2給定的常數,將樂曲翻譯成碼表輸入機器,而程序不變。本實驗辦法簡便,即使不懂音樂的人,將一首陌生的曲子翻譯成代碼也是易事,和著機器的演奏學唱一首歌曲,其趣味無窮。
程序清單(略,請參看源程序的說明)。
程序框圖如圖2所示。
《單片機音樂程序的設計圖》
本課由單片機教程網提供,有問題指出。
硬件連接說明:
隨便找一個仿真機或者什么單片機實驗板,只要能工作的就行,將程序輸入,運行,然后找個音箱(你計算機旁邊應當就有一對吧)撥出插頭,插頭的前端接在P1。0上,后面部分找根線接單片機的地,就應當有聲了,然后怎么改進硬件連接就是你的事了。。。。
音樂程序匯編代碼代碼1 -------------Voice.asm--------------------------
ORG 0000H
LJMP START
ORG 000BH
INC 20H ;中斷服務,中斷計數器加1
MOV TH0,#0D8H
MOV TL0,#0EFH ;12M晶振,形成10毫秒中斷
RETI
START:
MOV SP,#50H
MOV TH0,#0D8H
MOV TL0,#0EFH
MOV TMOD,#01H
MOV IE,#82H
MUSIC0:
NOP
MOV DPTR,#DAT ;表頭地址送DPTR
MOV 20H,#00H ;中斷計數器清0
MOV B,#00H ;表序號清0
MUSIC1:
NOP
CLR A
MOVC A,@A+DPTR ;查表取代碼
JZ END0 ;是00H,則結束
CJNE A,#0FFH,MUSIC5
LJMP MUSIC3
MUSIC5:
NOP
MOV R6,A
INC DPTR
MOV A,B
MOVC A,@A+DPTR ;取節拍代碼送R7
MOV R7,A
SETB TR0 ;啟動計數
MUSIC2:
NOP
CPL P1.0
MOV A,R6
MOV R3,A
LCALL DEL
MOV A,R7
CJNE A,20H,MUSIC2 ;中斷計數器(20H)=R7否?
;不等,則繼續循環
MOV 20H,#00H ;等于,則取下一代碼
INC DPTR
; INC B
LJMP MUSIC1
MUSIC3:
NOP
CLR TR0 ;休止100毫秒
MOV R2,#0DH
MUSIC4:
NOP
MOV R3,#0FFH
LCALL DEL
DJNZ R2,MUSIC4
INC DPTR
LJMP MUSIC1
END0:
NOP
MOV R2,#64H ;歌曲結束,延時1秒后繼續
MUSIC6:
MOV R3,#00H
LCALL DEL
DJNZ R2,MUSIC6
LJMP MUSIC0
DEL:
NOP
DEL3:
MOV R4,#02H
DEL4:
NOP
DJNZ R4,DEL4
NOP
DJNZ R3,DEL3
RET
NOP
DAT:
db 26h,20h,20h,20h,20h,20h,26h,10h,20h,10h,20h,80h,26h,20h,30h,20h
db 30h,20h,39h,10h,30h,10h,30h,80h,26h,20h,20h,20h,20h,20h,1ch,20h
db 20h,80h,2bh,20h,26h,20h,20h,20h,2bh,10h,26h,10h,2bh,80h,26h,20h
db 30h,20h,30h,20h,39h,10h,26h,10h,26h,60h,40h,10h,39h,10h,26h,20h
db 30h,20h,30h,20h,39h,10h,26h,10h,26h,80h,26h,20h,2bh,10h,2bh,10h
db 2bh,20h,30h,10h,39h,10h,26h,10h,2bh,10h,2bh,20h,2bh,40h,40h,20h
db 20h,10h,20h,10h,2bh,10h,26h,30h,30h,80h,18h,20h,18h,20h,26h,20h
db 20h,20h,20h,40h,26h,20h,2bh,20h,30h,20h,30h,20h,1ch,20h,20h,20h
db 20h,80h,1ch,20h,1ch,20h,1ch,20h,30h,20h,30h,60h,39h,10h,30h,10h
db 20h,20h,2bh,10h,26h,10h,2bh,10h,26h,10h,26h,10h,2bh,10h,2bh,80h
db 18h,20h,18h,20h,26h,20h,20h,20h,20h,60h,26h,10h,2bh,20h,30h,20h
db 30h,20h,1ch,20h,20h,20h,20h,80h,26h,20h,30h,10h,30h,10h,30h,20h
db 39h,20h,26h,10h,2bh,10h,2bh,20h,2bh,40h,40h,10h,40h,10h,20h,10h
db 20h,10h,2bh,10h,26h,30h,30h,80h,00H
END
音樂程序匯編代碼代碼2 -------------Voice1.asm--------------------------
;標題 ‘八月桂花香’發聲程序
;摘要 詳見‘無線電’92年3期
;作者 周振安
ORG 0000H
LJMP START
ORG 000BH
INC 20H ;中斷服務,中斷計數器加1
MOV TH0,#0D8H
MOV TL0,#0EFH ;12M晶振,形成10毫秒中斷
RETI
START:
MOV SP,#50H
MOV TH0,#0D8H
MOV TL0,#0EFH
MOV TMOD,#01H
MOV IE,#82H
MUSIC0:
NOP
MOV DPTR,#DAT ;表頭地址送DPTR
MOV 20H,#00H ;中斷計數器清0
MOV B,#00H ;表序號清0
MUSIC1:
NOP
CLR A
MOVC A,@A+DPTR ;查表取代碼
JZ END0 ;是00H,則結束
CJNE A,#0FFH,MUSIC5
LJMP MUSIC3
MUSIC5:
NOP
MOV R6,A
INC DPTR
MOV A,B
MOVC A,@A+DPTR ;取節拍代碼送R7
MOV R7,A
SETB TR0 ;啟動計數
MUSIC2:
NOP
CPL P1.0
MOV A,R6
MOV R3,A
LCALL DEL
MOV A,R7
CJNE A,20H,MUSIC2 ;中斷計數器(20H)=R7否?
;不等,則繼續循環
MOV 20H,#00H ;等于,則取下一代碼
INC DPTR
; INC B
LJMP MUSIC1
MUSIC3:
NOP
CLR TR0 ;休止100毫秒
MOV R2,#0DH
MUSIC4:
NOP
MOV R3,#0FFH
LCALL DEL
DJNZ R2,MUSIC4
INC DPTR
LJMP MUSIC1
END0:
NOP
MOV R2,#64H ;歌曲結束,延時1秒后繼續
MUSIC6:
MOV R3,#00H
LCALL DEL
DJNZ R2,MUSIC6
LJMP MUSIC0
DEL:
NOP
DEL3:
MOV R4,#02H
DEL4:
NOP
DJNZ R4,DEL4
NOP
DJNZ R3,DEL3
RET
NOP
DAT:
DB 18H, 30H, 1CH, 10H
DB 20H, 40H, 1CH, 10H
DB 18H, 10H, 20H, 10H
DB 1CH, 10H, 18H, 40H
DB 1CH, 20H, 20H, 20H
DB 1CH, 20H, 18H, 20H
DB 20H, 80H, 0FFH, 20H
DB 30H, 1CH, 10H , 18H
DB 20H, 15H, 20H , 1CH
DB 20H, 20H, 20H , 26H
DB 40H, 20H , 20H , 2BH
DB 20H, 26H, 20H , 20H
DB 20H, 30H , 80H , 0FFH
DB 20H, 20H, 1CH , 10H
DB 18H, 10H, 20H , 20H
DB 26H, 20H , 2BH , 20H
DB 30H, 20H , 2BH , 40H
DB 20H, 20H , 1CH , 10H
DB 18H, 10H, 20H, 20H
DB 26H, 20H , 2BH, 20H
DB 30H, 20H, 2BH , 40H
DB 20H, 30H, 1CH , 10H
DB 18H, 20H , 15H , 20H
DB 1CH, 20H , 20H , 20H
DB 26H, 40H, 20H , 20H
DB 2BH, 20H, 26H , 20H
DB 20H, 20H, 30H , 80H
DB 20H, 30H, 1CH , 10H
DB 20H, 10H, 1CH , 10H
DB 20H, 20H, 26H , 20H
DB 2BH, 20H, 30H , 20H
DB 2BH, 40H, 20H , 15H
DB 1FH, 05H, 20H , 10H
DB 1CH, 10H, 20H , 20H
DB 26H, 20H, 2BH , 20H
DB 30H, 20H, 2BH , 40H
DB 20H, 30H, 1CH , 10H
DB 18H, 20H , 15H , 20H
DB 1CH, 20H , 20H , 20H
DB 26H, 40H, 20H , 20H
DB 2BH, 20H, 26H , 20H
DB 20H, 20H, 30H, 30H
DB 20H, 30H, 1CH, 10H
DB 18H, 40H, 1CH, 20H
DB 20H, 20H, 26H, 40H
DB 13H, 60H, 18H , 20H
DB 15H, 40H, 13H , 40H
DB 18H, 80H, 00H
end
評論