1/03/2018

Tính toán vòng lặp tạo trễ cho vi điều khiển họ 8051 bằng code Assembly

    Vòng lặp tạo trễ là một đoạn mã thường xuyên xuất hiện trong các chương trình hợp ngữ Assembly dùng trong các hệ thống nhúng vận hành theo thời gian thực. Ví dụ: khi cần xuất điện áp dương ở chân P1.0 trong khoảng thời gian là 1s sau đó ngắt thì cần thời gian duy trì 1s ấy vi điều khiển giữ mức điện áp dương trước khi ngắt.



   Có hai cách để tạo trễ:
   1. Lập trình các đoạn mã chạy đi chạy lại để "câu" thời gian.
   2. Sử dụng bộ định thời.

   Để dễ hiểu ta xét đề bài: Tạo xung dao động tần số 1Hz xuất ra tại chân P1.0 bằng vi điều khiển 8051, lập trình bằng hợp ngữ Assembly với tần số thạch anh là 11,0592 MHz.
   - Với dao động có tần số là 1Hz tức là ta sẽ lập trình xuất xung 1 lần trong 1 giây, theo yêu cầu đề bài tạm định xung sẽ được duy trì theo 1 lệnh tức 1 chu kỳ máy, thời gian nghỉ giữa hai xung sẽ là 1s. Vậy ta cần thời gian trễ 1s.
   - 1 chu kỳ máy bằng 1/12 chu kỳ dao động của thạch anh. Với tần số thạch anh theo như đề bài là 11,0592MHz ta có tần số máy là 11,0592/12 = 0,9216MHz, chu kỳ máy là 1/0.9216 ≈ 1,085μs. Vậy để trễ 1s ta cần khoảng 1 triệu chu kỳ máy.
   - Mỗi lệnh cần số chu kỳ máy khác nhau để thực thi, để biết số chu kỳ máy tương ứng với mỗi lệnh ta có thể tra ở đây.
   - Sở dĩ tần số thạch anh hay được chọn là 11,0592MHz do để 8051 tương thích với cổng nối tiếp của IBM PC.

   1. Sử dụng hàm tạo trễ thông qua vòng lặp

   Các bước thực hiện chương trình: khai báo địa chỉ bắt đầu chương trình → nạp giá trị 01H vào thanh ghi A → gán giá trị thanh ghi A vào ngăn nhớ P1 (xuất giá trị ra cổng P1, bật chân P1.0) → nạp giái trị 00H vào thanh ghi A → gán giá trị thanh ghi A vào ngăn nhớ P1 (xuất giá trị ra cổng P1, tắt chân P1.0) → gọi hàm DELAY → nhẩy tới đầu chương trình, lặp lại chu kỳ.
   Hàm DELAY: nạp giá trị 7H vào thanh ghi R0 → nạp giá trị FFH vào thanh ghi R1 (1) → nạp giá trị FFH vào thanh ghi R2 (2) → dùng lệnh DJNZ giảm giá trị R2 tới 0 thoát khỏi vòng lặp → dùng lệnh DJNZ giảm giá trị thanh ghi R1, nhẩy tới (2) → dùng lệnh DJNZ giảm giá trị R0, nhẩy tới (1) → thoát khỏi vòng lặp.
   Code:
                                               ORG        00H                  ; đặt địa chỉ bộ nhớ chương trình
                            LOOP:       MOV       A, #01H            ; đặt giá trị 01H vào thanh ghi A
                                               MOV       P1, A                ; đưa A vào thanh ghi P1, cổng P1 sẽ được bật
                                               MOV        A, #00H          ; đặt giá trị 00H vào thanh ghi A
                                               MOV        P1, A               ; đưa A vào thanh ghi P1, cổng P1 sẽ được tắt
                                               ACALL    DELAY           ; gọi hàm DELAY
                                               SJMP        LOOP             ; nhẩy tới nhãn LOOP lặp lại chương trình
                                               RET                                  ; thoát khỏi vòng lặp
                            DELAY:                                              ; hàm DELAY 
                                               MOV        R0, #06H        ; nạp giá trị 07H vào thanh ghi R0
                            AGAIN1:   MOV        R1, #FFH        ; nạp 256 (FFh) vào thanh ghi R1
                            AGAIN:     MOV        R2, #FFH        ; nạp 256 vào thanh ghi R2
                            HERE:       DJNZ        R2, HERE       ; giảm dần R2, mỗi lần giảm nhẩy tới HERE
                                               DJNZ        R1, AGAIN    ; giảm dần R1, mỗi lần giảm nhẩy tới AGAIN
                                               DJNZ        R0, AGAIN1  ; giảm dần R2, mỗi lần nhẩy tới AGAIN1
                                               RET                                  ; thoát khỏi vòng lặp
                                               END                                 ; kếp thúc chương trình

   Giải thích hàm DELAY:
   - Như trên đã nói ta có thể tra bảng để tìm số chu kỳ máy các lệnh cần để thực thì, trong hàm DELAY thì lệnh MOV tốn 1 chu kỳ máy, hàm DJNZ tốn 2 chu kỳ máy.
   - Xét câu lệnh:
HERE:       DJNZ        R2, HERE
   Lệnh này có nghĩa là giảm giá trị thanh ghi R2 sau đó nhẩy tới nhãn HERE, vì trước đó ta đã nhập R2 giá trị FFH ở câu lệnh: "MOV        R2, #FFH". Nên câu lệnh trên lặp lại 256 lần, tổng cộng 256 x 2 = 512 chu kỳ máy.
   - Xét câu lệnh:
DJNZ        R1, AGAIN
   Tương tự như câu lệnh trên ta có 512 lần chu kỳ máy. Nhãn AGAIN được đặt ở câu lệnh: "AGAIN:     MOV        R2, #FFH" tức là mỗi lần lặp lại nạp cho R2 giá trị FFh vì R1 được nạp giá trị FFh ở trên ta có 256 x 1 = 256 chu kỳ máy.
   Tiếp đó chương trình thực hiện câu lệnh ngay sau là: "DJNZ        R2, HERE", lệnh này được lặp 256 mỗi lần tốn 512 chu kỳ máy như trên, tổng cộng là 512 x 216 = 131.072 chu kỳ máy
   - Tương tự như vậy với câu lệnh:
DJNZ        R2, AGAIN1
   Ta có thêm 6 (giá trị nạp cho R0) là 12 chu kỳ máy + cụm lệnh trên được lặp lại 7 lần nữa.
   - Tổng cộng:
3 (từ khai báo ban đầu) + 512 (lặp đầu tiên chỗ R0) + 256(2+1+512)(lặp chỗ R1) + 6(2+1+1+512+256(2+1+512))(lặp chỗ R2) = 926.491 chu kỳ
   - Vậy: 926.491 x 1.085 = 1.005.242,735 μs ≈ 1s
   *Ai muốn tìm kết quả chính xác hơn thì chịu khó thay đổi giá trị nạp vào các thanh ghi nhé, tính cái này hại óc muốn chết hà!

   2. Tạo trễ bằng bộ định thời

   Phần lý thuyết về bộ định thời tôi không nhắc lại nữa cần lưu ý đến giá trị các thanh ghi điều khiển, thanh ghi giá trị của bộ định thời.
   Với mỗi lần bộ đếm lên thêm một đơn vị tốn 1 chu kỳ máy, tương ứng với 1,085 μs. 
Khi cho chạy từ 0000 ➡ FFFF ta có 65.535 chu kỳ máy.
   Code:
                                               ORG        00H                  ; đặt địa chỉ bộ nhớ chương trình
                            LOOP:       MOV       A, #01H            ; đặt giá trị 01H vào thanh ghi A
                                               MOV       P1, A                ; đưa A vào thanh ghi P1, cổng P1 sẽ được bật
                                               MOV        A, #00H          ; đặt giá trị 00H vào thanh ghi A
                                               MOV        P1, A               ; đưa A vào thanh ghi P1, cổng P1 sẽ được tắt
                                               ACALL    DELAY           ; gọi hàm DELAY
                                               SJMP        LOOP             ; nhẩy tới nhãn LOOP lặp lại chương trình
                                               RET                                  ; thoát khỏi vòng lặp
                            DELAY:  
                                               MOV        TMOD, #10H  ; đặt chế độ 1, timer 1
                            AGAIN:     MOV        R1, #EH           ; đặt số lần bộ định thời chạy (15 lần)
                                               MOV        TL1, #00H       ; đặt giá trị 0 cho các bit thấp timer1
                                               MOV        TH1, #00H       ; đặt giá trị 0 cho các bit cao timer1
                                               SETB        TR1                  ; khởi chạy bộ định thời
                            BACK:      JNB           TF1, BACK      ; lặp tại chỗ chờ cờ tràn bật khi tới FFFF
                                              CLR           TR1                  ; tắt bộ định thời
                                              CLR           TF1                   ; xóa cờ tràn
                                              DJNZ          R3, AGAIN     ; lặp lại
                                              RET
                                              END

   Toàn AGAIN được lặp lại 15 lần nên ta có số chu kỳ được tính là:
1 + 14(1+1+1+1+2+65.535+1+1+2) =  917.631 chu kỳ máy
   Thời gian: 617.631 x 1,085 ≈ 1s
   Nếu cần tính xác hơn hay thay đổi các tham số các bạn chọn lại giá trị nạp vào R1, TL1, TH1 cho phù hợp.
   Việc sử dụng bộ đình thời có thể kết hợp với ngắt code sẽ uyển chuyển hơn, nhịp nhàng hơn. (cái này chưa nắm chắc không dám viết bài, khi nào sẽ viết sau)
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
    Nhìn chung tính toán bằng bộ định thời đơn gian hơn, hơn nữa ở các chương trình phức tạp trong lúc bộ định thời đếm vi xử lý có thể tiếp tục thực hiện các lệnh khác. Tuy vậy việc sử dụng bộ định thời cần nắm chắc kiến thức hơn, thực tế chạy lại không hay chính xác lắm. Vì vậy, với các yêu cầu đơn giản người ta thường dùng phương án 1 hơn.

3 comments:

  1. tks ad nha. dù thầy cô có dạy nhưng mình vẫn muốn tìm kiếm trên mạng. Blog đỉnh đó!

    ReplyDelete
  2. delay trên sai giá trị R0 với R2
    delay dưới thiếu giá trị R3

    ReplyDelete
  3. Có nhầm đau ko bạn? Biên dịch nạp thử delay 1s đầu tiên nó chớp quá nhanh cảm giác 1/4 giây

    ReplyDelete