Jun 27, 2014

Mở đầu 
  

Trước hết hãy tìm hiểu cách cho hiển thị các số dùng mã quét trên các chân Anode của các đèn số dùng mã 7 đoạn và sơ đồ mạch điện đồng hồ dùng để chạy đoạn chương trình nguồn sau đây:



 

Tất cả các linh kiện cho ráp trên một bảng mạch in nhỏ. Trên đó dùng 6 đèn số mã 7 đoạn để hiển thị giờ - phút - giây.




Sơ đồ mạch điện cho thấy, chúng ta dùng các xung ra ứng với bit 0, tuần tự cho ra trên các chân của các chân p2.0, p2.1, p2.2, p2.3, p2.4 và p2.5 để lần lượt cho chân anode của các đèn số nối lên nguồn 5V, như vậy trong một lúc chỉ có một đèn số phát sáng, với nhịp xung quay vòng nhanh và do mắt có hiện tượng lưu ảnh, chúng ta có cảm tưởng là 6 đèn Led cùng được kích sáng.

Chúng ta dùng 7 chân của cảng p1 để cho xuất các con số theo dạng mã 7 đoạn. Muốn Led nào sáng, chúng ta đặt bit 0 vào chân cathode của nó, vậy bit 1 tắt Led và bit 0 cấp dòng cho Led sáng. Theo sơ đồ chúng ta thấy:

Chân p1.0 kiểm soát dòng cấp cho Led-a trong đèn số. 
Chân p1.1 kiểm soát dòng cấp cho Led-b trong đèn số. 
Chân p1.2 kiểm soát dòng cấp cho Led-c trong đèn số. 
Chân p1.3 kiểm soát dòng cấp cho Led-d trong đèn số. 
Chân p1.4 kiểm soát dòng cấp cho Led-e trong đèn số. 
Chân p1.5 kiểm soát dòng cấp cho Led-f trong đèn số. 
Chân p1.6 kiểm soát dòng cấp cho Led-g trong đèn số. 

Sau khi đã chọn định phân cứng như sơ đồ mạch điện trên, chúng ta sẽ định ra được các dạng mã bit để tuần tự cho hiện các con số: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 trên các đèn số mã 7 đoạn.


tab:    ; Các mã dùng để hiện số trên đèn 7 đoạn đặt trong bảng có tên nhãn là tab:
db 0c0h, 0f9h, 0a4h, 0b0h, 99h, 92h, 82h, 0f8h, 80h, 90h    ; các mã hiện số viết theo dạng hệ 16.



Để dễ thấy ý nghĩa vật lý chúng ta sẽ viết lại các mã hệ 16 ở dạng mã hệ cơ 2 theo trọng giá  8-4-2-1.


* 0c0h,   viết lại ở dạng mã hệ cơ 2: 1100 0000b ứng với số 0
* 0f9h,    viết lại ở dạng mã hệ cơ 2: 1111 1001b ứng với số 1
* 0a4h,   viết lại ở dạng mã hệ cơ 2: 1010 0100b ứng với số 2
* 0b0h,   viết lại ở dạng mã hệ cơ 2: 1011 0000b ứng với số 3
* 99h,     viết lại ở dạng mã hệ cơ 2: 1001 1001b ứng với số 4
* 92h,     viết lại ở dạng mã hệ cơ 2: 1001 0010b ứng với số 5
* 82h,     viết lại ở dạng mã hệ cơ 2: 1000 0010b ứng với số 6
* 0f8h,    viết lại ở dạng mã hệ cơ 2: 1111 1000b ứng với số 7
* 80h,     viết lại ở dạng mã hệ cơ 2: 1000 0000b ứng với số 8
* 90h,     viết lại ở dạng mã hệ cơ 2: 1001 0000b ứng với số 9



 Tìm hiểu tác dụng của các nút nhấn và các led chỉ thị trạng thái:

 



Dùng hình động để giải thích hoạt động của đèn số dùng mã 7 đoạn:


Cách cho xuất số liệu trên mã 7 đoạn, để hiện các số 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.



Cách dùng mã quét quay vòng, mỗi lần chỉ có một đèn số phát sáng:







Cách dùng MIDE-51 cho biên dịch đoạn chương trình nguồn sau đây ra dạng mã lệnh:



 



Sau khi biên dịch thành công, chúng ta có các dòng mã lệnh như sau: 

:020000000130CD
:0200030021B921
:02000B0001E30F
:10003000758150C20075210075220075230075245A
:1000400000752500752600752700752800752900A4
:10005000758C05758A05758902D2AFD2A8C2A9C26E
:100060008CC288E523B52609E524B52704D2B7015B
:100070007DE523B52807E524B52902C2B73000E3A2
:10008000118E0163C0F9A4B0999282F88090A823E0
:10009000900084E8C4540F93F590C2A0319FD2A081
:1000A000E8540F93F590C2A1319FD2A1A924E9C4CD
:1000B000540F93F590C2A2319FD2A2E9540F93F549
:1000C00090C2A3319FD2A3AA25EAC4540F93F590FE
:1000D000C2A4319FD2A4EA540F93F590C2A5319FD8
:1000E000D2A522C0E0C0D00521E521B4C8387521D1
:1000F000000522E522B40A2E752200B280B2817476
:10010000012525D4F525B4601D752500740125242D
:10011000D4F524B4601075240074012523D4F5238C
:10012000B42403752300D0D0D0E032A826900084F8
:10013000E8C4540F93F590C2A0319FD2A0E8540FA9
:1001400093F590C2A1319FD2A1A927E9C4540F937E
:10015000F590C2A2319FD2A2E9540F93F590C2A3A9
:10016000319FD2A322A828900084E8C4540F93F5AD
:1001700090C2A0319FD2A0E8540F93F590C2A13154
:100180009FD2A1A929E9C4540F93F590C2A2319F2F
:10019000D2A2E9540F93F590C2A3319FD2A3227546
:1001A00030027531FFD531FDD530F72275326475D7
:1001B00033FAD533FDD532F722C0E0C0D0C2A8C291
:1001C0008CC2A9118E20B2FB118E30B2FBD2B1D2FB
:1001D000B0118E20B413118E30B4FBA82374012803
:1001E000D4F523B424EB75230020B313118E30B360
:1001F000FBA824740128D4F524B460D5752400200C
:10020000B507118E30B5FB418720B2C5118E30B2D3
:10021000FBC2B0D2B1312B20B413312B30B4FBA8C8
:1002200026740128D4F526B424EB75260020B313D8
:10023000312B30B3FBA827740128D4F527B460D53F
:1002400075270020B2CF312B30B2FBD2B0C2B13112
:100250006520B413316530B4FBA828740128D4F5A7
:1002600028B424EB75280020B313316530B3FBA804
:1002700029740128D4F529B460D575290020B2CF9E
:10028000316530B2FB21CDD2A8D28CD200D2A9D216
:06029000B7D0D0D0E0322F
:00000001FF



Bây giờ Bạn dùng hộp nạp, cho nạp các dòng mã lệnh này vào bộ nhớ EEPROM của ic lập trình AT89C51, gắn IC này vào mạch điện, Bạn sẽ có một đồng hồ có chức năng chỉnh giờ mở giờ tắt và có thể dùng chân p3.7 để điều khiển các thiết bị điện tử kh́ac.


Đây là chương trình nguồn của mạch điện đồng hồ có hẹn, Bạn có thể cắt chương trình này dán vào vùng soạn thảo của MIDE-51 gõ phím F9 để cho dịch ra file .hex, và nạp các dòng mã lệnh này vào bộ nhớ EEPROM nằm trong ic lập trình AT89C51 để làm mạch điện đồng hồ có hẹn. Bạn xem phần giải thích cách dùng các câu lệnh ở phần bên dưới. 


;; Dong ho co nut chinh mo chinh tat
;;------------------------------------------
k1 bit p3.2
k2 bit p3.4
k3 bit p3.3
k4 bit p3.5
c_hour equ 23h
c_minute equ 24h
c_second equ 25h
on_hour equ 26h
on_minute equ 27h
off_hour equ 28h
off_minute equ 29h
org 0000h
jmp main
org 0003h
jmp wint0
org 000bh
jmp wt0
org 0030h
main:
mov sp, #50h
clr 00h
mov 21h, #0
mov 22h, #0
mov c_hour, #0
mov c_minute, #0
mov c_second, #0
mov on_hour, #0
mov on_minute, #0
mov off_hour, #0
mov off_minute, #0
;;
mov th0, #05
mov tl0, #05
mov tmod, #00000010b ;02h
;;
setb ea
setb ex0
clr et0
clr tr0
clr it0
m:
mov a, c_hour
cjne a, on_hour, off_time
mov a, c_minute
cjne a, on_minute, off_time
setb p3.7
jmp next
off_time:
mov a, c_hour
cjne a, off_hour, next
mov a, c_minute
cjne a, off_minute, next
clr p3.7
next: jnb 00h, m
call disp1
jmp m
;;
tab:
db 0c0h, 0f9h, 0a4h, 0b0h, 99h, 92h, 82h, 0f8h, 80h
db 90h
;;
disp1:
mov r0, c_hour
mov dptr, #tab
mov a, r0
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.0
call dl
setb p2.0
mov a, r0
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.1
call dl
setb p2.1
;;
mov r1, c_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3
;;
mov r2, c_second
mov a, r2
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.4
call dl
setb p2.4
mov a, r2
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.5
call dl
setb p2.5
ret
;;
wt0:
push acc
push psw
inc 21h
mov a, 21h
cjne a, 0c8h, t0reti ; 200d=0c8h 0.5ms*200=100ms
mov 21h, #0
inc 22h
mov a, 22h
cjne a, #10, t0reti ; 100ms*10=1s
mov 22h, #0
cpl p0.0
cpl p0.1
;;
mov a, #01h
add a, c_second
da a
mov c_second, a
cjne a, #60h, t0reti
mov c_second, #0
;;
mov a, #01h
add a, c_minute
da a
mov c_minute, a
cjne a, #60h, t0reti
mov c_minute, #0
;;
mov a, #01h
add a, c_hour
da a
mov c_hour, a
cjne a, #24h, t0reti
mov c_hour, #0
;;
t0reti:
pop psw
pop acc
reti
;;
disp2:
mov r0, on_hour
mov dptr, #tab
mov a, r0
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.0
call dl
setb p2.0
mov a, r0
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.1
call dl
setb p2.1
;;
mov r1, on_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3
ret
;;
disp3:
mov r0, off_hour
mov dptr, #tab
mov a, r0
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.0
call dl
setb p2.0
mov a, r0
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.1
call dl
setb p2.1
;;
mov r1, off_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3
ret
;;
dl:
mov 30h, #02h
dl1: mov 31h, #0ffh
djnz 31h, $
djnz 30h, dl1
ret
;;
del:
mov 32h, #100
del1: mov 33h, #250
djnz 33h, $
djnz 32h, del1
ret
;;
wint0:
push acc
push psw
clr ex0
clr tr0
clr et0
k1_11:
call disp1
jb k1, k1_11
k11_1:
call disp1
jnb k1, k11_1
;;
ph4:
setb p3.1
setb p3.0
ph0: call disp1
jb k2, ph01
;;
k2_1:
call disp1
jnb k2, k2_1
mov r0, c_hour
mov a, #01h
add a, r0
da a
mov c_hour, a
cjne a, #24h, ph0
mov c_hour, #0
ph01:
jb k3, ph1
k3_1:
call disp1
jnb k3, k3_1
mov r0, c_minute
mov a, #01h
add a, r0
da a
mov c_minute, a
cjne a, #60h, ph0
mov c_minute, #0
ph1:
jb k4, ph10
k4_1:
call disp1
jnb k4, k4_1
jmp tend
ph10:
jb k1, ph0
k1_1:
call disp1
jnb k1, k1_1
clr p3.0
setb p3.1
;;--------------------------------
ph2:
call disp2
jb k2, ph201
k22_1:
call disp2
jnb k2, k22_1
mov r0, on_hour
mov a, #01h
add a, r0
da a
mov on_hour, a
cjne a, #24h, ph2
mov on_hour, #0
ph201:
jb k3, ph210
k22_2:
call disp2
jnb k3, k22_2
mov r0, on_minute
mov a, #01h
add a, r0
da a
mov on_minute, a
cjne a, #60h, ph2
mov on_minute, #0
ph210: jb k1, ph2
k22_3:
call disp2
jnb k1, k22_3
setb p3.0
clr p3.1
;;----------------------------------------
ph3:
call disp3
jb k2, ph301
k33_1:
call disp3
jnb k2, k33_1
mov r0, off_hour
mov a, #01h
add a, r0
da a
mov off_hour, a
cjne a, #24h, ph3
mov off_hour, #0
ph301:
jb k3, ph310
k33_2:
call disp3
jnb k3, k33_2
mov r0, off_minute
mov a, #01h
add a, r0
da a
mov off_minute, a
cjne a, #60h, ph3
mov off_minute, #0
ph310: jb k1, ph3
k33_3:
call disp3
jnb k1, k33_3
jmp ph4
;;----------------------------------------
tend:
setb ex0
setb tr0
setb 00h
setb et0
setb p3.7
pop psw
pop acc
reti
end




Phân tích ý nghĩa và cách dùng của các câu lệnh trong chương trình nguồn:



1. Khởi đầu Bạn dùng các định nghĩa bit và equ để đặt tên cho các bit và thanh ghi để tiện dụng.

;; Dong ho co nut chinh mo chinh tat
;;------------------------------------------

; đặt tên cho các nút nhấn
k1 bit p3.2   ; đặt tên chân p3.2 là k1
k2 bit p3.4   ; như trên
k3 bit p3.3   ; như trên 
k4 bit p3.5   ; như trên


; đặt tên các thanh nhớ dùng cất các con số chỉ giờ-phút- giây 
c_hour equ 23h     ; đặt tên cho thanh nhớ có địa chỉ 23h là c_hour
c_minute equ 24h  ; làm như trên
c_second equ 25h ; làm như trên 


; đặt tên cho các thanh nhớ dùng cất số giờ cho mở
on_hour equ 26h   ; làm như trên
on_minute equ 27h ; làm như trên


; đặt tên cho các thanh nhớ dùng cất số giờ cho tắt
off_hour equ 28h  ; làm như trên
off_minute equ 29h  ; làm như trên



2. Cho chương trình khởi đầu từ thanh reset, có địa chỉ 0000h. 

; Vào chương trình 
org 0000h
jmp main  ; cho nhẩy đến tên nhãn main, nhẩy qua các thanh dùng cho ngắt


3. Đặt các chương trình cho các dạng ngắt

org 0003h ; ngắt theo bit trên INT0
jmp wint0  ; nhẩy đến tên nhãn wint0
org 000bh ; ngắt theo bit TF0, bit trang của Timer10
jmp wt0   ; nhẩy đến tên nhãn wt0


org 0030h  ; Các chương trình điều khiển đồng hồ khởi từ địa chỉ này
main:   ; tên nhãn dùng cho lệnh nhẩy
mov sp, #50h  định địa chỉ cho ngăn xếp
clr 00h     ; cho xóa bit có địa chỉ 00h


; Đặt trị khởi đầu cho các thanh ghi dùng hiển thị giờ-phút-giây
mov 21h, #0   ; trị khởi đầu đặt số 0 vào thanh ghi 21h
mov 22h, #0  ; làm như trên
mov c_hour, #0 ; làm như trên
mov c_minute, #0  ; làm như trên
mov c_second, #0  ; làm như trên
mov on_hour, #0  ; làm như trên
mov on_minute, #0 ; làm như trên
mov off_hour, #0  ; làm như trên
mov off_minute, #0 ; làm như trên


; Khai báo các trị cho Timer 0
mov th0, #05 ; nạp trị khởi đầu cho thanh đếm 8 bit th0
mov tl0, #05  ; nạp trị khởi đầu cho thanh đếm 8 bit tl0
mov tmod, #00000010b ; chọn định cho timer 0 làm việc ở mode 2

Để hiểu rõ hơn hoạt động của các timer trong AT89C51, Bạn xem hình sau:


  

Trong thanh tmod, có 2 bit dùng để chọn mode cho các đồng hồ timer trong AT89C51.

Nếu Bạn chọn:

M1 = 0 , M0 = 0 là cho timer chạy ở mode 0, mode dùng thanh đếm 13 bit
M1 = 0 , M0 = 1 là cho timer chạy ở mode 1, mode dùng thanh đếm 16 bit
M1 = 1 , M0 = 0 là cho timer chạy ở mode 2, mode dùng thanh đếm 8 bit
M1 = 1 , M0 = 1 là cho timer chạy ở mode 3, mode hổn hợp.

Với dòng khai báo trong thanh tmod là 00000010b thì chúng ta chọn timer 0 cho làm việc ở mode 2, tức dùng thanh tl0 làm thanh đếm 8 bit. Khi thanh tl0 đếm đến tràn số, bit báo tràn tf0sẽ nhẩy lên 1. Nó sẽ có 2 tác dụng.

* Tác dụng thứ nhất: là cho trị trong tl0 về 0 và đồng thời lấy trị có trong th0 cho nạp vào thanh tl0.
* Tác dụng thứ hai: là nhẩy ngắt theo bit tf0. Chương trình nhẩy về địa chỉ 000bh của bộ nhớ 


Hình vẽ trên cho thấy hoạt động của Timer 0 (giải thích tương tự cho Timer 1) như sau: Trong AT89C51 có mạch dao động tạo ra xung nhịp, tần số xung lấy theo thạnh anh trên chân 18, 19. Xung này có thể đưa vào bộ đếm của các timer, Bạn cũng có thể lấy xung ngoài đưa vào thanh đếm trên chân T0 hay chân p3.4. Để cho xung nhịp đưa vào thanh đếm, Bạn hãy định  bit cho C/T đảo, nếu định nó là bit 1 thì mạch đếm sẽ lấy xung ngoài đưa vào trên chân T0, nếu định là bit 0 thì mạch đếm sẽ lấy xung của mạch dao động nội. Xung nhịp trước khi vào bộ đếm còn phải qua một khóa điện, khóa điện này chịu điều khiển theo các bit sau:

* Bit tr0, nếu cho tr0 được định ở bit 1, khóa điện sẽ đóng lại và nếu tr0 ở bit 0 nó sẽ làm hở khóa điện và cắt xung nhịp không cho vào thanh đếm.

* Bit INT0 đảo, khi bit Gate chọn ở bit 1 thì ngắt ngoài INT0 sẽ có tác dụng đến mạch đếm, khi INT0 là bit 1 nó cho đếm và khi INT0 ở bit 0, nó sẽ ngắt mạch đếm. Khi bit Gate chọn ở bit 0 thì ngắt ngoài trên chân INT0 sẽ mất tác dụng.

Tóm lại, nếu Bạn không dùng ngắt ngoài INT0 thì đặt bit Gate ở mức bit 0. Lúc này dùng bit tr0 để đóng mở mạch đếm. Nghĩa là:

setb tr0 là cho xung nhịp vào bộ đếm của timer 0
clr tr0 là ngắt xung nhịp không cho vào bộ đếm của timer0
  

; Khai báo chọn dạng ngắt và điều khiển hoạt động của timer 0.
 setb ea  ; bit ea dùng tắt/mở 6 dạng ngắt trong AT89C51, set cho dùng ngắt
setb ex0 ; đặt ex0 = 1 là cho dùng ngắt ngoài trên chân INT0
clr et0   ; tạm thời cho tắt ngắt theo bit báo tràn tf0
clr tr0    ; tạm thời cho tắt xung nhịp vào bộ đếm tl0, tắt time 0
clr it0    ; chọn hình thức ngắt theo mức volt thấp của ngắt ngoài INT0

Muốn hiều rõ vai trò của các ngắt trong AT89C51, chúng ta xem hình vẽ sau:

 

Chức năng Ngắt là gì?

Ngắt là một chức năng rất hay của các ic lập trình, khi Bạn đã chọn dấu hiệu nhẩy ngắt, tức chọn kiểu ngắt, không cần biết chương trình của Bạn lúc này đang làm gì, mỗi khi xuất hiện dấu hiện báo ngắt, chương trình đang chạy sẽ dừng lại và cho nhẩy đến địa chỉ đã qui định của các ngắt để chạy chương trình ngắt, cho đến khi gặp câu lệnh reti thì kết thúc chương trình ngắt và quay trở lại tiếp tục làm công việc đã tạm dừng trước đó. Thí dụ:

Bạn đã chọn dạng ngắt theo bit báo tràn của timer 0, đó là bit tf0. Khi timer 0 đang đếm xung nhịp, xung vào các thanh đếm tl0, th0, bit tf0 đã cho ở bit 0 với câu lệnh clr tf0, khi các thanh đếm tràn số thì bit tf0 sẽ nhẩy lên bit 1 và lúc này ngắt tf0 sẽ bị kích hoạt, nó tạm dừng chương trình đang chạy, nhẩy về địa chỉ 000bh và chạy chương trình có ở địa chỉ này, cho đến khi gặp câu lệnh reti, nó sẽ ngưng chạy chương trình ngắt và quay lại tiếp tục chạy chương trình đã tạm dừng trước đó.



4. Cho kiểm tra với các trị đã định trước

; Cho kiểm tra trị trong các vùng nhớ off_timer và on_timer
m:  ; đặt tên nhãn m:
mov a, c_hour ; chuyển trị trong c_hour vào a
cjne a, on_hour, off_time ; cho so sánh trị trong on_hour và a để chọn kiểu nhẩy
mov a, c_minute ; chuyển trị trong c_minute vào a
cjne a, on_minute, off_time ; cho so sánh trị trong on_minute với a để chọn kiểu nhẩy
setb p3.7 ; đặt bit điều khiển trạng thái trên chân p3.7 lên mức áp cao
jmp next ; cho nhẩy đến tên nhãn next:
off_time: ; đặt tên nhãn off_time
mov a, c_hour ; chuyển trị trong c_hour vào a
cjne a, off_hour, next ; cho so sánh trị trong off_hour và a để chọn kiểu nhẩy
mov a, c_minute ; chuyển trị trong c_minute vào a
cjne a, off_minute, next ; so sánh trị trong off_minute và a để chọn kiểu nhẩy
clr p3.7 ; đặt bit trên chân p3.7 xuống mức áp thấp
next: jnb 00h, m ; cho nhẩy theo bit kiểm tra 00h
call disp1  ; cho gọi trình hiển thị số
jmp m  ; nhẩy về tên nhãn m:


Ý nghĩa của đoạn chương trình trên là cho kiểm tra trị có trong c_hour và c_minute với các trị đã có trong on_hour, on_minute và off_hour, off_minute để điều khiển trạng thái bit p3.7. Ở đây Bạn chú ý cách dùng câu lệnh cjne (compare jump no equal), một câu lệnh dùng phép so sánh 2 trị xem bằng hay không bằng để chọn kiểu nhẩy, đây là một lệnh rất hay. Cũng chú ý đến bit kiểm tra có địa chỉ bit được chọn là 00h

 ; tạo bảng để cất các trị dùng làm hiện các số mã thập phân trên các Led 7 đoạn 
tab:   ; đặt tên nhãn
db 0c0h, 0f9h, 0a4h, 0b0h, 99h, 92h, 82h, 0f8h, 80h, 90h  ; mã hiện số khi cho xuất trên cảng p1


Bạn xem phần giải thích cách cho hiện các số trên các đèn số dùng mã 7 đoạn


5. Viết chương trình hiển thị số 

; đây là đoạn chương trình dùng hiển thị số lên bảng đèn số

; Viết chương trình hiển thị giờ (hour)
disp1:   ; đặt tên nhãn hiện số là disp1:
mov r0, c_hour ; chuyển trị trong c_hour vào r0
mov dptr, #tab ; đặt bảng tra mã con số vào thanh ghi con trỏ
mov a, r0  ; chuyển trị trong r0 vào a
swap a  ; cho đảo vị trí 4 bit cao / thấp trong thanh a
anl a, #0fh  ; lấy logic and để che 4 bit cao, giữ nguyên trị ở 4 bit thấp
movc a, @a+dptr  ; lấy trị trong bảng hiện số gắn vào a
mov p1, a  ; cho xuất trị trong a ra cảng p1 để hiện số
clr p2.0  ; đặt chân p2.0 xuống mức thấp để mở đèn số này
call dl  ; gọi chương trình làm chậm dl: để tạo mức sáng cho con số
setb p2.0  ; tắt đèn số đã mở trước khi mở qua đèn số khác
mov a, r0 ; chuyển trị còn trong r0 vào a
anl a, #0fh  ; dùng logic and để che 4 bit cao, giữ nguyên 4 bit thấp
movc a, @a+dptr  ; truy bảng lấy mã hiện số chuyển vào a
mov p1, a  ; lại cho xuất mã con số trong a ra cảng p1
clr p2.1  ; đặt chân p2.1 xuống mức áp thấp để mở đèn số
call dl  ; cho làm chậm để có mức sáng tốt
setb p2.1 ; đặt chân p2.1 lên cao để tắt đèn số này tránh lem số


; Viết chương trình hiển thị các con số chỉ phút (minute)
; Bạn xem cách viết tương tự như phần trên
mov r1, c_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3


; Viết chương trình cho hiển thị giây ̣(second)
; Bạn xem cách viết tương tự như phần trên. 
mov r2, c_second
mov a, r2
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.4
call dl
setb p2.4
mov a, r2
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.5
call dl
setb p2.5
ret

Trong các câu lệnh trên có nhiều câu lệnh dùng rất hay, như:

* Trong thanh đếm 8 bit, 4 bit cao có thể là hàng chục và 4 bit thấp là hàng đơn vị, trong trường hợp này Bạn dùng lệnh swap  a để chuyển đổi vị trí của 4 bit thấp lên cao và cao qua thấp.

* Muốn lấy trị thập phân có trên 4 bit thấp chúng ta có thể cho 4 bit cao đều bằng 0, và cho bảo toàn trị của 4 bit thấp, muốn vậy, chúng ta dùng logic and, câu lệnh anl a, #0fh (00001111b). 

* Để lấy các số mã có trong bảng với tên nhãn là tab: , chúng ta dùng câu lệnh: movc a, @a+dptr. Trước đó trong a chứa trị thập phân ở dạng cơ 2, từ trị này sẽ tìm ra được mã của con số có trong bảng tra tab:, và mã này được lấy ra và rồi cũng cho cất vào a, để sau đó sẽ cho xuất ra trên cảng p1.


6. Đoạn chương trình dùng cho mạch đếm theo bước tăng 1

; đây là chương trình ngắt mỗi khi xuất hiện bit báo tràn tf0
wt0:   ; đặt tên nhã wt0
push acc  ; tạm cho cất trị của a vào ngăn xếp, để mượn dùng a
push psw  ; tạm cho cất trị trong thanh psw vào ngăn xếp
inc 21h  ; cho tăng trị có trong thanh 21h lên +1
mov a, 21h  ; cho chuyển trị của thanh 21h vào a
cjne a, 0c8h, t0reti ; 200d=0c8h 0.5ms*200=100ms ; so sánh trị trong a bằng 0c8h chưa để nhẩy đến tắt ngắt
mov 21h, #0 ; trả trị trong thanh 21h trở về 0
inc 22h  ; bây giờ cho tăng trị trong thanh 22h
mov a, 22h  ; cũng chuyển trị trong thanh 22h vào a
cjne a, #10, t0reti ; 100ms*10=1s  ; lại so sánh trị trong a với trị 10, để nhẩy đến tắt ngắt
mov 22h, #0 ; lại trả trị trong 22h về mức đếm 0
cpl p0.0  ; lấy bù bit trên chân p0.0, có thể gắn led trên chân này để hiển thị trang thái đếm
cpl p0.1 ; lấy bù bit trên chân p0.1, có thể gắn led trên chân này để hiển thị trang thái đếm


7. Tạo trị tăng trong các thanh nhớ c_second, c_minute, c_hour 


;;
mov a, #01h ; cho trị 1 vào thanh a
add a, c_second ; cho cộng trị trong a với trị trong c_second, kết quả cất vào a
da a ; biến đổi tạo trị thập phân dạng cơ 2 đặt trong vùng 4 bit thấp, 4 bit cao
mov c_second, a  ; chuyển trị có trong a cất vào c_second
cjne a, #60h, t0reti  ; so sánh trị trong a bằng 60 chưa để chọn hướng nhẩy
mov c_second, #0 ; trả trị trong c_second về lại mức 0.


;;
mov a, #01h  ; đặt trị 1 vào thanh a
add a, c_minute ; cho cộng trị trong a và trị của c_minute, kết quả cất vào a
da a  ; biến đổi trị trong a ra số hệ thập phân dạng cơ 2, đặt trong 4 bit thấp và 4 bit cao
mov c_minute, a ; chuyển trị trong a vào cất trong c_minute
cjne a, #60h, t0reti  ; cho a so sánh với trị 60 để chọn hướng nhẩy
mov c_minute, #0  ; trả trị trong c_minute về lại mức 0


;;
mov a, #01h  ; đặt trị 1 vào thanh a
add a, c_hour  ; cho cộng trị trong a và trị của c_hour, kết quả cất vào a
da a  ; biến đổi trị trong a ra dạng số thập phân hệ cơ 2, cất ở vùng 4 bit thấp và 4 bit cao
mov c_hour, a  ; chuyển trị trong a vào cất trong c_hour
cjne a, #24h, t0reti ; so sánh trị trong a với số 24, để chọn hướng nhẩy
mov c_hour, #0  ; trả trị trong c_hour về lại mức 0


;;
t0reti:   ; đặt tên nhãn để dừng chạy chương trình ngắt
pop psw  ; lấy trị đã cất trong ngăn xếp  trả lại cho thanh psw
pop acc  ; lấy trị đã cất trong ngăn xếp tra lại thanh a
reti   ; dừng chạy chương trình ngắt, quay lại chạy chương trình chính


8. Viết chương trình hiển thị số cho mode chỉnh giờ


; Hiện số lúc đang chỉnh định hẹn giờ mở
; Tham khảo chương hình hiện giờ lúc bình thường
;;
disp2:  ; tên nhãn dùng cho lệnh nhẩy
mov r0, on_hour
mov dptr, #tab
mov a, r0
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.0
call dl
setb p2.0
mov a, r0
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.1
call dl
setb p2.1


;;
mov r1, on_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3
ret

; Hiện số lúc đang chỉnh định hẹn giờ tắt
; Tham khảo chương hình hiện giờ lúc bình thường
 ;;
disp3:  ; tên nhãn dùng cho lệnh nhẩy
mov r0, off_hour
mov dptr, #tab
mov a, r0
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.0
call dl
setb p2.0
mov a, r0
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.1
call dl
setb p2.1


;;
mov r1, off_minute
mov a, r1
swap a
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.2
call dl
setb p2.2
mov a, r1
anl a, #0fh
movc a, @a+dptr
mov p1, a
clr p2.3
call dl
setb p2.3
ret

̣9. Các đoạn chương trình làm trể


; Làm trể để có độ sáng rõ
;;
dl:
mov 30h, #02h
dl1: mov 31h, #0ffh
djnz 31h, $
djnz 30h, dl1
ret


;;
del:
mov 32h, #100
del1: mov 33h, #250
djnz 33h, $
djnz 32h, del1
ret


10. Chương trình viết cho các nút chỉnh




Hình vẽ trên cho thấy công dụng được giao cho các nút chỉnh. Đoạn chương trình sau được viết cho 4 nút chỉnh K1, K2, K3 và K4. Khi Bạn nhất nút K1 sẽ nhẩy ngắt ngoài và vào mode chỉnh.Nhấn K1 lần đầu sẽ cho chỉnh giờ-phút bình thường, lúc này K2 dùng chỉnh giờ và K3 dùng chỉnh phút. Nhấn K4 ra mode chỉnh. Nhấn K1 lần nữa vào vào mode chỉnh giờ-phút cho mode on_timer, lúc này K2 cũng dùng chỉnh giờ và K3 dùng chỉnh phút. Nhấn K4 ra mode chỉnh. Nhấn K1 lần nữa vào mode chỉnh off_timer và lúc này K2 cũng dùng chỉnh giờ, K3 dùng chỉnh phút. Nhấn K4 ra mode chỉnh.


;;
wint0:   ; đặt tên nhãn cho lệnh ngắt ngoài ứng với nút nhấn K1
push acc  ; tạm cho cất trị trong thanh acc vào ngăn xếp
push psw  ; tạm cho cất trị trong thanh psw vào ngăn xếp
clr ex0   ; tạm cho tắt ngắt ngoài
clr tr0  ; tạm dừng mạch đếm của timer 0
clr et0   ; tạm dừng ngắt theo bit báo tràn tf0

k1_11:  ; đặt tên nhãn cho lệnh nhẩy
call disp1  ; cho gọi chương trình hiển thị số
jb k1, k1_11 ; kiểm tra bit 1 của nút nhấn K1

k11_1:  ; đặt tên nhãn cho lệnh nhẩy
call disp1  ; tiếp tục cho gọi chương trình hiện số
jnb k1, k11_1 ; kiểm tra bit 0 trên nút nhấn K1

; Khi nhất nút K1, sẽ kích nhẩy ngắt ngoài _INT0, chương trình sẽ nhẩy đến tên nhãn WINT0:, ở đây người ta tiếp tục cho kiểm tra trạng thái bit của nút nhấn K1, đồng thời luôn cho gọi chương trình hiển thị để tránh hiển thị số bị gián đoạn trong mode điều chỉnh giờ. Chỉ khi bỏ nút nhất K1 ra, lúc đó sẽ chuyển qua kiểm tra trạng thái bit của các nút nhấn khác, như K2, K3.

;;
ph4:   ; đặt tên nhãn cho lệnh nhẩy
setb p3.1 ; cho tắt Led trên chân p3.1
setb p3.0  ; cho tắt Led trên chân p3.0

ph0:  ; đặt tên nhãn cho lệnh nhẩy
call disp1  ; tiếp tục cho gọi chương trình hiển thị số
jb k2, ph01 ; kiểm tra bit 1 trên nút nhấn K2


;;
k2_1:  ; đặt tên nhãn cho lệnh nhẩy
call disp1  ; tiếp tục cho hiển thị số
jnb k2, k2_1  ; kiểm tra bit 0 trên nút nhấn K2
mov r0, c_hour ; chuyển trị trong c_hour vào thanh r0
mov a, #01h  ; đặt trị 1 vào thanh a
add a, r0  ; cho trị trong r0 cộng với trị trong a, kết quả cất vào a
da a  ; biến đổi ra dạng hai số thập phân dạng cơ 2, cất trong 4 bit thấp và 4 bit cao
mov c_hour, a  ; chuyển trị có trong a vào thanh c_hour
cjne a, #24h, ph0  ; so sáng trị trong a với số 24, để định hướng nhẩy
mov c_hour, #0  ; đến đây trả trị trong c_hour về lại mức 0

ph01:   ; đặt tên nhãn cho lệnh nhẩy
jb k3, ph1 ; kiểm tra bit 1 trên nút nhấn K3

k3_1:  ; đặt tên nhãn cho lệnh nhẩy
call disp1 ; tiếp tục cho hiện số tránh gián đoạn lúc vào mode chỉnh
jnb k3, k3_1 ; kiểm tra bit 0 trên nút nhấn K3
mov r0, c_minute ; chuyển trị có trong c_minute vào thanh r0
mov a, #01h ; đặt trị 1 vào thanh a
add a, r0  ; cho trị trong r0 cộng với trị trong a, kết quả cất vào
da a  ; biến đổi ra dạng số thập phân hệ cơ 2, cất trong 4 bit thấp và 4 bit cao
mov c_minute, a  ; chuyển trị có trong a cất vào thanh c_minute
cjne a, #60h, ph0   ; cho so sánh a với trị 60 để chọn hướng nhẩy
mov c_minute, #0  ; trả trị trong c_minute về mức 0

ph1:  ; đặt tên nhãn dùng cho lệnh nhẩy
jb k4, ph10 ; kiểm tra bit 1 trên nút nhất K4

k4_1:   ; đặt tên nhãn dùng cho lệnh nhẩy
call disp1  ; tiếp tục gọi trình hiển thị số
jnb k4, k4_1  ; kiểm tra bit 0 trên nút nhấn K4
jmp tend  ; nhẩy đến tên nhãn tend:, kết thục trình đồng hồ

ph10:  ; đặt tên nhãn dùng cho lệnh nhẩy
jb k1, ph0 ; kiểm tra bit 1 trên nút nhấn K1

k1_1:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp1  ; tiếp tục gọi trình hiển thị số
jnb k1, k1_1  ; kiểm tra bit 0 trên nút nhấn K1 
clr p3.0   ; làm sáng Led trên chân p3.0
setb p3.1  ; làm tắt Led trên chân p3.1


;;--------------------------------
ph2:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp2  ; cho gọi chương trình hiển thị 2 con số
jb k2, ph201  ; kiểm tra bit 1 trên nút nhấn K2

k22_1:  ; đặt tên nh̃n dùng cho lệnh nhẩy
call disp2  ; tiếp tục cho gọi trình hiển thị số disp2
jnb k2, k22_1 ; kiểm tra bit 0 trên nút nhấn K2
mov r0, on_hour ; chuyển trị có trong on_hour vào r0
mov a, #01h ; cho trị 1 vào thanh a
add a, r0  ; cho trị trong r0 cộng với trị trong a, kết quả cất vào a
da a ; biến đổi thành hai số thập phân hệ cơ 2, cho cất vào 4 bit thấp và 4 bit cao
mov on_hour, a ; chuyển trị trong a vào cất trong thanh on_hour
cjne a, #24h, ph2 ; cho so sánh trị trong a với số 24 để chọn hướng nhẩy
mov on_hour, #0 ; trả trị trong thanh on_hour về mức 0

ph201:  ; đặt tên nhãn dùng cho lệnh nhẩy
jb k3, ph210  ; cho kiểm tra bit 1 trên nút nhấn K3

k22_2:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp2 ; tiếp tục cho hiển thị số với disp2
jnb k3, k22_2  ; kiểm tra bit 0 trên nút nhấn K3
mov r0, on_minute  ; cho chuyển trị trong on_hour vào thanh r0
mov a, #01h ; đặt trị 1 vào thanh a
add a, r0  ; cho trị trong r0 cộng với trị trong a, kết quả cất vào a
da a  ; biến đổi trị trong a ra hai số thập phân hệ cơ 2, cho cất vào 4 bit thấp và 4 bit cao
mov on_minute, a  ; chuyển trị trong a cất vào thanh on_minute
cjne a, #60h, ph2  ; cho so sánh trị trong a với số 60 để chọn hướng nhẩy
mov on_minute, #0  ; trả trị trong thanh on_minute về mức 0

ph210:   ; đặt tên nhãn dùng cho lệnh nhẩy
jb k1, ph2  ; kiểm tra bit 1 trên nút chỉnh K1

k22_3:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp2  ; tiếp tục cho hiển thị số với disp2
jnb k1, k22_3 ; kiểm tra bit 0 trên nút nhấn K1
setb p3.0  ; cho tắt Led trên chân p3.0
clr p3.1   ; cho sáng Led trên chân p3.1


;;----------------------------------------
ph3:   ; đặt tên nhãn dùng cho lệnh nhẩy
call disp3  ; cho hiển thị số với trình disp3
jb k2, ph301  ; kiểm tra bit 1 trên nút nhấn K2

k33_1:   ; đặt tên nhãn dùng cho lệnh nhẩy
call disp3  ; tiếp tục cho hiện số với trình disp3
jnb k2, k33_1  ; kiểm tra bit 0 trên nút nhấn K2
mov r0, off_hour  ; chuyển trị trong off_hour vào thanh r0
mov a, #01h  ; đặt trị 1 vào thanh a
add a, r0  ; cho cộng trị trong r0 với a, kết quả cho cất trong a
da a  ; biến đổi số trong a ra dạng hai số thập phân hệ cơ 2, cất vào 4 bit thấp và 4 bit cao
mov off_hour, a ; chuyển trị trong a cất vào thanh off_hour
cjne a, #24h, ph3  ; cho a so sánh với trị 24, để định hướng nhẩy
mov off_hour, #0  ; trả trị trong off_hour về lại mức 0

ph301:  ; đặt tên nhãn dùng cho lệnh nhẩy
jb k3, ph310  ; kiểm tra bit 1 trên nút nhấn K3

k33_2:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp3  ; tiếp tục cho hiện số với trình disp3
jnb k3, k33_2  ; kiểm tra bit 0 trên nút nhấn K3
mov r0, off_minute  ; chuyển trị trong off_minute vào thanh r0
mov a, #01h  ; đặt trị 1 vào thanh ghi a
add a, r0  ; cho trị trong r0 cộng với trị trong a, kết quả cất vào a
da a  ; biến đổi trị trong a ra hai số thập phân hệ cơ 2, cất trong 4 bit thấp và 4 bit cao
mov off_minute, a  ; chuyển trị trong a cất vào thanh off_minute
cjne a, #60h, ph3   ; cho trị trong a so sánh với con số 60, để định hướng nhẩy
mov off_minute, #0  ; trả trị trong thanh off_minute về mức 0

ph310:  ; đặt tên nhãn dùng cho lệnh nhẩy
jb k1, ph3  ; kiểm tra bit 1 trên nút nhấn K1

k33_3:  ; đặt tên nhãn dùng cho lệnh nhẩy
call disp3 ; tiếp tục cho hiển thị số với trình disp3
jnb k1, k33_3  ; kiểm tra bit 0 trên nút nhấn K1
jmp ph4  ; nhẩy đến tên nhãn ph4


;;----------------------------------------
tend:  ; đặt tên nhãn dùng cho lệnh nhẩy
setb ex0  ; cho mở lại chức năng ngắt ngoài, ngắt với _INT0
setb tr0  ; cho xung vào mạch đếm của timer 0
setb 00h  ; đặt bit kiểm tra về trạng thái 00h
setb et0 ; cho mở ngắt theo bit báo tràn tf0
setb p3.7  ; đặt chân p3.7 lên mức áp cao
pop psw  ; hoàn trả lại trị trước đó cho thanh psw
pop acc  ; hoàn trả lại trị trước đó cho thanh a
reti  ; dùng chương trình ngắt
end ; dừng biên dịch ra mã lệnh tại đây

Tạm kết bài viết này


Trên đây là một thí dụ về cách dùng ic lập trình AT89C51 40 chân để làm mạch điện đồng hồ có thêm chức năng chỉnh giờ mở (on_timer) và chỉnh giờ tắt (off_timer), nó có thể dùng làm đồng hồ hẹn giờ để đóng mở các thiết bị. Thật ra còn có rất nhiều cách viết các chương trình đồng hồ cho các ic lập trình, tuy nhiên nếu Bạn xem và hiểu kỹ bài viết này Bạn sẽ thấy được nhiều phương hướng để có thể viết được các chương trình khác tương tự, hay hơn và có nhiều tính năng hơn. Khi có dịp chúng ta sẽ trở lại chuyên đề này với các thí dụ hấp dẫn hơn.  

0 comments :

Post a Comment