• Giới thiệu.
Một ngắt là sự xảy ra một điều kiện - một sự kiện - làm tạm thời ngưng chương trình hiện hành, để điều kiện đó được phục vụ bằng một chương trình khác. Các ngắt đóng một vai trò quan trọng trong thiết kế các ứng dụng vi điều khiển. Chúng cho phép hệ thống đáp ứng bất đồng bộ với một sự kiện và giải quyết sự kiện đó trong khi một chương trình khác đang thực thi.
Một hệ thống được điều khiển bằng ngắt làm người sử dụng cảm giác như hệ thống thực hiện nhiều công việc một cách đồng thời. Dĩ nhiên CPU mỗi lần không thể thực thi nhiều hơn một lệnh. Nhưng nó có thể tạm ngưng việc thực hiện một chương trình này, để thực hiện chương trình khác, rồi quay về chương trình thứ nhất. Việc phục vụ ngắt giống như việc gọi một chương trình con, CPU chuyển qua thực hiện chương trình con, sau đó quay trở lại chương trình chính. Khác biệt là trong hệ thống điều khiển bằng ngắt, thì sự ngắt quãng không xảy ra như kết quả của một lệnh (lệnh CALL subroutine), mà là đáp ứng với một “sự kiện” xảy ra bất đồng bộ với chương trình chính. Hệ thống sẽ không xác định chương trình chính sẽ bị ngắt quãng tại đâu.
Chương trình giải quyết ngắt được gọi là chương trình phục vụ ngắt (ISR: Interrupt Service Routine) hoặc chương trình xử lý ngắt. ISR thực thi khi đáp ứng một ngắt, và thông thường là để thực hiện tác vụ xuất nhập dữ liệu với một thiết bị. Khi ngắt xảy ra, chương trình chính tạm thời dừng hoạt động và rẽ nhánh đến ISR: ISR thực thi và kết thúc bằng lệnh trở về từ ngắt, lúc này chương trình chính tiếp tục thực hiện ở chỗ mà nó tạm dừng. Thường người ta xem chương trình chính như thực thi ở mức cơ sở (base - level), và các ISR thực thi ở mức ngắt (interrupt - level). Người ta cũng dùng các thuật ngữ foreground chỉ mức cơ
sở và background chỉ mức ngắt. Hình ảnh khái quát của các ngắt được mô tả trong hình 8.17:
• Tổ chức ngắt của 8051.
Có 5 nguồn yêu cầu ngắt trong hệ thống 8051: 2 ngắt ngoài, 2 ngắt từ timer và 1 ngắt cổng nối tiếp. Tất cả các ngắt đều được mặc định bị cấm sau khi reset hệ thống, và được cho phép từng nguồn riêng biệt bằng phần mềm.
Khi có hai hoặc nhiều yêu cầu ngắt đồng thời, hoặc một ngắt xảy ra trong khi một ngắt khác đang được phục vụ, hệ thống sẽ đáp ứng bằng cách xét theo vòng tuần tự và theo mức ưu tiên để định trình cho việc thực hiện các ngắt. Vòng tuần tự thì cố định, nhưng ưu tiên ngắt thì có thể lập trình được.
Cho phép và cấm các ngắt: Mỗi nguồn ngắt được cho phép hoặc cấm một cách riêng biệt qua thanh ghi cho phép ngắt (IE - Interrupt Enable) có định địa chỉ theo bit ở địa chỉ A8H. Mỗi nguồn ngắt sẽ có 1 bit trong thanh ghi IE để có thể lập trình cho phép riêng biệt, ngoài ra còn có một bit cho phép/cấm toàn bộ các ngắt (khi được xóa sẽ cấm tất cả các ngắt), như trình bày trong bảng sau:
Bit Ký hiệu Địa chỉ Chức năng (1: cho phép; 0: cấm) IE7 IE6 IE5 IE4 IE3 IE2 IE1 IE0 EA - - ES ET1 EX1 ET0 EX0 AFH AEH ADH ACH ABH AAH A9H A8H
Cho phép/cấm toàn bộ các ngắt. Không định nghĩa.
Dự phòng cho thế hệ kế tiếp. Cho phép ngắt cổng nối tiếp. Cho phép ngắt timer 1.
Cho phép ngắt ngoài cấp từ INT1. Cho phép ngắt timer 0
Cho phép ngắt ngoài INT0
Như vậy khi muốn cho phép bất kỳ ngắt nào cần phải lập hai bit: bit cho phép riêng và bit cho phép toàn bộ. Ví dụ, các ngắt từ Timer 1 được cho phép như sau: SETB ET1
; Cho phép ngắt từ Timer 1
SETB EA ; Đặt bit cho phép toàn bộ. Cũng có thể sử dụng lệnh sau: MOV IE, # 10001000B
1 Main ISR Main ISR Main ISR Main Đ áp ư ́ng n gă ́t Q ua y v ề
Mặc dù hai cách này có cùng kết quả sau khi reset hệ thống, nhưng chúng sẽ có kết quả sẽ khác nếu IE được ghi ở giữa chương trình. Cách thứ nhất không ảnh hưởng đến 5 bit khác trong thanh ghi IE, trái lại cách thứ hai sẽ xóa các bit khác. Nên khởi trị IE theo cách thứ hai thường sử dụng ở đầu chương trình (nghĩa là sau khi mở máy hoặc reset hệ thống), còn khi muốn cho phép và cấm các ngắt ngay trong chương trình thì nên dùng cách thứ nhất, để tránh ảnh hưởng đến các bit khác trong thanh ghi IE.
Ưu tiên ngắt: Mỗi nguồn ngắt được lập trình riêng vào một trong hai mức ưu tiên qua thanh ghi ưu tiên ngắt IP (Interrupt Priority) được địa chỉ theo bit ở địa chỉ B8H. Chức năng các bit của IP được mô tả trong bảng sau:
Bit Ký hiệu Địa chỉ Chức năng (1: mức ưu tiên cao; 0: mức ưu tiên thấp) IP7 IP6 IP5 IP4 IP3 IP2 IP1 IP0 - - - PS PT1 PX1 PT0 PX0 - - BDH BCH BBH BAH B9H B8H
Không định nghĩa. Không định nghĩa. Dự phòng.
Ưu tiên cho ngắt cổng nối tiếp. Ưu tiên cho ngắt timer 1. Ưu tiên cho ngắt ngoài INT1. Ưu tiên cho ngắt timer 0. Ưu tiên cho ngắt ngoài INT0.
IP mặc định bị xóa sau khi reset hệ thống để đặt tất cả các ngắt ở mức ưu tiên thấp. Việc “ưu tiên” cho phép một ISR sẽ bị ngắt bởi một ngắt khác, nếu ngắt này có độ ưu tiên cao hơn ngắt đang phục vụ. Điều này thực hiện dễ dàng trên 8051, vì chỉ có hai mức ưu tiên. Nếu một ISR có ưu tiên thấp đang thực thi, khi một ngắt có ưu tiên cao xảy ra thì ISR hiện hành sẽ bị ngắt. ISR có ưu tiên cao không thể bị ngắt.
Chương trình chính thực hiện ở mức cơ sở và không liên hệ với bất cứ ngắt nào, nó có thể luôn bị ngắt, bất chấp ưu tiên của ngắt. Nếu hai ngắt có độ ưu tiên khác nhau xảy ra đồng thời, thì ngắt có độ ưu tiên cao hơn sẽ được phục vụ trước.
Hỏi vòng tuần tự: Nếu hai ngắt cùng độ ưu tiên xảy ra đồng thời, vòng tuần tự sẽ xác định ngắt nào được phục vụ trước. Vòng tuần tự được qui định là: INT 0, Timer 0, INT 1, Timer 1, cổng nối tiếp.
Hình 8.18 minh họa các cơ chế phục vụ của 5 nguồn ngắt,. Trạng thái của tất cả các nguồn ngắt khả dụng qua các bit cờ tương ứng trong các SFR. Dĩ nhiên, nếu có bất kỳ ngắt nào bị cấm, ngắt không xảy ra, nhưng phần mềm vẫn có thể kiểm tra cờ ngắt.
1 IE0 IE1 INT0 TF0 INT1 TF1 RI TI IT0 IT1 IE IP High Priority Low Priority In te rru pt po llin g s eq ue nc e Interrupt Enable Global Enable
Các ví dụ timer và cổng nối tiếp trong các mục trước đã sử dụng các cờ ngắt, mà không gây ra ngắt thật sự.
Ngắt cổng nối tiếp có từ logic OR của ngắt thu (RI) và ngắt phát (TI). Các bit cờ tạo các ngắt được tóm tắt ở bảng sau:
Ngắt Cờ SFR và vị trí bit
INT0 INT1 Timer 1 Timer 0 Serial port Serial port IE0 IE1 TF1 TF0 TI RI TCON.1 TCON.3 TCON.7 TCON.5 SCON.1 SCON.0
• Các vector xử lý ngắt.
Khi một ngắt xảy ra và được CPU chấp nhận, chương trình chính sẽ bị ngắt quãng và những hoạt động sau xảy ra:
- Hoàn tất việc thực thi lệnh hiện hành. - Cất PC vào ngăn xếp.
- Trạng thái ngắt hiện hành được cất bên trong. - Các ngắt bị chặn ở mức ngắt.
- Nạp vào PC địa chỉ vector của ISR. - ISR thực thi.
ISR thực thi và đáp ứng các yêu cầu của ngắt. ISR hoàn tất bằng lệnh RETI (quay về từ ngắt). Điều này làm lấy lại giá trị cũ của PC từ đỉnh ngăn. Việc thực hiện chương trình chính tiếp tục ở chỗ mà nó đã bị dừng.
• Các vector ngắt.
Khi chấp nhận ngắt, giá trị được nạp vào PC được gọi là vector ngắt. Nó là địa chỉ bắt đầu của ISR cho nguồn tạo ngắt. Các vector ngắt được cho ở bảng sau:
Ngắt Cờ Địa chỉ vector ngắt
Reset INT0 Timer 0 INT1 Timer 1 Serial port RST IE0 TF0 IE1 TF1 RI hoặc TI 0000H 0003H 000BH 0013H 001BH 0023H
Vector reset hệ thống (RST ở địa chỉ 0000H) có trong bảng, vì nó giống ngắt: nó ngắt chương trình chính và nạp giá trị mới cho PC.
Khi “chỉ đến một ngắt”, cờ gây ngắt tự động được xóa bởi phần cứng. Ngoại lệ chỉ có RI và TI của các ngắt cổng nối tiếp cần được xoá bằng phần mềm. Vì có tới hai nguồn có thể xảy ra cho ngắt cổng nối tiếp, nên CPU không xóa cờ ngắt này. Các bit phải được kiểm tra trong ISR để xác định nguồn ngắt và cờ tạo ngắt sẽ được xóa bằng phần mềm. Thông thường sẽ có một lệnh rẽ nhánh thích hợp tuỳ theo nguồn ngắt.
Vì các vector ngắt ở phần đầu của bộ nhớ chương trình, nên lệnh đầu tiên của chương trình chính thường là lệnh nhảy qua vùng nhớ này, ví dụ như LJMP 0030H.
• Thiết kế chương trình dùng các ngắt.
Các ví dụ về timer và cổng nối tiếp trong các phần trước không sử dụng các ngắt, mà sử dụng các “vòng lặp đợi” để kiểm tra các cờ báo tràn (TF0 hoặc TF1), hoặc các cờ thu và phát cổng nối tiếp (TI hoặc RI). Với phương pháp này thời gian hoạt động có giá trị của CPU bị tiêu tốn trong các vòng lặp đợi các cờ tác động. Điều này hoàn toàn không thích hợp với các ứng dụng điều khiển, trong đó bộ vi điều khiển phải tương tác với nhiều thiết bị nhập và xuất đồng thời.
Phần này sẽ khảo sát cách phát triển chương trình dùng ngắt để điều khiển. Mỗi chương trình bắt đầu tại địa chỉ 0000H, với giả thiết rằng nó sẽ bắt đầu thực hiện sau khi reset hệ thống. Khung một chương trình độc lập dùng ngắt có thể thực hiện như sau:
ORG 0000H ; Điểm vào reset LJMP MAIN
… ; Các điểm vào ISR
ORG 0030H ; Điểm vào chương trình chính …
MAIN: ; Bắt đầu chương trình chính. …
…
Lệnh đầu tiên nhảy đến địa chỉ 0030H, vừa qua các vị trí vector bắt đầu của các ISR. Như trình bày trên hình 8.19, chương trình chính bắt đầu ở địa chỉ 0030H.
Các chương trình phục vụ ngắt có kích thước nhỏ: Các chương trình phục vụ ngắt phải bắt đầu ở gần phần đầu của bộ nhớ chương trình, dưới các địa chỉ trong bảng Các vector ngắt. Mặc dù chỉ có 8 byte ở giữa các điểm vào ngắt, có thể đủ để thực hiện hoạt động phục vụ ngắt mong muốn và quay về chương trình chính.
Nếu chỉ có một nguồn ngắt được sử dụng, ví dụ Timer 0, thì có thể sử dụng một khung chương trình như sau:
ORG 0000H ; Reset
LJMP MAIN
ORG 000BH ; Điểm vào Timer 0
T0ISR: ; Bắt đầu ISR cho Timer 0
RETI ; Quay về chương trình chính
MAIN: ; Chương trình chính
Nếu sử dụng nhiều ngắt, cần phải bảo đảm mỗi chương trình phục vụ ngắt bắt đầu ở đúng địa chỉ đã qui định cho nó, và không chạy quá sang ISR kế. Trong ví dụ trên, vì chỉ có một ngắt được sử dụng, chương trình chính có thể bắt đầu ngay sau lệnh RETI.
Các chương trình phục vụ ngắt có kích thước lớn: Nếu ISR dài hơn 8 byte, cần có lệnh nhảy để chuyển nó tới nơi khác trong bộ nhớ chương trình, nếu không nó sẽ đi lố qua điểm vào của ngắt kế. Với lệnh nhảy đến vùng nhớ khác, có thể mở rộng chiều dài ISR. Ví dụ chỉ xét Timer 0, có thể sử dụng khung chương trình sau:
ORG 0000H ; Điểm bắt đầu sau khi reset LJMP MAIN
ORG 000BH ; Điểm bắt đầu của ngắt Timer 0 LJMP T0ISR
ORG 0030H ; Dưới các vector ngắt tiếp theo MAIN:
1 Bộ nhớ chương
trình bên ngoài.
Chương trình chính
Điểm bắt đầu sau khi reset và khi xảy ra các
ngắt LJMP main
Hình 8.19: Tổ chức bộ nhớ chương trình khi sử dụng các ngắt. 002F
0030 FFFF
…
T0ISR: ; ISR cho Timer 0
…
RETI ; Quay về chương trình chính.
Xét một chương trình đơn giản nhất không thực hiện gì cả, chương trình chính chỉ khởi động các timer, cổng nối tiếp và các thanh ghi ngắt sau đó không làm gì nữa. Công việc hoàn toàn được thực hiện trong các ISR. Sau các lệnh khởi động, chương trình chính chứa
lệnh: HERE: SJMP HERE
Hay dạng viết gọn như sau: SJMP $
Khi ngắt xảy ra, chương trình chính bị ngắt quãng tạm thời, trong khi ISR thực hiện. Lệnh RETI ở cuối ISR trả điều khiển về chương trình chính và nó tiếp tục không làm gì cả. Trong nhiều ứng dụng điều khiển, nhiều công việc thật ra được thực hiện hoàn toàn trong các ISR. Khi cần các công việc thực hiện ngoài ngắt, lệnh SJMP $ (HERE: SJMP HERE) có thể được thay thế bằng các lệnh cần thực hiện trong ứng dụng.
• Các ngắt của 8051.
Các ngắt timer: Các ngắt timer có địa chỉ vector ngắt là 000BH (Timer 0) và 001BH (Timer 1). Ngắt timer xảy ra khi các thanh ghi timer (TLx/THx) tràn và set cờ báo tràn (TFx) lên 1.
Chú ý rằng các cờ timer (TFx) không cần phải xóa bằng phần mềm. Khi cho phép các ngắt, TFx tự động bị xóa bằng phần cứng khi CPU chuyển đến ISR.
Các ngắt cổng nối tiếp: Ngắt cổng nối tiếp xảy ra khi hoặc cờ ngắt phát (TI) hoặc cờ ngắt thu (RI) được đặt lên 1. Ngắt phát xảy ra khi truyền xong một ký tự vừa được ghi vào SBUF. Ngắt thu xảy ra khi một ký tự đã được nhận xong và đang đợi trong SBUF để được đọc.
Ngắt cổng nối tiếp khác với ngắt timer là cờ gây ra ngắt không bị xóa bằng phần cứng khi CPU chuyển tới ISR. Nguyên nhân là do có hai nguồn ngắt cổng nối tiếp: TI và RI. Nguồn ngắt phải được xác định trong ISR và cờ tạo ngắt cần được xóa bằng phần mềm.
Các ngắt ngoài: Các ngắt ngoài xảy ra khi có một mức thấp hoặc cạnh xuống trên chân INT0 hoặc INT1 của 8051. Các cờ tạo các ngắt này là các bit IE0 và IE1 trong TCON. Khi quyền điều khiển đã chuyển đến ISR, cờ tạo ra ngắt chỉ được xóa nếu ngắt được tích cực bằng cạnh xuống. Nếu ngắt được tích cực theo mức, thì mức cao của nguồn yêu cầu ngắt bên xoá cờ thay cho phần cứng.
Việc chọn ngắt tích cực mức thấp hay tích cực cạnh xuống được lập trình qua các bit IT0 và IT1 trong TCON. Ví dụ, nếu IT1 = 0, ngắt ngoài 1 được kích khởi bằng mức thấp ở chân INT1. Nếu IT1 = 1, ngắt ngoài 1 sẽ được kích khởi bằng cạnh xuống. Trong chế độ này, nếu các mẫu liên tiếp trên chân INT1 chỉ mức cao trong một chu kỳ và thấp trong chu kỳ kế, cờ yêu cầu ngắt IE1 trong TCON được đặt lên 1. Rồi bit cờ IE1 yêu cầu ngắt.
Vì các chân ngắt ngoài được lấy mẫu một lần ở mỗi chu kỳ máy, ngõ vào cần được giữ trong tối thiểu 12 chu kỳ dao động để bảo đảm lấy mẫu đúng. Nếu ngắt ngoài được tác động theo cạnh xuống, nguồn bên ngoài phải giữ chân yêu cầu cao tối thiểu 1 chu kỳ và giữ nó ở mức thấp thêm một chu kỳ nữa để bảo đảm phát hiện được cạnh xuống. IE0 và IE1 tự động được xóa khi CPU chuyển tới ngắt.
Nếu ngắt ngoài được tác động theo mức, nguồn bên ngoài phải giữ yêu cầu tác động cho đến khi ngắt được yêu cầu thật sự được tạo ra. Rồi nó phải không tác động yêu cầu trước khi ISR được hoàn tất, nếu không một ngắt khác sẽ được lặp lại. Thông thường khi vào ISR người ta làm nguồn yêu cầu đưa tín hiệu tạo ngắt về trạng thái không tác động.
2.3 TẬP LỆNH VÀ HƯỚNG DẪN LẬP TRÌNH TRÊN 8051.
Các chương trình được cấu tạo từ nhiều lệnh nối tiếp nhau, một chương trình thường cần được xây dựng một cách logic nhất, để có thể thực hiện một cách nhanh chóng hiệu quả nhất. Cũng như các bộ vi xử lý thông thường, mỗi họ vi điều khiển có một tập lệnh riêng của nó.
Tập lệnh của 8051 được xây dựng tối ưu cho các ứng dụng điều khiển 8 bit, nó có các chế