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.
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à...
vivian.wang.yun
Kinh doanh linh kiện và thiết bị ngành điện tử và điều khiển điện