Giáo trình Vi điều khiển - Chương 3: Các lệnh nhảy, vòng lặp và lệnh gọi

Trong một chuỗi lệnh cần thực hiện thường có nhu cần cần chuyển điều
khiển chương trình đến một vị trí khác. Có nhiều lệnh để thực hiện điều này
trong 8051, ở chương này ta sẽ tìm hiểu các lệnh chuyển điều khiển có trong
hợp ngữ của 8051 như các lệnh sử dụng cho vòng lặp, các lệnh nhảy có và
không có điều khiển, lệnh gọi và cuối cùng là mô tả về một chương trình con
giữ chậm thời gian 

pdf 16 trang thamphan 27/12/2022 1880
Bạn đang xem tài liệu "Giáo trình Vi điều khiển - Chương 3: Các lệnh nhảy, vòng lặp và lệnh gọi", để tải tài liệu gốc về máy hãy click vào nút Download ở trên.

File đính kèm:

  • pdfgiao_trinh_vi_dieu_khien_chuong_3_cac_lenh_nhay_vong_lap_va.pdf

Nội dung text: Giáo trình Vi điều khiển - Chương 3: Các lệnh nhảy, vòng lặp và lệnh gọi

  1. CHƯƠNG 3 Các lệnh nhảy, vòng lặp và lệnh gọi Trong một chuỗi lệnh cần thực hiện thường có nhu cần cần chuyển điều khiển chương trình đến một vị trí khác. Có nhiều lệnh để thực hiện điều này trong 8051, ở chương này ta sẽ tìm hiểu các lệnh chuyển điều khiển có trong hợp ngữ của 8051 như các lệnh sử dụng cho vòng lặp, các lệnh nhảy có và không có điều khiển, lệnh gọi và cuối cùng là mô tả về một chương trình con giữ chậm thời gian. 3.1 Vòng lặp và các lệnh nhảy. 3.1.1 Tạo vòng lặp trong 8051. Qúa trình lặp lại một chuỗi các lệnh với một số lần nhất định được gọi là vòng lặp. Vòng lặp là một trong những hoạt động được sử dụng rộng rãi nhất mà bất kỳ bộ vi sử lý nào đều thực hiện. Trong 8051 thì hoạt động vòng lặp được thực hiện bởi lệnh “DJNZ thanh ghi, nhãn”. Trong lệnh này thanh ghi được giảm xuống, nếu nó không bằng không thì nó nhảy đến địa chỉ đích được tham chiếu bởi nhãn. Trước khi bắt đầu vòng lặp thì thanh ghi được nạp với bộ đếm cho số lần lặp lại. Lưu ý rằng, trong lệnh này việc giảm thanh ghi và quyết định để nhảy được kết hợp vào trong một lệnh đơn. Ví dụ 3.1: Viết một chương trình để: a) xoá ACC và sau đó b) cộng 3 vào ACC 10 lần. Lời giải: MOV A, #0 ; Xoá ACC, A = 0 MOV R2, #10 ; Nạp bộ đếm R2 = 10 BACK: ADD A, #03 ; Cộng 03 vào ACC DJNZ R2, AGAIN ; Lặp lại cho đến khi R2 = 0 (10 lần) MOV R5, A ; Cắt A vào thanh ghi R5 Trong chương trình trên đây thanh ghi R2 được sử dụng như là bộ đếm. Bộ đếm lúc đầu được đặt bằng 10. Mỗi lần lặp lại lệnh DJNZ giảm R2 không bằng 0 thì nó nhảy đến địa chỉ đích gắn với nhãn “AGAIN”. Hoạt động lặp lại này tiếp tục cho đến khi R2 trở về không. Sau khi R2 = 0 nó thoát khỏi vòng lặp và thực hiện đứng ngay dưới nó trong trường hợp này là lệnh “MOV R5, A”. Lưu ý rằng trong lệnh DJNZ thì các thanh ghi có thể là bất kỳ thanh ghi nào trong các thanh ghi R0 - R7. Bộ đếm cũng có thể là một ngăn nhớ trong RAM như ta sẽ thấy ở chương 5. Ví dụ 3.2: Số lần cực đại mà vòng lặp ở ví dụ 3.1 có thể lặp lại là bao nhiêu? Lời giải:
  2. JZ OVER ; Nhảy đến OVER nếu A = 0 MOV A, R1 ; Nạp giá trị của R1 vào A JZ OVER ; Nhảy đến OVER nếu A = 0 OVER Trong chương trình này nếu R0 hoặc R1 có giá trị bằng 0 thì nó nhảy đến địa chỉ có nhãn OVER. Lưu ý rằng lệnh JZ chỉ có thể được sử dụng đối với thanh ghi A. Nó chỉ có thể kiểm tra xem thanh ghi A có bằng không không và nó không áp dụng cho bất kỳ thanh ghi nào khác. Quan trọng hơn là ta không phải thực hiện một lệnh số học nào như đếm giảm để sử dụng lệnh JNZ như ở ví dụ 3.4 dưới đây. Ví dụ 3.4: Viết một chương trình để xác định xem R5 có chứa giá trị 0 không? Nếu bằng thì nạp nó cho giá trị 55H. Lời giải: MOV A, R5 ; Sao nội dung R5 vào A JNZ NEXT ; Nhảy đến NEXT nếu A không bằng 0 MOV R5, #55H ; NEXT: b- Lệnh JNC (nhảy nếu không có nhớ, cờ CY = 0). Trong lệnh này thì bit cờ nhớ trong thanh ghi cờ PSW được dùng để thực hiện quyết định nhảy. Khi thực hiện lệnh “JNC nhãn” thì bộ xử lý kiểm tra cờ nhớ xem nó có được bật không (CY = 1). Nếu nó không bật thì CPU bắt đầu nạp và thực hiện các lệnh từ địa chỉ của nhãn. Nếu cờ CY = 1 thì nó sẽ không nhảy và thực hiện lệnh kế tiếp dưới JNC. Cần phải lưu ý rằng cũng có lệnh “JC nhãn”. Trong lệnh JC thì nếu CY = 1 nó nhảy đến địa chỉ đích là nhãn. Ta sẽ xét các ví dụ về các lệnh này trong các ứng dụng ở các chương sau. Ngoài ra còn có lệnh JB (nhảy nếu bit có mức cao) và JNB (nhảy nếu bit có mức thấp). Các lệnh này được trình bày ở chương 4 và 8 khi nói về thao tác bit. Bảng 3.1: Các lệnh nhảy có điều kiện. Lệnh Hoạt động JZ Nhảy nếu A = 0 JNZ Nhảy nếu A ≠ 0 DJNZ Giảm và nhảy nếu A = 0 CJNE A, byte Nhảy nếu A ≠ byte
  3. Hãy nhớ rằng, mặc dù bộ đếm chương trình trong 8051 là 16 bit, do vậy cho không gian địa chỉ là 64k byte, nhưng bộ nhớ chương trình ROM trên chíp lớn như vậy. 8051 đầu tiên chỉ có 4k byte ROM trên chíp cho không gian chương trình, do vậy mỗi byte đều rất quý giá. Vì lý do đó mà có cả lệnh nhảy gần SJMP chỉ có 2 byte so với lệnh nhảy xa LZ0MP dài 3 byte. Điều này có thể tiết kiệm được một số byte bộ nhớ trong rất nhiều ứng dụng mà không gian bộ nhớ có hạn hẹp. b- Lệnh nhảy gồm SJMP. Trong 2 byte này thì byte đầu tiên là mã lệnh và byte thứ hai là chỉ tương đối của địa chỉ đích. Đích chỉ tương đối trong phạm vi 00 - FFH được chia thành các lệnh nhảy tới và nhảy lùi: Nghĩa là -128 đến +127 byte của bộ nhớ tương đối so với địa chỉ hiện thời của bộ đếm chương trình. Nếu là lệnh nhảy tới thì địa chỉ đích có thể nằm trong khoảng 127 byte từ giá trị hiện thời của bộ đếm chương trình. Nếu địa chỉ đích ở phía sau thì nó có thể nằm trong khoảng -128 byte từ giá trị hiện hành của PC. 3.1.5 Tính toán địa chỉ lệnh nhảy gần. Ngoài lệnh nhảy gần SJMP thì tất cả mọi lệnh nhảy có điều kiện như JNC, JZ và DJNZ đều là các lệnh nhảy gần bởi một thực tế là chúng đều lệnh 2 byte. Trong những lệnh này thì byte thứ nhất đều là mã lệnh, còn byte thứ hai là địa chỉ tương đối. Địa chỉ đích là tương đối so với giá trị của bộ đếm chương trình. Để tính toán địa chỉ đích byte thứ hai được cộng vào thanh ghi PC của lệnh đứng ngay sau lệnh nhảy. Để hiểu điều này hãy xét ví dụ 3.6 dưới đây. Ví dụ 3.6: Sử dụng tệp tin liệt kê dưới đây hãy kiểm tra việc tín toán địa chỉ nhảy về trước. 01 0000 ORG 0000 02 0000 7800 MOV R0, #0 03 0002 7455 MOV A, #55H 04 0004 6003 JZ NEXT 05 0006 08 INC R0 06 0007 04 AGAIN: INC A 07 0008 04 INC A 08 0009 2477 NEXT: ADD A, #77h 09 000B 5005 JNC OVER 10 000D E4 CLR A 11 000E F8 MOV R0, A 12 000F F9 MOV R1, A 13 0010 FA MOV R2, A 14 0011 FB MOV R3, A 15 0012 2B OVER: ADD A, R3
  4. công việc cần phải được thực hiện thường xuyên. Điều này làm cho chương trình trở nên có cấu trúc hơn ngoài việc tiết kiệm được thêm không gian bộ nhớ. Trong 8051 có 2 lệnh để gọi đó là: Gọi xa LCALL và gọi tuyệt đối ACALL mà quyết định sử dụng lệnh nào đó phụ thuộc vào địa chỉ đích. 3.2.1 Lệnh gọi xa LCALL. Trong lệnh 3 byte này thì byte đầu tiên là mã lệnh, còn hai byte sau được dùng cho địa chỉ của chương trình con đích. Do vậy LCALL có thể được dùng để gọi các chương trình con ở bất kỳ vị trí nào trong phạm vi 64k byte, không gian địa chỉ của 8051. Để đảm bảo rằng sau khi thực hiện một chương trình được gọi để 8051 biết được chỗ quay trở về thì nó tự động cất vào ngăn xếp địa chỉ của lệnh đứng ngay sau lệnh gọi LCALL. Khi một chương trình con được gọi, điều khiển được chuyển đến chương trình con đó và bộ xử lý cất bộ đếm chương trình PC vào ngăn xếp và bắt đầu nạp lệnh vào vị trí mới. Sau khi kết thúc thực hiện chương trình con thì lệnh trở về RET chuyển điều khiển về cho nguồn gọi. Mỗi chương trình con cần lệnh RET như là lệnh cuối cùng (xem ví dụ 3.8). Các điểm sau đây cần phải được lưu ý từ ví dụ 3.8. 1. Lưu ý đến chương trình con DELAY khi thực hiện lệnh “LCALL DELAY” đầu tiên thì địa chỉ của lệnh ngay kế nó là “MOV A, #0AAH” được đẩy vào ngăn xếp và 8051 bắt đầu thực hiện các lệnh ở địa chỉ 300H. 2. Trong chương trình con DELAY, lúc đầu bộ đếm R5 được đặt về giá trị 255 (R5 = FFH). Do vậy, vòng lặp được lặp lại 256 lần. Khi R5 trở về 0 điều khiển rơi xuống lệnh quay trở về RET mà nó kéo địa chỉ từ ngăn xếp vào bộ đếm chương trình và tiếp tục thực hiện lệnh sau lệnh gọi CALL. Ví dụ 3.8: Hãy viết một chương trình để chốt tất cả các bit của cổng P1 bằng cách gửi đến nó giá trị 55H và AAH liên tục. Hãy đặt một độ trễ thời gian giữa mỗi lần xuất dữ liệu tới cổng P1. Chương trình này sẽ được sử dụng để kiểm tra các cổng của 8051 trong chương tiếp theo. Lời giải: ORG 0000 BACK: MOV A, #55H ; Nạp A với giá trị 55H MOV P1, A ; Gửi 55H đến cổng P1 LCALL DELAY ; Tạo trễ thời gian MOV A, #0AAH ; Nạp A với giá trị AAH MOV P1, A ; Gửi AAH đến cổng P1 LCALL DELAY ; Giữ chậm SJMP BACK ; Lặp lại vô tận ; - Đây là chương trình con tạo độ trễ thời gian
  5. 010 0010 ; Đây là chương trình con giữ chậm 011 0300 MOV 300H 012 0300 DELAY: 013 0300 7DFF MOV R5, #FFH ; Nạp R5 = 255 014 0302 DDFE AGAIN:DJNZ R5, AGAIN ; Dừng ở đây 015 0304 22 RET ; Trở về nguồn gọi 016 0305 END ; Kết thúc nạp tin hợp ngữ Lời giải: Khi lệnh LCALL đầu tiên được thực hiện thì địa chỉ của lệnh “MOV A, #0AAH” được cất vào ngăn xếp. Lưu ý rằng byte thấp vào trước và byte cao vào sau. Lệnh cuối cùng của chương trình con được gọi phải là lệnh trở về RET để chuyển CPU kéo (POP) các byte trên đỉnh của ngăn xếp vào bộ đếm chương trình PC và tiếp tục thực hiện lệnh tại địa chỉ 07. Sơ đồ bên chỉ ra khung của ngăn xếp sau lần gọi LCALL đầu tiên. 0A 09 00 08 07 SP = 09 3.2.3 Sử dụng lệnh PUSH và POP trong các chương trình con. Khi gọi một chương trình con thì ngăn xếp phải bám được vị trí mà CPU cần trở về. Sau khi kết thúc chương trình con vì lý do này chúng ta phải cẩn thận mỗi khi thao tác với các nội dung của ngăn xếp. Nguyên tắc là số lần đẩy vào (PUSH) và kéo ra (POP) luôn phải phù hợp trong bất kỳ chương trình con được gọi vào. Hay nói cách khác đối với mỗi lệnh PUSH thì phải có một lệnh POP. Xem ví dụ 3.10. 3.2.4 Gọi các chương trình con. Trong lập trình hợp ngữ thường có một chương trình chính và rất nhiều chương trình con mà chúng được gọi từ chương trình chính. Điều này cho phép ta tạo mới chương trình con trong một mô-đun riêng biệt. Mỗi mô-đun có thể được kiểm tra tách biệt và sau đó được kết hợp với nhau cùng với chương trình chính. Quan trọng hơn là trong một chương trình lớn thì các mô- đun có thể được phân cho các lập trình viên khác nhau nhằm rút ngắn thời gian phát triển. Ví dụ 3.10: Phân tích ngăn xếp đối với lệnh LCALL đầu tiên trong đoạn mã. 01 0000 ORG 0
  6. Sau lệnh LCALL thứ Sau lệnh PUSH 4 Sau lệnh POSH 5 nhất 0B 0B 0B 67 R5 0A 0A 99 0A 09 R4 R4 09 00 09 00 09 00 PCH PCH PCL 08 0B 0B 0B 08 0B PCL PCL PCL Cần phải nhấn mạnh rằng trong việc sử dụng LCALL thì địa chỉ đích của các chương trình con có thể ở đâu đó trong phạm vi 64k byte không gian bộ nhớ của 8051. Điều này không áp dụng cho tất cả mọi lệnh gọi CALL chẳng hạn như đối với ACALL dưới đây: ; MAIN program calling subroutines ORG 0 MAIN: LCALL SUBR-1 LCALL SUBR-2 LCALL SUBR-3 HERE: SJMP MAIN ; end of MAIN ; SUBR-1l RET ; end of subroutinel 1 ; SUBR-1l RET ; end of subroutinel 2 ; SUBR-1l RET ; end of subroutinel 3 END ; end of the asm file Hình 3.1: Chương trình chính hợp ngữ của 8051 có gọi các chương trình con.
  7. 3.3.1 Chu kỳ máy: Đối với CPU để thực hiện một lệnh thì mất một chu kỳ đồng hồ này được coi như các chu kỳ máy. Phụ lục AppendixA.2 cung cấp danh sách liệt kê các lệnh 8051 và các chu kỳ máy của chúng. Để tính toán một độ trễ thời gian, ta sử dụng danh sách liệt kê này. Trong họ 8051 thì độ dài của chu kỳ máy phụ thuộc vào tần số của bộ dao động thạch anh được nối vào hệ thống 8051. Bộ dao động thạch anh cùng với mạch điện trên chip cung cấp xung đồng hồ cho CPU của 8051 (xem chương 4). Tần số của tinh thể thạch anh được nối tới họ 8051 dao động trong khoảng 4MHz đến 30 MHz phụ thuộc vào tốc độ chíp và nhà sản xuất. Thường xuyên nhất là bộ dao động thạch anh tần số 11.0592MHz được sử dụng để làm cho hệ 8051 tương thích với cổng nối tiếp của PC IBM (xem chương 10). Trong 8051, một chu kỳ máy kéo dài 12 chu kỳ dao động. Do vậy, để tính toán chu kỳ máy ta lấy 1/12 của tần số tinh thể thạch anh, sau đó lấy giá trị nghịch đảo như chỉ ra trong ví dụ 3.13. Ví dụ 3.13: Đoạn mã dưới đây trình bày tần số thạch anh cho 3 hệ thống dựa trên 8051 khác nhau. Hãy tìm chu kỳ máy của mỗi trường hợp: a) 11.0592MHz b) 16MHz và c) 20MHz. Lời giải: a) 11.0592/12 = 921.6kHz; Chu kỳ máy là 1/921.6kHz = 1.085μs (micro giây) b) 16MHz/12 = 1.333MHz; Chu kỳ máy MC = 1/1.333MHz = 0.75μs c) 20MHz/12 = 1.66MHz ⇒ MC = 1/1.66MHz = 0.60μs Ví dụ 3.14: Đối với một hệ thống 8051 có 11.0592MHz hãy tìm thời gian cần thiết để thực hiện các lệnh sau đây. a) MOV R3, #55 b) DEC R3 c) DJNZ R2 đích d) LJMP e) SJMP f) NOP g) MUL AB Lời giải: Chu kỳ máy cho hệ thống 8051 có tần số đồng hồ là 11.0592MHz Là 1.085μs như đã tính ở ví dụ 3.13. Bảng A-1 trong phụ lục Appendix A trình bày số chu kỳ máy đối với các lệnh trên. Vậy ta có: Lệnh Chu kỳ Thời gian thực hiện máy (a) MOV R3, #55 1 1 × 1.085 μs = 1.085 μs
  8. tốn thời gian một cách đơn giản. Điều này được chỉ ra trong ví dụ 3.16 dưới đây. Ví dụ 3.16: Hãy tìm độ trễ thời gian cho chương trình con sau. Giả thiết tần số dao động thạch anh là 11.0592MHz. Số chu kỳ máy DELAY: MOV R3, #250 1 HERE : NOP 1 NOP 1 NOP 1 NOP 1 DJNZ R3, HERE 2 RET 1 Lời giải: Thời gian trễ bên trong vòng lặp HERE là [250 (1 + 1 + 1 + 1 + 1 + 2)] × 1.0851μs = 1627.5μs. Cộng thêm hai lệnh ngoài vòng lặp ta có 1627.5μs × 1.085μs = 1629.67μs. 3.3.3 Độ trễ thời gian của vòng lặp trong vòng lặp. Một cách khác để nhận được giá trị từ độ trễ lớn là sử dụng một vòng lặp bên trong vòng lặp và cũng được gọi là vòng lặp lồng nhau. Xem ví dụ 3.17 dưới đây. Ví dụ 3.17: Đối với một chu kỳ máy 1.085μs hãy tính thời gian giữ chậm trong chương trình con sau: DELAY: chu kỳ máy MOV R2, #200 1 AGAIN: MOV R3, #250 1 HERE: NOP 1 NOP 1 DJNZ R3, HERE 2 DJNZ R2, AGAIN 2 RET 1 Lời giải: