This is default featured slide 1 title
Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.
This is default featured slide 2 title
Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.
This is default featured slide 3 title
Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.
This is default featured slide 4 title
Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.
This is default featured slide 5 title
Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.
AVR Studio 4, 5 and 6 download links
Assembly cho AVR
Phần
này tôi giới thiệu một số instruction mà chúng ta rất hay sử dụng khi lập
trình
cho
AVR. Tôi sẽ chia các instruction này ra thành nhiều nhóm dựa theo phạm vi tác
động và chức năng của chúng.
Trước
hết chúng ta thống nhất một số cách sử dụng ký hiệu trong cách viết cú pháp
của các instruction như sau:
Khái
niệm nguồn (Source), đích (Destination) là chỉ các toán hạng và kết quả trong
các phép toán đại số và Logic, ví dụ ADD R1, R2 là lệnh cộng 2 giá trị chứa
trong 2 thanh ghi R1, R2, trong trường hợp này cả R1 và R2 đều được gọi là
nguồn vì chứa giá trị trước khi thực hiện phép cộng. Sau khi phép cộng được
thực hiện, kết quả được chứa lại trong R1 và vì thế R1
được
gọi là đích trong trường hợp này. R1 vừa là nguồn, vừa là đích trong khi R2
chỉ là
nguồn,
nếu viết ví dụ này dưới dạng tổng quát sẽ là : ADD Rd, Rr.
- LDI (LoaD Immediate).
- MOV (MOVE).
- CLR (CLEAR Register).
- SER (SET Register).
- CBR (CLEAR Bit in Register).
- SBR (SET Bit in Register).
- BLD (Bit LoaD from T Flag).
- BST (Bit Storage from T Flag).
- CPI (COMPARE with Immediate).
- ANDI (AND with Immediate).
- AND (Logical AND).
- ORI (Logical OR with Immediate).
- OR (Logical OR).
- LSL (Logical Shift Left).
Kết quả là R1=10000110 và cờ C =1
vì thanh ghi R1 đã được dịch sang trái 1
vị trí, trước khi dịch bit 7 của
R1 là 1 nên sau khi dịch bit này được chứa trong C,
cho nên C=1.
- LSR (Logical Shift Right).
Kết quả là R1=01100001 và cờ C =1
vì thanh ghi R1 đã được dịch sang phải
1 vị trí, trước khi dịch bit 0 của
R1 là 1 nên sau khi dịch bit này được chứa
trong C, cho nên C=1.
- ADD (ADD without Carry).
- INC (INCrement).
- SUB (SUBtract without Carry).
- SUBI (SUBtract Immediate).
- DEC (DECrement).
- MUL (MULtiply unsigned).
Bốn instruction sau đây được thiết kế riêng để truy cập
vùng nhớ I/O, các instruction
này sử dụng địa chỉ I/O của các thanh ghi trong vùng nhớ
này. Vì là thiết kế riêng cho
vùng nhớ I/O, bạn không thể sử dụng các thanh ghi này để
truy cập RF hay SRAM.
Trong các cú pháp của instruction này, khái niệm địa chỉ A
là địa chỉ I/O, 0 ≤ A ≤ 63
, nếu trong ví dụ A=0x00 thì đó là thanh ghi đầu tiên của
vùng I/O, không phải là
thanh ghi R0.
- OUT (OUTPUT Data).
- IN (INPUT Data).
- SBI (Set Bit in I/O Register).
- CBI (Clear Bit in I/O Register).
Trong Register File của AVR, các thanh ghi từ R26 đến
R31ngoài chứa năng
thanh ghi thông thường còn có chức năng là con trỏ
(Pointer) trong việc truy
cập bộ nhớ (cả bộ nhớ data và bộ nhớ Program). Nếu được sử
dụng như các
Pointer, các thanh ghi trên được biết đến với tên gọi X,
Y, Z. Định nghĩa như
sau: X=R27:R26, Y=R29:R28, Z=R31:R30. Chúng là 3 thanh ghi
16 bit được
định nghĩa trước cho tất cả các AVR. Ngoài ra trong các
file định nghĩa cho
chip chúng ta có thêm 6 định nghĩa khác là XL, XH, YL, YH,
ZL, ZH cũng
chính là tên gọi của R26-> R31. Phần này chúng ta khảo
sát một số instruction
dùng truy cập toàn bộ khồi nhớ của AVR bằng cách sử dụng
địa chỉ trực tiếp
và bằng cách sử dụng Pointer.
- LDS (LoaD direct from data Space).
- STS (STorage direc to data
Space).
Sử dụng địa chỉ trực tiếp thì câu lệnh sẽ đơn giản nhưng
rất khó nhớ phần địa chỉ,
thông thường SRAM là vùng chúng ta hay sử dụng để chứa
biến tạm thời, trong
các ngôn ngữ cấp cao ta chỉ cần nhớ tên biến nhưng với ASM
chúng ta phải nhớ
địa chỉ của chúng. Một cách tốt để tránh việc này là dùng
chỉ thị (DIRECTIVE,
bạn xem lại bài 1) . EQU để gán tên biến cho 1 địa chỉ, ví
dụ .EQU
bientam = 0x0060 và sau đó sử dụng bientam thay cho
0x0060.
Một cách khác được dùng để truy cập bộ nhớ mà không dùng
địa chỉ tuyệt đối
là sử dụng sử dụng con trỏ. Có 2 instruction hỗ trợ con
trỏ là LD(LoaD indirect
from data Space), và ST (STorage indirec to data
Space), LD đọc dữ liệu từ
SRAM vào thanh ghi còn ST lưu dữ liệu từ thanh ghi vào
SRAM. Cả 3 con
trỏ X, Y và Z đều có thể được dùng nhưng có một số điểm
lưu ý: cả 3 đều
dùng được trong trường hợp truy xuất thông thường nhưng
với cách truy cập
có offset, con trỏ X không sử dụng được. Để truy xuất bộ
nhớ chương trình
bằng con trỏ thì Z là giải pháp duy nhất…Dưới đây là 1 số
cách sử dụng LD,
ST kết hợp với con trỏ, chúng ta xét thông qua các ví dụ.
Ví dụ 1:
CLR R27 ; xóa R27, tức
xóa byte cao của pointer X
LDI R26, 0x60 ; load giá trị 0x60 vào R26, tức byte thấp của pointer X ; sau 2 dòng trên, giá pointer X là 0x0060, sẵn sàng để trỏ đến vị trí đầu tiên
trong SRAM.
LD R1, X+ ; Load giá trị ở ố nhớ 0x0060 vào R1 (vì X trỏ đến 0x0060), sao đó
tăng giá trị ;X lên 1,
như thế sau lệnh này X=0x0061
LD R2, X+ ; Load giá trị ở ố nhớ 0x0061 vào R2, sao đó tăng giá trị ;X lên 1,
như thế sau lệnh này
X=0x0062
LD R3, X ; Load giá trị ở ô nhớ 0x0062 vào R3 và không thay đổi X LD R4, -X ; Giảm giá trị của X trước (X=0x0061), sau đó load giá trị ở ô nhớ
0x0061 vào R4
Từ ví dụ này chúng ta thấy có 3 cách cơ bản để load dữ
liệu từ SRAM bằng
con trỏ, cách Load trực tiếp trong trường hợp LD R3,
X, cách load post
-increment (hoặc post-decrement) như trong trường hợp LD
R1, X+ và cách
load pre-decrement (hoặc pre-increment) trong trường hợp
LD R4, -X.
Chúng ta có thể viết lại ví dụ trên nhưng sử dụng con trỏ
Y hoặc Z thay cho
X. Ví dụ viết cho instruction ST cũng hoàn toàn tương tự.
Tuy nhiên cách truy cập theo cách pre hay post đều làm
thay đổi giá trị của con
trỏ, điều này có 1 bất lợi là nếu chúng ta muốn quay lại
vị trí ô nhớ nào đó,
chúng ta phải tiếp tục thay đổi con trỏ. Để tránh việc làm
này, 1 cách truy cập
khác được hỗ trợ là truy cập “Offset”. Xét ví dụ sau:
LD R1, Y+1
Đây chính là cách truy cập Offset dùng con trỏ Y, cách
viết trên là tương đương
với cách viết
LD R1, Y+
Nhưng điểm khác biệt ở đây là cách viết Offset không làm
thay đổi giá trị của
con trỏ Y. Sử dụng Offset có ưu điểm như sử dụng mảng
(array) trong các ngôn
ngữ lập trình cấp cao. Cần chú ý là giá trị offset không
vượt quá 63 và phương
pháp này chỉ dùng cho 2 thanh ghi Y và Z.
Không giống như các ngôn ngữ cấp cao, khi lập trình bằng
ASM bạn không được
hỗ trợ các cấu trúc điều khiển như If, For, While…người
lập trình ASM phải tự
xây dựng cho mình các cấu trúc này từ những instruction cơ
bản. Nếu bạn có trong
tay tài liệu tra cứu instruction cho AVR bạn sẽ thấy có
rất nhiều instruction có
dạng BRxx, với BR là viết tắt của từ Branch (rẽ nhánh).
Đây là các instruction
cơ bản giúp bạn xây dựng các cấu trúc điều khiển tương
đương If, For, While
…cho riêng mình.
Trước hết ta sẽ khảo sát instruction BRNE bằng cách xem
lại ví dụ trong bài
"Làm quen AVR", đây là đoạn chương trình
con DELAY:
DELAY:
LDI R20, 0xFF DELAY0: LDI R21, 0xFF DELAY1: DEC R21 BRNE DELAY1 DEC R20 BRNE DELAY0 RET
Bạn hãy chú ý 4 dòng lệnh nằm giữa đoạn chương trình trên
(bắt đầu từ dòng 4),
dòng đầu tiên thì bạn đã biết - load giá trị 255 vào thanh
ghi R21, sau đó tôi
đặt 1 label DELAY1- xem như là 1 cột mốc, dòng 3,
instruction DEC bạn mới
được học hôm nay - giảm giá trị thanh ghi R21 đi 1 đơn vị,
và cuối cùng
BRNE DELAY1, BRNE là viết tắt của BRanch if Not Equal – rẽ
nhánh nếu
không bằng, thực ra bản chất của lệnh này là rẽ nhánh nếu
cờ Zero không
bằng 1. Như thế câu lệnh BRNE DELAY1 của chúng ta được AVR
thực
hiện như sau: kiểm tra cờ Z, nếu Z=1 tiếp tục thực hiện
dòng tiếp theo sau
mà không quan tâm đến nhãn DELAY1, nhưng nếu Z=0 thì nhảy
đến nhãn
DELAY1. Bạn thấy rằng ban đầu R21 =255, sau khi giảm 1 bởi
DEC, thanh
ghi R21=254≠0, cờ Z =0, rẽ nhánh xảy ra, bộ đếm chương
trình nhảy về nhãn
DELAY1. Quá trình này lặp lại khoảng 255 lần trước khi R21
=0 dẫn đến Z=1.
Bao bên ngoài vòng lặp của nhãn DELAY1 là vòng lặp của
nhãn DELAY0, cách
hiểu hoàn toàn tương tự nhưng trước khi lệnh DEC R20 được
thực thi thì phải
chờ cho vòng lặp DELAY1 kêt thúc. Bản thân DELAY0 cũng là
1 vòng lặp
255 lần. kết quả cuối cùng là ta thu được 1 vòng lặp
khoảng 255x255 lần mà
không làm gì cả, đó chính là ý nghĩa và cách hoạt động của
đoạn chương trình
con DELAY.
Bên cạnh BRNE chúng ta có 1 số instruction phục vụ rẽ
nhánh khác như:
- BREQ (BRanch if EQual).
- BRLO (BRanch if LOwer).
- BRSH (BRanch if Same or Higher).
đó việc rẽ nhánh sẽ xảy ra nếu
thanh ghi Rd ≥Rr.
Còn rất nhiều instruction rẽ nhánh
bạn có thể sử dụng để tạo cấu trúc điều
khiển, chú ý là các instruction
này đều hoạt động dựa trên trạng thái của 1
cờ nào đó, do đó bạn cần lựa chọn
1 lệnh phù hợp để thực thi trước các
instruction rẽ nhánh này, để làm
được như vậy bạn cần xem kỹ tài liệu
hướng dẫn INSTRUCITON cho AVR.
|