Tải bản đầy đủ (.pdf) (22 trang)

Ch3pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (211.97 KB, 22 trang )

<span class='text_page_counter'>(1)</span><div class='page_container' data-page=1>

<b>CHƯƠNG 3: LẬP TRÌNH HỢP NGỮ HỌ MCS-51 </b>



Tại phần trước chúng ta đã chỉ ra cấu trúc 3-bus chung cho tất cả các họ vi xử lý. Với cấu trúc
này, tất cả các họ vi xử lý khác nhau có thể thực hiện được mọi cơng việc mà ta có thể thấy
trong cuộc sống, từ điều khiển các q trình phức tạp, truyền thơng, trò chơi điện tử… Một câu
hỏi đặt ra là: vậy thì cái gì đặc trưng cho bộ vi xử lý để nó có thể thực hiện chức năng riêng biệt
của vi xử lý. Đó chính là phần mềm (software), hay chương trình.


<b>I. CÁC KHÁI NIỆM CƠ BẢN VỀ LẬP TRÌNH CHO VI XỬ LÝ VÀ VI ĐIỀU KHIỂN </b>
<b>1. Chương trình </b>


- Chương trình (program) là chuỗi các câu lệnh hay phát biểu được viết trong một dạng đặc
thù (ngơn ngữ lập trình). Các lệnh này khi được thực hiện bởi vi xử lý sẽ thực hiện những
thao tác nhất định với kết quả đốn trước được.


- Có nhiều loại ngơn ngữ lập trình:
+ Ngơn ngữ máy (machine language)


Mã nhị phân


Mã bát phân hoặc thập lục phân


+ Hợp ngữ (assembly language) [cần có assembler – trình dịch hợp ngữ]
Mã kí hiệu


+ Ngơn ngữ cấp cao [cần có compiler – trình biên dịch]
Pascal, Fortran, Basic, C, C++, …


<b>2. Lưu đồ chương trình </b>


- Bước đầu tiên của việc lập trình là xác định rõ ràng mục đích của chương trình và trình tự


cần thiết để đạt mục đích đó. Một trong những cơng cụ quan trọng trong việc phát triển
chương trình là lưu đồ chương trình.


- Lưu đồ chương trình là biểu diễn bằng hình ảnh thứ tự các thao tác cần làm để giải quyết
một vấn để cụ thể. Lưu đồ chương trình khơng phụ thuộc ngơn ngữ lập trình hoặc loại vi xử
lý cụ thể nào. Lưu đồ chương trình chỉ phụ thuộc vào cơng việc mà người lập trình muốn
hồn thành.


- Các khối cơ bản dùng trong lưu đồ chương trình:


Khối bắt đầu và


kết thúc Khối thực hiện Khối quyết định (khối rẽnhánh có điều kiện)


Gọi chương trình con Khối nhập/xuất <sub>(khi qua trang mới) Chỗ nối chung</sub>Đầu nối


</div>
<span class='text_page_counter'>(2)</span><div class='page_container' data-page=2>

của mình. Có lưu đồ chương trình thì dù sau một thời gian dài ta vẫn có thể quay lại với
chương trình của mình một cách dễ dàng mà khơng bị “lạc” trong các dòng lệnh vốn rất chi
tiết của chương trình hợp ngữ.


<b>3. Khn dạng của chương trình hợp ngữ </b>


Một chương trình hợp ngữ có thể bao gồm:
- Các lệnh (instruction) của vi xử lý/vi điều khiển
- Các chỉ dẫn (directive) của trình dịch hợp ngữ
- Các điều khiển (control) của trình dịch hợp ngữ
- Các chú thích (comment)


Các lệnh là các mã gợi nhớ quen thuộc và sẽ được dịch ra mã máy tương ứng với vi xử lý/vi
điều khiển. Các chỉ dẫn của trình dịch hợp ngữ là các lệnh của trình dịch hợp ngữ dùng để


định nghĩa cấu trúc chương trình, các ký hiệu, dữ liệu, các hằng số… Các điều khiển của
trình dịch hợp ngữ thiết lập các chế độ trình dịch hợp ngữ và các luồng hợp dịch trực tiếp.
Các chú thích giúp cho chương trình dễ đọc bằng cách đưa ra các giải thích về mục đích và
hoạt động của các chuỗi lệnh.


Các dòng chứa các lệnh và các chỉ dẫn phải được viết theo các qui luật mà trình dịch hợp
ngữ hiểu được. Mỗi dòng được chia thành các trường cách biệt nhau bởi khoảng trắng hay
khoảng tab. Khuôn dạng tổng quát của mỗi dòng như sau:


<b>Tên (Nhãn) </b> <b>Mã gợi nhớ </b> <b>Các toán hạng </b> <b>Chú thích </b>


trong đó chỉ có trường mã gợi nhớ là bắt buộc. Với trình dịch hợp ngữ ASM51, trường mã
gợi nhớ khơng cần ở trên cùng một dịng với trường nhãn. Tuy nhiên, trường toán hạng phải
ở trên cùng một dịng với trường mã gợi nhớ. Có thể viết các dòng này bằng chữ hoa hay
chữ thường và chúng sẽ được coi là tương đương vì trình dịch hợp ngữ không phân biệt kiểu
chữ.


<i><b>a. Trường tên</b></i>


Trường này có thể chứa các nhãn, tên biến, hay tên chương trình con (thủ tục). Các tên
và nhãn này sẽ được trình dịch hợp ngữ gán bằng các địa chỉ cụ thể của lệnh (hoặc dữ
liệu) theo sau. Tên và nhãn có thể có độ dài từ 1 đến 31 ký tự, không chứa khoảng trắng,
phải bắt đầu bằng ký tự chữ, dấu ‘?’ hay dấu ‘_’ và tiếp theo phải là các ký tự chữ, các
ký số, dấu ‘?’ hay dấu ‘_’. Các tên và nhãn khơng được trùng với các từ khóa (các mã
gợi nhớ, các chỉ dẫn, các toán tử hay các ký hiệu định nghĩa trước). Nói chung, ta cứ đặt
các tên bình thường và có ý nghĩa là sẽ ít bị lỗi. Một nhãn được kết thúc bằng dấu ‘:’.


<i><b>b. Trường mã gợi nhớ</b></i>


Trường này chứa mã gợi nhớ (mnemonic) cho biết chức năng của lệnh (ví dụ như ADD,


MOV, DIV, MUL, INC…) hay chỉ dẫn của trình dịch hợp ngữ (ví dụ như ORG, END,
EQU, DB…). Các chỉ dẫn không được dịch ra mã máy.


<i><b>c. Trường toán hạng (operand) </b></i>


Trường này chứa địa chỉ hay dữ liệu mà lệnh sẽ sử dụng. Tùy theo từng loại lệnh mà có
thể có 0, 1, 2 hay 3 toán hạng. Các toán hạng cách nhau bởi dấu phẩy.


<i><b>d. Trường chú thích (comment) </b></i>


</div>
<span class='text_page_counter'>(3)</span><div class='page_container' data-page=3>

Khi ta viết chương trình bằng kí tự của bảng chữ cái, ta gọi đây là dạng mnemonic (mã gợi
nhớ). Vi xử lý/vi điều khiển không hiểu được các chữ cái này. Chúng đòi hỏi các lệnh phải
được viết dưới dạng đặc biệt gọi là object code (mã đối tượng, mã lệnh). Mnemonic được
dùng trong chương trình gốc dưới dạng source code (mã nguồn) do người lập trình viết bằng
hợp ngữ hoặc (ngơn ngữ lập trình khác). Cịn object code là ngơn ngữ chỉ bao gồm các số 1
và 0, đây là ngôn ngữ mà vi xử lý/vi điều khiển sẽ làm việc được. Vi xử lý/vi điều khiển đọc
object code từ bộ nhớ dưới dạng các byte và biên dịch các dữ liệu thành lệnh cần thực thi.
™ Một số chỉ dẫn của trình dịch hợp ngữ thường dùng:


• <b>ORG </b>


Daïng: ORG <i>bieuthuc</i>


Chỉ dẫn ORG thay đổi nội dung bộ đếm chương trình theo giá trị của <i>bieuthuc</i> để thiết
lập nơi bắt đầu mới của chương trình cho các phát biểu theo sau nó.


• <b>END </b>


Dạng: END



END là phát biểu cuối cùng của chương trình nguồn. Nhãn khơng được sử dụng trên
dịng này.


• <b>EQU</b>


Dạng: <i>kyhieu</i> EQU <i>bieuthuc</i>


Chỉ dẫn EQU gán giá trị của <i>bieuthuc</i>cho <i>kyhieu</i>. <i>Kyhieu</i> phải là tên hợp lệ.


• <b>DB</b>


Daïng: <i>nhan:</i> DB <i>bieuthuc{, bieuthuc][…] </i>


Chỉ dẫn DB thường được dùng để định nghĩa các giá trị byte tương ứng với các <i>bieuthuc</i>


trong bộ nhớ chương trình bắt đầu từ địa chỉ tương ứng với <i>nhan</i>. <i><b> </b></i>


<b>4. Biên dịch chương trình </b>


- Chương trình phải được chuyển sang thành dạng object code trước khi vi xử lý/vi điều
khiển có thể thực hiện chương trình. Quá trình chuyển từ chương trình dạng source code
sang object code gọi là biên dịch/hợp dịch (assembling).


- Sau đó ta nạp object code này vào bộ nhớ vi xử lý/vi điều khiển và vi xử lý/vi điều khiển
chạy chương trình.


Việc chuyển đổi mnemonic sang object code thường thực hiện bằng máy tính. Trước hết ta
dùng một chương trình gọi là <i>editor</i> để viết và lưu mã nguồn vào bộ nhớ. Ta có thể sử dụng
editor quen thuộc của Window như <i>Notepad</i> hoặc trong môi trường DOS ta sử dụng lệnh <i>edit</i>



trong <i>Norton Commandor</i>. Sau đó ta cho chạy chương trình biên dịch gọi là <i>assembler, </i>


chương trình này sẽ lấy mã lệnh trong bộ nhớ và biên dịch sang tập tin dưới dạng object
code. Cuối cùng, ta dùng một chương khác để nạp object code từ bộ nhớ của máy tính vào
bộ nhớ của vi xử lý/vi điều khiển.


<b>5. Nạp chương trình vào bộ nhớ </b>


Khi vi xử lý/vi điều khiển được cấp nguồn điện lần đầu hay được khởi động lại bằng cách
kích hoạt vào chân RST thì vi xử lý/vi điều khiển thực hiện một số lệnh bên trong. Một
trong các lệnh đó là xuất một địa chỉ đặc biệt lên bus địa chỉ và đọc opcode đầu tiên tại địa
chỉ đó. Đối với Z80, 8080, 8085 và họ MCS-51 thì đó là địa chỉ 0000H. Do đó, opcode đầu
tiên của chương trình cần thực hiện phải được đặt ở vị trí 0000H trong bộ nhớ chương trình
và khi viết chương trình nên sử dụng chỉ dẫn ORG 0000H ở dòng đầu tiên của chương trình
chính để báo cho trình dịch hợp ngữ biết chương trình được bắt đầu ở địa chỉ 0000H.


</div>
<span class='text_page_counter'>(4)</span><div class='page_container' data-page=4>

lệnh sẽ dẫn đến các kết quả không lường trước được. Phương pháp duy nhất để phân biệt
giữa opcode và dữ liệu là cần phải biết địa chỉ chính xác của opcode đầu tiên tring chương
trình và nạp chương trình đúng vị trí và theo thứ tự.


<b>II. CÁC KIỂU ĐỊNH ĐỊA CHỈ </b>


Các kiểu định địa chỉ là phần cần thiết cho toàn bộ tập lệnh của mỗi một bộ vi xử lý hay vi điều
khiển. Các kiểu định địa chỉ cho phép xác định rõ nguồn và đích của dữ liệu theo nhiều cách
khác nhau mà vi xử lý hay vi điều khiển sử dụng trong quá trình thực thi lệnh.


Có 8 kiểu định địa chỉ đối với họ MCS-51:
- Thanh ghi (register)


- Trực tiếp (direct)


- Gián tiếp (indirect)
- Tức thời (immediate)
- Tương đối (relative)
- Tuyệt đối (absolute)
- Dài (long)


- Chỉ số (index)


<b>1. Định địa chỉ thanh ghi </b>


- Trong lệnh truy xuất đến các thanh ghi R0 ÷ R7 của bank thanh ghi tích cực. Các lệnh này
được mã hóa dài 1 byte, trong đó dùng 3 bit thấp nhất để chỉ thanh ghi được truy xuất.


- Ngồi ra, trong lệnh cũng có thể truy xuất đến các thanh ghi đặc biệt như: thanh ghi tích
lũy (ký hiệu A), con trỏ dữ liệu (ký hiệu DPTR), bộ đếm chương trình (ký hiệu PC), cờ nhớ
(ký hiệu C) và cặp thanh ghi AB (ký hiệu AB). Các lệnh này không cần các bit địa chỉ, bản
thân opcode của lệnh đã chỉ ra thanh ghi được dùng.


Ví dụ: INC R1 ; tăng nội dung thanh ghi R1 leân 1
INC A ; tăng nội dung thanh ghi A lên 1
INC DPTR ; taêng nội dung thanh ghi DPTR lên 1


<b>2. Định địa chỉ trực tiếp </b>


- Dùng để truy xuất các ô nhớ trong RAM nội (địa chỉ từ 00H ÷ 7FH) hay các thanh ghi chức
năng đặc biệt (địa chỉ từ 80H ÷ FFH). Một byte được thêm vào tiếp theo opcode để xác định
địa chỉ.


- Trình dịch hợp ngữ cho phép sử dụng tên các thanh ghi chức năng đặc biệt (thay cho địa
chỉ).



Ví dụ:


INC ACC ; tăng nội dung thanh ghi A leân 1


Lệnh này cùng chức năng với lệnh INC A ở trên nhưng khác kiểu định


địa chỉ → các byte mã lệnh khác nhau.


MOV P0,A ; chuyển nội dung của thanh ghi A vào Port 0


(≡MOV 80H,A)


INC 30H ; tăng nội dung ô nhớ 30H lên 1


Nội dung 30H ban đầu là 06H, sau lệnh trên (30H) = 07H




06H 30H




Opcode n2 n1 n0


</div>
<span class='text_page_counter'>(5)</span><div class='page_container' data-page=5>

<b>3. Định địa chỉ gián tiếp </b>


- Trong lệnh dùng các thanh ghi R0 và R1 làm con trỏ để chỉ ra địa chỉ ô nhớ trong RAM nội
mà lệnh tác động đến. Bit thấp nhất trong mã lệnh sẽ xác định thanh ghi nào được dùng.
- Quy ước: dùng dấu @ trước R0 và R1 cho kiểu định địa chỉ này.



Ví dụ: (R1) = 30H, (30H) = 52H


INC @R1 ; tăng nội dung ô nhớ được trỏ bởi R1


→(R1) = 30H, (30H) = 53H




- Kiểu định địa chỉ này thường dùng khi truy xuất tới một vùng nhớ liên tiếp.


Ví dụ: Các bước sau được thực hiện để xóa 50 ơ nhớ trong RAM nội bắt đầu từ địa chỉ 30H:


<b>4. Định địa chỉ tức thời </b>


- Các dữ liệu tức thời được dùng trực tiếp trong lệnh có thể là một hằng số, một ký số, một
biểu thức tốn học… Trình dịch hợp ngữ sẽ tự động tính tốn và thay thế dữ liệu tức thời vào
mã lệnh.


- Quy ước: dùng dấu # trước các tốn hạng tức thời.


Ví dụ: MOV A,#7 ; nạp giá trị 7 vào thanh ghi A, (A) = 7H
MOV A,#7+8 ; nạp giá trị 15 vào thanh ghi A, (A) = 0FH


MOV A,#‘B’ ; nạp giá trị 66 (mã ASCII của ký tự B) vào thanh ghi A, (A) = 42H


<b>5. Định địa chỉ tương đối </b>


- Được dùng trong các lệnh nhảy ngắn.



… …


52H 30H 30H R1 → 53H 30H 30H R1


… …


Opcode Dữ liệu tức thời


Opcode i


(R0) = 30H


Đủ 50 byte ?
- (@R0) = 0
- Tăng R0
N


</div>
<span class='text_page_counter'>(6)</span><div class='page_container' data-page=6>

- Một địa chỉ tương đối (hay còn gọi là <i>offset</i>) là một giá trị 8 bit có dấu (từ –128 đến +127)
cho biết độ lệch từ vị trí lệnh theo ngay sau lệnh nhảy đến đích. Giá trị này được cộng thêm
vào thanh ghi bộ đếm chương trình (PC) để tạo ra địa chỉ của lệnh tiếp theo cần được thực
thi.


Ví dụ: Tính <i>offset</i> cho kiểu định địa chỉ tương đối trong hai trường hợp nhảy tới và nhảy lùi.


- Thông thường các đích nhảy được xác định bằng các nhãn và trình dịch hợp ngữ sẽ xác
định <i>offset</i> tương đối tương ứng.


Ví dụ: Nếu nhãn THERE được đặt ở địa chỉ 1040H và lệnh:


SJMP THERE



ở trong bộ nhớ chương trình tại địa chỉ 1000H và 1001H → địa chỉ của lệnh kế tiếp sẽ là
1000H + 02H = 1002H, nên offset tương đối sẽ là: offset = 1040H – 1002H = 3EH. Trình
dịch hợp ngữ sẽ gán offset tương đối là 3EH cho byte 2 của lệnh SJMP THERE.


- Có ưu điểm là khơng phụ thuộc vào vị trí nhưng bị giới hạn về tầm nhảy.


<b>6. Định địa chỉ tuyệt đối </b>


- Chỉ dùng trong các lệnh AJMP và ACALL.


- Cho phép rẽ nhánh chương trình trong trang 2K hiện hành của bộ nhớ chương trình (tức
tầm nhảy của lệnh là trong trang 2K hiện hành của bộ nhớ chương trình).


- 11 bit thấp của địa chỉ đích, trong đó 3 bit cao (A8 ÷ A10) được đưa vào cùng với opcode
tạo thành byte thứ 1 của lệnh và 8 bit thấp (A0 ÷ A7) tạo thành byte thứ 2 của lệnh. 5 bit cao
của địa chỉ đích là 5 bit cao hiện hành của bộ đếm chương trình (PC) →lệnh theo ngay sau


lệnh rẽ nhánh và đích đến phải ở trong cùng 1 trang 2K.


- Kiểu định địa chỉ này có tầm nhảy bị hạn chế và cung cấp mã phụ thuộc vị trí.


010A
0109
0108
0107 5
0106 4
0105 3
0104 2
0103 1


0102
0101 05
0100 80
00FF


Code
memory


SJMP 0107H
Offset tương
đối từ địa chỉ
0102H là “5”


(a) Nhảy ngắn tới


2043
2042
2041 F6 -1
2040 80 -2
203F -3
203E -4
203D -5
203C -6
203B -7
203A -8
2039 -9
2038 -10


Code
memory



SJMP 2038H


Offset tương
đối từ địa chỉ
2042H là “-10”


(b) Nhảy ngắn lùi


<b>Hình 3.1</b>


A10 A9 A8 Opcode A7 A0


</div>
<span class='text_page_counter'>(7)</span><div class='page_container' data-page=7>

Ví dụ: Nếu nhãn THERE đặt tại địa chỉ 0F46H và lệnh:


AJMP THERE


ở trong bộ nhớ tại địa chỉ 0900H và 0901H →lệnh theo ngay sau lệnh nhảy bắt đầu ở địa chỉ


0902H sẽ có 5 bit cao địa chỉ trùng với địa chỉ nhãn THERE là 00001 (trang 1).


→Mã hố lệnh:


<b>7. Định địa chỉ dài </b>


- Chỉ dùng trong các lệnh LCALL và LJMP.


- Đây là các lệnh rẽ nhánh 3 byte, với 2 byte sau (byte 2 và byte 3) là địa chỉ đích của lệnh
(16 bit).



- Ưu điểm là có thể sử dụng tồn bộ vùng nhớ chương trình 64K, nhưng lệnh lại dài đến 3
byte và phụ thuộc vào vị trí.


<b>8. Định địa chỉ chỉ số </b>


- Dùng một địa chỉ nền (chứa trong thanh ghi PC hay DPTR) và một offset (chứa trong thanh
ghi A) để tạo địa chỉ được tác động cho các lệnh JMP hoặc MOVC.


<i>(Địa chỉ được tác động) = (PC) hoặc (DPTR) + (A) </i>


- Thường dùng khi truy xuất dữ liệu trong một bảng dữ liệu đã được định nghĩa trước. Khi
đó, thanh ghi PC hay DPTR sẽ giữ địa chỉ đầu bảng và thanh ghi A


giữ địa chỉ offset của dữ liệu cần truy xuất trong bảng.
Ví dụ: Bảng các giá trị bình phương của số nguyên.


Nếu DPTR chứa địa chỉ đầu bảng (tương ứng nhãn TABLE)
và (A) = 3, thì sau khi thực hiện lệnh:


MOVC A, @A+DPTR


→ (A) = 09H.


1 1 1 0 0 0 0 1 0 1 0 0 0 1 1 0


<b>Hình 3.2</b>


FFFF


F800



2K trang 31


1800


……….


17FF
1000


2K trang 2


0FFF


0800


2K trang 1


07FF


0000


2K trang 0


32 x 2K = 64K
Trong moät trang


2K bất kỳ, chỉ có
11 bit thấp thay
đổi



(a) Bộ nhớ được chia thành nhiều trang 2K




A15 A10 A0


5 bit xác định
trang 2K


11 bit xác định địa chỉ
trong một trang 2K
(b) Bên trong một trang 2K


Địa chỉ A15 ÷A8 Địa chỉ A7 ÷ A0
Opcode


……


10H
09H
04H
01H
00H
TABLE


</div>
<span class='text_page_counter'>(8)</span><div class='page_container' data-page=8>

<b>III. TẬP LỆNH (tham khảo thêm “Tóm tắt tập lệnh” và “Mô tả lệnh”) </b>


Tập lệnh của 8051 được chia làm 5 nhóm:
- Nhóm lệnh số học



- Nhóm lệnh logic


- Nhóm lệnh chuyển dữ liệu


- Nhóm lệnh rẽ nhánh (chuyển điều khiển)
- Nhóm lệnh xử lý bit


<b>IV. CÁC KỸ THUẬT LẬP TRÌNH </b>


Lập trình cấu trúc (structured programming) là một kỹ thuật tổ chức và mã hóa các chương trình
nhằm giảm sự phức tạp, dễ dàng gỡ rối và hiệu chỉnh chương trình. Lập trình cấu trúc nhấn
mạnh đến các nhiệm vụ lập trình. Người lập trình phân tích một nhiệm vụ lớn thành nhiều cơng
việc nhỏ hơn, sau đó dần dần chi tiết, cụ thể hóa để được các vấn đề đơn giản. Qua đó, tìm ra
cách giải quyết vấn đề dưới dạng những thuật giải cụ thể rõ ràng, để có thể minh họa bằng
ngơn ngữ giải thuật. Trong lập trình cấu trúc, các thuật giải này được gọi là các <i>chương trình </i>
<i>con</i>. Cách thức phân tích và thiết kế như vậy ta gọi là nguyên lý lập trình từ trên xuống (<i></i>
<i>top-down</i>), để thể hiện quá trình suy diễn từ cái chung nhất cho đến cái cụ thể nhất.


Tất cả các vấn đề lập trình đều có thể được giải quyết chỉ dựa trên 3 cấu trúc:
* Các phát biểu


* Các vịng lặp
* Các lựa chọn


<b>1. Các phát biểu </b>


- Các phát biểu là một cách thức cơ bản để thực hiện một cơng việc nào đó.


- Các phát biểu còn bao gồm cả việc gán giá trị cho 1 biến hoặc gọi chương trình con.



<b>2. Cấu trúc vòng lặp </b>


- Được sử dụng để thực hiện một hoặc một số công việc lặp đi lặp lại nhiều lần.
- Có hai dạng vịng lặp cơ bản:


<i><b>a.WHILE/DO </b></i>


+ Cú pháp:


<i>WHILE [điều kiện] DO </i>
<i>[các phát biểu] </i>


+ Lưu đồ:


Statement
Enter


Condition
true?


Exit
No


Yes


</div>
<span class='text_page_counter'>(9)</span><div class='page_container' data-page=9>

Giaûi
Pseudo-code:


[sum = 0]



WHILE [length > 0] DO Begin
[sum = sum + @pointer]
[taêng pointer]


[giaûm length]
End


Lưu đồ:


Cộng giá trị với
sum
Enter


Length > 0 ?


Exit


No


Yes


Sum = 0


Tăng pointer


Giảm length


Mã 8051:



SUM: CLR A


LOOP: CJNE R7, #0, STATEMENT
JMP EXIT


STATEMENT: ADD A, @R0


INC R0
DEC R7
JMP LOOP


EXIT: RET


Hoặc:


SUM: CLR A


INC R7
MORE: DJNZ R7, SKIP


RET


SKIP: ADD A, @R0


INC R0
SJMP MORE


<i><b>b. REPEAT…UNTIL </b></i>


Cú pháp:



</div>
<span class='text_page_counter'>(10)</span><div class='page_container' data-page=10>

Lưu đồ:


Statement
Enter


Condition
true?


Exit
Yes
No


<i><b>Ví dụ 1</b></i>: Viết chương trình con để tìm một chuỗi được kết thúc bởi ký tự Null được trỏ bởi
thanh ghi R0 và xác định xem trong chuỗi có ký tự Z hay không. Kết quả trả về ACC = Z
nếu có ký tự Z ở trong chuỗi, nếu khơng thì ACC = 0.


Giải:
Pseudo-code:


REPEAT


[ACC = @pointer]
[taêng pointer]


Until [ACC == ‘Z’ or ACC == 0]
Lưu đồ:


Taêng pointer
Enter



Ký tự = “Z” ?


Exit


Yes


Nhận một ký tự


Ký tự = 0 ?


No


No


Yes


Maõ 8051:


STATEMENT: MOV A, @R0


INC R0
JZ EXIT


CJNE A, #’Z’, STATEMENT


</div>
<span class='text_page_counter'>(11)</span><div class='page_container' data-page=11>

<i><b>Ví dụ 2</b></i>: Viết chương trình lấy 10 byte trong RAM nội, địa chỉ bắt đầu là 30H ghi ra RAM
ngoài ở địa chỉ bắt đầu là 2000H.


Giải:



<i>Cách 1</i>: Dùng vòng lặp CJNE


ORG 0000H


MOV R0,#30H ; R0 trỏ tới địa chỉ 30H trong RAM nội
MOV DPTR,#2000H ; DPTR trỏ tới địa chỉ ngoài 2000H


LOOP:MOV A,@R0 ; ghi nội dung ô nhớ được trỏ bởi R0 vào A


MOVX @DPTR, A ; ghi nội dung trong A ra địa chỉ được trỏ bởi DPTR
INC R0 ; tăng chỉ số địa chỉ trong R0


INC DPTR ; tăng chỉ số địa chỉ trong DPTR
CJNE R0,#3AH,LOOP ; đủ 10 byte chưa?


END


<i>Cách 2</i>: Dùng vòng lặp DJNZ


ORG 0000H


MOV R0,#30H ; R0 trỏ tới địa chỉ 30H trong RAM nội
MOV DPTR,#2000H ; DPTR trỏ tới địa chỉ ngoài 2000H
MOV R7,#10 ; R7 chứa số lần lặp là 10


LOOP:MOV A,@R0 ; ghi nội dung ô nhớ được trỏ bởi R0 vào A


MOVX @DPTR, A ; ghi nội dung trong A ra địa chỉ được trỏ bởi DPTR
INC R0 ; tăng chỉ số địa chỉ trong R0



INC DPTR ; tăng chỉ số địa chỉ trong DPTR
DJNZ R7,LOOP ; đủ 10 byte chưa?


END


<i><b>Ví dụ 3</b></i>: Viết chương trình cộng một chuỗi số BCD không nén (unpacked – BCD) được


cất trong RAM nội bắt đầu từ ô nhớ 31H. Chiều dài chuỗi chứa trong ô nhớ 30H. Kết quả
(giả sử là 1 byte) cất vào ơ nhớ 2FH.


Giải:


ORG 0000H


MOV R0,#31H ; R0 trỏ tới địa chỉ 31H trong RAM nội
CLR A ; ban đầu cho (A) = 0


LOOP:ADD A,@R0 ; cộng nội dung ô nhớ được trỏ bởi R0 vào A
INC R0 ; tăng chỉ số địa chỉ trong R0


DJNZ 30H,LOOP ; hết chuỗi chưa?


MOV 2FH,A ; cất kết quả sau khi cộng vào ô nhớ 2FH
END


<i><b>Ví dụ 4</b></i>: Viết chương trình đổi các ký tự chữ thường trong các ô nhớ từ 30H →39H trong


RAM nội sang chữ hoa. Biết:



Ký tự Mã ASCII (Hex)


Chữ hoa (‘A’ ÷ ‘Z’) 41H ÷ 5AH
Chữ thường (‘a’ ÷ ‘z’) 61H ÷ 7AH


Giải:


<i>Nhận xét: </i>


</div>
<span class='text_page_counter'>(12)</span><div class='page_container' data-page=12>

ORG 0000H


MOV R0,#30H ; R0 trỏ tới địa chỉ 30H trong RAM nội


LOOP:XCH A,@R0 ; hốn đổi nội dung A và ơ nhớ được trỏ bởi R0


ANL A,#11011111B ; xóa bit 5 trong mã ASCII, các bit khác giữ nguyên


XCH A,@R0 ; cất kết quả trở lại ô nhớ được trỏ bởi R0
INC R0 ; tăng chỉ số địa chỉ trong R0


CJNE R0,#3AH,LOOP ; xử lý xong ô nhớ 39H chưa?
END


<b>3. Cấu trúc lựa chọn </b>


<i><b>a. IF…THEN…ELSE </b></i>


Cú pháp:


<i>IF</i> [điều kiện]



<i>THEN</i> [phát biểu 1]


<i>ELSE</i> [phát biểu 2]
Lưu đồ:


Statement 1


Enter


Condition
true?


Exit


Statement 2


Yes No


<i><b>Ví dụ 1</b></i>: viết một đoạn chương trình nhập một ký tự từ RAM ngồi có địa chỉ là 2000H
và kiểm tra ký tự đó. Nếu ký tự nhận được là loại mã ASCII hiển thị được (có giá trị
trong khoảng 20H → 7EH) thì xuất lại nó, nếu khơng thì xuất lại ký tự ‘.’ ra RAM ngoài
ở địa chỉ 2001H.


Lưu đồ:


Xuất lại ký tự đó ra
RAM ngồi 2001H


Enter



Ký tự hiển
thị được ?


Exit


Xuất ký tự ‘.’ ra
RAM ngoài 2001H


Yes No


</div>
<span class='text_page_counter'>(13)</span><div class='page_container' data-page=13>

Pseudo-code:
[nhập ký tự]


IF [ký tự == graphic]
THEN [xuất lại ký tự]
ELSE [xuất ký tự ‘.’]
Mã 8051:


ENTER: ACALL INCH


ACALL ISGRPH
JNC STMENT2


STMENT1: ACALL OUTCH


JMP EXIT
STMENT2: MOV A, #’.’


ACALL OUTCH


EXIT: (tiếp tục)


Hay


ACALL INCH


ACALL ISGRPH


JC SKIP
MOV A, #’.’
SKIP: ACALL OUTCH


(tiếp tục)


<i><b>Ví dụ 2</b></i>: Viết chương trình kiểm tra một khối dữ liệu trong RAM ngồi có chiều dài 100
byte bắt đầu từ địa chỉ 1000H xem có bao nhiêu giá trị chẵn và lẻ. Số giá trị chẵn và lẻ
được lưu tương ứng trong 2 ơ nhớ 30H và 31H.


Giải:


<i>Cách 1</i>:


ORG 0000H


MOV DPTR,#1000H ; DPTR trỏ tới địa chỉ ngoài 1000H
MOV R7,#100 ; R7 chứa số lần lặp là 100


LOOP: MOVX A,@DPTR ; đọc ô nhớ được trỏ bởi DPTR vào A để kiểm tra
JB ACC.0,ODD ; (ACC.0) = 1 → số lẻ



INC 30H ; (ACC.0) = 0 → số chẳn
SJMP CONT ; xét byte kế tiếp


ODD: INC 31H


CONT: INC DPTR ; tăng chỉ số địa chỉ trong DPTR
DJNZ R7,LOOP ; đủ 100 byte chưa?


END


<i>Cách 2</i>: Có thể thay dòng lệnh <i>JB ACC.0, ODD </i>bằng:


ANL A,#01H
JNZ ODD


<i><b>Ví dụ 3</b></i>: Viết chương trình con tên COMPARE_8 so sánh số nhị phân 8 bit trong thanh


ghi A với giá trị chuẩn trong ô nhớ RAM nội có địa chỉ 30H. Kết quả trả về:
+ Nếu (A) > (30H) thì xóa P1.0


+ Nếu (A) = (30H) thì xóa P1.1
+ Nếu (A) < (30H) thì xoùa P1.2


</div>
<span class='text_page_counter'>(14)</span><div class='page_container' data-page=14>

<i>Hướng dẫn</i>: Sử dụng lệnh so sánh CJNE:


<i>CJNE arg1, arg2, arg3 </i>
<i>Neáu arg1 ≥ arg2 thì C = 0 </i>
<i>Nếu arg1 < arg2 thì C = 1 </i>
<i>Giải thuật</i>:



<i>Chương trình</i>:


COMPARE_8:


CJNE A,30H,NOT_EQUAL ; (A) # (30H)


CLR P1.1 ; (A) = (30H)


SJMP EXIT
NOT_EQUAL:


JNC GREATER ; (C) = 0 → (A) > (30H)
CLR P1.2 ; (C) = 1 → (A) < (30H)
SJMP EXIT


GREATER:


CLR P1.0
EXIT: RET


<i><b>b. CASE </b></i>


Cú pháp:


<i>CASE</i> [biểu thức] <i>OF</i>


0: [phát biểu 0]
1: [phát biểu 1]
2: [phát biểu 2]
.



.
.


n: [phát biểu n]
[phát biểu mặc định]


<i>END_CASE </i>


Lưu đồ:


N


N
Y


Y
COMPARE_8


(A) = (30H)


Xóa P1.1 Xóa P1.0 Xóa P1.2


(C) = 0


</div>
<span class='text_page_counter'>(15)</span><div class='page_container' data-page=15>

Phát bieåu 0
Enter


Biểu thức 0 ?



Exit


Phát biểu 1
Biểu thức 1 ?


No Biểu thức 2 ? No Biểu thức n ?


Phát biểu 2 Phát biểu n Phát biểu mặc định
No


Yes Yes Yes Yes


No


<i><b>Ví dụ</b></i>: Một chương trình u cầu các đáp ứng 0, 1, 2, hoặc 3 của một user để chọn một
trong bốn hành động. Viết đoạn chương trình nhập một ký tự từ bàn phím và nhảy đến
ACT0, ACT1, ACT2 hoặc ACT3, phụ thuộc vào đáp ứng của user. Bỏ qua việc kiểm tra
lỗi.


Pseudo-code:


[nhập một ký tự]
CASE [ký tự] OF
‘0’: [hành động 0]
‘1’: [hành động 1]
‘2’: [hành động 2]
‘3’: [hành động 3]
END_CASE


Lưu đồ:



Hành động 0
Enter


Ký tự = ‘0’ ?


Exit


Hành động 1
Ký tự = ‘1’ ?


No Ký tự = ‘2’ ? No Ký tự = ‘3’ ?


Hành động 2 Hành động 3


No


Yes Yes Yes Yes No


Nhập ký tự


Maõ 8051


CALL INCH


CJNE A, #’0’, SKIP1
ACT0: .


.
.



</div>
<span class='text_page_counter'>(16)</span><div class='page_container' data-page=16>

ACT1: .
.
.


SKIP2: CJNE A, #’2’, SKIP3
ACT2: .


.
.


SKIP3: CJNE A, #’3’, EXIT
ACT2: .


.
.


EXIT: (tiếp tục)
Hay


CALL INCH
ANL A, #3
RL A


MOV DPTR, #TABLE
JMP @A+DPTR
TABLE: AJMP ACT0


AJMP ACT1



AJMP ACT2


ACT3: .
.
.


JMP EXIT
ACT0: .


.
.


JMP EXIT
ACT1: .


.
.


JMP EXIT
ACT2: .


.
.


EXIT: (tiếp tục)


<b> 4. Kỹ thuật tạo trễ </b>


<i><b>Ví dụ 1</b></i>: Viết chương trình con tên DELAY_100 để tạo trễ (trì hỗn) 100 μs. Giả sử dùng
thạch anh 12 MHz (fOSC = 12 MHz).



Giaûi:


<i>Hướng dẫn</i>: Dùng thời gian thực thi lệnh để tính thời gian tạo trễ cần thiết.
fOSC = 12 MHz → fM = fOSC / 12 = 1 MHz → TM = 1 μs


<i>Cách 1</i>: Dùng các leänh NOP


DELAY_100:


</div>
<span class='text_page_counter'>(17)</span><div class='page_container' data-page=17>

NOP
NOP

NOP


RET ; 2 μs → 98 + 2 = 100 μs
<i>Cách 2</i>: Dùng vịng lặp để chương trình ngắn gọn hơn.


DELAY_100:


MOV R7,#49 ; 1 μs


LOOP: DJNZ R7,LOOP ; 2 μs / 1 leänh → 2 x 49 = 98 μs


RET ; 2 μs


<i>Tổng quát: </i>
DELAY_T:


MOV R7,#N ; 1 TM, N ≤ 255



LOOP: DJNZ R7,LOOP ; 2 TM / 1 leänh → (2 x N) TM


RET ; 2 TM


→ tổng thời gian tạo trễ: T = [(1 + 2 x N + 2)TM] ≅ [(2 x N) TM] μs


<i>Chú ý</i>:


Lệnh <i>LOOP: DJNZ R7, LOOP</i> tương đương với lệnh <i>DJNZ R7,$</i>. Dấu $ là một ký
hiệu đặc biệt của trình dịch hợp ngữ ASM51 để tham chiếu vị trí của lệnh hiện
hành.


<i><b>Ví dụ 2</b></i>: Viết chương trình con tên DELAY_10ms để tạo trễ 10 ms. Giả sử dùng thạch


anh 12 MHz (fOSC = 12 MHz).


Giaûi:


Thời gian cần tạo trễ: T = 10 ms = 10000 μs = 10000 TM.


Nếu dùng vịng lặp như ví dụ 8 thì thời gian tạo trễ tối đa khoảng ≅ 2 x 255 = 510μs
< 10000μs. Như vậy cần dùng 2 vòng lặp lồng nhau.


<i>Chương trình</i>:


DELAY_10ms:


MOV R7,#20 ; 1 μs
LOOP: MOV R6,#250 ; 1 μs



DJNZ R6,$ ; 2 μs / 1 leänh
DJNZ R7,LOOP ; 2 μs / 1 leänh


RET ; 2 μs


→ tổng thời gian tạo trễ: T = 1 + 20(1 + 2 x 250 + 2) + 2 = 10063 μs ≅ 10000 μs


<i>Tổng quát</i>:


DELAY_T:


MOV R7,#M ; 1 TM


LOOP: MOV R6,#N ; 1 TM


DJNZ R6,$ ; 2 TM / 1 leänh


DJNZ R7,LOOP ; 2 TM / 1 leänh


RET ; 2 TM


→ tổng thời gian tạo trễ: T = [1 + M(1 + 2 x N + 2) + 2]TM≅ [(2 x M x N)TM] μs


<i><b>Ví dụ 3</b></i>: Viết chương trình tạo sóng vng tuần hồn đối xứng tại chân P1.0 có tần số là
f= 10 KHz. Giả sử fOSC = 12 MHz.


Giaûi:


</div>
<span class='text_page_counter'>(18)</span><div class='page_container' data-page=18>

f = 10 KHz → T = 0,1 ms = 100 μs



Sóng vng đối xứng → tH = tL = 50 μs = 50TM


<i>Chương trình</i>:


ORG 0000H


LOOP: CPL P1.0 ; 1 μs
MOV R7,#23 ; 1 μs


DJNZ R7,$ ; 2 x 23 = 46 μs
SJMP LOOP ; 2 μs


END


T


</div>
<span class='text_page_counter'>(19)</span><div class='page_container' data-page=19>

<b>CÂU HỎI VÀ BÀI TẬP CHƯƠNG 3 </b>



<b>3.1.</b> Hãy cho biết cách định địa chỉ của mỗi lệnh sau:


a.MOV R1,A b.MOV A,#12H c.MOV A,@R1 d.PUSH B


e.MOV A,12H f.SJMP LOOP g.ACALL SUB1 h.LJMP 0567H


<b>3.2.</b> Để nạp vào R4 giá trị 56H thì có cần dấu ‘#’ trong lệnh: MOV R4,#56H không?


<b>3.3.</b> Sự khác biệt giữa 2 lệnh sau là gì?


INC A


INC ACC


<b>3.4.</b> Hãy xác định offset tương đối cho lệnh:


SJMP AHEAD


Biết lệnh đặt tại địa chỉ 0400H và 0401H, nhãn AHEAD đặt tại địa chỉ 041FH.


<b>3.5.</b> Hãy xác định offset tương đối cho lệnh:


SJMP BACK


Bieát lệnh đặt tại địa chỉ A050H và A051H, nhãn BACK đặt tại địa chỉ 9FE0H.


<b>3.6.</b> Giả sử lệnh:


AJMP THERE


trong bộ nhớ chương trình ở địa chỉ 2FF0H và 2FF1H, nhãn THERE ở địa chỉ 2F96H. Hãy xác
định các byte mã máy của lệnh trên?


<b>3.7.</b> Giả sử thanh ghi A chứa 5AH. Hãy cho biết giá trị trong thanh ghi A sau khi thực hiện
lệnh sau:


XRL A,#0FFH


Hãy tìm một lệnh khác tương đương với lệnh trên?


<b>3.8.</b> Giả sử thanh ghi A chứa 50H và thanh ghi PSW chứa 0CH. Hãy cho biết giá trị trong
thanh ghi A sau khi thực hiện lệnh sau:



RLC A


<b>3.9.</b> Hãy xác định nội dung của thanh ghi A sau khi thực hiện các chuỗi lệnh sau:


a. MOV A,#25
MOV R7,#18H
ADD A,R7


b. MOV A,#7FH
MOV 50H,#29H
MOV R0,#50H
XCHD A,@R0


<b>3.10.</b> Cho biết giá trị của cờ CY sau khi thực hiện các đoạn mã sau:


a. CLR C b. MOV A,#54H c. MOV A,#250


CPL C ADD A,#0C4H ADD A,#05


<b>3.11.</b> Các lệnh nào sau đây là không hợp lệ:


1.MOV R7,#500 2.MOV A,50H 3.ADD A,R5 4.ADD R3,A


5.MOV R1,#50 6.MOV A,#F5H 7.ADD A,#50H 8.MOV @R1,R7


9.PUSH A 10.MOV A,@R3 11.MOV R7,#00 12.MOV R6,R7


<b>3.12.</b> Cho nội dung của các ô nhớ và thanh ghi: (30H) = 12H; (B) = 34H; (A) = 05H. Hãy xác
định nội dung của ô nhớ 30H, thanh ghi A và B sau khi thực thi đoạn chương trình sau:



MOV R1,#30H
XCH A,B
XCHD A,@R1
SWAP A
XCHD A,@R1
SWAP A


</div>
<span class='text_page_counter'>(20)</span><div class='page_container' data-page=20>

MOV R0,#20H
MOV R1,#30H


MOV R2,#2 ; *
CLR C


NEXT: MOV A,@R0


ADDC A,@R1


MOV @R0,A


INC R0


INC R1


DJNZ R2,NEXT


a. Sau khi thực hiện lệnh thứ 3 (có chú thích *), nội dung của R0, R1 và R2 là bao nhiêu?
b. Lệnh có nhãn NEXT được thực thi bao nhiêu lần?


c. Sau khi hồn tất chương trình trên thì nội dung của R0, R1 và R2 là bao nhiêu?



d. Cho nội dung của các ô nhớ ban đầu là: (20H) = 23H, (21H) = 45H, (30H) = 67H, (31H) =
89H:


• Cho biết nội dung của các ô nhớ trên sau khi thực thi xong chương trình trên?


• Chức năng của chương trình trên?


<b>3.14.</b> Xét chương trình sau:


ORG 0000H
MOV R0,#05H
MOV R1,#40H


MOV R2,#0 ; *
MOV @R1,#0


AGAIN: MOV DPTR,#TABLE
MOV A,R2


MOVC A,@A+DPTR
ADD A,@R1
MOV @R1,A
INC R2


DJNZ R0,AGAIN
AJMP EXIT


TABLE: DB 10H,11H,12H,13H,14H
EXIT: NOP



END


a. Sau khi thực hiện lệnh có chú thích *, nội dung của R0, R1 và R2 là bao nhiêu?
b. Lệnh có nhãn AGAIN được thực thi bao nhiêu lần?


c. Sau khi hoàn tất chương trình trên thì nội dung của R0, R1 và R2 là bao nhiêu?
d. Nội dung của ô nhớ 40H sau khi thực thi xong chương trình trên?


e. Chức năng của chương trình trên?


f. Nếu kết quả của chương trình trên được cất vào ơ nhớ 50H thì lệnh nào cần sửa đổi?


<b>3.15.</b> Viết 1 đoạn chương trình xóa 20 byte ơ nhớ RAM nội bắt đầu từ địa chỉ 30H.


<b>3.16.</b> Viết 1 đoạn chương trình xóa 100 byte ơ nhớ RAM ngồi bắt đầu từ địa chỉ 1000H.


<b>3.17.</b> Viết 1 đoạn chương trình đọc 20 byte ơ nhớ RAM ngồi bắt đầu từ địa chỉ 2000H và ghi
vào RAM nội bắt đầu từ địa chỉ 30H.


</div>
<span class='text_page_counter'>(21)</span><div class='page_container' data-page=21>

<b>3.19.</b> Viết 1 chương trình con cộng 2 số 16 bit khơng dấu, số hạng 1 cất trong R7_A (R7: byte
cao, A: byte thấp), số hạng 2 cất trong ô nhớ 31H_30H (31H: byte cao, 30H: byte thấp). Kết quả
trả về cất trong R7_A.


<b>3.20.</b> Viết 1 chương trình con trừ 2 số 16 bit không dấu, số bị trừ cất trong R7_A (R7: byte
cao, A: byte thấp), số trừ cất trong ô nhớ 31H_30H (31H: byte cao, 30H: byte thấp). Kết quả trả
về cất trong R7_A.


<b>3.21.</b>

<b>*</b>

Viết 1 chương trình con nhân số 16 bit cất trong R7_A (R7: byte cao, A: byte thấp) với
số 8 bit cất trong B. Kết quả trả về 24 bit cất trong R7_B_A.


<b>3.22.</b>

<b>*</b>

Vieát 1 chương trình con chia số 16 bit cất trong R7_A (R7: byte cao, A: byte thấp) cho
số 8 bit cất trong B. Kết quả: thương số cất trong R7_A, dư số cất trong B.


<b>3.23.</b> Viết 1 chương trình con lấy bù 2 của số 16 bit cất trong R7_A (R7: byte cao, A: byte
thấp). Kết quả trả về cất trong R7_A.


<b>3.24.</b> Viết chương trình con BINTOBCD chuyển số nhị phân trong A sang số BCD 3 digit (ký
số) cất trong các ô nhớ 32H, 31H và 30H (32H: trăm, 31H: chục, 30H: đơn vị).


<b>3.25.</b> Viết chương trình con BCDTOBIN chuyển số BCD khơng nén (2 ký số) trong A sang số
nhị phân cất trong ơ nhớ 30H.


<b>3.26.</b>

<b>* </b>

Viết chương trình con BCDTOBIN16 chuyển số nhị phân 16 bit trong R7_A (R7: byte
cao, A: byte thấp) sang số BCD 5 digit cất trong các ô nhớ theo thứ tự từ chục ngàn đến đơn vị
là: 34H_33H_32H_31H_30H.


<b>3.27.</b> Viết chương trình đổi các ký tự chữ hoa trong các ơ nhớ 30H →3FH trong RAM nội sang


chữ thường.


<b>3.28.</b> Viết chương trình đổi các ký tự chữ hoa trong các ô nhớ 30H →3FH trong RAM nội sang


chữ thường và ngược lại.


<b>3.29.</b> Viết chương trình con SOSANH16 so sánh số 16 bit không dấu cất trong R7_A (R7: byte
cao, A: byte thấp) với số 16 bit không dấu làm giá trị chuẩn cất trong ô nhớ 31H_30H (31H:
byte cao, 30H: byte thấp). Kết quả trả về:


- (C) = 1 neáu (R7_A) < (31H_30H)


- (C) = 0 neáu (R7_A) ≥ (31H_30H)


<i>Lưu ý: phải bảo toàn nội dung của các thanh ghi và ơ nhớ ở trên. </i>


<b>3.30.</b> Viết chương trình kiểm tra một khối dữ liệu trong RAM ngồi có chiều dài 200 byte bắt
đầu từ địa chỉ 1000H xem có bao nhiêu byte có giá trị là 0. Kết quả (số byte có giá trị là 0) được
lưu vào ơ nhớ có địa chỉ là 0FFFH trong RAM ngồi.


<b>3.31.</b> Viết chương trình kiểm tra một khối dữ liệu trong RAM ngồi có chiều dài 50 byte bắt
đầu từ địa chỉ 2000H xem có bao nhiêu byte có giá trị là 0, dương và âm. Kết quả được lưu vào
các ô nhớ trong RAM nội như sau:


- Ô nhớ 30H: chứa số byte bằng 0.
- Ô nhớ 31H: chứa số byte dương.
- Ô nhớ 32H: chứa số byte âm.


<b>3.32.</b> Viết 1 chương trình con tên DELAY_500 tạo trễ 500μs, biết fOSC = 12 MHz.
<b>3.33.</b> Viết 1 chương trình con tên DELAY_20ms tạo trễ 20ms, biết fOSC = 12 MHz.
<b>3.34.</b> Viết 1 chương trình con tên DELAY_1s tạo trễ 1s, biết fOSC = 12 MHz.


<b>3.35.</b> Viết chương trình xuất chuỗi xung vng đối xứng có tần số f = 1KHz ra chân P1.0.


</div>
<span class='text_page_counter'>(22)</span><div class='page_container' data-page=22>

<b>3.37.</b> Viết chương trình điều khiển đèn giao thông tại ngã tư đường (đèn xanh sáng 4s, đèn
vàng sáng 1s, đèn đỏ sáng 5s). Dùng port 1 để điều khiển các đèn với quy định nếu xuất ra mức
logic 0 là đèn sáng:


<b>Chân Đèn đường 1 Chân Đèn đường 2 </b>


P1.0 Xanh P1.3 Xanh
P1.1 Vàng P1.4 Vàng


P1.2 Đỏ P1.5 Đỏ


<b>3.38.</b> Cho mạch kết nối như sau:


a. Hãy viết chương trình để sáng từng LED theo chiều D1 → D8 và lặp lại, thời gian sáng
của mỗi LED là 0,2s.


b. Tương tự câu a nhưng theo chiều D8 → D1.


c. Kết hợp câu a và b: sáng theo chiều D1 → D8 rồi D8 → D1 và lặp lại.


d. Hãy viết chương trình để các LED sáng lan tỏa theo chiều D1 → D8 rồi tắt dần theo chiều
D1 → D8 và lặp lại, thời gian cách nhau giữa 2 LED là 0,2s.


e. Tương tự câu d nhưng theo chiều từ D8 → D1.


<b>3.39.</b> Cho mạch kết nối như sau:


Hãy viết chương trình để xuất giá trị BCD trong ô nhớ 30H ra LED 7 đoạn.
P1.0
P1.3
P1.4
P1.5
P1.6
P1.1
P1.2
P1.7
8051
D1
D3


D7
74HC373
1
11
2
5
6
9
12
15
16
19
3
4
7
8
13
14
17
18
OE
LE
Q1
Q2
Q3
Q4
Q5
Q6
Q7
Q8

D1
D2
D3
D4
D5
D6
D7
D8
D5


330 x 8


</div>

<!--links-->

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×