Đếm giờ,phút,giây


* Nút K1 dùng chọn các mode chỉnh: Mode chỉnh giờ, mode chỉnh giờ mở, mode chỉnh giờ tắt.

* Nút K2 dùng để chỉnh phần giờ.

* Nút K3 dùng để chỉnh phần phút.
* Nút K4 cho đồng hồ trở lại hoạt động bình thường.


Trong mạch dùng 6 Led số mã 7 đoạn, có chân anode chung. Chúng ta dùng 2 số cho hiển thị giờ, 2 số cho hiển thị phút và 2 số cho hiển thị giây.

Trong mạch cũng dùng các Led đơn để chỉ trạng thái hoạt động của đồng hồ. Một Led (trên chân p3.7) dùng chỉ trạng thái báo giờ. Dùng 2 Led (trên chân p0.0, p0.1) chỉ trạng thái mode chỉnh và 2 Led nữa (trên chân p3.0, p3.1) dùng chỉ trang thái đang điều chỉnh.

Trong câu hỏi em nói dùng 10 Led để chỉ thị trạng thái chỉnh, quả thật tôi không hiểu em nói gì? Trạng thái chỉnh gì?

Ở mạch này, em có thể chỉnh hẹn giờ báo mở (Timer ON) và chỉnh hẹn giờ báo tắt (Timer OFF)  trạng thái báo với Led gắn trên chân p3.7.

Nhưng thôi - theo tôi nghĩ - dù sao đi nữa khi các em đã hiểu thật rõ chương trình đồng hồ này rồi thì việc cho nó biến hóa ra các dạng thức khác, sẽ không còn là công việc quá phức tạp nữa, phải không?

Mong các em đọc kỹ bài viết soạn lại này, tôi nghĩ trên phương diện dùng nó để học hỏi làm quen với công việc lập trình với ic vi điều khiển cũng rất đáng công. Chúc các em học hành tấn bộ, càng học càng giỏi, muốn là làm được. Chào! (VKH)



Khái quát về ic vi điều khiển AT89C51.

Muốn lập trình cho ic vi điều khiển nào (hay cả các ic vi xử lý nữa), trước tiên Bạn phải hiểu rõ các đặc tính của ic đó. Với ic vi điều khiển AT89C51 cũng không ngoại lệ. Trong phần này chúng ta sẽ vắn tắt về cấu trúc nội tại của loại ic này. Với ic AT89C51, chúng ta cần biết 4 điều về nó, đó là:

(1) Công dụng các chân của ic. Các chân cơ bản và nhất là các chân cảng xuất nhập dữ liệu.
(2) Cấu trúc các khối chức năng bên trong để hiểu sự vận hành của nó.
(3) Cách tổ chức các bộ nhớ, và nhất là phải hiều thật rõ các thanh nhớ đặc dụng.
(4) Tập lệnh mà ic đó hiểu và chấp hành.


(1) Công dụng các chân của ic. Các chân cơ bản và nhất là các chân cảng xuất nhập dữ liệu


Hình vẽ cho thấy, ic AT89C51 có 40 chân. Các chân chia ra làm 2 nhóm:

Nhóm chân cơ bản gồm có chân 20 nối masse và chân 40 nối vào đường nguồn 5V. Chân 18, 19 gắn thạch anh để định tần cho mạch dao động, do một lệnh cần 12 phân đoạn, nên tần số xung nhịp sẽ là 1MHz hay chu kỳ lệnh sẽ 1us. Chân số 9 dùng làm chân reset, nó tạo ra tác dụng reset với mức volt cao. Chân 31 dùng chọn định làm việc với bộ nhớ trong hay cả với bộ nhớ ngoài. Các chân 29, 30 sẽ dùng khi chi ic AT89C51 làm việc với các bộ nhớ ngoài.

Nhóm các chân dùng làm cảng xuất nhập dữ liệu. IC AT89C51 có 4 cảng, mỗi cảng 8 bit, vậy có 32 chân dùng xuất nhập bit. Người ta đã dùng tập tin định nghĩa đặt tên cho cảng và đặt tên cho các chân này. Đó là:

* Các chân 39, 38, 37, 36, 35, 33, 32 đã được đặt tên là p0.0, p0.1, p0.2, p0.3, p0.4, p0.5, p0.6, p0.7 và cả 8 chân gôm lại gọi là p0 (hay gọi là cảng port 0).

* Các chân 1, 2, 3, 4, 5, 6, 7, 8 đã được đặt tên là p1.0, p1.1, p1.2, p1.3, p1.4, p1.5, p1.6, p1.7 và cả 8 chân gôm lại gọi là p1 (hay gọi là cảng port 1).

* Các chân 21, 22, 23, 24, 25, 26, 27, 28 đã được đặt tên là p2.0, p2.1, p2.2, p2.3, p2.4, p2.5, p2.6, p2.7 và cả 8 chân gôm lại gọi là p2 (hay gọi là cảng port 2).

* Các chân 10, 11, 12, 13, 14, 15 , 16, 17, 18 đã được đặt tên là p3.0, p3.1, p3.2, p3.3, p3.4, p3.5, p3.6, p3.7 và cả 8 chân gôm lại gọi là p3 (hay gọi là cảng port 3).

Với các tên đã đặt như vậy, nên khi viết các câu lệnh, Bạn có thể dùng trực tiếp các tên gọi này, nhờ vậy cũng rất tiện. Bên cạnh hình trên là một sơ đồ mạch điện cơ bản, khi Bạn dùng ic AT89C51 Bạn có thể ráp theo sơ đồ mạch điện này. Các chương trình điều khiển của Bạn sẽ phải thông quả 4 cảng này để điều khiển các thiết bị, do đó việc hiểu rõ tác dụng của các cảng này là rất quan trọng.



(2) Cấu trúc các khối chức năng bên trong để hiểu sự vận hành của nó.



Từ sơ đồ khối chức năng trên, Bạn sẽ thấy cách vận hành bên trong của ic vi điều khiển AT89C51. Trong ic có khối xử lý phép toán ALU, ALU kết hợp với thanh ghi acc (còn gọi là thanh a) và thanh b, đây là khối toán thuật quan trong của ic. Bộ lấy câu lệnh của chương trình trong khối nhớ Flash để chấp hành, cách chạy chương trình theo thanh đếm PC và địa chỉ thì theo thanh ghi con trỏ dptr. Bộ nhớ RAM trong đó có các thanh ghi đặc dụng dùng điều khiển các cơ phận của ic. Ngoài ra là sự điều hành 4 cảng p0, p1, p2, p3 dùng để xuất nhập dữ liệu dạng bit. Cuối cùng là khối dao động tạo xung nhịp theo thạch anh gắn thêm từ bên ngoài.


(3) Cách tổ chức các bộ nhớ, và nhất là phải hiểu thật rõ các thanh nhớ đặc dụng (SFR).



 

Hình vẽ cho thấy các bộ nhớ trong của ic AT89C51. Với bộ nhớ RAM ,ngoài 128 thanh dùng làm các thanh nhớ đặc dụng (SFR) còn lại 128 thanh ở vùng thấp có thể cho ghi đọc tùy ý. Với bộ nhớ EEPROM, có 4K thanh nhớ, tức 1024x4 số thanh nhớ nội. Khi Bạn chỉ muốn chạy chương trình có trong bộ nhớ nội thì cho chân /EA ở mức áp cao (nối vào nguồn 5V), trường hợp Bạn còn muốn chạy các chương trình có trong bộ nhớ ngoài thì cho chân /EA xuống mức áp thấp, tức nối xuống masse.

Khi Bạn cho ghi các chương trình từ file .hex vào bộ nhớ này, thì chân /EA phải nhận đủ mức áp Vpp=12V (chuyện này hộp nạp sẽ làm).

Do ic AT89C51 chỉ có thể xử lý bộ địa chỉ 16 bit (8 bit x2), nên nó chỉ có thể truy cập các bộ nhớ ngoài RAM hay ROM có dung lượng là 65536 thanh nhớ (quen gọi là bộ nhớ 64K).



Hình vẽ cho thấy cách sắp xếp của bộ nhớ RAM của ic AT89C51. Trong ic vi điều khiển nào cũng vậy, việc hiểu rõ cấu trúc của bộ nhớ RAM là rất quan trọng, Ở đây nó có 256 thanh nhớ, được chia ra làm 2 phần. Phần 128 thanh nhớ vùng trên có nhiều thanh dùng làm thanh nhớ đặc dụng (SFR),  ở vùng này cũng còn lại nhiều thanh không dùng, nó có công dụng dự phòng tăng tính khả tin cho ic và Phân 128 thanh nhớ ở vùng dưới dùng làm các thanh nhớ ghi đọc tùy ý. Đây chính là các thanh nhớ "sân khấu" dùng điều hành các hoạt động của ic, các thanh nhớ ghi đọc được này càng nhiều thì hoạt động của ic càng linh động và nhanh. Cái đặc sắc của ic AT89C51 là trong một số thanh nhớ (Bạn xem hình), người ta còn tạo ra địa chỉ cho từng bit nhớ. Vậy chúng ta có thể truy cập các thanh nhớ bằng địa chỉ thanh và còn có thể truy cập từng bit nhớ bằng địa chỉ bit.





Hình trên đây, tôi vẽ lại vùng các thanh nhớ có cho bố trí các bit nhớ. Có Bạn hỏi làm sao phân biệt được việc truy cập địa chỉ của bit và địa chỉ của thanh. Vấn đề này được qui định rất rõ ràng, nó tùy theo tác vụ của câu lệnh.

Một thí dụ:

mov 00h, #10   ; Lệnh này cho nạp trị thập phân 10 vào thanh ghi 00h (tức thanh r0)

clr 00h  ; Lệnh này cho xóa bit 00h (hay đặt 0 vào địa chỉ 00h),  nhìn bảng chúng ta thấy bit đầu của thanh nhớ 20h có địa chỉ bit là 00h (Bạn xem hình) .

Trong 2 câu lệnh trên, cũng đều có địa chỉ là 00h. Nhưng khi dùng cho các câu lệnh có tác vụ trên thanh thì nó là địa chỉ thanh, như lệnh mov (move) trên. Và khi dùng với câu lệnh có tác vụ  trên bit thì đó là địa chỉ bit, như lệnh clr (clear bit)

Để tiện dùng, người ta đã đặt tên cho 8 thanh nhớ trong bank0 là:  r0, r1, r2, r3, r4, r5, r6, r7, khi ở bank 0 nó có địa chỉ tương ứng là 00h, 01h, 02h, 03h, 04h, 05h, 06h, 07h. Bạn có thể cho dời 8 thanh này vào bank1, bank2 hay bank3 bằng các set bit trong thanh ghi trạng thái PSW với 2 bit RS1, RS0.

Nếu Bạn chọn:

RS1 = 0 và RS0 = 0  , có nghĩa là 8 thanh r0, r1, r2, r3, r4, r5, r6, r7 nằm ở bank0 (mặc định).
RS1 = 0 và RS0 = 1  , có nghĩa là 8 thanh r0, r1, r2, r3, r4, r5, r6, r7 cho nằm ở bank1.
RS1 = 1 và RS0 = 0  , có nghĩa là 8 thanh r0, r1, r2, r3, r4, r5, r6, r7 cho nằm ở bank2.
RS1 = 1 và RS0 = 1  , có nghĩa là 8 thanh r0, r1, r2, r3, r4, r5, r6, r7 cho nằm ở bank3.

Trong 8 thanh r0, r1, r2, r3, r4, r5, r6, r7 chỉ có 2 thanh r0, r1 cho Bạn truy cập địa chỉ gián tiếp rất hay với ký hiệu là @r0, @r1. Bạn xem các câu lệnh sau:

mov r0, #20h     ; Câu này là nạp trị 20h vào thanh ghi r0 (r0 có địa chỉ mặc định là 00h trong RAM).

mov @r0, #20h ; Trong câu lệnh này là Bạn cho nạp trị 20h vào thanh nhớ có địa chỉ là 20h ( vì trong r0 đã có trị 20h, @r0 không phải là địa chỉ 00h).

Các thanh khác như r2, r3, r4, r5, r6, r7 không có tác vụ này.


(4) Tập lệnh mà ic đó hiểu và chấp hành (Bảng liệt kê các câu lệnh dùng cho AT89C51).



  

Sơ đồ mạch điện:  Đồng hồ có chỉnh giờ dùng ic vi điều khiển AT89C51



(Sơ đồ này tôi dùng OrCAD vẽ lại theo các câu lệnh trong chương trình nguồn ở bên dưới)


Còn đây là bài thực hành đồng hồ đã tìm được trên mạng tiếng Hoa (Tương ứng với chương trình dưới. Hình này và sơ đồ mạch điện cho nhiều chi tiết cụ thể hơn. Bạn hãy tham khảo hình theo các câu lệnh)

(Hình chụp bo thực hành: Đồng hồ có hẹn giờ chạy với ic AT89C51)

Sơ đồ gốc của bài thực hành này, tư liệu lấy từ trang Web tiếng Hoa  (Hình khá mờ):
 
 
Giải thích mạch điện (Tôi tách sơ đồ trên ra làm 5 phần như sau):


Hình 1: Cho thấy mạch điện cơ bản của ic AT89C51 .


Chân 20 nối masse, chân 40 nối nguồn 5V. Chân 18, 19 gắn thạch anh định tần (Mạch dùng thạch anh có tần số 6MHz). Chân 9 nối vào mạch reset, khi cấp điện tụ c1 (10uF) nạp dòng qua R4 (10K), tạm thời đưa chân 9 lên mức áp cao để tạo tác dụng reset, lúc này chương trình sẽ quay lại từ thanh 00h của bộ nhớ ROM (trong AT89C51). Chân 31 cho nối vào nguồn 5V là muốn chỉ dùng bộ nhớ ROM trong.

Hình 2: Gắn các nút chỉnh và các Led chỉ thị trên port 3.

Led chỉ thị trên chân P3.1, P3.0 dùng chỉ trạng thái mode đang chỉnh. Led trên chân p3.7 dùng báo mạch đã đến đúng giờ mở hay đúng giờ tắt, Bạn có thể dùng bit này để điều khiển một TRIAC, đóng mở các thiết bị ngoài. Theo chương trình nguồn thì

Khóa điện K1 là chọn mode.
Khóa điện K2 là chỉnh số chỉ giờ
Khóa điện K3 là chỉnh số chỉnh giây
Khóa điện K4 là trở lại chức năng đồng hồ.


Hình 3: Công dụng của port2.


Dùng 6 chân trên port 2 để đóng mở các transistor 8050, nhầm cấp 5V cho chân anode chung của các đèn số. Dùng chân p2.6 làm tắt mở Led chỉ thị và dùng tín hiệu trên chân p2.7 qua tầng khuếch đại với transistor 8550 để kích thích loa, báo bằng âm thanh.

Hình 4: Mạch kiểm soát các thiết bị ngoài với bit trên chân p3.7.


Khi chân p3.7 lên mức áp cao (đúng giờ mở, ON), nó qua bộ ghép quang điện cấp áp cho cực G của TRIAC, TRIAC dẫn điện và đèn sẽ được cấp điện nên phát sáng.

... và đến khi chân p3.7 xuống mức áp thấp (đúng giờ tắt OFF), nó qua bộ ghép quang điện sẽ làm tắt TRIAC và đèn sẽ tắt.

Đúng giờ hẹn (ON) thì đèn sáng và sau đó... đúng giờ hẹn (OFF) thì đèn sẽ tắt và qui trình sẽ lập lại.

Hình 5: Dùng port2 và port 1 để điều khiển bảng đèn số với các Led theo mã 7 đoạn.



Sơ đồ cho thấy: Mã hiện số 7 đoạn cho xuất ra trên các chân của port 1. Nó cấp dòng cho các chân cathode của đèn số và port 2 dùng tắt mở các transistor 8050 (pnp) để mỗi lần chỉ cấp 5V cho một chân anode chung của một đèn số. Với cách làm việc đủ nhanh này, Bạn sẽ luôn thấy giờ - phút - giây hiện ra trên 6 đèn số 7 đoạn trên (nhưng thật ra mỗi lần chỉ mở có một đèn số).

Để hiểu rõ hơn về cách hiện số trên đèn số Led 7 đoạn, Bạn xem hình động sau:


Trong đó mã nhị phân xuất ra để làm các chân cathode của các Led trong đèn thông masse được cho xuất ra trên cảng port 1. Bạn thấy với mã hiện số xuất ra, nếu chúng ta sắp các Led theo hàng ngang thì chẳng thấy gì lạ cả, chỉ thấy các Led nhấp nháy, nhưng khi sắp các Led này theo dạng hình chữ Nhật  () thì vấn đề lại khác, nó chính là các con số từ 0...9.



 Chương trình nguồn.


;; 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



(Bạn có thể cho cắt file nguồn này dán vào trình MIDE để cho biên dịch ra file .hex và cho nạp file này vào ic AT89C51, nạp xong gắn ic này vào bo mạch điện trên, khi mạch được cấp điện các Led số sẽ hiện ra Giờ-Phút-Giây)



Sau khi biên dịch xong, trình MIDE báo không lỗi (no errors), lúc này Bạn sẽ có file mã lấy họ .hex, trong file này, Bạn thấy các hàng số viết theo hệ đếm 16, khi nạp vào ic nó sẽ được cho đổi ra dạng nhị phân và nạp vào bộ nhớ EEP-ROM của ic vi điều khiển AT89C51.

:060000000200300201C9FC
:03000B000200EB05
:10003000758150C20075210075220075230075245A
:1000400000752500752600752700752800752900A4
:10005000758C05758A05758902D2AFD2A8C2A9C26E
:100060008CC288E523B5260AE524B52705D2B70258
:10007000007EE523B52807E524B52902C2B7300084
:10008000E212009080DDC0F9A4B0999282F88090CD
:10009000A823900086E8C4540F93F590C2A01201E3
:1000A000AFD2A0E8540F93F590C2A11201AFD2A134
:1000B000A924E9C4540F93F590C2A21201AFD2A2B1
:1000C000E9540F93F590C2A31201AFD2A3AA25EA77
:1000D000C4540F93F590C2A41201AFD2A4EA540FF6
:1000E00093F590C2A51201AFD2A522C0E0C0D00501
:1000F00021E521B5C8387521000522E522B40A2E74
:10010000752200B280B28174012525D4F525B46032
:100110001D75250074012524D4F524B460107524C0
:100120000074012523D4F523B42403752300D0D013
:10013000D0E032A826900086E8C4540F93F590C210
:10014000A01201AFD2A0E8540F93F590C2A1120102
:10015000AFD2A1A927E9C4540F93F590C2A212010E
:10016000AFD2A2E9540F93F590C2A31201AFD2A36C
:1001700022A828900086E8C4540F93F590C2A012DC
:1001800001AFD2A0E8540F93F590C2A11201AFD2F3
:10019000A1A929E9C4540F93F590C2A21201AFD2CC
:1001A000A2E9540F93F590C2A31201AFD2A3227516
:1001B00030027531FFD531FDD530F72275326475C7
:1001C00033FAD533FDD532F722C0E0C0D0C2A8C281
:1001D0008CC2A9119020B2FB119030B2FBD2B1D2E7
:1001E000B0119020B413119030B4FBA823740128EF
:1001F000D4F523B424EB75230020B313119030B34E
:10020000FBA824740128D4F524B460D575240020FB
:10021000B508119030B5FB02029820B2C41190309D
:10022000B2FBC2B0D2B1313320B413313330B4FB9E
:10023000A826740128D4F526B424EB75260020B333
:1002400013313330B3FBA827740128D4F527B460E9
:10025000D575270020B2CF313330B2FBD2B0C2B156
:10026000317120B413317130B4FBA828740128D443
:10027000F528B424EB75280020B313317130B3FB9B
:10028000A829740128D4F529B460D575290020B2B5
:10029000CF317130B2FB21DDD2A8D28CD200D2A9ED
:0702A000D2B7D0D0D0E0324C
:00000001FF




Phân tích chương trình nguồn.



;; ---------------------------------------------------------------------------------------
 ;; Bạn cho định nghĩa các bit, các thanh nhớ bằng chữ cho dễ nhớ dễ dùng trong câu lệnh.
;;----------------------------------------------------
;; Dong ho co nut chinh mo chinh tat
;;----------------------------------------------------
        k1      bit p3.2              ; Định nghĩa chữ K1 là chân p3.2
        k2      bit p3.4              ; Định nghĩa chữ K2 là chân p3.4
        k3      bit p3.3               ; Định nghĩa chữ K3 là chân p3.3
        k4      bit p3.5               ; Định nghĩa chữ K4 là chân p3.5

;; Dùng tên đặt cho các thanh địa chỉ để sau này dùng trong các câu lệnh sẽ dễ nhớ hơn.
        c_hour          equ 23h       ; Lấy thanh nhớ 23h để nhớ số đếm giờ
        c_minute        equ 24h     ; Lấy thanh nhớ 24h để nhớ số đếm phút
        c_second        equ 25h    ; Lấy thanh nhớ 25h để nhớ số đếm giây
        on_hour         equ 26h      ; Lấy thanh nhớ 26h để nhớ số đếm giờ hẹn mở
        on_minute       equ 27h    ; Lấy thanh nhớ 27h để nhớ số đếm phút hẹn mở
        off_hour        equ 28h       ; Lấy thanh nhớ 28h để nhớ số đếm giờ hẹn tắt
        off_minute      equ 29h     ; Lấy thanh nhớ 29h để nhớ số đếm phút hẹn tắt

;; Khởi đầu chương trình đồng hồ từ địa chỉ thanh 0000h tức thanh reset. 
org 0000h                            ; Khởi từ địa chỉ của thanh reset.                 

        jmp main                      ; Cho nhẩy qua các thanh nhớ đã dùng cho chức năng ngắt
org 0003h                            ; Nơi chạy trình ngắt ngoài theo bit 0 trên chân p3.2.
        jmp wint0                     ; Chương trình ngắt theo phím nhấn gắn trên p3.2
org 000bh                            ; Nơi chạy trình ngắt theo bit tràn tf0 của timer T0.
        jmp wt0                         ; Chương trình ngắt theo bit tràn tf0 của timer T0
org 0030h

; ------------Khởi đầu chương trình chính----------------
main:                                       ; Tên nhãn chương trình chính
        mov sp, #50h                 ; Xác định địa chỉ chọn vùng làm ngăn xếp

;; Các trị khởi đầu, tất cả cho về trị 0.

        clr 00h                           ; Cho xóa bit kiểm tra lấy ở địa chỉ 00h              
        mov 21h, #0                  ; Cho xóa sạch thanh nhớ có địa chỉ là 21h
        mov 22h, #0                  ; Cho xóa sạch thanh nhớ có địa chỉ là 22h
        mov c_hour, #0             ; Cho xóa sạch thanh nhớ dùng đếm giờ
        mov c_minute, #0          ; Cho xóa sạch thanh nhớ dùng đếm phút
        mov c_second, #0          ; Cho xóa sạch thanh nhớ  dùng đếm giây
        mov on_hour, #0              ; Cho xóa sạch thanh nhớ dùng hẹn giờ mở
        mov on_minute, #0          ; Cho xóa sạch thanh nhớ dùng hẹn phút mở
        mov off_hour, #0              ; Cho xóa sạch thanh nhớ dùng hẹn giờ tắt
        mov off_minute, #0           ; Cho xóa sạch thanh nhớ dùng hẹn phút tắt

;;----------------Đặt mode đồng hồ T0 và đặt trị khởi đầu vào thanh th0, tl0 của timer T0--------------
;;Dùng đồng hồ timer 0, mode 2, mode tự nạp lại để cho đếm thời gian
        mov th0, #05       ; Đặt trị khởi đầu cho thanh th0.
        mov tl0, #05         ; ; Đặt trị khởi đầu cho thanh tl0.
        mov tmod, #00000010b    ;02h (Chọn đồng hồ T0, chạy theo mode 2, mode 8 bit, tự nạp lại).

;;---------------Đặt các ngắt và điều kiện làm việc của đồng hồ-----------------
;;
        setb ea          ; Cho mở tất cả 6 ngắt của AT89C51
        setb ex0        ; Đặt ngắt cho /INT0 (Cho mở ngắt ngoài /INT0, trên chân p3.2)
        clr et0            ;  Tắt ngắt theo bit tràn của T0
        clr tr0             ; Cho ngưng chạy đồng hồ T0
        clr it0             ; Cho xóa tính ưu tiên của ngắt /INT0

;---------------- Khởi đầu cho đếm giờ-----------------
;-----------Kiểm tra đúng giờ để báo mở (ON).
m:
        mov a, c_hour                        ; Chuyển trị trong thanh đếm giờ vào a
        cjne a, on_hour, off_time       ; So sánh trị trong a với trị trong on_hour, qua so với off_time
        mov a, c_minute   ; Chuyển trị trong thanh đếm phút vào a
        cjne a, on_minute, off_time ; So sánh trị trong a với trị trong on_minute, qua so với off_time
        setb p3.7       ; Đặt p3.7 lên mức cao để báo giờ mở
        jmp next        ; Nhẩy về tên nhãn next để tiếp tục phần đồng hồ

;-----------Kiểm tra đúng giờ để báo tăt (OFF).
off_time:
        mov a, c_hour  ; chuyển trị trong c_hour vào thanh a.
        cjne a, off_hour, next  ; So sánh trị của a với trị trong off_hour.
        mov a, c_minute   ; chuyển trị trong c_minute vào thanh a.
        cjne a, off_minute, next  ; So sánh trị của a với trị trong off_minute
        clr p3.7            ; Đặt p3.7 xuống mức áp thấp để báo giờ tắt

next:   jnb 00h, m   ; Kiểm tra bit 00h, nếu nó ở bit 0 thì nhẩy đến tên nhãn m
        call disp1  ; Gọi chương trình cho hiển thị đồng hồ
        jmp m ; nhẩy trở lại tên nhãn m tiếp tục kiểm tra giờ mở giờ tắt

;;-------------Bảng chứa các mã hiện số trên Led 7 đoạn--------
tab:
db 0c0h (0), 0f9h (1), 0a4h (2), 0b0h (3), 99h (4), 92h (5), 82h (6), 0f8h (7), 80h (8), 90h (9)

;; Ghi chú: Bảng mã này sẽ làm sáng các Led trên đèn số, tùy theo sự sắp xếp của Bạn.
;; Bạn có thể chõn định lại cho đúng. Nếu đưa về dạng hệ cơ 2 sẽ dễ thấy hơn.
;; 0c0h (0),   1100-0000
;; 0f9h (1),    1111-1001
;; 0a4h (2),   1010-0100
;; 0b0h (3),   1011-0000
;; 99h (4),     1001-1001
;; 92h (5),     1001-0010
;; 82h (6),     1000-0010
;; 0f8h (7),     1111-1000
;; 80h (8),      1000-0000
;; 90h (9)       1001-0000

,, Nhìn các dãy số nhị phân, Bạn thấy khi cho xuất bit 0 là Led sáng và cho xuất bit 1 là Led tắt
 ;; Vậy theo bảng số này, bố trí trên port 1 cho 7 chân cathode của đèn số sẽ là:
;;  a (p1.0), b(p1.1), c(p1.2), d(p1.3), e(p1.4), f(p1.5), g(p1.6), (p1.7 không và là dấu chấm).

;;-----------Chương trình cho hiện số lúc đấm giờ bình thường----------------------
disp1:

; Cho hiện số ở 2 số chỉ giờ (hour)
        mov r0, c_hour ; chuyển trị trong c_hour vào thanh r0
        mov dptr, #tab  ; Đặt địa chỉ bảng vào thanh ghi con trỏ
        mov a, r0         ; Chuyển trị trong r0 vào thanh a
        swap a               ; hoán đổi vị trí 4 bit cao 4 bit thấp trong thanh a
        anl a, #0fh           ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr  ; Vào bảng lấy mã theo trị của a, mã này cất vào a
        mov p1, a  ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.0  ; Cho mở số giờ (hàng đơn vị ) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl  ; Cho làm trễ để đèn số đủ sáng
        setb p2.0 ; tắt đèn hiện giờ  (đv) để tránh lem số
        mov a, r0  ; Chuyển trị có trong thanh r0 vào lại a
        anl a, #0fh   ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr ; Vào bảng lấy mã theo trị của a, mã này cất vào a
        mov p1, a   ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.1 ; Cho mở số giờ (hàng chục) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl    ; Cho làm trễ để đèn số đủ sáng
        setb p2.1  ; tắt đèn hiện giờ (h chục) để tránh lem số


;; Cho hiện số ở 2 số chỉ phút (minute)
        mov r1, c_minute ; Chuyển trị trong c_minute vào thanh r1
        mov a, r1 ; Chuyển trị trong r1 vào thanh a
        swap a    ; hoán đổi vị trí 4 bit cao 4 bit thấp trong thanh a
        anl a, #0fh   ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr   ; Vào bảng lấy mã theo trị của a, mã này cất vào a
        mov p1, a   ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.2  ; Cho mở số phút (hàng đơn vị ) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl      ; Cho làm trễ để đèn số đủ sáng
        setb p2.2   ; tắt đèn hiện phút (h đơn vị) để tránh lem số
        mov a, r1    ; Lại chuyển trị trong r1 vào thanh a
        anl a, #0fh    ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr  ; Vào bảng lấy mã theo trị của a, mã này cất vào
        mov p1, a  ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.3  ; Cho mở số phút (hàng chục ) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl      ; Cho làm trễ để đèn số đủ sáng
        setb p2.3   ; tắt đèn hiện phút (h chục) để tránh lem số

;; Cho hiện số ở 2 số chỉ giây (second)
        mov r2, c_second ; Chuyển trị trong c_second vào thanh r2
        mov a, r2    ; Chuyển trị trong r2 vào thanh a
        swap a       ; hoán đổi vị trí 4 bit cao 4 bit thấp trong thanh a
        anl a, #0fh   ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr  ; Vào bảng lấy mã theo trị của a, mã này cất vào a
        mov p1, a   ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.4   ; Cho mở số giây (hàng đơn vị ) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl    ; Cho làm trễ để đèn số đủ sáng
        setb p2.4  ; tắt đèn hiện giây (h đơn vị) để tránh lem số
        mov a, r2   ; Lại chuyển trị trong r2 vào thanh a
        anl a, #0fh    ; Lấy logic AND để che 4 bit cao, giữ nguyên 4 bit thấp
        movc a, @a+dptr  ; Vào bảng lấy mã theo trị của a, mã này cất vào
        mov p1, a  ; Cho xuất mã hiện số lấy trong bảng ra trên cảng port1
        clr p2.5  ; Cho mở số giây (hàng chục ) bằng cách cấp +5V cho đèn số 7 đoạn
        call dl     ; Cho làm trễ để đèn số đủ sáng
        setb p2.5   ; tắt đèn hiện giây (h đơn vị) để tránh lem số
        ret             ; Quay lại sau câu lệnh CALL DISP1

;; -------------Chương trình đếm thời gian----------------
wt0:
        push acc  ; Tạm cất trị của thanh a vào ngăn xếp.
        push psw ; Tạm cất thanh trạng thái vào ngăn xếp
        inc 21h  ; cho tăng trị trong thanh 21h theo bước +1.
        mov a, 21h  ; Chuyển trị trong thanh 21h vào thanh a
        cjne a, 0c8h, t0reti ; 200d=0c8h    0.5ms*200=100ms. Cho so sánh a với trị 0c8h để nhẩy
        mov 21h, #0  ; Trả trị trong thanh 21h về trị 0
        inc 22h     ; cho tăng trị trong thanh 22h theo bước +1
        mov a, 22h   ; Chuyển trị trong thanh 22h vào thanh a
        cjne a, #10, t0reti  ; 100ms*10=1s. Cho so sánh a với trị 10 để nhẩy
        mov 22h, #0    ; Trả trị trong thanh 22h về trị 0
        cpl p0.0  ; Lấy bù bit p0.0
        cpl p0.1  ; Lấy bù bit p0.0


;;------------Cho đếm Giờ - Phút – Giây theo bước tăng +1 -------------

;; Đếm giây
        mov a, #01h          ; Cho chuyển trị 01h vào thanh a
        add a, c_second   ; Cho cộng trị trong thanh c_second với a, kết quả cất vào a
        da a                       ; Đổi về dạng số thập phân ở 4 bit cao và 4 bit thấp
        mov c_second, a    ; Chuyển trị trong a trở lại thanh c-second
        cjne a, #60h, t0reti  ; So sánh trị trong a là 60 chưa để nhẩy
        mov c_second, #0  ; là 60 rồi thì trả thanh c_second trở lại trị 0

;; Đếm phút
        mov a, #01h           ; Cho chuyển trị 01h vào thanh a
        add a, c_minute   ; Cho cộng trị trong thanh c_minute với a, kết quả cất vào a
        da a           ; Đổi về dạng số thập phân ở 4 bit cao và 4 bit thấp
        mov c_minute, a   ; Chuyển trị trong a trở lại thanh c-minute
        cjne a, #60h, t0reti   ; So sánh trị trong a là 60 chưa để nhẩy
        mov c_minute, #0  ; là 60 rồi thì trả thanh c_minute trở lại trị 0

;; Đếm giờ
        mov a, #01h    ; Cho chuyển trị 01h vào thanh a
        add a, c_hour   ; Cho cộng trị trong thanh c_hour với a, kết quả cất vào a
        da a     ; Đổi về dạng số thập phân ở 4 bit cao và 4 bit thấp
        mov c_hour, a    ; Chuyển trị trong a trở lại thanh c-hour
        cjne a, #24h, t0reti    ; So sánh trị trong a là 24 chưa để nhẩy
        mov c_hour, #0  là 24 rồi thì trả thanh c_hour trở lại trị 0

;; Chương trình dừng ngắt timer 0
t0reti:
        pop psw
        pop acc
        reti

;;---------------Cho hiện giờ - phút lúc chỉnh ON-----------------
;; Viết giống như đoạn disp1
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

;;------------------Cho hiện giờ - phút lúc chỉnh OFF----------------------
;; Viết giống như đoạn disp1
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

;;--------------------Chương trình làm trễ để giữ cho Led đủ sáng ------------------
dl:
        mov 30h, #02h
dl1:    mov 31h, #0ffh
        djnz 31h, $
        djnz 30h, dl1
        ret

;;----------------------Chương trình làm trễ------------------------
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

;----------------------Chương trình kiểm tra phím nhấn -------------------
k1_11:
        call disp1   ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jb k1, k1_11  ; Kiểm tra phím nhấn K1, chờ nhấn phím để có bit 0

;---------------Kiểm tra phím nhấn K1, chờ bỏ phím
k11_1:
        call disp1    ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jnb k1, k11_1   ; Kiểm tra phím nhấn K1, chờ bỏ nhấn phím để có bit 1

;;
ph4:
        setb p3.1    ; Đặt chân p3.1 lên mức áp cao
        setb p3.0     ; Đặt chân p3.0 lên mức áp cao

ph0:    call disp1   ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jb k2, ph01    ; Kiểm tra phím nhấn K2, chờ nhấn phím để có bit 0

;;
k2_1:
        call disp1         ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jnb k2, k2_1      ; Kiểm tra phím nhấn K2, chờ bỏ nhấn phím để có bit 1
        mov r0, c_hour  ; Cho chuyển trị trong c_hour vào thanh r0
        mov a, #01h      ; Đặt trị 01h vào thanh ghi a
        add a, r0            ; Cho cộng trị trong a với 1 và cất vào a
        da a                   ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov c_hour, a   ; Cho chuyển trị trong a ra thanh c-hour
        cjne a, #24h, ph0  ; Cho so sánh a với số 24 để nhẩy
        mov c_hour, #0   ; Trả trị trong c-hour về 0

ph01:
        jb k3, ph1   ; Kiểm tra phím nhấn K3, chờ nhấn phím để có bit 0


k3_1:
        call disp1          ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jnb k3, k3_1     ; Kiểm tra phím nhấn K3, chờ bỏ nhấn phím để có bit 1
        mov r0, c_minute   ; Cho chuyển trị trong c_minute vào thanh r0
        mov a, #01h    ; Đặt trị 01h vào thanh ghi a
        add a, r0   ; Cho cộng trị trong a với r0 và cất vào a
        da a          ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov c_minute, a     ; Cho chuyển trị trong a ra thanh c-minute
        cjne a, #60h, ph0   ; Cho so sánh a với số 60 để nhẩy
        mov c_minute, #0   ; Trả trị trong c-minute về 0

ph1:
        jb k4, ph10  ; Kiểm tra phím nhấn K4, chờ nhấn phím để có bit 0

k4_1:
        call disp1    ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jnb k4, k4_1  ; Kiểm tra phím nhấn K4, chờ bỏ nhấn phím để có bit 1
        jmp tend  ; Nhẩy không điều kiện đến tên nhãn tend (kết thúc Timer End)

;---------------Vào mode chỉnh giờ - phút cho mode ON---------
ph10:
        jb k1, ph0  ; Kiểm tra phím nhấn K1, chờ nhấn phím để có bit 0

k1_1:
        call disp1   ; Vẫn cho hiển thị đồng hồ với giờ - phút - giây
        jnb k1, k1_1   ; Kiểm tra phím nhấn K1, chờ bỏ nhấn phím để có bit 1
        clr p3.0         ; Đặt p3.0 ở mức áp thấp.
        setb p3.1      ; Đặt p3.1 ở mức áp cao

;;--------------------------------
ph2:
        call disp2  ; Cho hiển thị giờ - phút ở mode ON
        jb k2, ph201   ; Kiểm tra phím nhấn K2, chờ nhấn phím để có bit 0

k22_1:
        call disp2   ; Vẫn cho hiển thị giờ - phút ở mode ON
        jnb k2, k22_1   ; Kiểm tra phím nhấn K2, chờ bỏ nhấn phím để có bit 1
        mov r0, on_hour  ; Chuyển trị trong on_hour vào thanh r0
        mov a, #01h   ; Đặt trị 01h vào thanh ghi a
        add a, r0       ; Cho cộng trị trong a với r0 và cất vào a
        da a             ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov on_hour, a   ; Cho chuyển trị trong a ra thanh on_hour
        cjne a, #24h, ph2  ; Cho so sánh a với số 24 để nhẩy
        mov on_hour, #0  ; Trả trị trong on-hour về 0

ph201:
        jb k3, ph210  ; Kiểm tra phím nhấn K3, chờ nhấn phím để có bit 0

k22_2:
        call disp2     ; Vẫn cho hiển thị giờ - phút ở mode ON
        jnb k3, k22_2   ; Kiểm tra phím nhấn K3, chờ bỏ nhấn phím để có bit 1
        mov r0, on_minute ; Chuyển trị trong on_minute  vào thanh r0
        mov a, #01h    ; Đặt trị 01h vào thanh ghi a
        add a, r0    ; Cho cộng trị trong a với r0 và cất vào a
        da a             ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov on_minute, a   ; Cho chuyển trị trong a ra thanh on_minute
        cjne a, #60h, ph2    ; Cho so sánh a với số 60 để nhẩy
        mov on_minute, #0    ; Trả trị trong on-minute về 0

ph210:  jb k1, ph2     ; Kiểm tra phím nhấn K1, chờ nhấn phím để có bit 0

k22_3:
        call disp2   ; Vẫn cho hiển thị giờ - phút ở mode ON
        jnb k1, k22_3   ; Kiểm tra phím nhấn K1, chờ bỏ nhấn phím để có bit 1
        setb p3.0     ; Đặt bit p3.0 lên mức áp cao
        clr p3.1        ; Đặt bit p3.1 xuống mức áp thấp

;;----------------------------------------
ph3:
        call disp3      ; Cho hiển thị giờ - phút ở mode OFF
        jb k2, ph301   ; Kiểm tra phím nhấn K2, chờ nhấn phím để có bit 0

k33_1:
        call disp3    ; Vẫn cho hiển thị giờ - phút ở mode OFF
        jnb k2, k33_1   ; Kiểm tra phím nhấn K2, chờ bỏ nhấn phím để có bit 1
        mov r0, off_hour  ; Chuyển trị trong off_hour vào thanh r0
        mov a, #01h   ; Đặt trị 01h vào thanh ghi a
        add a, r0   ; Cho cộng trị trong a với r0 và cất vào a
        da a  ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov off_hour, a    ; Cho chuyển trị trong a ra thanh off_hour
        cjne a, #24h, ph3   ; Cho so sánh a với số 24 để nhẩy
        mov off_hour, #0   ; Trả trị trong off-hour về 0

ph301:
        jb k3, ph310   ; Kiểm tra phím nhấn K3, chờ nhấn phím để có bit 0

k33_2:
        call disp3     ; Vẫn cho hiển thị giờ - phút ở mode OFF
        jnb k3, k33_2   ; Kiểm tra phím nhấn K2, chờ bỏ nhấn phím để có bit 1
        mov r0, off_minute   ; Chuyển trị trong off_minute vào thanh r0
        mov a, #01h   ; Đặt trị 01h vào thanh ghi a
        add a, r0   ; Cho cộng trị trong a với r0 và cất vào a
        da a    ; Thập phân hóa 4 bit cao và 4 bit thấp
        mov off_minute, a   ; Cho chuyển trị trong a ra thanh off_minute
        cjne a, #60h, ph3   ; Cho so sánh a với số 60 để nhẩy
        mov off_minute, #0 ; Trả trị trong off-minute  về 0

ph310:  jb k1, ph3  ; Kiểm tra phím nhấn K1, chờ nhấn phím để có bit 0



k33_3:  
        call disp3    ; Vẫn cho hiển thị giờ - phút ở mode OFF
        jnb k1, k33_3    ; Kiểm tra phím nhấn K1, chờ bỏ nhấn phím để có bit 1
        jmp ph4   ; Nhẩy không điều kiện về tên nhãn ph4

;;----------------------------------------
tend:
        setb ex0   ; Set lại bit ex0, cho mở ngắt ngoài trên chân p3.2
        setb tr0  ; Set lại bit tr0, cho chạy đồng hồ T0
        setb 00h   ; Set lại bit kiểm tra ở vị trí 00h
        setb et0   ; Set lại bit et0, cho mở ngắt theo bit tràn của timer T0
        setb p3.7 ; Đặt chân p3.7 lên mức áp cao
        pop psw  ; Từ ngắn xếp hoàn trả lại trị cho thanh trạng thái psw
        pop acc  ; Từ ngăn xếp hoàn trả lại trị cho thanh a
        reti  ; Dừng chương trình ngắt
end

Khi viết đoạn chương trình trên, Bạn cần hiểu rõ một số vấn đề sau:

* Cách dùng ngắt theo bit báo tràn tf0 để đếm thời gian. Cứ mỗi khi xuất hiện bit tràn tf0=1 là nhẩy về chạy chương trình ngắt ở địa chỉ 000bh, ở đây có lệnh nhẩy vào chương trình ngắt với tên nhãn là  wt0 để đếm thời gian và khi đếm xong thì nhẩy đến tên nhãn là toreti và khi gặp câu lệnh reti thì dừng ngắt.

* Chú ý cách dùng câu lệnh da <thanh nhớ (chứa trị nhị phân)>

* Lệnh nhẩy jb <bit>, jnb <bit>. Lệnh nhẩy sau khi kiểm tra các bit của các phím nhấn.

* Lệnh cjne a, <trị>, tên nhãn. Lệnh nhẩy sau phép so sánh để định trị giới hạn của mỗi số đếm, số đếm có thể là 24 (cho giờ), hay là 60 (cho giây, cho phút).

* Để lấy trị liện kê trong bảng nạp vào thanh a thì Bạn dùng câu lệnh:

mov dptr, #tab                   ; Đặt địa chỉ của bảng vào thanh ghi con trỏ dptr.

Ở đây Bạn xác định trị cho số a, rồi dùng câu lệnh sau để lấy trị trong bảng.

movc a, @a+dptr              ; Vào bảng lấy mã theo trị của a, lấy xong mã cho cất vào a


Previous article
Next article

Leave Comments

Đăng nhận xét

Ads Atas Artikel

Ads Tengah Artikel 1

Ads Tengah Artikel 2

Ads Bawah Artikel