Professional Documents
Culture Documents
BT VXL
BT VXL
Đối với việc viết mã máy của các lệnh trong họ microcontroller AVR, ta cần dựa vào cấu
trúc mã lệnh của từng lệnh cụ thể. Dưới đây là mã máy cho mỗi lệnh được liệt kê:
1. LDI R18,0
Opcode cho LDI Rd,K có dạng: 1110 KKKK dddd KKKK với Rd là R16 đến R31,
và K là giá trị hằng số.
R18 là 0010 (tính từ 16) và K = 0000 0000 .
Mã máy: 1110 0000 0010 0000
2. LDI R29,0x01
R29 là 1101 (tính từ 16) và K = 0000 0001 .
Mã máy: 1110 0001 1101 0001
3. LDI R28,0x0A
R28 là 1100 (tính từ 16) và K = 0000 1010 .
Mã máy: 1110 1010 1100 1010
4. LDI R19,10
R19 là 0011 (tính từ 16) và K = 0000 1010 .
Mã máy: 1110 1010 0011 1010
5. ST -Y,R18
Opcode cho ST -Y,Rr có dạng khác và phụ thuộc vào cụ thể của compiler,
nhưng thường là 1001 001r rrrr 0010 , với r là số của thanh ghi.
R18 có số là 0010.
Mã máy: Cần thông tin cụ thể hơn về mã máy của AVR được sử dụng.
6. DEC R19
Opcode cho DEC Rr có dạng: 1001 010r rrrr 1010 , với r là số của thanh ghi.
R19 có số là 0011.
Mã máy: 1001 0100 0011 1010
7. BRNE TIEP
Opcode cho BRNE k có dạng: 1111 01kk kkkk k001 , với k là offset từ vị trí
hiện tại. Giá trị của k phụ thuộc vào địa chỉ của nhãn TIEP và địa chỉ hiện
tại.
Mã máy: Phụ thuộc vào địa chỉ cụ thể của nhãn TIEP và vị trí lệnh BRNE
trong bộ nhớ.
Nếu biết tần số hoạt động của AVR (ví dụ, 1MHz), ta có thể tính được thời gian thực tế
bằng cách chia tổng số chu kỳ cho tần số hoạt động.
Khởi tạo thanh ghi R18 với giá trị 0, R29 và R28 với các giá trị khởi tạo cho một
địa chỉ nhớ (có thể là địa chỉ base của một mảng), và R19 với giá trị 10.
Sau đó, chương trình lưu giá trị 0 từ R18 vào vị trí nhớ được chỉ định bởi thanh
ghi Y (R29:R28) và giảm địa chỉ Y sau mỗi lần lưu.
Điều này được lặp lại 10 lần, giảm R19 mỗi lần và kiểm tra nếu R19 không bằng 0
để tiếp tục lặp.
Cơ bản, đoạn chương trình này thiết lập một vùng nhớ 10 bytes với giá trị 0, bắt
đầu từ địa chỉ cao và đi xuống thấp hơn.
LSL R20 ; Dịch trái byte thấp, bit cao nhất chuyển vào C của SREG
ROL R21 ; Dịch trái byte cao và nhận bit C từ phép dịch trước
b, ; Lưu ý: AVR không trực tiếp hỗ trợ quay một giá trị 16-bit qua cả hai byte.
ROL R20 ; Quay trái byte thấp, bit cao nhất chuyển vào C của SREG
ROL R21 ; Quay trái byte cao và nhận bit C từ phép quay trước
9,10,
.org 0x0000
rjmp start
.org 0x0100
start:
ldi ZH, high(2*TAB) ; Địa chỉ cao của TAB trong bộ nhớ Flash
ldi XH, high(S_BUF) ; Địa chỉ cao của S_BUF trong SRAM
copy_loop:
cpse R16, R1 ; Kiểm tra xem byte vừa đọc có phải là ký tự kết thúc chuỗi không
.org 0x0100
start_reading:
read_loop:
in R16, PIND ; Đọc giá trị từ PORTD
out PORTB, R16 ; Nếu trong khoảng 0x20 đến 0x7F, xuất ra PORTB
rjmp read_loop
not_valid:
rjmp read_loop
11,12,
; Chương trình đọc byte data từ PORTA và xuất bit0 đầu tiên tìm được ra PORTC
.org 0x0000
start:
read_loop:
check_bit:
lsr R16 ; Dịch bit sang phải để kiểm tra từng bit
rjmp check_bit ; Tiếp tục kiểm tra các bit tiếp theo
bit_set:
COMP16:
movw R22, R18 ; Sao chép giá trị của R19:R18 vào R22:R21
cp R21, R23 ; So sánh byte cao của số thứ nhất và số thứ hai
cpc R20, R22 ; So sánh byte thấp của số thứ nhất và số thứ hai kèm theo carry của byte cao
ret
13,14,
ADD16:
adc result_low, Y+
ld result_high, Z+
adc result_high, Y+
brcc no_carry
rjmp done
no_carry:
done:
ret
SUB16:
ld result_low, Z+
sbb result_low, Y+
ld result_high, Z+
sbb result_high, Y+
; Kiểm tra carry để xem có tràn (âm) không
brcc no_overflow
rjmp done
no_overflow:
done:
ret
15,16
; Chương trình con MUL16_8 - Nhân số nhị phân 16 bit cho 8 bit
; KQ = số nhân(8):số bị nhân(16)
MUL16_8:
; Kết quả của mỗi lần nhân được cộng vào kết quả
mul R21, R23 ; Nhân byte cao của số bị nhân với số nhân
movw R24, R0 ; Kết quả byte cao được lưu trong R1:R0
mul R21, R22 ; Nhân byte thấp của số bị nhân với số nhân
add R20, R1 ; Cộng byte thấp của kết quả với kết quả từ lần nhân trước
adc R21, R24 ; Cộng byte giữa của kết quả với kết quả từ lần nhân trước
adc R22, R25 ; Cộng byte cao của kết quả với kết quả từ lần nhân trước
ret
; Chương trình con DIV16_8 - Chia số nhị phân 16 bit cho 8 bit
; Kết quả trả về thương số 16 bit (trong thanh ghi số bị chia) và dư số 8 bit
DIV16_8:
div_loop:
rol R23
rol R24
rol R20
sbrs R23, 7 ; Nếu dư số âm hoặc số chia âm, không thực hiện phép trừ
rjmp no_sub
; Trừ số chia từ dư số
sub R23, R21 ; Trừ byte cao của số chia từ byte cao của dư số
sbrs R23, 7 ; Nếu dư số âm sau phép trừ, không cộng lại số chia
no_sub:
; Dịch kết quả thương sang trái để chuẩn bị cho bit mới
rol R23
rol R24
rol R20
rol R22
brne div_loop ; Lặp lại quá trình cho đến khi dịch hết các bit
ret
17,18,
.def x = R16
rjmp main
main:
; Tính y = x^2 - x + 1
mov y_low, x ; y = x
loop:
rjmp loop
Chương trình chuyển số BCD thành số HEX
.include "m324pdef.inc" ; Định nghĩa ATmega324P
BCD_HEX8:
ldi temp, 10
div hex_output, temp ; Chia hex_output cho 10 (lưu kết quả vào hex_output)
ldi temp, 10
add temp, r0
ret
19,20,
; Trả về kết quả số BCD (00-99) và C=1 nếu kết quả tràn
ADD_BCD:
; Cờ tràn (carry) được trả về trong thanh C (C=1 nếu kết quả tràn, ngược lại C=0)
; Cộng byte thấp của số BCD thứ nhất với byte thấp của số BCD thứ hai
subi R20, 10 ; Trừ 10 từ byte thấp của kết quả (vì chúng ta đang làm việc với BCD)
no_overflow:
; Cộng byte cao của số BCD thứ nhất với byte cao của số BCD thứ hai
done:
ret
; Trả về kết quả số BCD (00-99) và C=1 nếu kết quả tràn (âm)
SUB_BCD:
; Cờ tràn (carry) được trả về trong thanh C (C=1 nếu kết quả tràn, ngược lại C=0)
; Trừ byte thấp của số BCD thứ hai từ byte thấp của số BCD thứ nhất
no_overflow_sub:
; Trừ byte cao của số BCD thứ hai từ byte cao của số BCD thứ nhất
done_sub:
ret
21
DELAY_MS:
; Tham số: R16 là biến thời gian (1, 10, 100 tùy thuộc vào cấu hình)
delay_loop:
ldi R18, 250 ; Đặt giá trị lớn để tạo ra thời gian trễ
ldi R19, 5
delay_inner_loop:
dec R19
brne delay_inner_loop
dec R18
brne delay_inner_loop
dec R16
brne delay_loop
ret
22,
rjmp main
main:
; Tính toán giá trị tải vào thanh ghi OCR0A để đạt được tần số 1KHz
sei
main_loop:
rjmp main_loop
23,
rjmp main
main:
ldi temp, (1<<WGM01) | (1<<WGM00) ; Chọn chế độ Fast PWM cho Timer 0
; Tính toán giá trị tải vào thanh ghi OCR0A để đạt được tần số 1KHz và tỷ lệ nhiệt độ 30%
sei
main_loop:
rjmp main_loop
(c) DECR7:
1,
(b) Tính giá trị R17 sau khi thực thi mỗi dòng lệnh:
Vì vậy, sau khi thực thi toàn bộ đoạn lệnh, giá trị cuối cùng của R17 là 168.
2,
R20 = 50
R21 = 33
R1:R0 = 0x0672 (tương ứng với kết quả của phép nhân 1650)
Ô nhớ 0x1C7 chứa giá trị 0x72 .
Ô nhớ 0x1C8 chứa giá trị 0x06 .
LOOP1: ST X+,R17: Lưu giá trị từ thanh ghi R17 (0) vào bộ đệm tại địa chỉ hiện tại
của X, sau đó tăng địa chỉ của X. Điều này khởi tạo bộ đệm với giá trị 0.
DEC R16: Giảm giá trị của R16 đi 1.
BRNE LOOP1 : Nếu R16 chưa bằng 0, quay lại nhãn LOOP1 . Điều này tạo thành một
vòng lặp lặp lại 10 lần, tương ứng với kích thước của bộ đệm.
LDI R17,$80 : Load giá trị hexa 80 vào thanh ghi R17.
STS DAT1,R17 : Lưu giá trị từ thanh ghi R17 vào vùng nhớ DAT1 . Vì DAT1 được khai
báo sau S_BUF, địa chỉ thực tế của nó phụ thuộc vào kích thước của S_BUF và vị trí
bắt đầu của .DSEG . Dựa trên khai báo, DAT1 nằm tại địa chỉ
Chương 6
6,1
.org 0x00
init:
; Cấu hình PORTA để nhập (đọc) dữ liệu với điện trở kéo lên
ldi r16, 0xFF ; Đặt giá trị 0xFF để cấu hình tất cả các chân là đầu vào
out DDRA, r16 ; Cấu hình tất cả các chân của PORTA là đầu vào
ldi r16, 0xFF ; Kích hoạt điện trở kéo lên cho tất cả các chân
ldi r16, 0xFF ; Đặt giá trị 0xFF để cấu hình tất cả các chân là đầu ra
out DDRB, r16 ; Cấu hình tất cả các chân của PORTB là đầu ra
main:
; Một vòng lặp vô hạn, có thể đọc dữ liệu từ PORTA và ghi ra PORTB ở đây
6,2
.org 0x00
init:
; Cấu hình PORTB là port xuất/nhập, ban đầu là port xuất và xóa tất cả tín hiệu xuất = 0
ldi r16, 0x00 ; Xóa tất cả tín hiệu xuất trên PORTB bằng 0
; Cấu hình PORTC với 3 bit thấp xuất, 2 bit cao kế tiếp nhập, có điện trở kéo lên
ldi r16, 0x07 ; 0000 0111 - 3 bit thấp là đầu ra, các bit còn lại là đầu vào
; Kích hoạt điện trở kéo lên cho 2 bit đầu vào cao của PORTC
ldi r16, 0x60 ; 0110 0000 - Chỉ kích hoạt điện trở kéo lên cho 2 bit đầu vào
main:
; Đặt một vòng lặp vô hạn ở đây nếu muốn thực hiện thêm hoạt động nào đó
; Ví dụ: Đọc tín hiệu từ PORTC và thực hiện hành động dựa trên tín hiệu đó
; Hoặc cập nhật tín hiệu xuất trên PORTB hoặc PORTC dựa trên logic nào đó
6,3
.org 0x00
init:
; Cấu hình PORTB là port xuất/nhập, ban đầu là port xuất và xóa tất cả tín hiệu xuất = 0
ldi r16, 0x00 ; Xóa tất cả tín hiệu xuất trên PORTB bằng 0
ldi r16, 0x07 ; 0000 0111 - 3 bit thấp là đầu ra, các bit còn lại là đầu vào
; Kích hoạt điện trở kéo lên cho 2 bit đầu vào cao của PORTC
ldi r16, 0x60 ; 0110 0000 - Chỉ kích hoạt điện trở kéo lên cho 2 bit đầu vào
check_signals:
signal_pc3_low:
rjmp check_signals
signal_pc4_low:
rjmp check_signals
6,4
.org 0x00
rjmp init
.org 0x100
init:
ldi r16, 0xFE ; 1111 1110 - PC0 là đầu vào, các chân khác là đầu ra (nếu cần)
ldi r16, 0x01 ; Kích hoạt điện trở kéo lên cho PC0
main_loop:
sbis PINC, 0 ; Kiểm tra PC0, nếu PC0 = 1 thì nhảy qua check_PC0
rjmp output_string
rjmp main_loop ; Lặp lại kiểm tra nếu PC0 không phải là 0
output_string:
ldi r26, low(TABLE) ; Đặt địa chỉ thấp của TABLE vào X
ldi r27, high(TABLE) ; Đặt địa chỉ cao của TABLE vào X
print_char:
lpm r16, Z+ ; Đọc ký tự từ bảng tra vào r16 và tăng Z
breq main_loop ; Nếu là NULL thì quay lại vòng lặp chính
6,5
.org 0x00
rjmp init
.org 0x100
init:
out DDRC, r16 ; PC0 là đầu vào, PC2 là đầu ra, các chân khác có thể là đầu ra
out PORTC, r16 ; Kích hoạt điện trở kéo lên cho PC0
main_loop:
sbis PINC, 0
rjmp output_string
rjmp main_loop
output_string:
ldi r26, low(TABLE)
print_char:
lpm r16, Z+
breq main_loop
6,6
.org 0x00
rjmp start
; Định nghĩa
#define DEBOUNCE_TIME 10 ; Khoảng thời gian debounce tương ứng với ~10ms
start:
; Cấu hình PORTC là input cho nút nhấn với điện trở kéo lên
main_loop:
sbic PINC, 0
rcall switch_pressed_SW0
sbic PINC, 1
rcall switch_pressed_SW1
rjmp main_loop
switch_pressed_SW0:
; Chống rung
call debounce
; L0 sáng, L1 tối
ret
switch_pressed_SW1:
; Chống rung
call debounce
; L1 sáng, L0 tối
ret
debounce:
debounce_loop:
dec r17
brne debounce_loop
ret
6,7
rjmp init
init:
; Cài đặt PORTC làm đầu vào với điện trở kéo lên cho các nút nhấn
rjmp main_loop
; Kiểm tra các nút nhấn và chuyển đến chương trình con tương ứng
sbis PINC, 0
rcall turn_off_leds
sbis PINC, 1
rcall toggle_leds
sbis PINC, 2
rcall shift_led
sbis PINC, 3
rcall center_to_sides_leds
rjmp main_loop
turn_off_leds:
clr r16
ret
; Chuyển đổi trạng thái của các LED với thời gian sáng/tối 0.5s
toggle_leds:
ret
; Dịch LED từ bit thấp đến cao và quay vòng
shift_led:
ret
center_to_sides_leds:
Ret
((((.org 0x00
rjmp init
.org 0x30 ; Bắt đầu chương trình sau phần vector ngắt
.equ DELAY_500MS = 19531 ; Giả định cho 0.5s với việc sử dụng vòng lặp đơn giản
init:
sbic PINC, 0
rjmp turn_off_leds
sbic PINC, 1
rjmp toggle_leds
sbic PINC, 2
rjmp shift_led
sbic PINC, 3
rjmp center_to_sides_leds
rjmp main_loop
turn_off_leds:
clr r16
rjmp main_loop
toggle_leds:
rjmp main_loop
shift_led:
rjmp main_loop
center_to_sides_leds:
rjmp main_loop
; Mã hàm đợi đơn giản (chú ý: không chính xác theo thời gian thực)
wait_half_second:
wait_loop:
dec r19
brne wait_loop
dec r18
brne wait_loop
ret))
6,10
ROL_16:
; Quay trái byte thấp, cũng đưa bit Carry vào byte thấp
rol r16 ; Quay trái L_BYTE, đồng thời đưa bit từ cờ Carry vào
; Kết thúc, bit cao nhất từ H_BYTE đã được đẩy vào bit thấp nhất của L_BYTE
Ret
6,11
.org 0x00
rjmp init
init:
out PORTC, r16 ; Kích hoạt điện trở kéo lên cho PORTC
clr r16
main_loop:
; Kiểm tra nút nhấn và chuyển đổi trạng thái tương ứng
; mã cụ thể cho việc xử lý nút nhấn và thực hiện phép chia không được cung cấp
; Hãy bổ sung logic cho từng trạng thái và xử lý nút nhấn tại đây
rjmp main_loop
6,22
Macro/Hàm Ghi vào EEPROM:
; Macro để ghi một byte vào EEPROM
WRITE_EEPROM:
rjmp WRITE_EEPROM
ret
; Tham số: R18 là địa chỉ, Kết quả trả về trong R19
READ_EEPROM:
rjmp READ_EEPROM
out EEAR, R18 ; Đặt địa chỉ EEPROM
ret