Download as pdf or txt
Download as pdf or txt
You are on page 1of 38

Chương 3

4, (a) Viết mã máy các lệnh

Đố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ớ.

(b) Tính thời gian thực thi đoạn chương trình


Thời gian thực thi cho mỗi lệnh trong AVR thường là một hoặc hai chu kỳ máy, tùy
thuộc vào lệnh cụ thể và loại microcontroller. Với giả định rằng mỗi lệnh thực thi trong
1 chu kỳ máy (và lệnh nhảy có điều kiện mất 2 chu kỳ nếu nhảy), ta có:

 LDI: 1 chu kỳ x 4 lệnh = 4 chu kỳ


 ST -Y: 1 chu kỳ
 DEC: 1 chu kỳ
 BRNE: 1 hoặc 2 chu kỳ (2 chu kỳ khi nhảy, giả sử luôn nhảy cho tới lần cuối cùng)
 Vòng lặp này thực hiện 10 lần do DEC R19 từ 10 xuống 0.

Tổng số chu kỳ = (4 chu kỳ từ LDI) + (10 lần * (1 (ST) + 1 (DEC) + 2 (BRNE))) = 4 + 10 * 4


= 44 chu kỳ máy.

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.

(c) Ý nghĩa đoạn chương trình

Đoạn chương trình thực hiện các nhiệm vụ sau:

 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.

7,a, ; Dịch trái số nhị phân 16-bit R21:R20

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.

; Đoạn lệnh sau giả định rằng cờ Carry ban đầu là 0.

CLC ; Xóa cờ Carry để bắt đầu

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,

; Chương trình chuyển chuỗi từ Flash sang SRAM

.org 0x0000

rjmp start

.org 0x0100

TAB: .db "YourDataHere", 0x00 ; Chuỗi dữ liệu trong bộ nhớ Flash

start:

ldi ZH, high(2*TAB) ; Địa chỉ cao của TAB trong bộ nhớ Flash

ldi ZL, low(2*TAB) ; Địa chỉ thấp của TAB

ldi XH, high(S_BUF) ; Địa chỉ cao của S_BUF trong SRAM

ldi XL, low(S_BUF) ; Địa chỉ thấp của S_BUF

copy_loop:

lpm R16, Z+ ; Đọc byte từ Flash và tăng địa chỉ Z

st X+, R16 ; Lưu byte vào SRAM và tăng địa chỉ X

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

rjmp copy_loop ; Nếu không, tiếp tục vòng lặp

; Chương trình đọc data từ PORTD và xuất ra PORTB

.org 0x0100

start_reading:

ldi R16, 0xFF ; Cấu hình PORTB là đầu ra

out DDRB, R16

ldi R16, 0x00 ; Cấu hình PORTD là đầu vào

out DDRD, R16

read_loop:
in R16, PIND ; Đọc giá trị từ PORTD

cpi R16, 0x20 ; So sánh giá trị với 0x20

blt not_valid ; Nếu nhỏ hơn 0x20, nhảy tới not_valid

cpi R16, 0x7F ; So sánh giá trị với 0x7F

bgt not_valid ; Nếu lớn hơn 0x7F, nhảy tới not_valid

out PORTB, R16 ; Nếu trong khoảng 0x20 đến 0x7F, xuất ra PORTB

rjmp read_loop

not_valid:

ldi R16, 0x00 ; Đặt PORTB = 0x00

out PORTB, R16

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:

ldi R16, 0xFF ; Cài đặt PORTC là đầu ra

out DDRC, R16

ldi R16, 0x00 ; Cài đặt PORTA là đầu vào

out DDRA, R16

read_loop:

in R16, PINA ; Đọc giá trị từ PORTA

clr R17 ; Đặt R17 = 0 để lưu vị trí của bit0

ldi R18, 1 ; Bắt đầu dò từ bit trọng số nhỏ nhất

check_bit:

lsr R16 ; Dịch bit sang phải để kiểm tra từng bit

brcc bit_set ; Nếu bit hiện tại là 1, nhảy tới bit_set


inc R17 ; Nếu bit hiện tại là 0, tăng R17 (lưu vị trí bit0)

rjmp check_bit ; Tiếp tục kiểm tra các bit tiếp theo

bit_set:

mov R16, R17 ; Di chuyển vị trí bit0 vào R16

out PORTC, R16 ; Xuất vị trí bit0 ra PORTC

rjmp read_loop ; Lặp lại quá trình đọc và kiểm tra

; Chương trình con so sánh 2 số nhị phân 16 bit

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,

.include "m324pdef.inc" ; Định nghĩa ATmega324P

; Định nghĩa các thanh ghi chung

.def temp = R16

.def result_low = R17

.def result_high = R18

.def carry_flag = R19

; Chương trình con ADD16

ADD16:

; Tham số: R15:R14 là số bị cộng, R13:R12 là số cộng

ldi temp, 0 ; Khởi tạo biến đếm carry_flag

; Cộng từng cặp bit tương ứng và theo dõi carry


ld result_low, Z+

adc result_low, Y+

ld result_high, Z+

adc result_high, Y+

; Kiểm tra carry để xem có tràn không

brcc no_carry

ldi carry_flag, 1 ; Nếu có tràn, set carry_flag là 1

rjmp done

no_carry:

ldi carry_flag, 0 ; Không có tràn, set carry_flag là 0

done:

ret

; Chương trình con SUB16

SUB16:

; Tham số: R15:R14 là số bị trừ, R13:R12 là số trừ

ldi temp, 0 ; Khởi tạo biến đếm carry_flag

; Trừ từng cặp bit tương ứng và theo dõi carry

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

ldi carry_flag, 1 ; Nếu có tràn (âm), set carry_flag là 1

rjmp done

no_overflow:

ldi carry_flag, 0 ; Không có tràn (âm), set carry_flag là 0

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:

; Số bị nhân (16 bit) được lưu trong R23:R22

; Số nhân (8 bit) được lưu trong R21

; Kết quả được trả về trong R23:R21

; Khởi tạo giá trị kết quả

clr R20 ; R20 lưu byte thấp của kết quả

clr R21 ; R21 lưu byte giữa của kết quả

clr R22 ; R22 lưu byte cao của kết quả

; Nhân từng byte của số bị nhân với số nhân

; 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:

; Số bị chia (16 bit) được lưu trong R23:R22

; Số chia (8 bit) được lưu trong R21

; Thương số (16 bit) được trả về trong R23:R22

; Dư số (8 bit) được trả về trong R20

; Khởi tạo giá trị thương và dư

clr R20 ; R20 lưu dư số

clr R23 ; R23 lưu byte cao của thương số

clr R24 ; R24 lưu byte thấp của thương số

; Duyệt từng bit của số bị chia

ldi R16, 16 ; Số lần dịch (số bit của số bị chia)

div_loop:

; Dịch trái số bị chia để chuẩn bị cho phép trừ

rol R23

rol R24

rol R20

; Kiểm tra dấu của dư số và số chia

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

subi R24, 1 ; Nếu dư số âm sau phép trừ, giảm thương số đi 1

no_sub:

; Dịch kết quả thương sang trái để chuẩn bị cho bit mới

rol R23

rol R24

; Dịch dư số sang trái để chuẩn bị cho bit mới

rol R20

; Dịch trái số bị chia để chuẩn bị cho bit tiếp theo

rol R22

dec R16 ; Giảm số lần dịch còn lại

brne div_loop ; Lặp lại quá trình cho đến khi dịch hết các bit

ret

17,18,

.include "m324pdef.inc" ; Định nghĩa ATmega324P

; Định nghĩa các thanh ghi chung

.def x = R16

.def y_low = R17


.def y_high = R18

; Chương trình chính

.org 0x0000 ; Địa chỉ bắt đầu chương trình

rjmp main

main:

ldi x, 8 ; Gán giá trị cho x (8-bit)

; Tính y = x^2 - x + 1

mov y_low, x ; y = x

mul y_low, x ; y = x * x (kết quả 16-bit, lưu trong R1:R0)

movw y_low, r0 ; Lưu giá trị của x^2 vào y_low

sub y_low, x ; y = x^2 - x

inc y_low ; y = x^2 - x + 1

; Kết quả được lưu trong thanh ghi y_low và y_high

; y_high là byte cao, y_low là byte thấp

; Gửi kết quả xuống PORTB (ví dụ)

out PORTB, y_high

out PORTC, y_low

; Vòng lặp vô hạn

loop:

rjmp loop
Chương trình chuyển số BCD thành số HEX
.include "m324pdef.inc" ; Định nghĩa ATmega324P

; Định nghĩa các thanh ghi chung

.def bcd_input = R17

.def hex_output = R17

; Chương trình con BCD_HEX8

BCD_HEX8:

; Tham số: bcd_input (R17) là số BCD đầu vào (00-99)

; Kết quả: hex_output (R17) là số HEX đầu ra ($00-$63)

; Chia BCD cho 10 để lấy hàng đơn vị

ldi hex_output, 0 ; Khởi tạo hex_output là 0

movw Z, bcd_input ; Thiết lập con trỏ Z trỏ vào bcd_input

ldi temp, 10

div hex_output, temp ; Chia hex_output cho 10 (lưu kết quả vào hex_output)

; Cộng kết quả với ASCII '0' để chuyển sang ký tự ASCII

ldi temp, '0'

add hex_output, temp

; Dịch số hàng chục của BCD về hàng đơn vị của HEX

ldi temp, 10

mul temp, hex_output ; Nhân temp với hex_output

; Cộng kết quả với ASCII '0' để chuyển sang ký tự ASCII


ldi temp, '0'

add temp, r0

mov hex_output, temp

ret

19,20,

; Chương trình con ADD_BCD - Cộng 2 số BCD (00-99)

; Trả về kết quả số BCD (00-99) và C=1 nếu kết quả tràn

ADD_BCD:

; Số BCD thứ nhất được lưu trong R20 và R21

; Số BCD thứ hai được lưu trong R22 và R23

; Kết quả được lưu trong R24 và R25

; 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)

; Khởi tạo giá trị kết quả và cờ tràn

clr R24 ; Byte thấp của kết quả

clr R25 ; Byte cao của kết quả

clr C ; Khởi tạo cờ tràn là 0

; Cộng byte thấp của số BCD thứ nhất với byte thấp của số BCD thứ hai

add R20, R22

brcc no_overflow ; Nếu không có tràn, không cần xử lý

; Nếu có tràn, thực hiện xử lý

inc R25 ; Tăng byte cao của kết quả

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

adc R21, R23

brcc done ; Nếu không có tràn, kết thúc

; Nếu có tràn, thực hiện xử lý

inc R25 ; Tăng byte cao của kết quả

done:

ret

; Chương trình con SUB_BCD - Trừ 2 số BCD (00-99)

; Trả về kết quả số BCD (00-99) và C=1 nếu kết quả tràn (âm)

SUB_BCD:

; Số BCD thứ nhất được lưu trong R20 và R21

; Số BCD thứ hai được lưu trong R22 và R23

; Kết quả được lưu trong R24 và R25

; 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)

; Khởi tạo giá trị kết quả và cờ tràn

clr R24 ; Byte thấp của kết quả

clr R25 ; Byte cao của kết quả

clr C ; Khởi tạo cờ tràn là 0

; Trừ byte thấp của số BCD thứ hai từ byte thấp của số BCD thứ nhất

sub R20, R22

brcc no_overflow_sub ; Nếu không có tràn, không cần xử lý

; Nếu có tràn, thực hiện xử lý

dec R25 ; Giảm byte cao của kết quả


addi R20, 10 ; Thêm 10 vào byte thấp của kết quả (vì chúng ta đang làm việc với BCD)

no_overflow_sub:

; Trừ byte cao của số BCD thứ hai từ byte cao của số BCD thứ nhất

sbc R21, R23

brcc done_sub ; Nếu không có tràn, kết thúc

; Nếu có tràn, thực hiện xử lý

dec R25 ; Giảm byte cao của kết quả

done_sub:

ret

21

.include "m324pdef.inc" ; Định nghĩa ATmega324P

DELAY_TIME_MS equ 1 ; Thời gian trễ (1ms)

; Chương trình con DELAY_MS với biến thời gian

DELAY_MS:

; Tham số: R16 là biến thời gian (1, 10, 100 tùy thuộc vào cấu hình)

ldi R16, DELAY_TIME_MS

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,

.include "m324pdef.inc" ; Định nghĩa ATmega324P

FREQUENCY equ 1000 ; Tần số (Hz)

; Chương trình chính

.org 0x0000 ; Địa chỉ bắt đầu chương trình

rjmp main

main:

; Cấu hình thanh ghi và đăng ký cần thiết cho đầu ra

ldi temp, (1<<DDB5) ; Cấu hình PB5 là đầu ra

out DDRB, temp

ldi temp, (1<<COM0A0) ; Cấu hình OC0A (PB5) là đầu ra

out TCCR0A, temp

ldi temp, (1<<WGM01) ; Chọn chế độ CTC cho Timer 0

out TCCR0B, temp


ldi temp, (1<<CS01) ; Chọn chia tỷ lệ tần số ở giá trị 8

out TCCR0B, temp

; Tính toán giá trị tải vào thanh ghi OCR0A để đạt được tần số 1KHz

ldi temp, (F_CPU / (2 * 8 * FREQUENCY)) - 1

out OCR0A, temp

; Bật ngắt toàn cục (nếu cần)

sei

; Vòng lặp chính

main_loop:

rjmp main_loop

23,

.include "m324pdef.inc" ; Định nghĩa ATmega324P

FREQUENCY equ 1000 ; Tần số (Hz)

DUTY_CYCLE_PERCENT equ 30 ; Tỷ lệ nhiệt độ (0-100%)

; Chương trình chính

.org 0x0000 ; Địa chỉ bắt đầu chương trình

rjmp main

main:

; Cấu hình thanh ghi và đăng ký cần thiết cho đầu ra

ldi temp, (1<<DDD7) ; Cấu hình PD7 là đầu ra

out DDRD, temp


ldi temp, (1<<COM0A1) | (1<<COM0A0) ; Cấu hình OC0A (PD7) là đầu ra với chế độ Fast PWM

out TCCR0A, temp

ldi temp, (1<<WGM01) | (1<<WGM00) ; Chọn chế độ Fast PWM cho Timer 0

out TCCR0B, temp

ldi temp, (1<<CS01) ; Chọn chia tỷ lệ tần số ở giá trị 8

out TCCR0B, temp

; 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%

ldi temp, (F_CPU / (2 * 8 * FREQUENCY)) - 1

out OCR0A, temp

ldi temp, (DUTY_CYCLE_PERCENT * temp) / 100

out OCR0B, temp

; Bật ngắt toàn cục (nếu cần)

sei

; Vòng lặp chính

main_loop:

rjmp main_loop

1, (a) LDI R23, $FA:

 Mã máy: 1110 1111 1110 1010


 Độ dài: 16 bit
 Số máy lệnh: 1

(b) MOV R6, R14:


 Mã máy: 0010 11rd dddd rrrr
 Độ dài: 16 bit
 Số máy lệnh: 1

(c) DECR7:

 Mã máy: 1001 010d dddd 0111


 Độ dài: 16 bit
 Số máy lệnh: 1

(d) OUTPORTC, R4:

 Mã máy: 1011 1AAr rrrr AAAA


 Độ dài: 16 bit
 Số máy lệnh: 1
 Ở đây, AAA là 4 bit cuối của thanh ghi R4 , và r rrrr là 5 bit cuối của thanh ghi Rd.

(e) ADWI R26, 5:

 Mã máy: 1001 10rd dddd rrrr


 Độ dài: 16 bit
 Số máy lệnh: 1
 rddddd là thanh ghi Rd hoặc Rd + 1.

2, (a) RJMP THERE:

 Mã máy: 0xC0, 0x0A


 Độ dài: 2 byte
 Số máy lệnh: 1

(b) RCALL THERE:

 Mã máy: 0xCD, 0x0A


 Độ dài: 2 byte
 Số máy lệnh: 3 (bao gồm lệnh RCALL và 2 lệnh để lưu địa chỉ trở về)

(c) RJMP BACK:

 Mã máy: 0x94, 0x0B


 Độ dài: 2 byte
 Số máy lệnh: 1

(d) RCALL BACK:

 Mã máy: 0x9F, 0x0B


 Độ dài: 2 byte
 Số máy lệnh: 3 (bao gồm lệnh RCALL và 2 lệnh để lưu địa chỉ trở về)

3, (a) BRNE LOOP, nhãn LOOP địa chỉ = 0x1E6:

 Mã máy: 1111 00kk kkkk k000


 Độ dài: 16 bit
 Số máy lệnh: 1
 kkkkk là giá trị offset tính bằng địa chỉ nhãn LOOP và địa chỉ hiện tại PC = 0x200.

(b) BREQ LOOP, nhãn LOOP địa chỉ = 0x22A:

 Mã máy: 1111 01kk kkkk k000


 Độ dài: 16 bit
 Số máy lệnh: 1
 kkkkk là giá trị offset tính bằng địa chỉ nhãn LOOP và địa chỉ hiện tại PC = 0x200.

(c) BRCS LOOP, nhãn LOOP địa chỉ = 0x209:

 Mã máy: 1111 01kk kkkk k001


 Độ dài: 16 bit
 Số máy lệnh: 1
 kkkkk là giá trị offset tính bằng địa chỉ nhãn LOOP và địa chỉ hiện tại PC = 0x200.

(d) BRCC LOOP, nhãn LOOP địa chỉ = 0x1F0:

 Mã máy: 1111 00kk kkkk k001


 Độ dài: 16 bit
 Số máy lệnh: 1
 kkkkk là giá trị offset tính bằng địa chỉ nhãn LOOP và địa chỉ hiện tại PC = 0x200.
Chương 4

1,

(a) Tính giá trị biểu thức mỗi dòng lệnh:

1. LDI R17, 0xAA << 3 & 0x55


 0xAA là 10101010 trong biểu diễn nhị phân.
 Khi dịch trái 3 bit (<< 3 ), ta được 01010000 (trong hệ thập phân là 80).
 AND (& ) với 0x55 (01010101 trong biểu diễn nhị phân) kết quả là 01010000 ,
tức là 80 trong hệ thập phân.
2. ORI R17, ($C4 >> 1) >= ($D7 >> 2) ? $C4 : $C7
 0xC4 >> 1 là phép dịch phải 1 bit của 0xC4 (11000100 ), kết quả là 01100010 (98
trong hệ thập phân).
 0xD7 >> 2 là phép dịch phải 2 bit của 0xD7 (11010111 ), kết quả là 00110101 (53
trong hệ thập phân).
 So sánh 98 >= 53 là đúng, vì vậy chọn giá trị $C4 (196 trong hệ thập phân).
 Áp dụng OR với giá trị R17 từ bước 1 (80), 80 OR 196 là 212 trong hệ thập
phân (11010100 trong nhị phân).
3. ADD R17, $F0 + ~$1B
 $F0 là 240 trong hệ thập phân.
 ~$1B là NOT của 00011011 (27 trong hệ thập phân), kết quả là 11100100 (-28
khi xem xét dưới dạng số có dấu).
 240 + (-28) là 212 trong hệ thập phân.
 Khi cộng 212 với giá trị R17 từ bước 2 (212), ta cần xem xét việc giữ giá trị
trong phạm vi byte (0 - 255). 212 + 212 là 424 , nhưng do kết quả cần phải
nằm trong khoảng giá trị của một byte, ta sẽ lấy modulo 256, kết quả là
168 (trong hệ thập phân).

(b) Tính giá trị R17 sau khi thực thi mỗi dòng lệnh:

 Sau dòng 1: R17 = 80.


 Sau dòng 2: Kết quả của phép OR là 212, vậy R17 = 212.
 Sau dòng 3: Kết quả của phép cộng (với modulo 256 nếu cần) là 168, vậy R17
cuối cùng là 168.

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,

1. , .EQU COUNT=10 và .EQU LENGTH=COUNT*2:


 COUNT được định nghĩa là 10.
 LENGTH được định nghĩa là COUNT * 2, tức là 20.
2. .DEF OPD1=R20 và .DEF OPD2=R21:
 OPD1 được định nghĩa là thanh ghi R20.
 OPD2 được định nghĩa là thanh ghi R21.
3. *LDI OPD1,(COUNT>>1)COUNT:
 (COUNT>>1) là phép toán dịch phải 1 bit của COUNT , tức là 10 >> 1 = 5 .
 Suy ra, (COUNT>>1)*COUNT = 5 * 10 = 50 .
 Lệnh này load giá trị 50 vào R20 .
4. LDI OPD2,LENGTH|COUNT+3:
 LENGTH|COUNT là phép toán OR giữa LENGTH và COUNT , tức là 20 | 10 = 30.
 30 + 3 = 33 .
 Lệnh này load giá trị 33 vào R21 .
5. MUL OPD1,OPD2:
 Nhân giá trị trong R20 (50) với giá trị trong R21 (33).
 Kết quả là 50 * 33 = 1650 .
 Kết quả của phép nhân được lưu vào R1:R0 (R1 là byte cao, R0 là byte
thấp).
6. STS LENGTH*COUNT+0x100,R1 và STS LENGTH*COUNT+0x100-1,R0:
 LENGTH * COUNT = 20 * 10 = 200.
 Địa chỉ cho STS là 200 + 0x100 = 0x1C8 cho R1 và 0x1C7 cho R0 .
 Kết quả của phép nhân 1650 = 0x0672, với 0x06 là giá trị của R1 và 0x72 là giá
trị của R0.
 R1 được lưu vào ô nhớ 0x1C8 và R0 vào 0x1C7 .

Tóm lại, sau khi thực hiện đoạn chương trình:

 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 .

3, Định nghĩa và Nhảy đến Chương trình Chính


 .EQU PAR=10 : Định nghĩa hằng số PAR với giá trị là 10. Đây có thể là kích thước của
một bộ đệm hoặc số lần lặp trong vòng lặp.
 .ORG 0: Thiết lập địa chỉ bắt đầu của chương trình tại vị trí 0 trong bộ nhớ chương
trình.
 RJMP MAIN : Nhảy tương đối đến nhãn MAIN. Điều này cho phép chương trình bỏ
qua phần khai báo dữ liệu ban đầu và bắt đầu thực thi từ chương trình chính.

Khai Báo Bộ Đệm

 .DSEG : Bắt đầu phần khai báo bộ nhớ dữ liệu.


 .ORG 0X200 : Thiết lập địa chỉ bắt đầu của bộ đệm dữ liệu tại 0x200.
 S_BUF: .BYTE PAR: Khai báo một bộ đệm S_BUF với kích thước bằng PAR (10 bytes).
 DAT1: .BYTE 1 : Khai báo một vùng nhớ DAT1 với kích thước 1 byte.

Chương trình Chính

 .CSEG : Bắt đầu phần chương trình chính.


 LDI R16,PAR : Load giá trị PAR (10) vào thanh ghi R16. Thanh ghi này sẽ được sử
dụng để đếm số lần lặp trong vòng lặp.
 LDI R17,0 : Load giá trị 0 vào thanh ghi R17. Giá trị này sẽ được sử dụng để khởi
tạo bộ đệm.
 LDI XH,HIGH(S_BUF) và LDI XL,LOW(S_BUF): Load địa chỉ cao và thấp của bộ đệm
S_BUF vào cặp thanh ghi X (XH:XL). Địa chỉ này sẽ được sử dụng để truy cập bộ
đệm.

Vòng Lặp Khởi Tạo Bộ Đệm

 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.

Ghi Giá Trị vào Địa Chỉ Cụ Thể

 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

rjmp init ; Jump to initialization routine

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

out PORTA, r16

; Cấu hình PORTB để xuất (ghi) dữ liệu

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

in r16, PINA ; Đọc dữ liệu từ PORTA

out PORTB, r16 ; Xuất dữ liệu đã đọc vào PORTB

rjmp main ; Quay lại vòng lặp main

6,2

.org 0x00

rjmp init ; Jump to initialization routine

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, 0xFF ; Đặt tất cả bits của PORTB là đầu ra


out DDRB, r16 ; Cấu hình PORTB

ldi r16, 0x00 ; Xóa tất cả tín hiệu xuất trên PORTB bằng 0

out PORTB, r16 ; Đặt tất cả chân của PORTB về 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

out DDRC, r16 ; Cấu hình PORTC

; 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

out PORTC, r16

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 đó

rjmp main ; Quay lại vòng lặp main

6,3

.org 0x00

rjmp init ; Jump to initialization routine

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, 0xFF ; Đặt tất cả bits của PORTB là đầu ra

out DDRB, r16 ; Cấu hình PORTB

ldi r16, 0x00 ; Xóa tất cả tín hiệu xuất trên PORTB bằng 0

out PORTB, r16 ; Đặt tất cả chân của PORTB về 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

out DDRC, r16 ; Cấu hình PORTC

; 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

out PORTC, r16

check_signals:

sbis PINC, 3 ; Skip next instruction if PC3 is high

rjmp signal_pc3_low ; Jump if PC3 is low

sbis PINC, 4 ; Skip next instruction if PC4 is high

rjmp signal_pc4_low ; Jump if PC4 is low

rjmp check_signals ; Keep checking signals

signal_pc3_low:

ldi r16, 0x04 ; Set PC2PC1PC0 to 100

out PORTC, r16

out PORTB, r17 ; Output data stored in R17 to PORTB

rjmp check_signals

signal_pc4_low:

ldi r16, 0x03 ; Set PC2PC1PC0 to 011

out PORTC, r16

in r17, PINB ; Read data from PORTB into R17

rjmp check_signals

6,4
.org 0x00

rjmp init

; Định nghĩa và khởi tạo bảng tra

.org 0x100

TABLE: .db 'Hello World!',0x00 ; Chuỗi kết thúc bằng mã NULL

init:

; Cấu hình PORTC để nhập (PC0 là đầu vào)

ldi r16, 0xFE ; 1111 1110 - PC0 là đầu vào, các chân khác là đầu ra (nếu cần)

out DDRC, r16

ldi r16, 0x01 ; Kích hoạt điện trở kéo lên cho PC0

out PORTC, r16

; Cấu hình PORTB là port xuất

ldi r16, 0xFF

out DDRB, r16

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

cpi r16, 0x00 ; Kiểm tra xem ký tự có phải là NULL không

breq main_loop ; Nếu là NULL thì quay lại vòng lặp chính

out PORTB, r16 ; Xuất ký tự ra PORTB

rjmp print_char ; Lặp lại cho đến khi gặp ký tự NULL

6,5

.org 0x00

rjmp init

.org 0x100

TABLE: .db 'Hello World!',0x00 ; Chuỗi kết thúc bằng mã NULL

init:

ldi r16, 0xFE

out DDRC, r16 ; PC0 là đầu vào, PC2 là đầu ra, các chân khác có thể là đầu ra

ldi r16, 0x01

out PORTC, r16 ; Kích hoạt điện trở kéo lên cho PC0

ldi r16, 0xFF

out DDRB, r16 ; Cấu hình PORTB là port xuất

main_loop:

sbis PINC, 0

rjmp output_string

rjmp main_loop

output_string:
ldi r26, low(TABLE)

ldi r27, high(TABLE)

print_char:

lpm r16, Z+

cpi r16, 0x00

breq main_loop

out PORTB, r16 ; Xuất ký tự ra PORTB

; Tạo xung trên PC2

sbi PORTC, 2 ; Set PC2 to 1

cbi PORTC, 2 ; Clear PC2 to 0 ngay lập tức

rjmp print_char ; Lặp lại cho đến khi gặp ký tự NULL

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

ldi r16, 0xFF

out DDRC, r16

out PORTC, r16

; Cấu hình PORTB là output cho LED


ldi r16, 0x00

out DDRB, r16

out PORTB, r16

main_loop:

; Kiểm tra SW0

sbic PINC, 0

rcall switch_pressed_SW0

; Kiểm tra SW1

sbic PINC, 1

rcall switch_pressed_SW1

rjmp main_loop

switch_pressed_SW0:

; Chống rung

call debounce

; L0 sáng, L1 tối

ldi r16, (1 << PORTB0)

out PORTB, r16

ret

switch_pressed_SW1:

; Chống rung

call debounce

; L1 sáng, L0 tối

ldi r16, (1 << PORTB1)

out PORTB, r16

ret
debounce:

; Đợi khoảng DEBOUNCE_TIME để chống rung

ldi r17, DEBOUNCE_TIME

debounce_loop:

dec r17

brne debounce_loop

ret

6,7

Cấu hình ban đầu:


.org 0x00

rjmp init

.org 0x100 ; Địa chỉ bắt đầu của chương trình

; Khai báo và cài đặt ban đầu

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

ldi r16, 0xFF

out DDRC, r16

out PORTC, r16

; Cài đặt PORTB làm đầu ra cho dãy LED

ldi r16, 0x00

out DDRB, r16

out PORTB, r16

sei ; Cho phép ngắt toàn cục nếu cần


; Chuyển đến vòng lặp chính

rjmp main_loop

Vòng lặp chính và xử lý nút nhấn:


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

Chương trình con cho từng chức năng:


; Tắt tất cả các LED

turn_off_leds:

clr r16

out PORTB, 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:

; Đoạn mã để bật/tắt các LED và đợi 0.5s

ret
; Dịch LED từ bit thấp đến cao và quay vòng

shift_led:

; Đoạn mã để dịch LED và đợi 0.5s

ret

; Sáng dần từ giữa ra biên

center_to_sides_leds:

; Đoạn mã để sáng dần các LED từ giữa ra biên và đợi 0.5s

Ret

((((.org 0x00

rjmp init

.org 0x30 ; Bắt đầu chương trình sau phần vector ngắt

; Các hằng số và cài đặt

.equ DELAY_500MS = 19531 ; Giả định cho 0.5s với việc sử dụng vòng lặp đơn giản

; Khởi tạo hệ thống

init:

; Cấu hình PORTC cho nút nhấn (input)

ldi r16, 0xFF

out DDRC, r16

out PORTC, r16

; Cấu hình PORTB cho LEDs (output)

ldi r16, 0x00

out DDRB, r16


main_loop:

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

out PORTB, r16

rjmp main_loop

toggle_leds:

; Toggle trạng thái và đợi

; Do hạn chế về độ dài, mã đợi và toggle cụ thể được bỏ qua

rjmp main_loop

shift_led:

; Mã dịch LED từ trái sang phải

; Do hạn chế về độ dài, mã dịch cụ thể được bỏ qua

rjmp main_loop

center_to_sides_leds:

; Mã sáng dần từ giữa ra biên


; Do hạn chế về độ dài, mã sáng dần cụ thể được bỏ qua

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:

ldi r18, HIGH(DELAY_500MS)

ldi r19, LOW(DELAY_500MS)

wait_loop:

dec r19

brne wait_loop

dec r18

brne wait_loop

ret))

6,10

; Giả sử r16 là L_BYTE và r17 là H_BYTE

ROL_16:

; Quay trái byte cao

clc ; Xóa cờ Carry để bắt đầu

rol r17 ; Quay trái H_BYTE qua cờ Carry

; Lưu ý: Cờ Carry giờ đây chứa bit cao nhất từ H_BYTE

; 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

; và bit cao nhất từ L_BYTE đã được chuyển vào cờ Carry

Ret

6,11
.org 0x00

rjmp init

; Cài đặt ban đầu

init:

ldi r16, 0xFF

out DDRC, r16 ; Cấu hình PORTC là đầu vào

out PORTC, r16 ; Kích hoạt điện trở kéo lên cho PORTC

ldi r16, 0x00

out DDRD, r16 ; Cấu hình PORTD là đầu vào

ldi r16, 0xFF

out DDRB, r16 ; Cấu hình PORTB là đầu ra

clr r16

out PORTB, r16 ; Xóa PORTB

sei ; Cho phép ngắt

; Vòng lặp chính

main_loop:

; Kiểm tra nút nhấn và chuyển đổi trạng thái tương ứng

; Do hạn chế về môi trường mô phỏng và giới hạn độ dài,

; 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

; Tham số: R18 là địa chỉ, R19 là dữ liệu

WRITE_EEPROM:

; Chờ cho đến khi EEPROM sẵn sàng

sbic EECR, EEPE

rjmp WRITE_EEPROM

out EEAR, R18 ; Đặt địa chỉ EEPROM

out EEDR, R19 ; Đặt dữ liệu cần ghi

; Cho phép ghi

ldi R20, (1<<EEMPE)

out EECR, R20

; Bắt đầu ghi

ldi R20, (1<<EEPE)

out EECR, R20

ret

Macro/Hàm Đọc từ EEPROM:

; Macro để đọc một byte từ EEPROM

; Tham số: R18 là địa chỉ, Kết quả trả về trong R19

READ_EEPROM:

; Chờ cho đến khi EEPROM sẵn sàng

sbic EECR, EEPE

rjmp READ_EEPROM
out EEAR, R18 ; Đặt địa chỉ EEPROM

; Bắt đầu đọc

ldi R20, (1<<EERE)

out EECR, R20

in R19, EEDR ; Đọc dữ liệu

ret

You might also like