Hướng Dẫn viết 10 chương trình ứng dụng cơ bản cho AT89C51
Trang 1Hướ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
Lời nói ñầu
Bạn biết, kiến thức là luôn luôn cải biến và phát triển, do ñó các bài viết của tôi ở ñây cũng luôn theo tôn chỉ của RIXIN, nghĩa là Nhật Tân (Ngày mới, ngày mới, ngày ngày mới 日新 日新 日新, , ,日新 日新 日新, , ,日日新 日日新 日日新) Các bài viết luôn ñược người soạn ñọc ñi ñọc lại và sửa lỗi, viết thêm vào các ý tưởng mới và các bài viết luôn ñược làm tươi Nó giống như một vườn cây ñược người trồng vườn chăm coi cẩn thận và khu vườn thì ñang ngày ngày sinh trưởng, các bài viết
sẽ luôn mới, luôn khác và mỗi ngày ñều ñược bổ xung tu chỉnh, và thêm mới Mong Bạn thường xuyên quay lại ñể xem và góp ý Người soạn: Vương
Khánh Hưng
Trước hết chúng ta hãy tìm hiểu nguyên lý làm việc của ic vi ñiều khiển AT89C51 (Bạn xem hình).
Trang 2IC AT89C51 có 40 chân Công dụng của các chân như sau:
từ trạng thái khởi ñầu.
* Chân 18, 19 mắc thạch anh ñịnh tần Như vậy tần số xung nhịp sẽ là 1Mhz (hay 1us) Ngang thạch anh mắc 2 tụ nhỏ dùng ñể bù nhiệt ổn tần (Bạn xem hình).
Trang 3* Chân 31 Bạn phải cho treo lên mức áp cao, ñể xác ñịnh là mạch chỉ làm việc với bộ nhớ nội (Vì mạch không dùng thêm bộ nhớ ngoài).
* Bạn chú ý, ic có 4 cảng, ñó là port 0 (p0), port 1 (p1), port 2 (p2) và port 3 (p3) Mỗi cảng có 8 chân Mọi hoạt ñộng của ic vi ñiều khiển ñều ñược thực hiện trên các cảng này.
Ghi nhận: Bạn thấy ic AT89C51 chỉ cần một ít linh kiện bên ngoài là ñã
có thể chấp hành các câu lệnh ñã có trong bộ nhớ EEP-ROM, Bạn dùng các câu lệnh này ñể xác ñịnh trạng thái ñiện áp cao thấp trên các cảng
và dùng các mức áp cao thấp này cho việc ñiều khiển các dạng mạch ñiện, hay các thiết bị.
Bài 1: Viết chương trình ñể Led trên chân số 1 (p1.0)
luôn nhấp nháy
Trang 4ORG 0000h ; Khởi ñầu từ thanh ghi có ñịa chỉ là 0000h
START: CLR P1.0 ; ðặt chân p1.0 xuống mức áp thấp, Led sẽ sáng LCALL DELAY ; Cho gọi trình làm trễ (có tên nhãn là delay)
SETB P1.0 ; ðặt chân p1.0 lên mức áp cao, Led sẽ tắt
LCALL DELAY ; Lại cho gọi trình làm trễ (có tên nhãn là delay) LJMP START ; Cho nhẩy về dòng lệnh với tên nhãn là start, công việc sẽ lập lại
DELAY: MOV R5,#20 ; Nạp trị 50 vào thanh nhớ r5
D1: MOV R6,#20 ; Nạp trị 20 vào thanh nhớ r6
D2: MOV R7,#248 ; Nạp trị 248 vào thanh nhớ r7
DJNZ R7,$ ; Cho giảm r7 theo bước -1 cho ñến 0
Trang 5Nghĩa là mọi việc sẽ bắt ñầu từ thanh nhớ có ñịa chỉ là 0000h ðây
cũng là thanh mà chương trình sẽ nhấy ñến mỗi khi Bạn nhấn nút
DJNZ R6,D1 2 lần 2×20=40 10002
Trang 6Nếu Bạn mov vào r6=20 thì thời gian trễ sẽ là 10ms.
Và ñể có thời gian trễ là 200ms Bạn dùng thêm thanh nhớ r5 Mov vào r5=20 Và như vậy ñoạn chương trình trễ delay 200ms như sau:
DELAY: MOV R5,#20 ; chuyển vào thanh ghi r5 số thập phân 20
D1: MOV R6,#20 ; chuyển vào thanh ghi r6 số thập phân 20.
D2: MOV R7,#248 ; chuyển vào thanh ghi r7 số thập phân 248.
DJNZ R7,$ ; cho số trong r7 giảm theo bước -1, ở tại chổ và chờ ñến khi bằng 0
DJNZ R6,D2 ; cho số trong r6 giảm theo bước -1, và nhẩy về D2 nếu chưa bằng 0.
DJNZ R5,D1 ; cho số trong r5 giảm theo bước -1, và nhẩy về D1 nếu chưa bằng 0.
RET ; quay trở lại thực hiện dóng lệnh sau lệnh call.
Bài 2: Viết chương trình dùng Led trên chân 1 (p1.0)
theo dõi khóa ñiện K1 trên chân 10 (p3.0).
* K1 ñóng lại Led sáng
* K1 bỏ ra Led tắt.
Trang 7ORG 0000H ; Khởi ñầu từ thanh ghi 0000h
START: JB P3.0, LIG ; Lệnh nhẩy Nếu chân p3.0 ở bit 1 thì nhẩy ñến tên nhãn
LIG, Led tắt
CLR P1.0 ; ðặt p1.0 về bit 0, chân p1.0 ở mức áp thấp Led sẽ sáng
SJMP START ; Nhảy về tên nhãn START
LIG: SETB P1.0 ; ðặt p1.0 về bit 1, chân p1.0 ở mức áp cao, Led tắt
SJMP START ; Nhảy về tên nhãn START
END ; Dừng biên dịch ở ñây
Ở ñây, Bạn dùng lệnh nhẩy theo ñiều kiện bit, tức lệnh jb (jump bit)
hay jnb (jump no bit).
Trang 8Nếu xét chân p3.0 ở mức áp cao (nút nhấn K1 hở) thì nhẩy ñến tên nhãn LIG, và
Nếu xét chân p3.0 ở mức áp thấp (nút nhấn K1 kín) thì thực hiện câu lệnh kế tiếp bên dưới.
* Khi khóa ñiện ñóng lại Led tương ứng sáng.
* Khi khóa ñiện nhã ra, Led tương tứng tắt.
Trang 9ORG 0000H ; Bắt ñầu từ thanh ghi 0000h
START: MOV A,P1 ; Lấy trang thái trên cảng port 1 nạp vào thanh ghi
ANL A,#0F0H ; dùng logic AND ñể che 4 bit thấp, giữ nguyên
RR A ; cho nội dung trong thanh ghi a quay qua phải 1
Trang 10RR A ; cho nội dung trong thanh ghi a quay qua phải 4 bit
XOR A,#0F0H ; dùng logic Ex-OR ñể phát hiện sự khác bit trên 4 bit thấp
; như vậy Led ứng với vị trí có bit 0 sẽ phát sáng MOV P1,A ; Chuyển trạng thái của thanh ghi a ra trên port 1
a- Dùng 4 phím ñể tắt mở 4 Led trên port 1
Bạn cho chuyển trạng thái của port 1 vào thanh ghi tích lũy a, chú ý trạng thái của 4 bit cao Nó phản ảnh nút nhấn K1, K2, K3, K4.
b- Dùng logic And với hằng 11110000b ñể cho che 4 bit thấp.
Trang 11Bạn lấy logic AND giữa thanh a và hằng 0f0h (hay là 11110000b), cách làm này là ñể bảo toàn trạng thái của 4 bit cao, còn 4 bit thấp ñều hóa
ra bit 0 Người ta quen gọi là cho che 4 bit thấp.
c- Dùng lệnh quay phải 4 lần ñể dời 4 bit cao xuống 4 bit thấp.
Dùng lệnh rr a cho quay bit qua phải, nếu cho quay 4 lần thì 4 bit thấp
sẽ lên nằm ở vùng 4 bit cao và 4 bit cao sẽ xuống nằm ở vùng 4 bit thấp Chúng ta nói cho chuyển trạng thái của 4 bit cao xuống vùng 4 bit thấp Cũng có thể dùng lệnh swap.
Trang 12Lấy logic Dị-OR ñể phát hiện các bit giống nhau hay khác nhau:
Nếu các bit giống nhau, kết quả sẽ là bit 0.
Nếu các bit khác nhau, kết quả sẽ là bit 1.
Bài 4: Viết chương trình cho 8 Led trên port 1 lần
lượt nhấp nháy, nhẩy qua trái rồi nhẩy qua phải.
Trang 13
START: MOV R2,#8 ; ðặt trị 8 (số lần ñếm) vào thanh ghi r2 MOV A,#0FEH ; ðặt trị 0feh tức 11111110b và thanh ghi a
LOOP: MOV P1,A ; Chuyển nội dung trong thanh ghi a vào cảng port 1
RLC A ; Cho nội dung trong thanh ghi a quay qua trái có cho quay
RRC A ; Cho nội dung trong thanh ghi a quay qua phải có cho quay
Trang 14qua bit cờ C DJNZ R2,LOOP1 ; ðếm lùi trị trong thanh ghi r2 Nếu chưa bằng 0 thì làm tiếp
Trang 15
c- Lệnh quay (rotate) chỉ có tác dụng với thanh ghi a, do ñó Bạn muốn thể hiện kết quả ra 8 Led trên port 1, Bạn phải chuyển nội dung có trong a ra port 1 Bạn dùng lệnh mov p1, a.
Tóm lại, trong bài này Bạn ñã làm quen với cách dùng lệnh quay có cho qua cờ C Với cách viết này, Bạn thấy chúng ta dùng rất ít câu lệnh mà vẫn khiến cho các Led sáng nhẩy (nhẩy qua nhẩy lại) trên port 1
Trang 16START: JB P1.7,START ; Chờ ñặt nút K1 ñặt xuống mức 0
JNB FLAG,NEXT ; Nếu bit cờ Flag là 0 thì nhẩy ñến tên nhãn
NEXT
MOV R2,#200 ; Nạp số thập phân 200 vào thanh ghi r2
DV: CPL P1.0 ; Lấy bù chân p1.0, lệnh bù bit có chức năng tự
ñảo
LCALL DELY500 ; Cho gọi chương trình làm trễ 500us
LCALL DELY500 ; gọi lần nữa chương trình dely500
DJNZ R2,DV ; Cho ñếm lùi và thực hiện lệnh bù cpl p1.0 ñến
CPL FLAG ; ðảo bit trong cờ Flag
NEXT: MOV R2,#200 ; Nạp lại số thập phân 200 vào thanh ghi r2
Trang 17LCALL DELY500 ; Cho gọi chương trình làm trễ 500us DJNZ R2,DV1 ; Cho ñếm lùi và thực hiện bù cpl p1.0 ñến 200 lần.
CPL FLAG ; Lại cho ñảo bit trong cờ Flag SJMP START ; Lập lại lệnh từ tên nhãn START DELY500: MOV R7,#250 ; Viết chương trình con làm trễ 500us Nạp số 250
LOOP: NOP ; Lệnh trống, dùng nó làm tăng thời gian trễ lên
DJNZ R7,LOOP ; ðếm lùi r7 ñể làm trễ RET ; Cho quay lại thực hiện dòng lệnh sau lệnh call
Bài 6: Viết chương trình dùng cách lấy dữ liệu khai
báo trong bảng, ñể tắt mở 8 Led trên port 1.
* Nháy nhẩy qua trái 2 lần
* Nháy nhẩy qua phải 2 lần.
Trang 18LOOP1: MOV P1,A ; Chuyển nội dung trong a ra trên cảng port 1
Trang 19LCALL DELAY ; Gọi chương trình làm trễ, tên nhãn là delay INC DPTR ; cho tăng ñịa chỉ bảng lên +1 JMP LOOP ; Nhẩy về tên nhãn LOOP DELAY: MOV R4,#20 ; Viết chương trình con dùng làm trễ, tên nhãn là delay
D1: MOV R5,#248 ; Nạp trị thập phân 248 vào r5 DJNZ R5,$ ; Cho giảm theo bước -1, chờ r5 bằng 0 DJNZ R4,D1 ; Cho r4 giảm theo bước -1, chưa bằng 0, nhẩy về D1
DJNZ R3,DELAY ; Cho r3 giảm theo bước -1, chưa bằng 0, nhẩy về DELAY
RET ; Quay lại chạy câu lệnh nằm sau lệnh call
TABLE: DB 0FEH,0FDH,0FBH,0F7H ; Khai báo các dữ liệu ñặt trong bảng, tên bảng là TABLE
DB 0EFH,0DFH,0BFH,07FH ; các dữ liệu lần lượt sắp trong bảng
DB 0FEH,0FDH,0FBH,0F7H ; các dữ liệu lần lượt sắp trong bảng
DB 0EFH,0DFH,0BFH,07FH ; các dữ liệu lần lượt sắp trong bảng
DB 07FH,0BFH,0DFH,0EFH ; các dữ liệu lần lượt sắp trong bảng
DB 0F7H,0FBH,0FDH,0FEH ; các dữ liệu lần lượt sắp trong bảng
DB 07FH,0BFH,0DFH,0EFH ; các dữ liệu lần lượt sắp trong bảng
DB 0F7H,0FBH,0FDH,0FEH ; các dữ liệu lần lượt sắp trong bảng
DB 00H, 0FFH,00H, 0FFH ; các dữ liệu lần lượt sắp trong bảng
DB 01H ; Dữ liệu dùng soa sánh ñể nhẩy (Xem câu
Trang 20Bạn dùng lệnh inc dptr dể cho trị trong a tăng lên +1.
Vậy khi dùng câu lệnh: movc a, @a +dptr, Bạn sẽ cho lấy mã dữ liệu
tiếp theo
Sau mỗi lần lấy dữ liệu từ bảng ra, Bạn dùng câu lệnh nhẩy theo phép
so sánh: cjne a, #01h, loop1 ñể xem ñã lấy ñến mã dữ liệu cuối cùng
chưa, vì Bạn ñặt mả 01H ở cuối bảng.
Ghi nhận: Dùng cách viết này, Bạn rất dễ chọn các kiểu nhấp nháy cho
các Led trên port 1 Bạn chỉ cần khai báo các mã ñiều khiển trong bảng
là ñược bạn có thể khai báo theo dạng mã nhị phân cho dễ nhìn thấy
sự sáng tắt của các Led.
Bài 7: Viết chương trình ñể trên ñèn số dùng Led 7
ñoạn lần lượt xuất hiện các con số 0, 1, 2, 3 Thời gian thay ñổi số là 0.2s.
Chú ý: Dùng loại ñèn số Led 7 ñoạn có Cathode chung, vậy Led sáng với mức 1.
Trang 22“8” hay 7FH
“9” hay 6FH
“A” hay 77H
“b” hay 7CH
“C” hay 39H
“d” hay 5EH
“E” hay 79H
“F” hay 71H
Do vậy ñể tạo ra bảng hiển thị các số thập phân, chúng ta sẽ viết như sau:
TABLE DB
3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH
START: MOV R1,#00H ; ðặt trị khởi ñầu 0 cho thanh ghi r1 NEXT: MOV A,R1 ; Cho chuyển nội dung trong r1 vào a MOV DPTR,#TABLE ; ðặt ñịa chỉ của bảng vào thanh ghi con trỏ dptr MOVC A,@A+DPTR ; Lấy các mã dữ liệu trong bảng theo trị của a và dữ
MOV P2,A ; Xuất dữ liệu trong a ra port 2 ñể cho hiện số LCALL DELAY ; Cho gọi chương trình làm trễ, tên nhãn là delay INC R1 ; Cho tăng trị trong r1 theo kiểu +1 CJNE R1,#10,NEXT ; So sánh trị trong r1, xem bằng 10 chưa? chưa, nhẩy
LJMP START ; Lệnh nhảy không ñiều kiện về câu lệnh có tên nhãn
DELAY: MOV R5,#20 ; Viết chương trình làm trễ Bạn xem lại các phần thí
TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH ; Bảng mã cho hiện các số thập phân
END ; Dừng biên dịch
Nói rõ hơn về loại ñèn hiện số dùng Led 7 ñoạn (Bạn xem hình):
Trang 23
Khi chân trên port 2 ở mức áp cao, thì Led tương ứng sẽ phát sáng Vậy chúng ta có thể ñặt mã ñể trên ñèn Led 7 ñoạn lần lượt hiện ra các con
Trang 24
Bài 8: Viết mạch ñếm số hiển thi theo hệ cơ 2 với 4
Led trên các chân 1 (p1.0), 2 (p1.1), 3 (p1.2), 4 (p1.3) Mỗi lần nhấn khóa ñiện SP1 trên chân 17 (p3.7) thì sẽ tăng lên +1.
Trang 25
ORG 0000h START: MOV R1,#00H ; Cho khởi ñầu với trị 0 trong thanh ghi r1
MOV A,R1 ; chuyển nội dung trong r1 vào a CPL A ; Lấy bù 8 bit trong thanh ghi a
MOV P1,A ; Xuất nội dung trong a ra cảng port 1
REL: JNB P3.7,REL ; Chờ buôn phím nhấn ở chân p3.7 LCALL DELAY10MS ; Gọi chương trình con làm trễ 10ms JNB P3.7,REL ; Tránh ảnh hưởng của rung phím INC R1 ; Cho tăng trị trong thanh ghi r1 MOV A,R1 ; Cho chuyển nội dung trong r1 vào
DELAY10MS: MOV R6,#20 ; Viết chương trình làm trễ 10ms
Trang 26(2) Tại sao phải lấy bù a rồi mới cho xuất trên cảng port1?
Vấn ñề rung phím là do phần tiếp xúc xấu, khi Bạn nhấn xuống, phím
sẽ bị rung, nó ngắt mở nhiều lần và tạo ra kết quả sai Do vậy Bạn phải viết ñoạn chương trình chờ 10ms rồi mới xác lập trạng hái của phím,
Trang 27ñiều này sẽ tăng ñộ ổn ñịnh khi dùng loại phím cơ khí Cách viết như sau:
REL: JNB P3.7,REL
; Khi bạn nhấn xuống, nếu phím tốt, nó sẽ ở bit 0 và sẽ ở trạng thái chờ buông phím Nếu có rung phím, nó sẽ nhẩy xuống câu lệnh dưới là cho gọi chương trình trễ 10ms
LCALL DELAY10MS ; Gọi chương trình con làm trễ 10ms
; Sau chương trình trễ nó sẽ quay lại ñể lại kiểm tra phím với câu lệnh: JNB P3.7,REL
Nếu nó ở bit 0 thì quay lại REL và lại chờ buông phím Chỉ khi nó ñã ở Bit 1 thì mới cho tăng trị trong r1.
Nếu 2 lần nhấn phím kết quả là:
00000010b > lấy bù là 11111101b Vậy Led ở p1.1 sẽ sáng Bạn cứ
lý luận tiếp tục sẽ thấy mạch này dùng ñếm hệ cơ 2, kết quả cho hiển thị trên các Led của port 1.
Trang 29MOV ID,#00H ; Trị khởi ñầu của ID là 0 START: JB K1,REL ; Chưa nhấn phím thì nhẩy ñến tên nhãn REL LCALL DELAY10MS ; Gọi chương trình làm trễ 10ms (tránh rung phím)
JB K1,REL ; Chưa nhấn phím thì nhẩy ñến tên nhãn REL INC ID ; Tăng trị trong ID MOV A,ID ; Cho chuyển nội dung trong ID và thanh ghi a CJNE A,#04,REL ; Cho so sánh a với #04 ñể phát hiện số lần nhấn K1
MOV ID,#00H ; Trả trị trong ID về trị o REL: JNB K1,$ ; Chờ buông phím K1
Trang 30MOV A,ID ; Cho chuyển nội dung trong ID và thanh ghi
a
CJNE A,#00H,IS0 ; Cho so sánh a với #00 ñể phát hiện số lần
CPL L1 ; Lấy bù L1 ñể tắt/mở Led D1 LCALL DELAY ; Gọi chương trình làm trễ với tên nhãn là delay SJMP START ; Nhẩy không ñiều kiện về tên nhãn START IS0: CJNE A,#01H,IS1 ; Cho so sánh a với #01 ñể phát hiện số lần
CPL L2 ; Lấy bù L2 ñể tắt/mở Led D2 LCALL DELAY ; Gọi chương trình làm trễ với tên nhãn là delay SJMP START ; Nhẩy không ñiều kiện về tên nhãn STARTIS1: CJNE A,#02H,IS2 ; Cho so sánh a với #02 ñể phát hiện số lần
CPL L3 ; Lấy bù L3 ñể tắt/mở Led D3 LCALL DELAY ; Gọi chương trình làm trễ với tên nhãn là delay SJMP START ; Nhẩy không ñiều kiện về tên nhãn START
IS2: CJNE A,#03H,IS3 ; Cho so sánh a với #03 ñể phát hiện số lần
CPL L4 ; Lấy bù L4 ñể tắt/mở Led D4 LCALL DELAY ; Gọi chương trình làm trễ với tên nhãn là delay SJMP START ; Nhẩy không ñiều kiện về tên nhãn START IS3: LJMP START ; Nhẩy không ñiều kiện về tên nhãn START