(028) 66844775 - 0902760910 banhang@phuclanshop.com
Danh mục sản phẩm

Trả lời thư: Đồng hồ có hẹn giờ chạy với ic vi điều khiển AT89C51

29 Tháng Tám 2015

Rất vui khi thấy   Bạnvào xem...

 

 
 

Dẫn nhập

 

 

Một e-mail gửi về tôi viết như sau:

 

Họ và tên: Khánh Huy
Địa chỉ: Sinh viên Đại Học ĐÀ Lạt
Điện thoại: 0979825351
Email: trankhanhhuytomato@gmail.com
Em chào phuclanshop.com và các Thầy !!! Em vô tình tìm tài liệu mới biết Thầy Em xin tự giới thiệu, em tên Huy là sinh viên Trường Đại học Đà lạt, khoa Điện Tử - Viễn Thông Nhóm chúng em đang làm Đồ Án 1 về vi điều khiển dùng IC 89c51, nhóm em được thầy chia nhóm làm đề tài về đồng hồ số dùng IC 89c51, nhưng cả bọn em mới học hợp ngữ chưa đầy nữa tháng nên cũng gặp nhiều khó khăn, nhiệm vụ chúng em là phải làm được đồng hồ chỉnh được giờ, chỉnh phút, hẹn giờ, có một điều khó khăn nữa là thầy cho tụi em 10 led đơn,tương ứng với 10 trạng thái (yêu cầu khi thầy chỉnh đúng giờ hẹn thì 1 đèn sáng,cứ lần 2 thì 2 đèn sáng) tụi em tìm tài liệu nhưng hầu như không có vấn đề liên quan đó,thầy gợi y giùm tụi em, có tài liệu liên quan mong thầy share bọn em, em là nhóm trưởng xin đại diện cả nhóm 6 người cám ơn thầy -Sinh viên Đại Học Đà Lạt

 

 

Trả lời:

 

Chào Huy và cả nhóm 6 em. Tôi đã xem thư Huy và lục tìm lại trong kho dữ liệu cũ thấy có một bài viết có thể đáp ứng được yêu cầu của nhóm em. Trong bài này có 4 nút, dùng chỉnh trạng thái của đồng hồ có hẹn giờ.

 

* 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ángvà 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

 

 

 

 

 

 

 

Nhắn Bạn

 

 Bạn click vào các dòng dưới để xem các đề tài có liên quan đến ic vi điều kiển:



 

Trả lời thư: Làm bảng đèn quang báo ma trận Led 8x8 chạy chữ chỉ dùng 1 ic AT89C51

 

Viết cho các Bạn mới làm quen với ic vi điều khiển, họ AT89C51, ic làm việc theo câu lệnh.

 

 Kể chuyện: Tôi dùng bo khiển AT89C2051, 12 đường ra để ráp đèn hào quang cho tượng Phật Bà trên đất Mỹ

 

Giới thiệu sản phẩm: Dùng bo vi điều khiển họ AT89... 16 đường ra làm bảng đèn quảng cáo.


 Hướng dẫn Bạn viết 10 chương trình ứng dụng cơ bản cho ic vi điều khiển AT89C51

 

 Giới thiệu cách dùng hộp nạp ic vi điều khiển TOP853

 

 

 

 

 

Tạm kết

 

Dùng ic vi điều khiển AT89C51 hay AT89C2051 để viết về mạch điện đồng hồ trên mang có rất nhiều, nhất là mang tiếng Hoa.

 

Sau đây tôi ghi lại tư liệu tìm được trên mạng, một chương trình dùng ic vi điều khiển AT89C2051 dùng làm đồng hồ có tính chuyên nghiệp hơn, để Bạn tham khảo.

 

Sơ đồ mạch điện:

 

 

 

Sơ đồ cho thấy, mạch dùng ic vi điều kiển AT89C2051 làm mạch điêu khiển cho hiển thị số chữ trên bảng đèn LCD loại 14 chân có nút chỉnh mức sáng và ic đồng hồ DS1307 chạy thạch anh 32768Hz (tức 2 lũy thừa 15). Mạch điện này chuyên nghiệp hơn, một thời làm ra kinh tế khi dùng nó làm bảng đèn đồng hồ số

 

 

Chương trình nguồn:

 

;* FILENAME:     AT1307.ASM
;* DISPLAY:      16X2 LCD 4-BIT OPERATION
;* MICRO:        ATMEL AT89C2051 FLASH BASED MICRO
;* PROJECT:      ALARM CLOCK
;* DATE:         06/10/98
;* TIMEKEEPER:   DS1307 SERIAL TIMEKEEPER I.C.
;
;
;
$MOD51
SCL       BIT    P1.0
SDA       BIT    P1.1
DS1307W   EQU    0D0H     ; SLAVE ADDRESS 1101 000 + 0 TO WRITE
DS1307R   EQU    0D1H     ; SLAVE ADDRESS 1101 000 + 1 TO READ
FLAGS     DATA   20H
LASTREAD  BIT    FLAGS.0
ACK       BIT    FLAGS.5
BUS_FLT   BIT    FLAGS.6
_2W_BUSY  BIT    FLAGS.7
BITCNT    DATA   21H
BYTECNT   DATA   22H
SECS      DATA   24H      ;   '   SECONDS STORAGE RAM
MINS      DATA   25H      ;   '   MINUTES   '     '
HRS       DATA   26H      ;   '   HOURS     '     '
DAY       DATA   27H      ;   '   DAY       '     '
DATE      DATA   28H      ;   '   DATE      '     '
MONTH     DATA   29H      ;   '   MONTH     '     '
YEAR      DATA   2AH      ;   '   YEAR      '     '
CONTROL   DATA   2BH      ; FOR STORAGE OF CONTROL REGISTER WHEN READ.
ALM_HOUR  DATA   2CH      ; INTERNAL (ALARM HOURS) STORAGE.
ALM_MIN   DATA   2DH      ; INTERNAL (ALARM MINUTES) STORAGE.
ALM_CNTRL DATA   2EH      ; INTERNAL STORAGE FOR ALARM (ON) TIME.
ALM_STORE EQU    08H      ; DS1307 RAM, ALARM STORAGE, BEGINNING.

; ***LCD CONTROL***

LCD_DATA  EQU    090H
LCD_DB4   EQU    094H     ; P1.4 HIGH NIBBLE OF PORT 1 IS USED FOR DATA
LCD_DB5   EQU    095H     ; P1.5 HIGH NIBBLE OF PORT 1 IS USED FOR DATA
LCD_DB6   EQU    096H     ; P1.6 HIGH NIBBLE OF PORT 1 IS USED FOR DATA
LCD_DB7   EQU    097H     ; P1.7 HIGH NIBBLE OF PORT 1 IS USED FOR DATA
LCD_RS    EQU    0B3H     ; P3.3 LCD REGISTER SELECT LINE
LCD_RW    EQU    0B4H     ; P3.4 LCD READ / WRITE LINE
LCD_E     EQU    0B5H     ; P3.5 LCD ENABLE LINE

; ***USER INPUT KEYS***

UP_BTTN   BIT    0B0H     ; P3.0  UP
DOWN_BTTN BIT    0B1H     ; P3.1  DOWN
ENTER     BIT    0B2H     ; P3.2  ENTER
ALARM_SET BIT    092H     ; P1.2  ALARM TIME SET SELECT

; ***SYSTEM INSTRUCTIONS***

CONFIG    EQU    28H      ; 4-BIT DATA,2 LINES,5X7 MATRIX LCD
ENTRYMODE EQU    6        ; INCREMENT CURSOR DON'T SHIFT DISPLAY

; ***CURSOR CONTROL INSTRUCTIONS***

OFFCUR    EQU    0CH
BLINKCUR  EQU    0DH

; ***DISPLAY CONTROL INSTRUCTIONS***

CLRDSP    EQU    01H
ONDSP     EQU    0EH

; ***MACRO'S***

SCL_HIGH  MACRO
          SETB   SCL      ; SET SCL HIGH
          JNB    SCL,$    ; LOOP UNTIL STRONG 1 ON SCL
          ENDM

CSEG     AT      0        ; RESET VECTOR
         JNB     ENTER,INIT_YEAR
         AJMP    START

INIT_YEAR:
         JNB     ENTER,INIT_YEAR
         MOV     YEAR,#97H; INITIALIZE YEAR IF NEW 1307 TO 1997
         ACALL   SET_CLOCK; ELSE GO ON.

; **********************************************************
; RESET GOES HERE TO START PROGRAM
; **********************************************************

START:
         MOV         YEAR,#97H  ; SET YEAR TO 1997 ON BOOT
         ACALL       OFF        ; ALARM OFF ON BOOT
         MOV         SP,#2FH    ; POSITION STACK ABOVE BUFFER
         MOV         IE,#0      ; DISABLE INTERUPTS
         SETB        SDA        ; ENSURE SDA HIGH
         SCL_HIGH               ; ENSURE SCL HIGH
         CLR         ACK        ; CLEAR STATUS FLAGS
         CLR         BUS_FLT
         CLR         _2W_BUSY
                               
; **********************************************************
; SUB SETS THE DS1307 OSCILLATOR
; **********************************************************

OSC_CONTROL:
         ACALL       SEND_START ; GENERATE START CONDITION
         MOV         A,#DS1307W ; 1101 0000 ADDRESS + WRITE-BIT
         ACALL       SEND_BYTE  ; SEND BYTE TO 1307
         MOV         A,#00H     ; ADDRESS BYTE TO REGISTER 00H
         ACALL       SEND_BYTE  ; SECONDS REGISTER, ALWAYS LEAVE
         SETB        LASTREAD   ; REG 00H-BIT #7 = 0 (LOW)
         ACALL       SEND_STOP  ; IF REG 00H-BIT #7 = 1 CLOCK
         ACALL       SEND_START ; OSCILLATOR IS OFF.
         MOV         A,#DS1307R ; 1101 0001 ADDRESS + READ-BIT
         ACALL       SEND_BYTE  ;
         ACALL       READ_BYTE  ; READ A BYTE FROM THE 1307
         CLR         ACC.7      ; CLEAR REG 00H-BIT #7 TO ENABLE
OSC_SET:                        ; OSCILLATOR.
         PUSH        ACC        ; SAVE ON STACK
         ACALL       SEND_STOP  ;
         ACALL       SEND_START ;
         MOV         A,#DS1307W ; SETUP TO WRITE
         ACALL       SEND_BYTE  ;
         MOV         A,#00H     ; REGISTER 00H ADDRESS
         ACALL       SEND_BYTE  ;
         POP         ACC        ; GET DATA TO START OSCILLATOR
         ACALL       SEND_BYTE  ; SEND IT
         ACALL       SEND_STOP
         ACALL       BEGIN

; **********************************************************
; TIME AND DATE ROUTINE, GET & DISPLAY MONTH/DATE/YEAR
; **********************************************************

GET_TIME:
         ACALL       READ_CLOCK            ; GET TIME,DATE,ALARM DATA
         JNB         ENTER,SET_TIME        ; GOTO SET TIME IF PUSHED
         JNB         ALARM_SET,SET_ALARMS  ; GOTO SET ALARMS ELSE SHOW TIME
         MOV         A,#1       ; LINE #1
         MOV         B,#1       ; COLUMN #1
         ACALL       PLACECUR4  ; PLACE CURSOR FOR DAY
         MOV         A,DAY      ; GET DAY
         ACALL       GET_DAY    ; SHOW DAY OF WEEK
         MOV         A,#1
         MOV         B,#7
         ACALL       PLACECUR4  ; PLACE CURSOR FOR MONTH
         MOV         A,MONTH    ; LOAD MONTH DATA
         ACALL       WRITE_BCD
         MOV         A,#1
         MOV         B,#10      ; PLACE CURSOR FOR DATE
         ACALL       PLACECUR4
         MOV         A,DATE
         ACALL       WRITE_BCD
         MOV         A,#1
         MOV         B,#13
         ACALL       PLACECUR4
         MOV         A,YEAR
         ACALL       WRITE_BCD

; **********************************************************
; NOW GET & DISPLAY TIME
; **********************************************************

         MOV         A,#2
         MOV         B,#4
         ACALL       PLACECUR4
         MOV         A,HRS
         ACALL       WRITE_BCD
         MOV         A,#2
         MOV         B,#7
         ACALL       PLACECUR4
         MOV         A,MINS
         ACALL       WRITE_BCD
         MOV         A,#2
         MOV         B,#10
         ACALL       PLACECUR4
         MOV         A,SECS
         ACALL       WRITE_BCD
         ACALL       CHECK_ALARM_TIME
         SJMP        GET_TIME

SET_ALARMS:
         JNB         ALARM_SET,SET_ALARMS
         ACALL       MDELAY
         AJMP        ALARMS

SET_TIME:
         JNB         ENTER,SET_TIME; HOLDS HERE UNTIL BUTTON IS RELEASED
         ACALL       MDELAY

SET_DAY:
         MOV         A,#1
         MOV         B,#0
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BY DAY

WAIT_DAY:                       ; WAIT HERE FOR BUTTON PRESS
         JNB         UP_BTTN,UP_DAY
         JNB         DOWN_BTTN,DOWN_DAY
         JNB         ENTER,ENTER_DAY
         ACALL       UPDATE
         SJMP        WAIT_DAY

REDO_DAY:                       ; RESETS DAY TO SUN
         MOV         A,#1
         MOV         DAY,A
         AJMP        WAIT_DAY

REDO_DAY_DOWN:                  ; RESETS DAY TO SAT
         MOV         A,#7H
         MOV         DAY,A
         AJMP        WAIT_DAY

OK_DAY:                         ; PLACES NEW DAY DATA INTO REGISTER
         MOV         DAY,A
         AJMP        WAIT_DAY

UP_DAY:                         ; CHANGES DAY DATA UP BY 1
         JNB         UP_BTTN,UP_DAY
         ACALL       MDELAY
         MOV         R1,#DAY
         MOV         A,@R1
         INC         A
         CJNE        A,#8H,OK_DAY
         AJMP        REDO_DAY

DOWN_DAY:                       ; CHANGES MONTH DATA DOWN BY 1
         JNB         DOWN_BTTN,DOWN_DAY
         ACALL       MDELAY
         MOV         R1,#DAY
         MOV         A,@R1
         DEC         A
         CJNE        A,#0,OK_DAY
         AJMP        REDO_DAY_DOWN
     
ENTER_DAY:                      ; DONE WITH DAY GO ON TO MONTH
         JNB         ENTER,ENTER_DAY
         ACALL       MDELAY

SET_MONTH:
         MOV         A,#1
         MOV         B,#0
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ' ',0      ; PRINT '*' FOR MONTH
         MOV         A,#1
         MOV         B,#6
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0

WAIT_MONTH:                     ; WAIT HERE FOR BUTTON PRESS
         JNB         UP_BTTN,UP_MONTH
         JNB         DOWN_BTTN,DOWN_MONTH
         JNB         ENTER,ENTER_MONTH
         ACALL       UPDATE
         SJMP        WAIT_MONTH

REDO_MONTH:                     ; RESETS MONTH TO 1
         MOV         A,#1
         MOV         MONTH,A
         AJMP        WAIT_MONTH

REDO_MONTH_DOWN:                ; RESETS MONTH TO 12
         MOV         A,#12H
         MOV         MONTH,A
         AJMP        WAIT_MONTH

OK_MONTH:                       ; PLACES NEW MONTH DATA INTO REGISTER
         MOV         MONTH,A
         AJMP        WAIT_MONTH

UP_MONTH:                       ; CHANGES MONTH DATA UP BY 1
         JNB         UP_BTTN,UP_MONTH
         ACALL       MDELAY
         MOV         R1,#MONTH
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#13H,OK_MONTH
         AJMP        REDO_MONTH

DOWN_MONTH:                     ; CHANGES MONTH DATA DOWN BY 1
         JNB         DOWN_BTTN,DOWN_MONTH
         ACALL       MDELAY
         MOV         R1,#MONTH
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0,OK_MONTH
         AJMP        REDO_MONTH_DOWN
     
ENTER_MONTH:                    ; DONE WITH MONTH GO ON TO DATE
         JNB         ENTER,ENTER_MONTH
         ACALL       MDELAY

SET_DATE:
         MOV         A,#1
         MOV         B,#6
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ' ',0      ; ERASE '*' BY MONTH
         MOV         A,#1
         MOV         B,#9
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BY DATE

WAIT_DATE:                      ; WAIT HERE UNTIL BUTTON PRESS
         JNB         UP_BTTN,UP_DATE
         JNB         DOWN_BTTN,DOWN_DATE
         JNB         ENTER,ENTER_DATE
         ACALL       UPDATE
         SJMP        WAIT_DATE

REDO_DATE:                      ; RESETS DATE TO 1
         MOV         A,#1
         MOV         DATE,A
         AJMP        WAIT_DATE

REDO_DATE_DOWN:                 ; RESETS DATE TO 31
         MOV         A,#31H
         MOV         DATE,A
         AJMP        WAIT_DATE

OK_DATE:                        ; PLACES NEW DATE DATA INTO REGISTER
         MOV         DATE,A
         AJMP        WAIT_DATE

UP_DATE:                        ; CHANGES DATE DATA UP BY 1
         JNB         UP_BTTN,UP_DATE
         ACALL       MDELAY
         MOV         R1,#DATE
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#32H,OK_DATE
         AJMP        REDO_DATE

DOWN_DATE:                      ; CHANGES DATE DATA DOWN BY 1
         JNB         DOWN_BTTN,DOWN_DATE
         ACALL       MDELAY
         MOV         R1,#DATE
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0,OK_DATE
         AJMP        REDO_DATE_DOWN

ENTER_DATE:                     ; DONE WITH DATE GO ON TO YEAR
         JNB         ENTER,ENTER_DATE
         ACALL       MDELAY

SET_YEAR:
         MOV    A,#1
         MOV    B,#9
         ACALL  PLACECUR4
         ACALL  PRTLCD4
         DB     '-',0           ; PRINT '-' BESIDE DATE
         MOV    A,#1
         MOV    B,#12
         ACALL  PLACECUR4
         ACALL  PRTLCD4
         DB     '*',0           ; PRINT '*' BESIDE YEAR

WAIT_YEAR:                      ; WAIT HERE FOR BUTTON PRESS
         JNB    UP_BTTN,UP_YEAR
         JNB    DOWN_BTTN,DOWN_YEAR
         JNB    ENTER,ENTER_YEAR
         ACALL  UPDATE
         SJMP   WAIT_YEAR

REDO_YEAR:                      ; RESETS YEAR TO 1
         MOV    A,#97H
         MOV    YEAR,A
         SJMP   WAIT_YEAR

REDO_YEAR_DOWN:                 ; RESETS YEAR TO 99
         MOV    A,#99H
         MOV    YEAR,A
         AJMP   WAIT_YEAR

OK_YEAR:                        ; PLACES NEW YEAR DATA INTO REGISTER
         MOV    YEAR,A
         AJMP   WAIT_YEAR

UP_YEAR:                        ; CHANGES YEAR DATA UP BY 1
         JNB    UP_BTTN,UP_YEAR
         ACALL  MDELAY
         MOV    R1,#YEAR
         MOV    A,@R1
         INC    A
         CJNE   A,#9AH,OK_YEAR
         AJMP   REDO_YEAR

DOWN_YEAR:                      ; CHANGES YEAR DATA DOWN BY 1
         JNB    DOWN_BTTN,DOWN_YEAR
         ACALL  MDELAY
         MOV    R1,#YEAR
         MOV    A,@R1
         DEC    A
         CJNE   A,#96H,OK_YEAR
         AJMP   REDO_YEAR_DOWN

ENTER_YEAR:                     ; DONE WITH YEAR GO ON TO HOUR
         JNB    ENTER,ENTER_YEAR
         ACALL  MDELAY
        
SET_HOUR:
         MOV         A,#1
         MOV         B,#12
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '-',0      ; PRINT '-' BESIDE YEAR
         MOV         A,#2
         MOV         B,#3
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BESIDE HOUR
         AJMP        WAIT_HOUR

WAIT_HOUR:                      ; WAIT HERE UNTIL BUTTON PRESS
         JNB         UP_BTTN,UP_HOUR
         JNB         DOWN_BTTN,DOWN_HOUR
         JNB         ENTER,ENTER_HOUR
         ACALL       UPDATE
         SJMP        WAIT_HOUR

REDO_HOUR:                      ; RESET HOUR TO 00
         MOV         A,#0
         MOV         HRS,A
         AJMP        WAIT_HOUR

REDO_HOUR_DOWN:                 ; RESET HOUR TO 23
         MOV         A,#23H
         MOV         HRS,A
         AJMP        WAIT_HOUR

OK_HOUR:                        ; PLACE NEW HOUR DATA INTO REGISTER
         MOV         HRS,A
         AJMP        WAIT_HOUR

UP_HOUR:                        ; CHANGES HOUR DATA UP BY 1
         JNB         UP_BTTN,UP_HOUR
         ACALL       MDELAY
         MOV         R1,#HRS
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#24H,OK_HOUR
         AJMP        REDO_HOUR

DOWN_HOUR:                      ; CHANGES HOUR DATA DOWN BY ONE
         JNB         DOWN_BTTN,DOWN_HOUR
         ACALL       MDELAY
         MOV         R1,#HRS
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0F9H,OK_HOUR
         AJMP        REDO_HOUR_DOWN

ENTER_HOUR:                     ; DONE WITH HOUR GO ON TO MINUTES
         JNB         ENTER,ENTER_HOUR
         ACALL       MDELAY

SET_MIN:
         MOV         A,#2
         MOV         B,#3
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ' ',0      ; ERASE '*' BESIDE HOUR
         MOV         A,#2
         MOV         B,#6
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BESIDE MINUTES

WAIT_MIN:                       ; WAIT HERE UNTIL BUTTON PRESS
         JNB         UP_BTTN,UP_MIN
         JNB         DOWN_BTTN,DOWN_MIN
         JNB         ENTER,ENTER_MIN
         ACALL       UPDATE
         SJMP        WAIT_MIN

REDO_MIN:                       ; RESET MINUTES TO 00
         MOV         A,#0
         MOV         MINS,A
         AJMP        WAIT_MIN

REDO_MIN_DOWN:                  ; RESETS MINUTES TO 59
         MOV         A,#59H
         MOV         MINS,A
         AJMP        WAIT_MIN

OK_MIN:                         ; PLACE NEW MINUTE DATA INTO REGISTER
         MOV         MINS,A
         AJMP        WAIT_MIN

UP_MIN:                         ; CHANGES MINUTES DATA UP BY 1
         JNB         UP_BTTN,UP_MIN
         ACALL       MDELAY
         MOV         R1,#MINS
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#60H,OK_MIN
         AJMP        REDO_MIN

DOWN_MIN:                       ; CHANGES MINUTES DATA DOWN BY 1
         JNB         DOWN_BTTN,DOWN_MIN
         ACALL       MDELAY
         MOV         R1,#MINS
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0F9H,OK_MIN
         AJMP        REDO_MIN_DOWN

ENTER_MIN:                      ; DONE WITH MINUTES GO ON TO SECONDS
         JNB         ENTER,ENTER_MIN
         ACALL       MDELAY

SET_SECONDS:
         MOV         A,#2
         MOV         B,#6
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ':',0      ; PRINT ':' BESIDE MINUTES
         ACALL       MDELAY
         MOV         A,#00000000B ; SECONDS ARE SET TO 00
         MOV         SECS,A
         ACALL       SET_CLOCK
         AJMP        GET_TIME   ; RETURN TO MAIN LOOP

ALARMS:
         ACALL       RESETLCD4
         ACALL       INITLCD4
         MOV         A,#1
         MOV         B,#0
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '-SET ALARM TIME-',0
         ACALL       BEGIN3     ; PRINT HOUR & MINUTE SEPERATORS
         ACALL       UPDATE3    ; DISPLAY ALARM TIME
         AJMP        ALM_A      ; GO TO SET HOURS & MINUTES ROUTINE
                                ; FOR ALARM TIME.
UPDATE:
         MOV         A,#1       ; LINE #1
         MOV         B,#1       ; COLUMN #1
         ACALL       PLACECUR4  ; PLACE CURSOR FOR DAY
         MOV         A,DAY      ; GET DAY DATA
         ACALL       GET_DAY    ; CONVERT BCD TO ASCII FOR LCD
         MOV         A,#1
         MOV         B,#7
         ACALL       PLACECUR4  ; PLACE CURSOR FOR MONTH
         MOV         A,MONTH    ; LOAD DATE MONTH
         ACALL       WRITE_BCD
         MOV         A,#1
         MOV         B,#10      ; PLACE CURSOR FOR DATE
         ACALL       PLACECUR4
         MOV         A,DATE     ; LOAD DATE DATA
         ACALL       WRITE_BCD
         MOV         A,#1
         MOV         B,#13
         ACALL       PLACECUR4
         MOV         A,YEAR
         ACALL       WRITE_BCD

; **********************************************************
; NOW GET & DISPLAY TIME
; **********************************************************

UPDATE2:
         MOV         A,#2
         MOV         B,#4
         ACALL       PLACECUR4
         MOV         A,HRS
         ACALL       WRITE_BCD
         MOV         A,#2
         MOV         B,#7
         ACALL       PLACECUR4
         MOV         A,MINS
         ACALL       WRITE_BCD
         MOV         A,#2
         MOV         B,#10
         ACALL       PLACECUR4
         MOV         A,SECS
         ACALL       WRITE_BCD
         MOV         R4,#OFFCUR
         ACALL       WRLCDCOM4  ; TURN OFF CURSOR
         ACALL       BIG_DELAY
         RET
UPDATE3:
         MOV         A,#2
         MOV         B,#6
         ACALL       PLACECUR4
         MOV         A,ALM_HOUR
         ACALL       WRITE_BCD
         MOV         A,#2
         MOV         B,#9
         ACALL       PLACECUR4
         MOV         A,ALM_MIN
         ACALL       WRITE_BCD
         MOV         R4,#OFFCUR
         ACALL       WRLCDCOM4
         ACALL       BIG_DELAY
         RET

CHECK_ALARM_TIME:
         MOV         A,MINS
         CJNE        A,ALM_MIN,OFF
         MOV         A,HRS
         CJNE        A,ALM_HOUR,OFF
         MOV         ALM_CNTRL,#10H ; LOAD VALUE TO TURN ON SQW OUT & ALARM
         ACALL       ALARM_CONTROL
         RET

OFF:     MOV         ALM_CNTRL,#00H
         ACALL       ALARM_CONTROL  ; LOAD VALUE TO TURN OFF ALARM & SQW OUT.
         RET
ALM_A:
         MOV         A,#2
         MOV         B,#5
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BESIDE HOUR

WAIT_HOUR1:                     ; WAIT HERE UNTIL BUTTON PRESS
         JNB         UP_BTTN,UP_HOUR1
         JNB         DOWN_BTTN,DOWN_HOUR1
         JNB         ENTER,ENTER_HOUR1
         ACALL       UPDATE3
         SJMP        WAIT_HOUR1

REDO_HOUR1:                     ; RESET HOUR TO 00
         MOV         A,#0
         MOV         ALM_HOUR,A
         AJMP        WAIT_HOUR1

REDO_HOUR_DOWN1:                ; RESET HOUR TO 23
         MOV         A,#23H
         MOV         ALM_HOUR,A
         AJMP        WAIT_HOUR1

OK_HOUR1:                       ; PLACE NEW HOUR DATA INTO REGISTER
         MOV         ALM_HOUR,A
         AJMP        WAIT_HOUR1

UP_HOUR1:                       ; CHANGES HOUR DATA UP BY 1
         JNB         UP_BTTN,UP_HOUR1
         ACALL       MDELAY
         MOV         R1,#ALM_HOUR
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#24H,OK_HOUR1
         AJMP        REDO_HOUR1

DOWN_HOUR1:                     ; CHANGES HOUR DATA DOWN BY ONE
         JNB         DOWN_BTTN,DOWN_HOUR1
         ACALL       MDELAY
         MOV         R1,#ALM_HOUR
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0F9H,OK_HOUR1
         AJMP        REDO_HOUR_DOWN1

ENTER_HOUR1:                    ; DONE WITH HOUR GO ON TO MINUTES
         JNB         ENTER,ENTER_HOUR1
         ACALL       MDELAY

SET_MIN1:
         MOV         A,#2
         MOV         B,#5
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ' ',0      ; ERASE '*' BESIDE HOUR
         MOV         A,#2
         MOV         B,#8
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*',0      ; PRINT '*' BESIDE MINUTES

WAIT_MIN1:                      ; WAIT HERE UNTIL BUTTON PRESS
         JNB         UP_BTTN,UP_MIN1
         JNB         DOWN_BTTN,DOWN_MIN1
         JNB         ENTER,ENTER_MIN1
         ACALL       UPDATE3
         SJMP        WAIT_MIN1

REDO_MIN1:                      ; RESET MINUTES TO 00
         MOV         A,#0
         MOV         ALM_MIN,A
         AJMP        WAIT_MIN1

REDO_MIN_DOWN1:                 ; RESETS MINUTES TO 59
         MOV         A,#59H
         MOV         ALM_MIN,A
         AJMP        WAIT_MIN1

OK_MIN1:                        ; PLACE NEW MINUTE DATA INTO REGISTER
         MOV         ALM_MIN,A
         AJMP        WAIT_MIN1

UP_MIN1:                        ; CHANGES MINUTES DATA UP BY 1
         JNB         UP_BTTN,UP_MIN1
         ACALL       MDELAY
         MOV         R1,#ALM_MIN
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD
         CJNE        A,#60H,OK_MIN1
         AJMP        REDO_MIN1

DOWN_MIN1:                      ; CHANGES MINUTES DATA DOWN BY 1
         JNB         DOWN_BTTN,DOWN_MIN1
         ACALL       MDELAY
         MOV         R1,#ALM_MIN
         MOV         A,@R1
         MOV         R4,A
         ACALL       CHECK_BCD_DOWN
         DEC         A
         CJNE        A,#0F9H,OK_MIN1
         AJMP        REDO_MIN_DOWN1

ENTER_MIN1:                     ; DONE WITH MINUTES GO ON TO SECONDS
         JNB         ENTER,ENTER_MIN1
         ACALL       MDELAY

SET_SECONDS1:
         MOV         A,#2
         MOV         B,#8
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          ':',0      ; PRINT ':' BESIDE MINUTES
         ACALL       MDELAY

STORE_ALARMS:
         ACALL       SEND_START ; SEND 2-WIRE START CONDITION
         MOV         A,#DS1307W ; SEND 1307 WRITE COMMAND
         ACALL       SEND_BYTE
         MOV         A,#ALM_STORE; POINT TO REGISTER
                                ; 08H ON THE DS1307
         ACALL       SEND_BYTE
         MOV         R1,#ALM_HOUR; START WITH HOURS REGISTER
                                ; IN INTERNAL RAM
SEND_LOOP2:
         MOV         A,@R1      ; LOAD HOURS DATA FOR ALARM
         ACALL       SEND_BYTE  ; SET HOURS
         MOV         R1,#ALM_MIN
         MOV         A,@R1      ; LOAD MINUTES DATA FOR ALARM
         ACALL       SEND_BYTE  ; SET MINUTES
         ACALL       SEND_STOP  ; SEND 2-WIRE STOP CONDITION
         ACALL       BEGIN      ; SETUP DISPLAY AGAIN BEFORE RETURNING.
         AJMP        GET_TIME   ; GO BACK TO SHOWING TIME AFTER
                                ; SETTING ALARM

; **********************************************************
; BCD CONVERSION ROUTINE
; ACC CONTAINS TIME DATA FROM RAM WHEN ROUTINE IS CALLED.
; **********************************************************

WRITE_BCD:
         PUSH        ACC        ; DECODE HIGH DIGIT
         SWAP        A
         ANL         A,#0FH
         ACALL       FIND       ; LOOK IT UP & THEN PRINT IT.
         POP         ACC        ; DECODE LOW DIGIT
         ANL         A,#0FH
         ACALL       FIND
         RET

; **********************************************************
; SUB CONTROLS THE ALARM OUTPUT
; THE DS1307 SQW OUTPUT DRIVES THE ALARM BUZZER @ 1Hz
; **********************************************************

ALARM_CONTROL:
         ACALL       SEND_START
         MOV         A,#DS1307W
         ACALL       SEND_BYTE
         MOV         A,#07H
         ACALL       SEND_BYTE
         MOV         R1,#ALM_CNTRL
         MOV         A,@R1      ; 10H = ALARM ON AT 1HZ
         ACALL       SEND_BYTE  ; 00H = ALARM OFF & SQW OUT = GND
         ACALL       SEND_STOP
         RET

; **********************************************************
; SET UP DISPLAY ON BOOT, AND REDO DISPLAY AFTER CHANGE
; **********************************************************

BEGIN:
         ACALL       RESETLCD4
         ACALL       INITLCD4
         MOV         A,#1
         MOV         B,#7
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '  -  -  ',0
         MOV         A,#2
         MOV         B,#0
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*     :  :     *',0
         MOV         R4,#OFFCUR ; TURN OFF CURSOR
         ACALL       WRLCDCOM4
         RET

BEGIN3:
         MOV         A,#2
         MOV         B,#0
         ACALL       PLACECUR4
         ACALL       PRTLCD4
         DB          '*       :      *',0
         MOV         R4,#OFFCUR ; TURN OFF CURSOR
         ACALL       WRLCDCOM4
         RET

; **********************************************************
; LOOKUP TABLE USED BY WRITE_BCD.
; **********************************************************

FIND:
         SJMP        LOOK_UP
ST:
         RET
LOOK_UP:
         CJNE        A,#0000000B,m1
         LCALL       PRTLCD4
         DB          '0',0
         LJMP        ST
m1:
         CJNE        A,#00000001B,m2
         LCALL       PRTLCD4
         DB          '1',0
         LJMP        ST
m2:
         CJNE        A,#00000010B,m3
         LCALL       PRTLCD4
         DB          '2',0
         LJMP        ST
m3:
         CJNE        A,#00000011B,m4
         LCALL       PRTLCD4
         DB          '3',0
         LJMP           ST
m4:
         CJNE        A,#00000100B,m5
         LCALL       PRTLCD4
         DB          '4',0
         LJMP        ST
m5:
         CJNE        A,#00000101B,m6
         LCALL       PRTLCD4
         DB          '5',0
         LJMP        ST
m6:
         CJNE        A,#00000110B,m7
         LCALL       PRTLCD4
         DB          '6',0
         LJMP        ST
m7:
         CJNE        A,#00000111B,m8
         LCALL       PRTLCD4
         DB          '7',0
         LJMP        ST
m8:
         CJNE        A,#00001000B,m9
         LCALL       PRTLCD4
         DB          '8',0
         LJMP        ST
m9:
         CJNE        A,#00001001B,m10
         LCALL       PRTLCD4
         DB          '9',0
         LJMP        ST
m10:
         LJMP        ST

; **********************************************************
; LOOKUP TABLE FOR DAY
; **********************************************************

GET_DAY:
         CJNE        A,#00000001B,MON
         LCALL       PRTLCD4
         DB          'SUN',0
         RET
MON:
         CJNE        A,#00000010B,TUE
         LCALL       PRTLCD4
         DB          'MON',0
         RET
TUE:
         CJNE        A,#00000011B,WED
         LCALL       PRTLCD4
         DB          'TUE',0
         RET
WED:
         CJNE        A,#00000100B,THU
         LCALL       PRTLCD4
         DB          'WED',0
         RET
THU:
         CJNE        A,#00000101B,FRI
         LCALL       PRTLCD4
         DB          'THU',0
         RET
FRI:
         CJNE        A,#00000110B,SAT
         LCALL       PRTLCD4
         DB          'FRI',0
         RET
SAT:
         CJNE        A,#00000111B,WHAT
         LCALL       PRTLCD4
         DB          'SAT',0
         RET
WHAT:
         RET

; **********************************************************
; SUB SETS THE CLOCK
; **********************************************************

SET_CLOCK:
        
         ACALL       SEND_START ; SEND 2-WIRE START CONDITION
         MOV         A,#DS1307W ; SEND 1307 WRITE COMMAND
         ACALL       SEND_BYTE
         MOV         A,#00H     ; SET DATA POINTER TO REGISTER
                                ; 00H ON THE DS1307
         ACALL       SEND_BYTE
         MOV         R1,#24H    ; START WITH SECONDS REGISTER
                                ; IN INTERNAL RAM
SEND_LOOP:
         MOV         A,@R1      ; MOVE FIRST BYTE OF DATA TO ACC
         ACALL       SEND_BYTE  ; SEND DATA ON 2-WIRE BUT
         INC         R1         ; LOOP UNTIL CLOCK DATA SENT
         CJNE        R1,#2BH,SEND_LOOP
         ACALL       SEND_STOP  ; SEND 2-WIRE STOP CONDITION
         RET

; **********************************************************
; INITIALIZE THE LCD 4-BIT MODE
; **********************************************************

INITLCD4:
         CLR         LCD_RS     ; LCD REGISTER SELECT LINE
         CLR         LCD_RW     ; READ / WRITE LINE
         CLR         LCD_E      ; ENABLE LINE
         MOV         R4, #CONFIG; FUNCTION SET - DATA BITS,
                                ; LINES, FONTS
         ACALL       WRLCDCOM4
         MOV         R4, #ONDSP ; DISPLAY ON
         ACALL       WRLCDCOM4
         MOV         R4, #ENTRYMODE ; SET ENTRY MODE
         ACALL       WRLCDCOM4  ; INCREMENT CURSOR RIGHT, NO SHIFT
         MOV         R4, #CLRDSP; CLEAR DISPLAY, HOME CURSOR
         ACALL       WRLCDCOM4
         RET

; **********************************************************
; SOFTWARE VERSION OF THE POWER ON RESET
; **********************************************************

RESETLCD4:
         CLR         LCD_RS     ; LCD REGISTER SELECT LINE
         CLR         LCD_RW     ; READ / WRITE LINE
         CLR         LCD_E      ; ENABLE LINE
         CLR         LCD_DB7    ; SET BIT PATTERN FOR...
         CLR         LCD_DB6    ; ... POWER-ON-RESET
         SETB        LCD_DB5
         SETB        LCD_DB4
         SETB        LCD_E      ; START ENABLE PULSE
         CLR         LCD_E      ; END ENABLE PULSE
         MOV         A, #4      ; DELAY 4 MILLISECONDS
         ACALL       MDELAY
         SETB        LCD_E      ; START ENABLE PULSE
         CLR         LCD_E      ; END ENABLE PULSE
         MOV         A, #1      ; DELAY 1 MILLISECOND
         ACALL       MDELAY
         SETB        LCD_E      ; START ENABLE PULSE
         CLR         LCD_E      ; END ENABLE PULSE
         MOV         A, #1      ; DELAY 1 MILLISECOND
         ACALL       MDELAY
         CLR         LCD_DB4    ; SPECIFY 4-BIT OPERATION
         SETB        LCD_E      ; START ENABLE PULSE
         CLR         LCD_E      ; END ENABLE PULSE
         MOV         A, #1      ; DELAY 1 MILLISECOND
         ACALL       MDELAY
         MOV         R4, #CONFIG; FUNCTION SET
         ACALL       WRLCDCOM4
         MOV         R4, #08H   ; DISPLAY OFF
         ACALL       WRLCDCOM4
         MOV         R4, #1     ; CLEAR DISPLAY, HOME CURSOR
         ACALL       WRLCDCOM4
         MOV         R4, #ENTRYMODE  ; SET ENTRY MODE
         ACALL       WRLCDCOM4
         AJMP        INITLCD4

; **********************************************************
; SUB WRITES A COMMAND WORD TO THE LCD
; COMMAND MUST BE PLACED IN R4 BY CALLING PROGRAM
; **********************************************************

WRLCDCOM4:
         CLR         LCD_E
         CLR         LCD_RS     ; SELECT SEND COMMAND
         CLR         LCD_RW     ; SELECT WRITE OPERATION
         PUSH        ACC        ; SAVE ACCUMULATOR
         MOV         A, R4      ; PUT DATA BYTE IN ACC
         MOV         C, ACC.4   ; LOAD HIGH NIBBLE ON DATA BUS
         MOV         LCD_DB4, C ; ONE BIT AT A TIME USING...
         MOV         C, ACC.5   ; BIT MOVE OPERATOINS
         MOV         LCD_DB5, C
         MOV         C, ACC.6
         MOV         LCD_DB6, C
         MOV         C, ACC.7
         MOV         LCD_DB7, C
         SETB        LCD_E      ; PULSE THE ENABLE LINE
         CLR         LCD_E
         MOV         C, ACC.0   ; SIMILARLY, LOAD LOW NIBBLE
         MOV         LCD_DB4, C
         MOV         C, ACC.1
         MOV         LCD_DB5, C
         MOV         C, ACC.2
         MOV         LCD_DB6, C
         MOV         C, ACC.3
         MOV         LCD_DB7, C
         ACALL       PULSEEWAIT4; PULSE THE ENABLE LINE...
                                ; AND WAIT FOR BUSY FLAG TO CLEAR
         POP         ACC
         RET

; **********************************************************
; SUB TO WRITE A DATA WORD TO THE LCD
; DATA MUST BE PLACED IN R4 BY CALLING PROGRAM
; **********************************************************

WRLCDDATA:
         CLR         LCD_E
         SETB        LCD_RS     ; SELECT SEND DATA
         CLR         LCD_RW     ; SELECT WRITE OPERATION
         PUSH        ACC        ; SAVE ACCUMULATOR
         MOV         A, R4      ; PUT DATA BYTE IN ACC
         MOV         C, ACC.4   ; LOAD HIGH NIBBLE ON DATA BUS
         MOV         LCD_DB4, C ; ONE BIT AT A TIME USING...
         MOV         C, ACC.5   ; BIT MOVE OPERATOINS
         MOV         LCD_DB5, C
         MOV         C, ACC.6
         MOV         LCD_DB6, C
         MOV         C, ACC.7
         MOV         LCD_DB7, C
         SETB        LCD_E      ; PULSE THE ENABLE LINE
         CLR         LCD_E
         MOV         C, ACC.0   ; SIMILARLY, LOAD LOW NIBBLE
         MOV         LCD_DB4, C
         MOV         C, ACC.1
         MOV         LCD_DB5, C
         MOV         C, ACC.2
         MOV         LCD_DB6, C
         MOV         C, ACC.3
         MOV         LCD_DB7, C
         ACALL       PULSEEWAIT4; PULSE THE ENABLE LINE...
                                ; AND WAIT FOR BUSY FLAG TO CLEAR
         POP         ACC
         RET

; **********************************************************
; GENERATES A POSITIVE PULSE ON THE LCD ENABLE LINE.
; WAITS FOR THE BUSY FLAG TO CLEAR BEFORE RETURNING.
; **********************************************************

PULSEEWAIT4:
         CLR         LCD_E
         SETB        LCD_E      ; PULSE THE ENABLE LINE
         CLR         LCD_E
         MOV         LCD_DATA, #0FFH ; PREPARE PORT FOR INPUT
         SETB        LCD_RW     ; PREPARE R/W FOR READ OPERATION
         PUSH        ACC        ; SAVE ACCUMULATOR CONTENTS
PEW:
         SETB        LCD_E      ; START THE ENABLE PULSE
         MOV         A, LCD_DATA; READ STATUS NIBBLE
         CLR         LCD_E      ; END ENABLE PULSE
         SETB        LCD_E      ; PRETEND READING STATUS LOW NIBBLE
         CLR         LCD_E
         JB          ACC.7, PEW ; LOOP WHILE BUSY FLAG IS SET
         POP         ACC        ; RESTORE ACCUMULATOR
         RET

; **********************************************************
; SUB TAKES THE STRING IMMEDIATELY FOLLOWING THE CALL AND
; DISPLAYS ON THE LCD. STRING MUST BE TERMINATED WITH A
; NULL (0).
; **********************************************************

PRTLCD4:
         POP         DPH        ; POP RETURN ADDRESS INTO DPTR
         POP         DPL
PRTNEXT:
         CLR         A          ; SET OFFSET = 0
         MOVC        A, @A+DPTR ; GET CHR FROM CODE MEMORY
         CJNE        A, #0, CHROK; IF CHR = 0 THEN RETURN
         SJMP        RETPRTLCD
CHROK:
         MOV         R4, A
         ACALL       WRLCDDATA  ; SEND CHARACTER
         INC         DPTR       ; POINT AT NEXT CHARACTER
         AJMP        PRTNEXT    ; LOOP TILL END OF STRING
RETPRTLCD:
         MOV         A,  #1H    ; POINT TO INSTRUCTION AFTER STRING
         JMP         @A+DPTR    ; RETURN WITH A JUMP INSTRUCTION

; **********************************************************
; SUB SETS THE CURSOR POSITION.
; **********************************************************

PLACECUR4:
         DEC         ACC        ; ACC=0 FOR LINE=1
         JNZ         LINE2      ; IF ACC=0 THEN FIRST LINE
         MOV         A, B
         ADD         A, #080H   ; CONSTRUCT CONTROL WORD FOR LINE 1
         SJMP        SETCUR
LINE2:
         MOV         A, B
         ADD         A, #0C0H   ; CONSTRUCT CONTROL WORD FOR LINE 2
SETCUR:
         MOV         R4, A      ; PLACE CONTROL WORD
         ACALL       WRLCDCOM4  ; ISSUE COMMAND
         RET

SETCGRAM:
         PUSH        B
         MOV         B, #8
         MUL         AB          ; multiply character number by 8
         POP         B           ; b holds row number
         ADD         A,B         ; a holds CGRAM address
         ADD         A,#40h      ; convert to instruction
         MOV         R4,A        ; place instruction
         ACALL       WRLCDCOM4   ; issue command
         RET

; **********************************************************
; 1 MILLISECOND DELAY ROUTINE
; **********************************************************

MDELAY:
         PUSH        ACC
         MOV         A,#0A6H
MD_OLP:
         INC         A
         NOP
         NOP
         NOP
         NOP
         NOP
         NOP
         NOP
         NOP
         JNZ         MD_OLP
         NOP
         POP         ACC
         RET

BIG_DELAY:
         MOV         R3,#1      ; DELAY ROUTINE.
X0B:     MOV         R2,#0FFH
X1B:     MOV         R5,#0FFH
X2B:     DJNZ        R5,X2B
         DJNZ        R2,X1B
         DJNZ        R3,X0B
         RET

; **********************************************************
; THIS SUB READS ONE BYTE OF DATA FROM THE DS1307
; **********************************************************

READ_BYTE:
         MOV         BITCNT,#08H; SET COUNTER FOR 8-BITS DATA
         MOV         A,#00H
         SETB        SDA        ; SET SDA HIGH TO ENSURE LINE
                                ; FREE
READ_BITS:
         SCL_HIGH               ; TRANSITION SCL LOW-TO-HIGH
         MOV         C,SDA      ; MOVE DATA BIT INTO CARRY
         RLC         A          ; ROTATE CARRY-BIT INTO ACC.0
         CLR         SCL        ; TRANSITION SCL HIGH-TO-LOW
         DJNZ        BITCNT,READ_BITS
                                ; LOOP FOR 8-BITS
         JB          LASTREAD,ACKN
                                ; CHECK TO SEE IF THIS IS
                                ; THE LAST READ
         CLR         SDA        ; IF NOT LAST READ SEND ACK-BIT

ACKN:
         SCL_HIGH               ; PULSE SCL TO TRANSMIT ACKNOWLEDGE
         CLR         SCL        ; OR NOT ACKNOWLEDGE BIT
         RET

; **********************************************************
; SUB SENDS START CONDITION
; **********************************************************

SEND_START:
         SETB        _2W_BUSY   ; INDICATE THAT 2-WIRE
         CLR         ACK        ; OPERATION IS IN PROGRESS
         CLR         BUS_FLT    ; CLEAR STATUS FLAGS
         JNB         SCL,FAULT
         JNB         SDA,FAULT
         SETB        SDA        ; BEGIN START CODITION
         SCL_HIGH
         CLR         SDA
         ACALL       DELAY
         CLR         SCL
         RET
FAULT:
         SETB        BUS_FLT
         RET

; **********************************************************
; SUB SENDS STOP CONDITION
; **********************************************************

SEND_STOP:
         CLR         SDA
         SCL_HIGH
         SETB        SDA
         CLR         _2W_BUSY
         RET

; **********************************************************
; SUB DELAYS THE BUS
; **********************************************************

DELAY:
         NOP                    ; DELAY FOR BUS TIMING
         RET

; **********************************************************
; THIS SUB SENDS 1 BYTE OF DATA TO THE DS1307
; CALL THIS FOR EACH REGISTER SECONDS TO YEAR
; ACC MUST CONTAIN DATA TO BE SENT TO CLOCK
; **********************************************************

SEND_BYTE:
         MOV         BITCNT,#08H; SET COUNTER FOR 8-BITS
SB_LOOP:
         JNB         ACC.7,NOTONE; CHECK TO SEE IF BIT-7 OF
         SETB        SDA        ; ACC IS A 1, AND SET SDA HIGH
         JMP         ONE
NOTONE:
         CLR         SDA        ; CLR SDA LOW
ONE:
         SCL_HIGH               ; TRANSITION SCL LOW-TO-HIGH
         RL          A          ; ROTATE ACC LEFT 1-BIT
         CLR         SCL        ; TRANSITION SCL LOW-TO-HIGH
         DJNZ        BITCNT,SB_LOOP; LOOP FOR 8-BITS
         SETB        SDA        ; SET SDA HIGH TO LOOK FOR
         SCL_HIGH               ; ACKNOWLEDGE PULSE
         CLR         ACK
         JNB         SDA,SB_EX  ; CHECK FOR ACK OR NOT ACK
         SETB        ACK        ; SET ACKNOWLEDGE FLAG FOR
                                ; NOT ACK
SB_EX:
         ACALL       DELAY      ; DELAY FOR AN OPERATION
         CLR         SCL        ; TRANSITION SCL HIGH-TO-LOW
         ACALL       DELAY      ; DELAY FOR AN OPERATION
         RET

; **********************************************************
; SUB READS THE CLOCK AND WRITES IT TO THE SCRATCHPAD MEMORY
; ON RETURN FROM HERE DATE & TIME DATA WILL BE STORED IN THE
; DATE & TIME REGISTERS FROM 24H (SECS) TO 2AH (YEAR)
; ALARM SETTINGS IN REGISTERS 2CH(HRS) AND 2DH(MINUTES).
; **********************************************************

READ_CLOCK:

         MOV         R1,#24H    ; SECONDS STORAGE LOCATION
         MOV         BYTECNT,#00H
         CLR         LASTREAD
         ACALL       SEND_START
         MOV         A,#DS1307W
         ACALL       SEND_BYTE
         MOV         A,#00H
         ACALL       SEND_BYTE
         ACALL       SEND_STOP
         ACALL       SEND_START
         MOV         A,#DS1307R
         ACALL       SEND_BYTE

READ_LOOP:
         MOV         A,BYTECNT
         CJNE        A,#09H,NOT_LAST
         SETB        LASTREAD

NOT_LAST:
         ACALL       READ_BYTE
         MOV         @R1,A
         MOV         A,BYTECNT
         CJNE        A,#00H,NOT_FIRST
         MOV         A,@R1
         CLR         ACC.7      ; ENSURE OSC BIT=0 (ENABLED)
         MOV         @R1,A
NOT_FIRST:
         INC         R1
         INC         BYTECNT
         MOV         A,BYTECNT
         CJNE        A,#0AH,READ_LOOP
         ACALL       SEND_STOP
         RET

; **********************************************************
; MOST OF THE REAL WORK IS DONE IN THE ADJUST ROUTINE
; THIS ROUTINE SIMPLY CHECKS TO SEE IF THE NUMBER IS
; ZERO OR ENDS IN NINE.
; **********************************************************

CHECK_BCD:
         MOV         A,R4       ; SAVES THE NUMBER TO R4
         CLR         C          ; CLEARS CARRY BIT
         ADD         A,#67H     ; CHECKS TO SEE IF EQUAL 99
         JC          ZERO       ; IF EQUAL THEN BECOMES 0
         MOV         A,R4       ; GETS ORIGINAL NUMBER
         ANL         A,#0FH     ; GETS RID OF UPPER 4 BITS
         CLR         C          ; CLEARS CARRY BIT
         ADD         A,#0F7H    ; CHECKS TO SEE IF NUMBER ENDS IN 9
         JC          ADJUST     ; IF LARGER ADJUST IT
         MOV         A,R4       ; GET ORIGINAL NUMBER
         INC         A          ; ITS UNDER 9 AND NOT 0 SO INCREASE
         RET                    ; BY 1 AND GO BACK.

; **********************************************************
; BCD CONVERSION ROUTINE.
; **********************************************************
ADJUST:
         MOV         A,R4       ; GET ORIGINAL NUMBER
         SWAP        A          ; CHANGE UPPER BITS TO LOWER BITS
         ANL         A,#0FH     ; GET RID OF WHAT WAS LOWER BITS
         INC         A          ; INCREASE BY ONE
         SWAP        A          ; SWITCH THE UPPER AND LOWER BITS BACK
         RET                    ; GO BACK WITH NEW NUMBER

ZERO:                           ; IF NUMBER IS ZERO SEND BACK A ZERO
         MOV         A,#00000000B
         RET

; **********************************************************
; THIS ROUTINE CHECKS TO SEE IF THE LOWER 4 BITS ARE
; EQUAL TO ZERO. IF THEY ARE THEN A STRAIGHT DECREASE
; CAN BE DONE. IF NOT THEN CONVERT.
; **********************************************************

CHECK_BCD_DOWN:
         MOV         A,R4       ; SAVE ORIGINAL NUMBER
         ANL         A,#0FH     ; GET RID OF UPPER FOUR BITS
         CJNE        A,#00000000B,GO_ON_BACK ;DO LOWER BITS =0 ?
         MOV         A,R4       ; GET ORIGINAL NUMBER
         SUBB        A,#06H     ; CONVERT IT TO A BINARY NUMBER
         RET

; **********************************************************
; IF THE NUMBER ENDS IN NUMBER LESS THAN 9 THEN
; GO BACK AND, DO A NORMAL DECREMENT.
; **********************************************************

GO_ON_BACK:
         MOV         A,R4       ; GET ORIGINAL NUMBER
         RET                    ; GO BACK

; **********************************************************
; END OF PROGRAM
; **********************************************************
  END


 

 

 Người soạnmời Bạn vào xem...

 

 

 Và với câu nhắn quen thuộc là...

 

 

 

 

 

  

 

Vui lòng để lại bình luận
  • online support profile

    vivian.wang.yun

    Kinh doanh linh kiện và thiết bị ngành điện tử và điều khiển điện
Top