Chơng 11
Lập trình các ngắt
Một ngắt là một sự kiện bên trong hoặc bên ngoài làm ngắt bộ vi điều khiển
để báo cho nó biết rằng thiết bị cần dịch vụ của nó. Trong chơng này ta tìm hiểu khái
niệm ngắt và lập trình ngắt.
11.1 Các ngắt của 8051.
11.1.1 Các ngắt ngợc với thăm dò.
Một bộ vi điều khiển có thể phục vụ một vài thiết bị, có hai cách để thực hiện
điều này đó là sử dụng các ngắt và thăm dò (polling). Trong phơng pháp sử dụng các
ngắt thì mỗi khi có một thiết bị bất kỳ cần đến dịch vụ của nó thì nó bao cho bộ vi
điều khiển bằng cách gửi một tín hiệu ngắt. Khi nhận đợc tín hiệu ngắt thì bộ vi điều
khiển ngắt tất cả những gì nó đang thực hiện để chuyển sang phục vụ thiết bị. Chơng
trình đi cùng với ngắt đợc gọi là trình dịch vụ ngắt ISR (Interrupt Service Routine)
hay còn gọi là trình quản lý ngắt (Interrupt handler). Còn trong phơng pháp thăm dò
thì bộ vi điều khiển hiển thị liên tục tình trạng của một thiết bị đã cho và điều kiện
thoả mãn thì nó phục vụ thiết bị. Sau đó nó chuyển sang hiển thị tình trạng của thiết
bị kế tiếp cho đến khi tất cả đều đợc phục vụ. Mặc dù phơng pháp thăm dò có thể
hiển thị tình trạng của một vài thiết bị và phục vụ mỗi thiết bị khi các điều kiện nhất
định đợc thoả mãn nhng nó không tận dụng hết cộng dụng của bộ vi điều khiển.
Điểm mạnh của phơng pháp ngắt là bộ vi điều khiển có thể phục vụ đợc rất nhiều
thiết bị (tất nhiên là không tại cùng một thời điểm). Mỗi thiết bị có thể nhận đợc sự
chú ý của bộ vi điều khiển dựa trên mức u tiên đợc gán cho nó. Đối với phơng pháp
thăm dò thì không thể gán mức u tiên cho các thiết bị vì nó kiểm tra tất cả mọi thiết
bị theo kiểu hơi vòng. Quan trọng hơn là trong phơng pháp ngắt thì bộ vi điều khiển
cũng còn có thể che hoặc làm lơ một yêu cầu dịch vụ của thiết bị. Điều này lại một
lần nữa không thể thực hiện đợc trong phơng pháp thăm dò. Lý do quan trọng nhất là
phơng pháp ngắt đợc u chuộng nhất là vì phơng pháp thăm dò làm lãng phí thời gian
của bộ vi điều khiển bằng cách hỏi dò từng thiết bị kể cả khi chúng không cần đến
dịch vụ. Nhằm để tránh thì ng ời ta sử dụng phơng pháp ngắt. Ví dụ trong các bộ
định thời đợc bàn đến ở chơng 9 ta đã dùng lệnh JNB TF, đích và đợi cho đến khi
bộ định thời quay trở về 0. Trong ví dụ đó, trong khi chờ đợi thì ta có thể làm việc đ-
ợc gì khác có ích hơn, chẳng hạn nh khi sử dụng phơng pháp ngắt thì bộ vi điều
khiển có thể đi làm các việc khác và khi cờ TF bật lên nó sẽ ngắt bộ vi điều khiển
cho dù nó đang làm bất kỳ điều gì.
11.1.2 Trình phục vụ ngắt.
Đối với mỗi ngắt thì phải có một trình phục vụ ngắt ISR hay trình quản lý
ngắt. khi một ngắt đợc gọi thì bộ vi điều khiển phục vụ ngắt. Khi một ngắt đợc gọi
thì bộ vi điều khiển chạy trình phục vụ ngắt. Đối với mỗi ngắt thì có một vị trí cố
định trong bộ nhớ để giữ địa chỉ ISR của nó. Nhóm các vị trí nhớ đợc dành riêng để
gửi các địa chỉ của các ISR đợc gọi là bảng véc tơ ngắt (xem hình 11.1).
11.1.3 Các bớc khi thực hiện một ngắt.
Khi kích hoạt một ngắt bộ vi điều khiển đi qua các bớc sau:
1. Nó kết thúc lệnh đang thực hiện và lu địa chỉ của lệnh kế tiếp (PC) vào ngăn xếp.
2. Nó cũng lu tình trạng hiện tại của tất cả các ngắt vào bên trong (nghĩa là không l-
u vào ngăn xếp).
3. Nó nhảy đến một vị trí cố định trong bộ nhớ đợc gọi là bảng véc tơ ngắt nới lu
giữ địa chỉ của một trình phục vụ ngắt.
4. Bộ vi điều khiển nhận địa chỉ ISR từ bảng véc tơ ngắt và nhảy tới đó. Nó bắt đầu
thực hiện trình phục vụ ngắt cho đến lệnh cuối cùng của ISR là RETI (trở về từ
ngắt).
5. Khi thực hiện lệnh RETI bộ vi điều khiển quay trở về nơi nó đã bị ngắt. Trớc hết
nó nhận địa chỉ của bộ đếm chơng trình PC từ ngăn xếp bằng cách kéo hai byte
trên đỉnh của ngăn xếp vào PC. Sau đó bắt đầu thực hiện các lệnh từ địa chỉ đó.
Lu ý ở bớc 5 đến vai trò nhạy cảm của ngăn xếp, vì lý do này mà chúng ta
phải cẩn thận khi thao tác các nội dung của ngăn xếp trong ISR. Đặc biệt trong ISR
cũng nh bất kỳ chơng trình con CALL nào số lần đẩy vào ngăn xếp (Push) và số lần
lấy ra từ nó (Pop) phải bằng nhau.
11.1.4 Sáu ngắt trong 8051.
Thực tế chỉ có 5 ngắt dành cho ngời dùng trong 8051 nhng nhiều nhà sản xuất
đa ra các bảng dữ liệu nói rằng có sáu ngắt vì họ tính cả lệnh tái thiết lập lại RESET.
Sáu ngắt của 8051 đợc phân bố nh sau:
1. RESET: Khi chân RESET đợc kích hoạt từ 8051 nhảy về địa chỉ 0000. Đây là
địa chỉ bật lại nguồn đợc bàn ở chơng 4.
2. Gồm hai ngắt dành cho các bộ định thời: 1 cho Timer0 và 1 cho Timer1. Địa chỉ
của các ngắt này là 000B4 và 001B4 trong bảng véc tơ ngắt dành cho Timer0 và
Timer1 tơng ứng.
3. Hai ngắt dành cho các ngắt phần cứng bên ngoài chân 12 (P3.2) và 13 (P3.3) của
cổng P3 là các ngắt phần cứng bên ngoài INT0 và INT1 tơng ứng. Các ngắt ngoài
cũng còn đợc coi nh EX1 và EX2 vị trí nhớ trong bảng véc tơ ngắt của các ngắt
ngoài này là 0003H và 0013H gán cho INT0 và INT1 tơng ứng.
4. Truyền thông nối tiếp có một ngắt thuộc về cả thu và phát. Địa chỉ của ngắt này
trong bảng véc tơ ngắt là 0023H.
Chú ý rằng trong bảng 11.1 có một số giới hạn các byte dành riêng cho mỗi
ngắt. Ví dụ, đối với ngắt INT0 ngắt phần cứng bên ngoài 0 thì có tổng cộng là 8 byte
từ địa chỉ 0003H đến 000AH dành cho nó. Tơng tự nh vậy, 8 byte từ địa chỉ 000BH
đến 0012H là dành cho ngắt bộ định thời 0 là TI0. Nếu trình phục vụ ngắt đối mặt
với một ngắt đã cho mà ngắn đủ đặt vừa không gian nhớ đợc. Nếu không vừa thì một
lệnh LJMP đợc đặt vào trong bảng véc tơ ngắt để chỉ đến địa chỉ của ISR, ở trờng
hợp này thì các byte còn lại đợc cấp cho ngắt này không dùng đến. Dới đây là các ví
dụ về lập trình ngắt minh hoạ cho các điều trình bày trên đây.
Từ bảng 11.1 cùng để ý thấy một thực tế rằng chí có 3 byte của không gian bộ
nhớ ROM đợc gán cho chân RESET. Đó là những vị trí địa chỉ 0, 1 và 2 của ROM.
Vị trí địa chỉ 3 thuộc về ngắt phần cứng bên ngoài 0 với lý do này trong chơng trình
chúng ta phaỉ đặt lệnh LJMP nh là lệnh đầu tiên và hớng bộ xử lý lệnh khỏi bảng véc
tơ ngắt nh chỉ ra trên hình 11.1.
Bảng 11.1: Bảng véc tơ ngắt của 8051.
Ngắt Địa chỉ ROM Chân
Bật lại nguồn (RESET) 0000 9
Ngắt phần cứng ngoài (INT0) 0003 12 (P3.2)
Ngắt bộ Timer0 (TF0) 000B
Ngắt phần cứng ngoài 1 (INT1) 0013 13 (P3.3)
Ngắt bộ Timer1 (TF1) 001B
Ngắt COM nối tiếp (RI và TI) 0023
11.1.5 Cho phép và cấm ngắt.
Khi bật lại nguồn thì tất cả mọi ngắt đều bị cấm (bị che) có nghĩa là không có
ngắt nào sẽ đợc bộ vi điều khiển đáp ứng nếu chúng đợc kích hoạt. Các ngắt phải đợc
kích hoạt bằng phần mềm để bộ vi điều khiển đáp ứng chúng. Có một thanh ghi đợc
gọi là cho phép ngắt IE (Interrupt Enable) chịu trách nhiệm về việc cho phép (không
che) và cấm (che) các ngắt. Hình 11.2 trình bày thanh ghi IE, lu ý rằng IE là thanh
ghi có thể đánh địa chỉ theo bít.
Từ hình 11.2 ta thấy rằng D7 của thanh ghi IE đợc gọi là bít cho phép tất cả
các ngắt EA (Euable All). Bít này phải đợc thiết lập lên 1 để phần còn lại của thanh
ghi hoạt động đợc. Bít D6 cha đợc sử dụng. Bít D54 đợc dành cho 8051, còn bít D4
dùng cho ngắt nối tiếp v.v
11.1.6 Các bớc khi cho phép ngắt.
Để cho phép một ngắt ta phải thực hiện các bớc sau:
1. Bít D7 của thanh ghi IE là EA phải đợc bật lên cao để cho phép các bít còn lại
của thanh ghi nhận đợc hiệu ứng.
2. Nếu EA = 1 thì tất cả mọi ngắt đều đợc phép và sẽ đợc đáp ứng nếu các bít tơng
ứng của chúng trong IE có mức cao. Nếu EA = 0 thì không có ngắt nào sẽ đ ợc
đáp ứng cho dù bít tơng ứng của nó trong IE có giá trị cao.
Để hiểu điểm quan trong này hãy xét ví dụ 11.1.
Hình 11.2: Thanh ghi cho phép ngắt IE.
EA IE.7 Nếu EA = 0 thì mọi ngắt bị cấm
Nếu EA = 1 thì mỗi nguồn ngắt đợc cho phép hoặc bị cấm bằng
các bật hoặc xoá bít cho phép của nó.
- - IE.6 Dự phòng cho tơng lai
ET2 IE.5 Cho phép hoặc cấm ngắt tràn hoặc thu của Timer2 (8051)
ES IE.4 Cho phép hoặc cấm ngắt cổng nối tiếp
ET1 IE.3 Cho phép hoặc cấm ngắt tràn của Timer1
EX1 IE.2 Cho phép hoặc cấm ngắt ngoài 1
ET0 IE.1 Cho phép hoặc cấm ngắt tràn của Timer0
EX0 IE.0 Cho phép hoặc cấm ngắt ngoài 0
* Ngời dùng không phải ghi 1 vào bít dự phòng này. Bít này có thể dùng cho
các bộ vi điều khiển nhanh với đặc tính mới
Ví dụ 11.1:
EA ET2 ES ET1 EX1 ET0 EX0
D0
D7
Hãy chỉ ra những lệnh để a) cho phép ngắt nối tiếp ngắt Timer0 và ngắt phần
cứng ngoài 1 (EX1) và b) cấm (che) ngắt Timer0 sau đó c) trình bày cách cấm tất cả
mọi ngắt chỉ bằng một lệnh duy nhất.
Lời giải:
a) MOV IE, #10010110B ; Cho phép ngắt nối tiếp, cho phép ngắt Timer0 và cho phép ngắt phần cứng ngoài.
Vì IE là thanh ghi có thể đánh địa chỉ theo bít nên ta có thể sử dụng các lệnh
sau đây để truy cập đến các bít riêng rẽ của thanh ghi:
SETB IE.7 ; EA = 1, Cho phép tất cả mọi ngắt
SETB IE.4 ; Cho phép ngắt nối tiếp
SETB IE.1 ; Cho phép ngắt Timer1
SETB IE.2 ; Cho phép ngắt phần cứng ngoài 1
(tất cả những lệnh này tơng đơng với lệnh MOV IE, #10010110B trên đây).
b) CLR IE.1 ; Xoá (che) ngắt Timer0
c) CLR IE.7 ; Cấm tất cả mọi ngắt.
11.2 Lập trình các ngắt bộ định thời.
Trong chơng 9 ta đã nói cách sử dụng các bộ định thời Timer0 và Timer1
bằng phơng pháp thăm dò. Trong phần này ta sẽ sử dụng các ngắt để lập trình cho
các bộ định thời của 8051.
11.2.1 Cờ quay về 0 của bộ định thời và ngắt.
Trong chơng 9 chúng ta đã nói rằng cờ bộ định thời TF đợc đặt lên cao khi bộ
định thời đạt giá trị cực đại và quay về 0 (Roll - over). Trong chơng trình này chúng
ta cũng chỉ ra cách hiển thị cờ TF bằng lệnh JNB TF, đích. Khi thăm dò cờ TF thì
ta phải đợi cho đến khi cờ TF đợc bật lên. Vấn đề với phơng pháp này là bộ vi điều
khiển bị trói buộc khi cờ TF đợc bật lên và không thể làm đợc bất kỳ việc gì khác. Sử
dụng các ngắt giải quyết đợc vấn đề này và tránh đợc sự trói buộc của bộ vi điều
khiển. Nếu bộ ngắt định thời trong thanh ghi IE đợc phép thì mỗi khi nó quay trở về
0 cờ TF đợc bật lên và bộ vi điều khiển bị ngắt tại bất kỳ viẹc gì nó đang thực hiện
và nhảy tới bảng véc tơ ngắt để phục vụ ISR. Bằng cách này thì bộ vi điều khiển có
thể làm những công việc khác cho đến khi nào nó đợc thông báo rằng bộ định thời
đã quay về 0. Xem hình 11.3 và ví dụ 11.2.
Hình 11.3: Ngắt bộ định thời TF0 và TF1.
Hãy để những điểm chơng trình dới đây của chơng trình trong ví dụ 11.2.
1. Chúng ta phải tránh sử dụng không gian bộ nhớ dành cho bảng véc tơ ngắt. Do
vậy, ta đặt tất cả mã khởi tạo tại địa chỉ 30H của bộ nhớ. Lệnh LJMP là lệnh đầu
tiên mà 8051 thực hiện khi nó đợc cấp nguồn. Lệnh LJMP lái bộ điều khiển tránh
khỏi bảng véc tơ ngắt.
1
000BH
TF0
Jumps to
Timer 0 Interruptor
1
001BH
TF1
Jumps to
Timer 1 Interruptor
2. Trình phục vụ ISR của bộ Timer0 đợc đặt ở trong bộ nhớ bắt đầu tự địa chỉ
000BH và vì nó quá nhỏ đủ cho vào không gian nhớ dành cho ngắt này.
3. Chúng ta cho phép ngắt bộ Timer0 với lệnh MOV IE, #1000 010H trong chơng
trình chính MAIN.
4. Trong khi dữ liệu ở cổng P0 đợc nhận vào và chuyển liên tục sang công việc P1
thì mỗi khi bộ Timer0 trở về 0, cờ TF0 đợc bật lên và bộ vi điều khiển thoát ra
khỏi vòng lặp BACK và đi đến địa chỉ 000BH để thực hiện ISR gắn liền với bộ
Timer0.
5. Trong trình phục vụ ngắt ISR của Timer0 ta thấy rằng không cần đến lệnh CLR
TF0 trớc khi lệnh RETI. Lý do này là vì 8051 xoá cờ TF bên trong khi nhảy đến
bảng véc tơ ngắt.
Ví dụ 11.2:
Hãy viết chơng trình nhân liên tục dữ liệu 8 bít ở cổng P0 và gửi nó đến cổng
P1 trong khi nó cùng lúc tạo ra một sóng vuông chu kỳ 200às trên chân P2.1. Hãy sử
dụng bộ Timer0 để tạo ra sóng vuông, tần số của 8051 là XTAL = 11.0592MHz.
Lời giải:
Ta sử dụng bộ Timer0 ở chế độ 2 (tự động nạp lại) giá trị nạp cho TH0 là
100/1.085às = 92.
; - - Khi khởi tạo vào chơng trình main tránh dùng không gian.
; Địa chỉ dành cho bảng véc tơ ngắt.
ORG 0000H
CPL P2.1 ; Nhảy đến bảng véc tơ ngắt.
;
; - - Trình ISR dành cho Timer0 để tạo ra sóng vuông.
ORG 0030H ; Ngay sau địa chỉ bảng véc-tơ ngắt
MAIN: TMOD, #02H ; Chọn bộ Timer0, chế độ 2 tự nạp lại
MOV P0, #0FFH ; Lấy P0 làm cổng vào nhận dữ liệu
MOV TH0, # - 92 ; Đặt TH0 = A4H cho 92
MOV IE, #82H ; IE = 1000 0010 cho phép Timer0
SETB TR0 ; Khởi động bộ Timer0
BACK: MOV A, P0 ; Nhận dữ liệu vào từ cổng P0
MOV P1, A ; Chuyển dữ liệu đến cổng P1
SJMP BACK ; Tiếp tục nhận và chuyển dữ liệu
; Chừng nào bị ngắt bởi TF0
END
Trong ví dụ 11.2 trình phục vụ ngắt ISR ngắn nên nó có thể đặt vừa vào
không gian địa chỉ dành cho ngắt Timer0 trong bảng véc tơ ngắt. Tất nhiên không
phải lúc nào cũng làm đợc nh vậy. Xét ví dụ 11.3 dới đây.
Ví dụ 11.3:
Hãy viết lại chơng trình ở ví dụ 11.2 để tạo sóng vuông với mức cao kéo dài
1085às và mức thấp dài 15às với giả thiết tần số XTAL = 11.0592MHz. Hãy sử
dụng bộ định thời Timer1.
Lời giải:
Vì 1085às là 1000 ì 1085às nên ta cần sử dụng chế độ 1 của bộ định thời
Timer1.
; - - Khi khởi tạo tránh sử dụng không gian dành cho bảng véc tơ ngắt.
ORG 0000H
LJMP MAIN ; Chuyển đến bảng véc tơ ngắt.
;
; - - Trình ISR đối với Timer1 để tạo ra xung vuông
ORG 001BH ; Địa chỉ ngắt của Timer1 trong bảng véc tơ ngắt
LJMP ISR-T1 ; Nhảy đến ISR
;
; - - Bắt đầu các chơng trình chính MAIN.
ORG 0030H ; Sau bảng véc tơ ngắt
MAIN: MOV TMOD, #10H ; Chọn Timer1 chế độ 1
MOV P0, #0FFH ; Chọn cổng P0 làm đầu vào nhận dữ liệu
MOV TL1, #018H ; Đặt TL1 = 18 byte thấp của - 1000
MOV TH1, #0FCH ; Đặt TH1 = FC byte cao của - 1000
MOV IE, #88H ; IE = 10001000 cho phép ngắt Timer1
SETB TR1 ; Khởi động bộ Timer1
BACK: MOV A, P0 ; Nhận dữ liệu đầu vào ở cổng P0
MOV P1, A ; Chuyển dữ liệu đến P1
SJMP BACK ; Tiếp tục nhận và chuyển dữ liệu
;
; - - Trình ISR của Timer1 phải đợc nạp lại vì ở chế độ 1
ISR-T1: CLR TR1 ; Dừng bộ Timer1
CLR P2.1 ; P2.1 = 0 bắt đầu xung mức thấp
MOV R2, #4 ; 2 chu kỳ máy MC (Machine Cycle)
HERE: DJNZ R2, HERE ; 4 ì 2 MC = 8 MC
MOV TL1, #18H ; Nạp lại byte thấp giá trị 2 MC
MOV TH1, #0FCH ; Nạp lại byte cao giá trị 2 MC
SETB TR1 ; Khởi động Timer1 1 MC
SETB P2.1 ; P2.1 = 1 bật P2.1 trở lại cao
RETI ; Trở về chơng trình chính
END
Lu ý rằng phần xung mức thấp đợc tạo ra bởi 14 chu kỳ mức MC và mỗi MC
= 1.085às và 14 ì 1.085às = 15.19às.
Ví dụ 11.4:
Viết một chơng trình để tạo ra một sóng vuông tần số 50Hz trên chân P1.2. Ví
dụ này tơng tự ví dụ 9.12 ngoại trừ ngắt Timer0, giả sử XTAL = 11.0592MHz.
Lời giải:
ORG 0
LJMP MAIN
ORG 000BH ; Chơng trình con phục vụ ngắt cho Timer0
CPL P1.2
MOV TL0, # 00
MOV TH0, # 0DCH
RETI
ORG 30H
; main program for initialization
MAIN: MOV TMOD, # 00000001B ; Chọn Timer0 chế độ 1
MOV TL0, # 0DCH
MOV IE, # 82H ; Cho phép ngắt Timer0
SETB TR0
HERE: SJMP HERE
END
P1.2
8051
50Hz square ware
11.3 Lập trình các ngắt phần cứng bên ngoài.
Bộ vi điều khiển 8051 có hai ngắt phần cứng bên ngoài là chân 12 (P3.2) và
chân 13 (P3.3) dùng cho ngắt INT0 và INT1. Khi kích hoạt những chân này thì 8051
bị ngắt tại bất kỳ công việc nào mà nó đang thực hiện và nó nhảy đến bảng véc tơ
ngắt để thực hiện trình phục vụ ngắt.
11.3.1 Các ngắt ngoài INT0 và INT1.
Chỉ có hai ngắt phần cứng ngoài trong 8051 là INT0 và INT1. Chúng đợc bố
trí trên chân P3.2 và P3.3 và địa chỉ của chúng trong bảng véc tơ ngắt là 0003H và
0013H. Nh đã nói ở mục 11.1 thì chúng đợc ghép và bị cấm bằng việc sử dụng thanh
ghi IE. Vậy chúng đợc kích hoạt nh thế nào? Có hai mức kích hoạt cho các ngắt
phần cứng ngoài: Ngắt theo mức và ngắt theo sờn. Dới đây là mô tả hoạt động của
mỗi loại.
11.3.2 Ngắt theo mức.
ở chế độ ngắt theo mức thì các chân INT0 và INT1 bình thờng ở mức cao
(giống nh tất cả các chân của cổng I/O) và nếu một tín hiệu ở mức thấp đợc cấp tới
chúng thì nó ghi nhãn ngắt. Sau đó bộ vi điều khiển dừng tất cả mọi công việc nó
đang thực hiện và nhảy đến bảng véc tơ ngắt để phục vụ ngắt. Điều này đợc gọi là
ngắt đợc kích hoạt theo mức hay ngắt theo mức và là chế độ ngắt mặc định khi cấp
nguồn lại cho 8051. Tín hiệu mức thấp tại chân INT phải đợc lâýu đi trớc khi thực
hiện lệnh cuối cùng của trình phục vụ ngắt RETI, nếu không một ngắt khác sẽ lại đ-
IE0
(TCON.1)
INTO
(Pin 3.2)
Level - tringgered
Edge - triggered
0003 ITO
0
1
IE0
(TCON.3)
INTO
(Pin 3.3)
Level - tringgered
Edge - triggered
0013 IT1
0
1
ợc tạo ra. Hay nói cách khác, nếu tín hiệu ngắt mức thấp không đợc lấy đi khi ISR
kết thúc thì nó không thể hiện nh một ngắt khác và 8051 nhảy đến bảng véc tơ ngắt
để thực hiện ISR. Xem ví dụ 11.5.
Ví dụ 11.5.
Giả sử chân INT1 đợc nối đến công tắc bình thờng ở mức cao. Mỗi khi nó
xuống thấp phải bật một đèn LED. Đèn LED đợc nối đến chân P1.3 và bình thờng ở
chế độ tắt. Khi nó đợc bật lên nó phải sáng vài phần trăm giây. Chừng nào công tắc
đợc ấn xuống thấp đèn LED phải sáng liên tục.
Lời giải:
ORG 0000H
LJMP MAIN ; Nhảy đến bảng véc tơ ngắt
; - - Chơng trình con ISR cho ngắt cứng INT1 để bật đèn LED.
ORG 0013H ; Trình phục vụ ngắt ISR cho INT1
SETB P1.3 ; Bật đèn LED
MOV R3, # 255 ;
BACK: DJNZ R3, BACK ; Giữ đèn LED sáng một lúc
CLR P1.3 ; Tắt đèn LED
RETI ; Trở về từ ISR
; - - Bắt đầu chơng trình chính Main.
ORG 30H
MAIN: MOV IE, #10000100B ; Cho phép ngắt dài
SJMP HERE ; Chờ ở đây cho đến khi đợc ngắt
END
ấn công tắc xuống sẽ làm cho đèn LED sáng. Nếu nó đợc giữ ở trạng thái đợc
kích hoạt thì đèn LED sáng liên tục.
Trong chơng trình này bộ vi điều khiển quay vòng liên tục trong vòng lặp
HERE. Mỗi khi công tắc trên chân P3.3 (INT1) đợc kích hoạt thì bộ vi điều khiển
thoát khỏi vòng lặp và nhảy đến bảng véc tơ ngắt tại địa chỉ 0013H. Trình ISR cho
INT1 bật đèn LED lên giữ nó một lúc và tắt nó trớc khi trở về. Nếu trong lúc nó thực
hiện lệnh quay trở về RET1 mà chân INT1 vẫn còn ở mức thấp thì bộ vi điều khiển
khởi tạo lại ngắt. Do vậy, để giải quyết vấn đề này thì chân INT1 phải đợc đa lên cao
tại thời điểm lệnh RET1 đợc thực hiện.
11.3.3 Trích mẫu ngắt theo mức.
Các chân P3.2 và P3.3 bình thờng đợc dùng cho vào - ra nếu các bít INT0 và
INT1 trong thanh ghi IE không đợc kích hoạt. Sau khi các ngắt phần cứng trong
thanh gi IE đợc kích hoạt thì bộ vi điều khiển duy trì trích mẫu trên chân INTn đối
với tín hiệu mức thấp một lần trong một chu trình máy. Theo bảng dữ liệu của nhà
INTI
8051
P1.3
to
LED
Vcc
sản xuất của bộ vi điều khiển thì chân ngắt phải đợc giữ ở mức thấp cho đến khi bắt
đầu thực hiện trình phục vụ ngắt ISR. Nếu chân INTn đợc đa trở lại mức cao trớc khi
bắt đầu thực hiện ISR thì sẽ chẳng có ngắt nào xảy ra. Tuy nhiên trong quá trình
kích hoạt ngắt theo mức thấp nên nó lại phải đa lên mức cao trớc khi thực hiện lệnh
RET1 và lại theo bảng dữ liệu của nhà sản xuất thì nếu chân INTn vẫn ở mức thấp
sau lệnh RETI của trình phục vụ ngắt thì một ngắt khác lại sẽ đợc kích hoạt sau khi
lệnh RET1 đợc thực hiện. Do vậy, để bảo đảm việc kích hoạt ngắt phần cứng tại các
chân INTn phải khẳng định rằng thời gian tồn tại tín hiệu mức thấp là khoảng 4 chu
trình máy và không đợc hơn. Điều này là do một thực tế là ngắt theo mức không đợc
chốt. Do vậy chân ngắt phải đợc gia ở mức thấp cho đến khi bắt đầu thực hiện ISR.
Hình 11.5: Thời gian tối thiểu của ngắt theo mức thấp (XTAL =
11.0592MHz)
11.3.4 Các ngắt theo sờn.
Nh đã nói ở trớc đây trong quá trình bật lại nguồn thì 8051 làm các chân
INT0 và INT1 là các ngắt theo mức thấp. Để biến các chân này trở thành các ngắt
theo sờn thì chúng ta phải viết chnơg trình cho các bít của thanh ghi TCON. Thanh
thi TCON giữ các bít cờ IT0 và IT1 xác định chế độ ngắt theo sờn hay ngắt theo mức
của các ngắt phần cứng IT0 và IT1 là các bít D0 và D2 của thanh ghi TCON tơng
ứng. Chúng có thể đợc biểu diễn nh TCON.0 và TCON.2 vì thanh ghi TCON có thể
đánh địa chỉ theo bít. Khi bật lại nguồn thì TCON.0 (IT0) và TCON.2 (IT1) đều ở
mức thấp (0) nghĩa là các ngắt phần cứng ngoài của các chân INT0 và INT1 là ngắt
theo mức thấp. Bằng việc chuyển các bít TCON.0 và TCON.2 lên cao qua các lệnh
SETB TCON.0 và SETB TCON.2 thì các ngắt phần cứng ngoài INT0 và INT1
trở thành các ngắt theo sờn. Ví dụ, lệnh SETB TCON.2 làm cho INT1 mà đợc gọi
là ngắt theo sờn trong đó khi một tín hiệu chuyển từ cao xuống thấp đợc cấp đến
chân P3.3 thì ở trờng hợp này bộ vi điều khiển sẽ bị ngắt và bị cỡng bức nhảy đến
bảng véc tơ ngắt tại địa chỉ 0013H để thực hiện trình phục vụ ngắt. Tuy nhiên là với
giải thiết rằng bít ngắt đã đợc cho phép trong thanh ghi IE.
Hình 11.6: Thanh ghi TCON.
1 chu trình máy
1.085às
4 chu trình máy (4MC)
4 ì 1.085às
đến chân INT0
hoặc INT1
Ghi chú: Khi bật lại nguồn (RESET) thì cả hai chân INT0 và INT1 đều ở mức
thấp tạo các ngắt ngoài theo mức.
TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
D0
D7
Bít TF1 hay TCON.7 là cờ tràn của bộ Timer1. Nó đợc lập bởi phần cứng khi bộ
đếm/ bộ định thời 1 tràn, nó đợc xoá bởi phần cứng khi bộ xử lý chỉ đến trình
phục vụ ngắt.
Bít TR1 hay TCON.6 là bít điều khiển hoạt động của Timer1. Nó đợc thiết lập và
xoá bởi phần mềm để bật/ tắt Timer1.
Bít TF0 hay TCON.5 tơng tự nh TF1 dành cho Timer0.
Bít TR0 hay TCON.4 tơng tự nh TR1 dành cho Timer0.
Bít IE1 hay TCON.3 cờ ngắt ngoài 1 theo sờn. Nó đợc thiết lập bởi CPU khi sờn
ngắt ngoài (chuyển từ cao xuống thấp) đợc phát hiện. Nó đợc xóa bởi CPU khi
ngắt đợc xử lý. Lu ý: Cờ này không chốt những ngắt theo mức thấp.
Bít IT1 hay TCON.2 là bít điều khiển kiểu ngắt. Nó đợc thiết lập và xoá bởi phần
mềm để xác định kiểu ngắt ngoài theo sờn xuống hay mức thấp.
Bít IE0 hay TCON.1 tơng tự nh IE1 dành cho ngắt ngoài 0.
Bít IT0 hay TCON.0 tơng tự nh bít IT1 dành cho ngắt ngoài 0.
Xét ví dụ 11.6, chú ý rằng sự khác nhau duy nhất giữa vì dụ này và ví dụ 11.5
là ở trong hàng đầu tiên của MAIN khi lệnh SETB TCON.2 chuyển ngắt INT1 về
kiểu ngắt theo sờn. Khi sờn xuống của tín hiệu đợc cấp đến chân INT1 thì đèn LED
sẽ bật lên một lúc. Đèn LED có thời gian sáng phụ thuộc vào độ trễ bên trong ISR
của INT1. Để bật lại đèn LED thì phải có một sờn xung xuống khác đợc cấp đến
chân P3.3. Điều này ngợc với ví dụ 11.5. Trong ví dụ 11.5 do bản chất ngắt theo mức
của ngắt thì đèn LED còn sáng chừng nào tín hiệu ở chân INT1 vẫn còn ở mức thấp.
Nhng trong ví dụ này để bật lại đèn LED thì xung ở chân INT1 phải đợc đa lên cao
rồi sau đó bị hạ xuống thấp để tạo ra một sờn xuống làm kích hoạt ngắt.
Ví dụ 11.6:
Giả thiết chân P3.3 (INT1) đợc nối với một máy tạo xung, hãy viết một chơng
trình trong đó sờn xuống của xung sẽ gửi một tín hiệu cao đến chân P1.3 đang đợc
nối tới đèn LED (hoặc một còi báo). Hay nói cách khác, đèn LED đợc bật và tắt
cùng tần số với các xung đợc cấp tới chân INT1. Đây là phiên bản ngắt theo sờn
xung của ví dụ 11.5 đã trình bày ở trên.
Lời giải:
ORG 0000H
LJMP MAIN
; - - Trình phục vụ ngắt ISR dành cho ngắt INT1 để bật đèn LED
ORG 0013H ; Nhảy đến địa chỉ của trình phục vụ ngắt INT1
SETB P1.3 ; Bật đèn LED (hoặc còi)
MOV R3, #225
BACK: DJNZ R3, HERE ; giữ đèn LED (hoặc còi) một lúc
CLR P1.3 ; Tắt đèn LED (hoặc còi)
RETI ; Quay trở về từ ngắt
; - - Bắt đầu chơng trình chính
ORG 30H
SETB TCON.2 ; Chuyển ngắt INT1 về kiểu ngắt theo sờn xung
MOV IE, #10001B ; Cho phép ngắt ngoài INT1
HERE: SJMP HERE ; Dừng ở đây cho đến khi bị ngắt
END
11.3.5 Trình mẫu ngắt theo sờn.
Trớc khi kết thúc phần này ta cần trả lời câu hỏi vậy thì ngắt theo sờn đợc
trích mẫu thờng xuyên nh thế nào? Trong các ngắt theo sờn, nguồn ngoài phải giữ ở
mức cao tối thiểu là một chu trình máy nữa đê đảm bảo bộ vi điều khiển nhìn thấy đ-
ợc sự chuyển dịch từ cao xuống thấp của sờn xung.
Thời hạn xung tối thiểu để phát hiện ra các ngắt theo sờn xung với tần số
XTAL = 11.0592MHz
Sờn xuống của xung đợc chốt bởi 8051 và đợc giữa bởi thanh ghi TCON. Các
bít TCON.1 và TCON.3 giữ các sờn đợc chốt của chân INT0 và INT1 tơng ứng.
TCON.1 và TCON.3 cũng còn đợc gọi là các bít IE0 và IE1 nh chỉ ra trên hình 11.6.
Chúng hoạt động nh các cờ ngắt đang đợc phục vụ (Interrupt-in-server). Khi một
cờ ngắt đang đợc phục vụ bật lên thì nó báo cho thế giới thực bên ngoài rằng ngắt
hiện nay đang đợc xử lý và trên chân INTn này sẽ không có ngắt nào đợc đáp ứng
chừng nào ngắt này cha đợc phục vụ xong. Đây giống nh tín hiệu báo bận ở máy
điện thoại. Cần phải nhấn mạnh hạt điểm dới đây khi quan tâm đến các bít IT0 và
IT1 của thanh ghi TCON.
1. Khi các trình phục vụ ngắt ISR kết thúc (nghĩa là trong thanh ghi thực hiện lệnh
RETI). Các bít này (TCON.1 và TCON.3) đợc xoá để báo rằng ngắt đợc hoàn tất
và 8051 sẵn sàng đáp ứng ngắt khác trên chân đó. Để ngắt khác đợc nhận và thì
tín hiệu trên chân đó phải trở lại mức cao và sau đó nhảy xuống thấp để đợc phát
hiện nh một ngắt theo sờn.
2. Trong thời gian trình phục vụ ngắt đang đợc thực hiện thì chân INTn bị làm ngơ
không quan tâm đến nó có bao nhiêu lần chuyển dịch từ cao xuống thấp. Trong
thực tế nó là một trong các chức năng của lệnh RETI để xoá bít tơng ứng trong
thanh ghi TCON (bít TCON.1 và TCON.3). Nó báo cho ta rằng trình phục vụ
ngắt xắp kết thúc. Vì lý do này mà các bít TCON.1 và TCON.3 đợc gọi là các cơ
báo ngắt đang đợc phục vụ cờ này sẽ lên cao khi một sờn xuống đợc phát hiện
trên chân INT và dừng ở mức cao trong toàn bộ quá trình thực hiện ISR. Nó chỉ
bị xoá bởi lệnh RETI là lệnh cuối cùng của ISR. Do vậy, sẽ không báo giờ cần
đến các lệnh xoá bít này nh CLR TCON.1 hay CLR TCON.3 trớc lệnh RETI
trong trình phục vụ ngắt đối với các ngắt cứng INT0 và INT1. Điều này không
đúng với trờng hợp của ngắt nối tiếp.
Ví dụ 11.7:
Sự khác nhau giữa các lệnh RET và RETI là gì? Giải thích tại sao ta không
thể dùng lệnh RET thay cho lệnh RETI trong trình phục vụ ngắt.
Lời giải:
Các hai lệnh RET và RETI đều thực thi các hành vi giống nhau là lấy hai byte
trên đỉnh ngăn xếp vào bộ đếm chơng trình và đa 8051 trở về nơi đó đã bỏ đi. Tuy
nhiên, lệnh RETI còn thực thi một nhiệm vụ khác nữa là xoá cờ ngắt đang đợc phục
vụ để báo rằng ngắt đã kết thúc và 8051 có thể nhập một ngắt mới trên chân này.
Nếu ta dùng lệnh RET thay cho RETI nh là lệnh cuối cùng của trình phục vụ ngắt
nh vậy là ta đã vô tình khoá mọi ngắt mới trên chân này sau ngắt đầu tiên vì trạng
1.085às
1.085às
1MC
1MC
thái của chân báo rằng ngắt vẫn đang đợc phục vụ. Đây là trờng hợp mà các cờ TF0,
TF1, TCON.1 và TCON.3 đợc xoá bởi lệnh RETI.
11.3.6 Vài điều bổ xung về thanh ghi TCON.
Bây giờ ta xét kỹ về các bít của thanh ghi TCON để hiểu vai trò của nó trong
việc duy trì các ngắt.
11.3.6.1 Các bít IT0 và IT1.
Các bít TCON.0 và TCON.2 đợc coi nh là các bít IT0 và IT1 tơng ứng. Đây là
các bít xác định kiểu ngắt theo sờn xung hay theo mức xung của các ngắt phần cứng
trên chân INT.0 và INT.1 tơng ứng. Khi bật lại nguồn cả hai bít này đều có mức 0 để
biến chúng thành ngắt theo tín hiệu mức thấp. Lập trình viên có thể điều khiển một
trong số chúng lên cao để chuyển ngắt phần cứng bên ngoài thành ngắt theo ngỡng.
Trong một hệ thống dựa trên 8051 đã cho thì một khi ta đã đặt về 0 hoặc 1 thì các bít
này sẽ không thay đổi vì ngời thiết kế đã cố định kiểu ngắt là ngắt theo sờn hay theo
mức rỗi.
11.3.6.2 Các bít IE0 và IE1.
Các bít TCON.1 và TCON.3 còn đợc gọi là IE0 và IE1 tơng ứng. Các bít này
đợc 8051 dùng để bám kiểu ngắt theo sờn xung. Nói các khác là nếu IT0 và IT1
bằng 0 thì có nghĩa là các ngắt phần cứng là ngắt theo mức thấp, các bít IE0 và IE1
không dùng đến làm gì. Các bít IE0 và IE1 đợc 8051 chỉ dùng để chốt sờn xung từ
cao xuống thấp trên các chân INT0 và INT1. Khi có chuyển dịch sờn xung trên chân
INT0 (hay INT1) thì 8051 đánh dấu (bật lên cao) các bít IEx trên thanh ghi TCON
nhảy đến bảng véc tơ ngắt và bắt đầu thực hiện trình phục vụ ngắt ISR. Trong khi
8051 thực hiện ISR thì không có một sờn xung nào đợc ghi nhận trên chân INT0
(hay INT1) để ngăn mọi ngắt trong ngắt. Chỉ trong khi thực hiện lệnh RETI ở cuối
trình phục vụ ngắt ISR thì các bít IEx mới bị báo rằng một sờn xung cao xuống thấp
mới trên chân INT0 (hay INT1) sẽ kích hoạt ngắt trở lại. Từ phần trình bày trên ta
thấy rằng các bít IE0 và IE1 đợc 8051 sử dụng bên trong để báo có một ngắt đang đ-
ợc xử lý hay không. Hay nói cách khác là lập trình viên không phải quan tâm đến cá
bít này.
11.3.6.3 Các bít TR0 và TR1.
Đây là những bít D4 và D6 (hay TCON.4 và TCON.6) của thanh ghi TCON.
Các bít này đã đợc giới thiệu ở chơng 9 chúng đợc dùng để khởi động và dừng các
bộ định thời Timer0 và Timer1 tơng ứng. Vì thanh ghi TCON có thể đánh địa chỉ
theo bít nên có thể sử dụng các lệnh SETB TRx và CLR TRx cũng nh các lệnh
SETB TCON.4 và CLR TCON.4.
11.3.6.4 Các bít TF0 và TF1.
Các bít này là D5 (TCON.5) và D7 (TCON.7) của thanh ghi TCON mà đã đợc
giới thiệu ở chơng 9. Chúng ta đợc sử dụng bởi các bộ Timer0 và Timer1 tơng ứng
để báo rằng các bộ định thời bị tràn hay quay về không. Mặc dù ta đã dùng các lệnh
JNB TFx, đích và CLR TFx nhng chúng ta cũng không thể sử dụng các lệnh nh
SETB TCON.5, đích và CLR TCON.5 vì TCON là thanh ghi có thể đánh địa chỉ
theo bít.
11.4 Lập trình ngắt truyền thông nối tiếp.
Trong chơng 10 chúng ta đã nghiên cứu về truyền thông nối tiếp của 8051.
Tất cả các ví dụ trong chơng ấy đều sử dụng phơng pháp thăm dò (polling). ậ chơng
này ta khám phá truyền thông dựa trên ngắt mà nó cho phép 8051 làm việc rất nhiều
việc ngoài việc truyền và nhận dữ liệu từ cổng truyền thông nối tiếp.
11.4.1 Các cờ RI và TI và các ngắt.
Nh đã nói ở chơng 10 thì cờ ngắt truyền TI (Transfer interrupt) đợc bật lên khi
bít cuối cùng của khung dữ liệu, bít stop đợc truyền đi báo rằng thanh ghi SBUF sẵn
sàng truyền byte kế tiếp. Trong trờng hợp cờ RI (Receive Interrupt) thì nó đợc bật
lên khi toàn bộ khung dữ liệu kể cả bít stop đã đơc nhận. Hay nói cách khác khi
thanh ghi SBUF đã có một byte thì cờ RI bật lên báo rằng byte dữ liệu nhận đợc cần
lấy đi cất vào nơi an toàn trớc khi nó bị mất (bị ghi đè) bởi dữ liệu mới nhận đợc.
Chừng nào còn nói về truyền thông nối tiếp thì tất cả mọi khái niệm trên đây đều áp
dụng giống nh nhau cho dù sử dụng phơng pháp thăm dò hay sử dụng phơng pháp
ngắt. Sự khác nhau duy nhất giữa hai phơng pháp này là ở cách phục vụ quá trình
truyền thông nối tiếp nh thế nào. Trong phơng pháp thăm dò thì chúng ta phải đợi
cho cờ (TI hay RI) bật lên và trong lúc chờ đợi thì ta không thể làm gì đợc cả. Còn
trong phơng pháp ngắt thì ta đợc báo khi 8051 đã nhận đợc một byte hoặc nó sẵn
sàng chuyển (truyền) byte kế tiếp và ta có thể làm các công việc khác trong khi
truyền thông nối tiếp đang đợc phục vụ.
Trong 8051 chỉ có một ngắt dành riêng cho truyền thông nối tiếp. Ngắt này đ-
ợc dùng cho cả truyền và nhận dữ liệu. Nếu bít ngắt trong thanh gi IE (là bít IE.4) đ -
ợc phép khi RI và TI bật lên thì 8051 nhận đợc ngắt và nhảy đến địa chỉ trình phục
vụ ngắt dành cho truyền thông nối tiếp 0023H trong bảng véc tơ ngắt để thực hiện
nó. Trong trình ISR này chúng ta phải kiểm tra các cờ TI và RI để xem cờ nào gây ra
ngắt để đáp ứng một cách phù hợp (xem ví dụ 11.8).
Hình 11.7: Ngắt truyền thông có thể do hai cờ TI và RI gọi.
11.4.2 Sử dụng cổng COM nối tiếp trong 8051.
Trong phần lớn các ứng dụng, ngắt nối tiếp chủ yếu đợc sử dụng để nhận dữ
liệu và không bao giờ đợc sử dụng để truyền dữ liệu nối tiếp. Điều này giống nh việc
báo chuông để nhận điện thoại, còn nếu ta muốn gọi điện thoại thì có nhiều cách
khác ngắt ta chứ không cần đến đổ chuông. Tuy nhiên, trong khi nhận điện thoại ta
phải trả lời ngay không biết ta đang làm gì nếu không thuộc gọi sẽ (mất) đi qua. T-
ơng tự nh vậy, ta sử dụng các ngắt nối tiếp khi nhận dữ liệu đi đến để sao chép cho
nó không bị mất: Hãy xét ví dụ 11.9 dới đây.
Ví dụ 11.8:
Hãy viết chơng trình trong đó 8051 đọc dữ liệu từ cổng P1 và ghi nó tới cổng
P2 liên tục trong khi đa một bản sao dữ liệu tới cổng COM nối tiếp để thực hiện
truyền nối tiếp giả thiết tần số XTAL là 11.0592MHz và tốc độ baud là 9600.
Lời giải:
ORG 0
RI
TI
0023H
LJMP MAIN
ORG 23H
LJMP SERIAL ; Nhảy đến trình phục vụ ngắt truyền thông nối tiếp
MAIN: MOVQP1, # 0FFH ; Lấy cổng P1 làm cổng đầu vào
MOV TMOD, # 20h ; Chọn Timer1, chế độ 2 tự nạp lại
MOV TH1, # 0FDH ; Chọn tốc độ baud = 9600
MOV SCON, # 50H ; Khung dữ liệu: 8 bít dữ liệu, 1 stop à cho phép REN
MOV IE, # 10010000B ; Cho phép ngắt nối tiếp
SETB TR1 ; Khởi động Timer1
BACK: MOV A, P1 ; Đọc dữ liệu từ cổng P1
MOV SBUF, A ; Lấy một bản sao tới SBUF
MOV P2, A ; Gửi nó đến cổng P2
SJMP BACK ; ở lại trong vòng lặp
;
; Trình phục vụ ngắt cổng nối tiếp
ORG 100H
SERIAL: JB TI,TRANS ; Nhảy đến cờ TI cao
MOV A, SBUF ; Nếu không tiếp tục nhận dữ liệu
CLR RI ; Xoá cờ RI vì CPU không làm điều này
RETI ; Trở về từ trình phục vụ ngắt
TRANS: CLR TI ; Xoá cờ TI vì CPU không làm điều này
RETI ; Trở về từ ISR
END
Trong vấn đề trên thấy chú ý đến vai trò của cờ TI và RI. Thời điểm một byte
đợc ghi vào SBUF thì nó đợc đóng khung và truyền đi nối tiếp. Kết quả là khi bít
cuối cùng (bít stop) đợc truyền đi thì cờ TI bật lên cao và nó gây ra ngắt nối tiếp đợc
gọi khi bít tơng ứng của nó trong thanh ghi IE đợc đa lên cao. Trong trình phục vụ
ngắt nối tiếp, ta phải kiểm tra cả cờ TI và cờ RI vì cả hai đều có thể gọi ngắt hay nói
cách khác là chỉ có một ngắt cho cả truyền và nhận.
Ví dụ 11.9:
Hãy viết chơng trình trong đó 8051 nhận dữ liệu từ cổng P1 và gửi liên tục
đến cổng P2 trong khi đó dữ liệu đi vào từ cổng nối tiếp COM đợc gửi đến cổng P0.
Giả thiết tần số XTAL là 11.0592MHz và tốc độ baud 9600.
Lời giải:
ORG 0
LJMP MAIN
ORG 23H
LJMP SERIAL
ORG 03H
MAIN: MOV P1, # FFH ; Lấy cổng P1 là cổng đầu vào
MOV TMOD, # 20H ; Chọn Timer và chế độ hai tự nạp lại
MOV TH1, # 0FDH ;Khung dữ liệu: 8 bít dữ liệu, 1 stop, cho phép REN
MOV SCON, # 50H ; Cho phép ngắt nối tiếp
MOV IE, # 10010000B ; Khởi động Timer1
SETB TR1 ; Đọc dữ liệu từ cổng P1
BACK: MOV A, P1 ; Gửi dữ liệu đến cổng P2
MOV P2, A ; ở lại trong vòng lặp
SJMP BACK
Không có nhận xét nào:
Đăng nhận xét