Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 41

Hướng dẫn TN VXL

Cấu trúc chương trình đề xuất cho các bài thí nghiệm.
Phần này đưa ra đề xuất cấu trúc chương trình cho TN VXL, từ đó đưa ra gợi ý về việc sắp xếp
code sao cho hợp lý để thực hiện bài TN. Khi code một yêu cầu nào đó, tốt nhất là phân tích và
sắp xếp chương trình gọn gàn, hợp lý, bài 1 còn dễ qua mấy bài sau thì @@.
.ORG 0
RJMP MAIN
//Khai báo các vector ngắt.
/* Từ 0x00 đến 0x3C là nơi khai báo các vector ngắt, không nên viết code trong vùng này,
vì vậy chương trình chính nên bắt đầu từ 0x40*/

.ORG 0X40
MAIN:
/*Trong MAIN khai báo những gì chỉ chạy 1 lần, ví dụ:
Cấu hình PORT INPUT, OUTPUT
Khởi động LCD 16x2, Khởi tạo hiển thị LCD
...*/

LOOP:
/*Chương trình trong vòng lập chạy liên tục, ví dụ kiểm tra nút nhấn, quét led ma trận,
quét led 7 đoạn, đọc giá trị ADC liên tục rồi hiển thị lên LCD...*/
/*...*/
JMP LOOP

//--------------------------------------------------------
//Các Chương trình con
//--------------------------------------------------------
CTRINH_CON_1:
//Nội dung Chương trình con thứ 1
RET

//--------------------------------------------------------
CTRINH_CON_2:
//Nội dung Chương trình con thứ 2
RET

//--------------------------------------------------------
//Các chương trình xử lý ngắt
//--------------------------------------------------------
TIMER1_ISR:
//Nội dung đoạn chương trình xử lý ngắt TIMER1
RETI
Ví dụ LAB1_1 Bài 1.

Trước khi làm bài này, SV cần hiểu được thiết kế trong board TN. Dưới đây là thiết kế khối DIP
SWITCH. DIP SWITCH là linh kiện có mã số U21 trong hình, (các bạn đã gặp nó trong TN KTS), và
được kết nối với hàng header 8x2 J41, thiết kế này giúp linh động trong việc kết nối với VXL, có
thể kết nối cả 8 DIP SW vào PORT A, B, C hoặc D tùy ý.
Giả sử DIP SW kết nối với PORT A thì DIP SW ở trạng thái OFF thì PORT A có giá trị là gì?
 Giá trị High-Z (thả nổi). Vì DIP SW off, mạch hở.
 Vì vậy PORT A phải khai báo thêm điện trở kéo lên, nếu vậy thì lúc này port A mức 1.

Tương tự khối BAR LED, tín hiệu điều khiển từ vxl mức 1 => đèn sáng.

Tóm lại: DIP SW OFF => PORT A mức 1 => Đèn phải tắt (theo đề) => Tức tín hiệu điều khiển đèn
mức 0, nó bị ngược lại so với tín hiệu đọc được.
Vì vậy tín hiệu đk đèn port b= tín hiệu đọc được từ port A XOR 0xFF. (Để đảo bit lại)

Chương trình đề xuất:


.ORG 0
RJMP MAIN
//--------------------------------------------------------
.ORG 0X40
MAIN:
/*PORT A kết nối vào DIP SWITCH => PORT A cần phải là input, input thì phải khai báo thêm
điện trở kéo lên. Không khai báo kéo lên sẽ bị nhiễu, lúc TN thử bỏ khai báo điện trở kéo
lên rồi đưa tay lại gần dây port sẽ biết.
PORT B kết nối với LED để hiển thị => PORT B là output, sau khi khai báo B là output thì
cần đặt giá trị ban đầu cho đèn là sáng hay tắt.
...*/
LDI R16,0x00
OUT DDRA,R16
LDI R16,0XFF
OUT PORTA,R16

LDI R16,0XFF
OUT DDRB,R16
LDI R16,0X00
OUT PORTB,R16

LOOP:
/* SWITCH OFF -> đèn tắt, chương trình cần làm là:
Đọc giá trị từ Port A
Lấy bù giá trị đã đọc
Xuất giá trị đã bù ra PORT B
*/
IN R19,PINA
LDI R17,0XFF
EOR R19,R17
OUT PORTB,R19
JMP LOOP
LAB1_1 Bài 2.

Gợi ý, mấy dấu ... là phần code các bạn tự bổ xung vô, bài này có thể bị tràn số khi hiển
thị, kệ đi hiển thị 8 bit cuối là đc rồi.

/* Kết nối dây


DIP SW -> PORT A
BARLED -> PORT B
*/

.ORG 0
RJMP MAIN

//--------------------------------------------------------
.ORG 0X40
MAIN:
/* Khai báo */
//PORT A input, PULL-up.
...

//PORT B OUTPUT, gán giá trị ban đầu bằng 0.


...

LOOP:
/* Vòng lập liên tục */
// Đọc giá trị PORT A, lưu vào R19
...

// Cộng thêm 5, xem lại các lệnh ADC, ADD, ADIW.


...

// Xuất giá trị ra PORT B


...

JMP LOOP
.ORG 0
RJMP MAIN

MAIN:
;----------------------------------------------------------
.ORG 0X40
; PORT A connected to DIP SWITCH -> PORT A needs to be input, input then needs to declare pull-up. Not
declared pull-up will be noisy, when OFF then LED lights up when hand is near port.
; PORT B connected to LED for display -> PORT B is output, after declaring output then need to set initial value for
LED is on or off.

LDI R16,0Xa0
OUT DDRA,R16
LDI R16,0XFF
OUT PORTA,R16

LDI R16,0XFF
OUT DDRB,R16
LDI R16,0Xe0
OUT PORTB,R16

LOOP:
;* SWITCH OFF -> LED off, program needs to:
; Read the value of Port A
; Add 5 to the read value
; Output the value to Port B

IN R19,PINA
SUBI R19,-5
OUT PORTB,R19

JMP LOOP
LAB1_1 Bài 3

Gợi ý, mấy dấu ... là phần code các bạn tự bổ xung vô. Đề ví dụ bị nhầm nhé, đúng là
0011_1111. Kết quả tối đa là 15*15 = 225 => 8 bit cuối của kết quả là được.
/* Kết nối dây
DIP SW -> PORT A
BARLED -> PORT B
*/

.ORG 0
RJMP MAIN

//--------------------------------------------------------
.ORG 0X40
MAIN:
/* Khai báo */
//PORT A input, PULL-up.
...

//PORT B OUTPUT, gán giá trị ban đầu bằng 0.


...

LOOP:
/* Vòng lập liên tục */
// Đọc giá trị PORT A, lưu vào R19
...

// Tách 4 bit cao trong R19 lưu vào R20, 4 bit thấp để trong R19
//VD: R20 = 0000 0011 và R19 = 00001111
...

// Thực hiện phép nhân ko dấu bằng hàm MUL, kết quả phép nhân là mấy bit, lưu trong đâu,
cái nào trong? (tự xem lại)
...

// Xuất giá trị 8 bit thấp ra PORT B


...

JMP LOOP
.ORG 0
RJMP MAIN

.ORG 0x400
MAIN:
; Khai báo PORT A là input, PULL-up.
; Khai báo PORT B là output, gán giá trị ban đầu bằng 0.

LOOP:
; Đọc giá trị từ PORT A, lưu vào R19
IN R19, PORTA

; Tách 4 bit cao trong R19, lưu vào R20


ANDI R19, 0x0F
SWAP R19
ANDI R19, 0x0F
MOV R20, R19

; Tách 4 bit thấp trong R19


IN R19, PORTA
ANDI R19, 0x0F

; Nhân 2 nibble và lưu kết quả vào R21


MUL R19, R20
MOV R21, R0

; Gửi kết quả ra PORT B


OUT PORTB, R21

RJMP LOOP
LAB1_1 Bài 4

Gợi ý, mấy dấu ... là phần code các bạn tự bổ xung vô. Số có dầu bù 2 nhé các bạn.
/* Kết nối dây
DIP SW -> PORT A
BARLED -> PORT B
*/

.ORG 0
RJMP MAIN

//--------------------------------------------------------
.ORG 0X40
MAIN:
/* Khai báo */
//PORT A input, PULL-up.
...

//PORT B OUTPUT, gán giá trị ban đầu bằng 0.


...

LOOP:
/* Vòng lập liên tục */
// Đọc giá trị PORT A, lưu vào R19
...

// Tách 4 bit cao trong R19 lưu vào R20, 4 bit thấp để trong R19
// VD: R20 = 0000 0011 và R19 = 00001111
...

// Kiểm tra bit 3 trong R20 và R19 có là bit 1 (số âm không), nếu là số âm, chèn 1111 vào
4 bit trọng số cao.
// VD: R20 = 0000 0011 và R19 = 1111 1111

// Thực hiện phép nhân bằng lệnh MUL, MULS, MULSU.


...

// Xuất giá trị 8 bit thấp ra PORT B


...

JMP LOOP
; Define constants for ports
.EQU DIP_SWITCH_PORT=PORTA
.EQU DIP_SWITCH_DDR=DDRA
.EQU LED_PORT=PORTB
.EQU LED_DDR=DDRB

; Main program
.ORG 0
RJMP MAIN
; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize DIP switch


CBI DIP_SWITCH_DDR, 0 ; Set DIP switch pin as input
SBI DIP_SWITCH_PORT, 0 ; Enable pull-up resistor on DIP switch pin

; Initialize LED
SBI LED_DDR, 0 ; Set LED pin as output

; Main loop
LOOP:
; Read value from DIP switch
IN R17, DIP_SWITCH_PORT

; Split value into two nibbles and multiply them


ANDI R17, 0x0F ; Get lower nibble
MOV R18, R17 ; Copy lower nibble to R18
SWAP R17 ; Move upper nibble to lower nibble
ANDI R17, 0x0F ; Get upper nibble
MUL R17, R18 ; Multiply upper and lower nibbles

; Display result on LED


OUT LED_PORT, R1 ; R1 contains the high byte of the result

; Go back to start of loop


RJMP LOOP
LAB1_1 Bài 5

Gợi ý, mấy dấu ... là phần code các bạn tự bổ xung vô. Bài này mục đích là làm việc trên
từng bit trong port. SW trong board thí nghiệm 1 chân xuống đất rồi, chân điều khiển nối
VXL, vậy nhấn nút sẽ là mức 0, không nhấn sẽ là mức 1 (phải khai báo input pull-up). Lúc
làm TN coi chừng nhầm với bàn phím ma trận 4x4 nhé.

/* Kết nối dây


DIP SW0 -> PA0
LED đơn -> PA1
*/

.ORG 0
RJMP MAIN

//--------------------------------------------------------
.ORG 0X40
MAIN:
/* Khai báo */
//PA0 input, PULL-up.
...

//PA1 OUTPUT, gán giá trị ban đầu bằng 0.


...

LOOP:
/* Vòng lập liên tục */
// Tự làm nha, nhớ chú thích đầy đủ.

JMP LOOP
; Define constants for ports
.EQU SWITCH_PORT=PORTA
.EQU SWITCH_DDR=DDRA
.EQU SWITCH_PIN=PINA
.EQU LED_PORT=PORTA
.EQU LED_DDR=DDRA

; Main program
.ORG 0
RJMP MAIN

; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize switch
CBI SWITCH_DDR, 0 ; Set switch pin as input
SBI SWITCH_PORT, 0 ; Enable pull-up resistor on switch pin

; Initialize LED
SBI LED_DDR, 1 ; Set LED pin as output

; Main loop
LOOP:
; Read value from switch
SBIC SWITCH_PIN, 0
RJMP SWITCH_PRESSED

; Switch is not pressed, turn off LED and go back to start of loop
CBI LED_PORT, 1
RJMP LOOP

SWITCH_PRESSED:
; Switch is pressed, turn on LED and go back to start of loop
SBI LED_PORT, 1
RJMP LOOP
LAB1_2 Bài 1

Bài này sinh viên đổi đoạn code đã cho thành dạng theo cấu trúc đã trình bày ở trên, đổi đi, xài
cho quen sau này học mấy bài sau đỡ viết lộn.
.include "m32Adef.inc"
.org 0
ldi r16,0x01
out DDRA,r16
start:
sbi PORTA,PINAO
cbi PORTA,PINAO
rjmp start
LAB1_2 Bài 2

Bài này đụng đến chương trình con rồi. Khi viết chương trình con, tốt nhất là viết chú thích cho
chương trình con đó, để sau này có gì dùng lại, đặc biệt là khi đi thi TN. Trong báo cáo phải viết
chú thích chương trình con.
Ví dụ: Đề bài yêu cầu viết chương trình con CONG5 có nhiệm vụ cộng 3 số 8 bit lưu trong R19,
R20, R21, kết quả trả về R23:R22.
Gợi ý viết chú thích:
//--------------------------------------------------------
//CONG5
//--------------------------------------------------------
/*
INPUT: R19, R20, R21
OUTPUT: R22, R23
Description: R23:R22 = R19 + R20 + R21
*/
CONG5:
//Đoạn chương trình thực hiện yêu cầu.

RET

.ORG 0
RJMP MAIN

.ORG 0X40
MAIN:
//Khai báo PA0 OUTPUT, gán giá trị ban đầu bằng 1
...

LOOP:
//Đảo bit PA0
...

//Gọi lại chương trình con Delay1s


...

JMP LOOP

//--------------------------------------------------------
//Các Chương trình con
//--------------------------------------------------------
/* Delay1ms
INPUT: None
OUTPUT: None
Description: Delay bằng vòng lập 1ms, với thạch anh 8MHz, hệ số chia 8.
*/
Delay1ms:
//Tự viết nhé
RET

//--------------------------------------------------------
Delay10ms:
// Gợi ý: Chương trình gọi lại Delay1ms 10 lần
RET

//--------------------------------------------------------
Delay100ms:
// Gợi ý: Chương trình gọi lại Delay1ms 100 lần
RET

//--------------------------------------------------------
Delay1s:
// Gợi ý: Chương trình gọi lại Delay1ms 1000 lần
RET
Delay1ms:

; Đặt giá trị cho bộ đếm

ldi r24, 0

ldi r25, 250

Delay1ms_loop:

; Giảm giá trị của bộ đếm

dec r24

brne Delay1ms_loop

dec r25

brne Delay1ms_loop

ret

main:

sbi DDRA, PA0 ; Cấu hình PA0 là output

loop:

sbi PORTA, PA0 ; Đặt PA0 thành high

rcall Delay1ms ; Gọi chương trình con Delay1ms

cbi PORTA, PA0 ; Đặt PA0 thành low

rcall Delay1ms ; Gọi chương trình con Delay1ms

rjmp loop ; Lặp lại vòng lặp

Delay10ms:

ldi r20, 10

Delay10ms_loop:
rcall Delay1ms

dec r20

brne Delay10ms_loop

ret

Delay100ms:

ldi r20, 100

Delay100ms_loop:

rcall Delay1ms

dec r20

brne Delay100ms_loop

ret

Delay1s:

ldi r20, 1000

Delay1s_loop:

rcall Delay1ms

dec r20

brne Delay1s_loop

ret

main:

sbi DDRA, PA0 ; Cấu hình PA0 là output

loop:

sbi PORTA, PA0 ; Đặt PA0 thành high

rcall Delay1s ; Gọi chương trình con Delay1s

cbi PORTA, PA0 ; Đặt PA0 thành low

rcall Delay1s ; Gọi chương trình con Delay1s

rjmp loop ; Lặp lại vòng lặp


LAB1_2 Bài 3

Thanh ghi dịch sử dụng là 74HC595.

Các chân quan trọng cần nhớ

Tên STT Loại Mô tả


/CLR 10 Input Chân clear dữ liệu trong 595, tích cực thấp (0 là
clear), nếu lập trình có yêu cầu dùng chân này thì
kết nối với VXL rồi set chân này lên 1, nếu muốn
clear thì cho xuống 0. Nếu không muốn lập trình
chân này thì cắm thẳng lên 5V.
SRCLK 11 Input Clock thanh ghi dịch, lấy cạnh lên (rising edge).
Các bạn hình dung trong con IC 595 có 1 thanh
ghi 8 bit, mỗi khi có cạnh lên, dữ liệu hiện tại có
trên chân SDI sẽ được dịch vào thanh ghi này.
VD: Sau khi clear 595, giả sử chân SDI luôn bằng
1, mỗi khi có clock trên chân SRCLK này, dữ liệu
trong thanh ghi sẽ thay đổi như sau:
0000 0000
1000 0000
1100 0000…

RCLK 12 Input Đây là Clock chốt, lấy cạnh lên.


Mỗi khi có cạnh lên, dữ liệu hiện tại chứa trong
thanh ghi 8 bit trong con 595 sẽ được đưa ra ngõ
ra ở các chân QA đến QH tương ứng.
SDI 14 Input Serial Data Input muốn đưa vào.
QA->QH Output Các bit out.
Để tạo hiệu ứng theo yêu cầu bài TN3, ta viết thứ tự thực hiện như sau:
Đảm bảo các chân SRCLK và RCLK = 0 ban đầu

SDI = 1 (đèn sáng)


SRCLK = 1 ; tạo xung cạnh lên đưa SDI =1 vào 595
SRCLK = 0
RCLK = 1 ; tạo xung chốt đưa dữ liệu ra QA đến QH
RCLK = 0
Delay 500 ms
Lập lại đoạn code trên 8 lần.

SDI = 0 (đèn tắt)


SRCLK = 1 ; tạo xung cạnh lên đưa SDI =0 vào 595
SRCLK = 0
RCLK = 1 ; tạo xung chốt đưa dữ liệu ra QA đến QH
RCLK = 0
Delay 500 ms
Lập lại đoạn code trên 8 lần.

Mở rộng để tham khảo cho các bài về sau. Giả sử có 8 bit được chứa trong R17= A7A6A5A4
A3A2A1A0, cần đặt 8 bit này ra con 595 thì ta làm thế nào. (MSB đi đầu)
Đảm bảo các chân SRCLK và RCLK = 0 ban đầu
Đặt chân SDI = A7
SRCLK = 1 ; tạo xung cạnh lên đưa A7 vào 595
SRCLK = 0

Đặt chân SDI = A6


SRCLK = 1 ; tạo xung cạnh lên đưa A6 vào 595
SRCLK = 0

Đặt chân SDI = A0


SRCLK = 1 ; tạo xung cạnh lên đưa A0 vào 595
SRCLK = 0
RCLK = 1 ; tạo xung cạnh lên đưa dữ liệu ra QA đến QH.
RCLK = 0
Trên đây là các bước cơ bản để hiểu thôi, còn các bạn viết phải dùng vòng lập để thực hiện việc
này. Tham khảo đoạn code dưới đây, sửa từ ví dụ 10.26 trong giáo trình. Trong ví dụ 10.26, 2
chân clock SRCLK và RCLK nối với nhau, code dưới đây có sửa lại 1 tí.
//Khúc này gán các chân port, pin bằng từ khóa, vì phần cứng thay đổi linh động được nên
định danh như vậy dễ sửa đổi. Ví dụ nay kết nối port A, hôm sau phần cứng lại kết nối
port B thì chỉ cần đổi trong định danh, nhanh gọn không thiếu xót.
.EQU shiftPORT=PORTA
.EQU ShiftDDR=DDRA
.EQU ShiftSDI=0 ;SDI => PA0
.EQU ShiftCLK=1 ;SRCLK => PA1
.EQU ShiftLATCH=2 ;RCLK => PA2

.ORG 0
RJMP MAIN

.ORG 0X40
MAIN:
//Khai báo PA0, PA1, PA2 là output, giá trị khởi tạo là 0. Nên viết bằng từ khóa định
danh phía trên luôn nhé.
...

LOOP:

JMP LOOP

//--------------------------------------------------------
//Các Chương trình con
//--------------------------------------------------------
/* SHO_8
INPUT: R20
OUTPUT: QA đến QH trên IC595
Description:
*/
SHO_8:
LDI R20,9
SH_LOOP:
ROL R17
BRCC BIT_0
SBI shiftPORT,shiftSDI
RJMP NEXT
BIT_0:
CBI shiftPORT,shiftSDI
NEXT:
SBI shiftPORT,shiftCLK
CBI shiftPORT,shiftCLK
DEC R20
BRNE SH_LOOP
SBI shiftPORT,shiftLATCH
CBI shiftPORT,shiftLATCH
RET
; Define constants for ports
.EQU LED_PORT=PORTB
.EQU LED_DDR=DDRB

; Main program
.ORG 0
RJMP MAIN

; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize LED
SBI LED_DDR, 0 ; Set LED pin as output

; Main loop
LOOP:
; Light up LEDs from left to right
LDI R17, 0x01
LDI R18, 8
LIGHT_UP:
OUT LED_PORT, R17
LSL R17
DEC R18
BRNE LIGHT_UP
CALL DELAY_500MS

; Turn off LEDs from left to right


LDI R17, 0xFF
LDI R18, 8
TURN_OFF:
OUT LED_PORT, R17
LSR R17
DEC R18
BRNE TURN_OFF
CALL DELAY_500MS

; Go back to start of loop


RJMP LOOP

; Subroutine to delay for 500ms


DELAY_500MS:
; Your delay code goes here
RET
LAB1_3 Bài 1

Lý thuyết về LCD

Module LCD được sử dụng nhiều trong các ứng dụng nhúng hiện nay, chúng dùng để hiển thị
thông tin một cách linh hoạt và tiết kiệm năng lượng. Có nhiều loại module LCD, trong đó thông
dụng nhất là loại LCD 16x2 là loại hiển thị 2 hàng mỗi hàng 16 ký tự. Mỗi ký tự được hiển thị
bởi một ma trận điểm ảnh có kích thước 5x8 điểm hoặc 5x11 điểm ảnh, tùy chế độ mà người lập
trình thiết lập.

Phần lớn các module LCD sử dụng giao tiếp 16 chân trong đó có:

 8 đường dữ liệu

 3 đường điều khiển

 3 đường cấp nguồn cho module

 2 đường cấp nguồn cho LED nền trên LCD

Châ Tên Mô tả chức năng


n

1 VSS GND.

2 VDD +5 Volt.
3 VEE Tương phản (contrast): dùng để điều chỉnh độ tương phản giữ các chữ cái và đèn
nền trong LCD. Chân này sẽ được nối vào một mạch chia áp dùng biến trở để dễ
dàng thay đổi độ tương phản khi vặn biến trở.

4 RS Chọn thanh ghi (Register Select): chọn thanh ghi trong LCD để giao tiếp.

5 R/W Đọc/ghi (Read/Write): Chọn chế độ đọc hoặc ghi vào LCD. Trong kit thí nghiệm
VXL, sinh viên chỉ thực hiện chế độ ghi vào LCD nên chân này được nối đất.

Ghi chú: đối với các tín hiệu có kí hiệu bởi dấu ‘/’ thì ta hiểu phần bên trái dấu ‘/’
tương ứng với bit 1, phần bên phải dấu ‘/’ tương ứng với bit 0.

6 E Cho phép (Enable): cho phép dữ liệu đọc/ghi trên LCD. Sau khi thiết lập các tín
hiệu cần vào LCD (từ các chân RS, R/W, dữ liệu [0-7]) thì chân này kích cạnh
xuống để thực hiện lệnh.

7 D0 Bit 0 của dữ liệu

8 D1 Bit 1 của dữ liệu

9 D2 Bit 2 của dữ liệu

10 D3 Bit 3 của dữ liệu

11 D4 Bit 4 của dữ liệu

12 D5 Bit 5 của dữ liệu

13 D6 Bit 6 của dữ liệu

14 D7 Bit 7 của dữ liệu

15 LED+ Cấp nguồn + cho đèn LED nền trên LCD

16 LED- Cấp nguồn – cho đèn LED nền trên LCD

(Phần sau đây là quan trọng phải nắm vững)

Trong LCD có chứa một CGRAM và một DDRAM:

 CGRAM: Vùng RAM này khi thí nghiệm sinh viên không cần tương tác với chúng.
CGRAM có kích thước 64 Bytes chứa sẵn bảng mã hiển thị các ký tự tương ứng theo mã
ASCII. Ngoài ra nó có chứa 1 vùng trống có thể tùy ý vẽ thêm các ký tự tùy chỉnh, vẽ 8
ký tự khác nhau khi dùng 5x8 điểm ảnh để thiết kế (một ký tự sử dụng 8 bytes) hoặc 4 ký
tự khác nhau khi dùng 5x11 điểm ảnh để thiết kế.
 DDRAM: Vùng RAM chứa ký tự hiển thị trên màn hình LCD. Màn hình LCD 16x2 gồm
có 2 hàng, mỗi hàng gồm 16 ký tự tương ứng với địa chỉ của DDRAM được cho ở dưới.
Người lập trình muốn hiển thị ký tự gì tại vị trí nào trên màn hình LCD thì cần ghi mã số
của ký tự đó vào ô nhớ tương ứng với vị trí cần ghi trên DDRAM. Ví dụ muốn ghi ký tự
O vào ô thứ 15 của hàng đầu tiên trên LCD (Như hình 1) thì cần ghi mã số của ký tự O
vào ô nhớ 0EH của DDRAM. Mã số ký tự thì được tra tại hình 3 như sau. Ghi chú: bảng
mã ký tự tương ứng với mã ASCII.
Các mã lệnh để giao tiếp với LCD được liệt kê ở bảng sau, như ta thấy mã lệnh liên quan đến các
tín hiệu vào module LCD như RS, R/W, Data bit 0 đến bit 7. Muốn ghi một lệnh vào LCD cần
điều khiển các chân tín hiệu kể trên tương ứng với lệnh cần ghi sau đó kích chân cho phép
(Enable) của LCD (kích cạnh xuống).
Trong bo mạch thí nghiệm vi xử lý, phần giao tiếp LCD được thiết kế theo giao tiếp 4 bit DATA
như hình sau.

Về lập trình ta nên chia mã lệnh ghi vào LCD thành 2 loại: ghi lệnh và ghi ký tự, ứng với chân
RS = 0 và RS = 1.

- Ghi lệnh (khi RS =0): là các lệnh cấu hình LCD, thiết lập con trỏ tắt hoặc bật, nhấp nháy
con trỏ…

- Ghi ký tự (khi RS =1): là ghi/ đọc ký tự vào DDRAM để hiển thị lên LCD.

Vì vậy ta sẽ viết 2 chương trình con WRITE_COMMAND và WRITE_DATA có nhiệm vụ ghi mã lệnh chứa
trong thanh ghi R17 ra LCD (dưới phần code gợi ý). Để sử dụng ta gán mã lệnh hoặc mã kí tự
vào R17 rồi gọi hàm này thôi, ví dụ sau.
LDI R17,$20
CALL WRITE_COMMAND

Ví dụ: Trước khi hiển thị LCD phải có một đoạn lệnh để cấu hình cho LCD như sau:
//Hàm con khởi tạo LCD
//Input: None
//Output: None
//Description: Khởi tạo LCD
INIT_LCD_4BIT:
LDI R17,$28
CALL WRITE_COMMAND
LDI R17,$01
CALL WRITE_COMMAND
LDI R17,$0C
CALL WRITE_COMMAND
LDI R17,$06
CALL WRITE_COMMAND
RET

Giờ sinh viên cần phải hiểu các mã lệnh 0x28, 0x01, 0x0C… có ý nghĩa gì.

Mã lệnh là 0x28 = 0b0010 1000 đây chính là lệnh bắt đầu với DB5 =1, khi dùng ghi lệnh thì RS
= 0, ta tra bảng, gióng xuống tìm DB5 = 1. Đây là câu lệnh Function set, với DL = 0, N =1 và F
=0. Tức là giao tiếp 4 bits, số lượng dòng hiển thị là 2 (LCD có 2 hàng trên và dưới), kích thước
ký tự là 5x8.

Ghi chú: đối với các tín hiệu có kí hiệu bởi dấu ‘/’ thì ta hiểu phần bên trái dấu ‘/’ tương ứng với
bit 1, phần bên phải dấu ‘/’ tương ứng với bit 0.

Tương tự mã lệnh 0x01=0b0000 0001 tương ứng với lệnh clear màn hình.

Tương tự với mã lệnh 0x0E = 0b0000 1110. Tức D = 1, C =1, B =0 có nghĩa là: cho phép hiển
thị, cho phép bật con trỏ, không cho con trỏ nhấp nháy.
Về phần ghi ký tự cũng tương tự. Gán mã ký tự mong muốn vào R17 rồi gọi hàm WRITE_DATA.
LDI R17,$41
CALL WRITE_DATA

Ví dụ ghi ký tự V ra LCD:
LDI R17,’V’
CALL WRITE_DATA

Sau khi thực hiện lệnh ghi ký tự, mã ký tự sẽ được ghi vào ô DDRAM hiện hành. Nếu địa chỉ
DDRAM hiện hành thuộc khoản 0x00-0x0F hoặc 0x40H-0x4F thì trên LCD sẽ hiển thị ký tự
tương ứng vào ô đó, đồng thời sau đó địa chỉ DDRAM sẽ tự cộng thêm 1 và sẽ ghi ký tự ở ô tiếp
theo nếu dùng thêm lệnh ghi ký tự. Trường hợp ô ký tự hiện hành là 0F (tức ô cuối cùng của
hàng 1), sau khi ghi lệnh ghi ký tự ra ô này, DDRAM sẽ được cộng lên 1 là 0x10 chứ không phải
là 0x40, nghĩa là ký tự sẽ không tự động nhảy xuống hàng dưới. Để xuống hàng hoặc ghi vào ô
ký tự cụ thể nào ta cần phải sử dụng thêm lệnh chọn địa chỉ DDRAM như sau, mã lệnh này được
tính là 0x80+địa chỉ DDRAM cần nhảy đến.

Ví dụ: muốn ghi chữ “VXL” lên LCD tại vị trí sau thì công việc cần làm là: gọi lệnh chọn địa chỉ
DDRAM tại ô 0x46, mã lệnh của lệnh này sẽ là 0x80 + 0x46 = 0xC6. Tiếp đó ghi các ký tự ‘V’,
‘X’, ‘L’.

Đoạn chương trình hiển thị chữ “VXL”, SV nên kết hợp với phương pháp tra bảng để hiển thị.
LDI R17,$C6
CALL WRITE_COMMAND
LDI R17,’V’
CALL WRITE_DATA
LDI R17,’X’
CALL WRITE_DATA
LDI R17,’L’
CALL WRITE_DATA

Code gợi ý cho bài Lab1_3 bài 1.

.EQU LCD_PORT=PORTA
.EQU LCD_DDR=DDRA
.EQU RS=0
.EQU RW=1
.EQU EN=2
.ORG 0

RJMP MAIN
.ORG 0X40
MAIN:
LDI R16,LOW(RAMEND) ;DUA DIA CHI RAMEND LEN VI TRI CAO NHAT
OUT SPL,R16
LDI R16,HIGH(RAMEND)
OUT SPH,R16

/*
Kết nối port A vào LCD, RS=PA0, RW=PA1, EN=PA2, Data[4:7] = PA4:PA7
=> Khai báo PA012 4567 (không có PA3) là output, gán các giá trị ban đầu như sau: Data =
0, RS=0, RW=1, EN=0.
*/
...
//Gọi chương trình con khởi tạo nguồn (POWERUP_LCD_4BIT) cho chế độ LCD 4 bit, cụ thể thì
xem lại giáo trình. Không gọi chương trình con này, LCD không hiển thị được.
...
//Gọi chương trình con cấu hình LCD.
...
//Gọi lệnh trỏ con trỏ về vị trí DDRAM 0x00
...

//Dưới đây là đoạn code tra bảng, xuất ký tự hàng thứ 1 ra LCD cho đến khi gặp ký tự 0x00
LDI ZH,HIGH(LINE1<<1)
LDI ZL,LOW(LINE1<<1)
DISPLAY_LINE1:
LPM R17,Z+
CPI R17,$00
BREQ NEWLINE
CALL WRITE_DATA
RJMP DISPLAY_LINE1
NEWLINE:
//Viết lệnh xuống hàng (lệnh chọn địa chỉ DDRAM tại 0x40)
...
//Tương tự, viết code xuất ra hàng thứ 2

LOOP:
// Bài này vòng lập không viết gì cả, vì mọi thứ chỉ cần chạy 1 lần thôi.
RJMP LOOP

//Chương trình con


;------------------------------------------------
//POWERUP_LCD_4BIT
//Input: None
//Output: None
//Description: Hàm con power up LCD
POWERUP_LCD_4BIT:
LDI R16,250
RCALL DELAY_US
LDI R16,250
RCALL DELAY_US
LDI R17,$30
RCALL OUT_COMMAND
LDI R16,50
RCALL DELAY_US
LDI R17,$30
RCALL OUT_COMMAND
LDI R16,2
RCALL DELAY_US
LDI R17,$20
RCALL OUT_COMMAND
RET

//Hàm con khởi tạo LCD


//Input: None
//Output: None
//Description: Khởi tạo LCD
INIT_LCD_4BIT:
LDI R17,$28
CALL WRITE_COMMAND
LDI R17,$01
CALL WRITE_COMMAND
LDI R17,$0C
CALL WRITE_COMMAND
LDI R17,$06
CALL WRITE_COMMAND
RET

//Hàm con ghi lệnh ra LCD


//Input: R17
//Output: None
//Description: Ghi lệnh chứa trong R17 ra LCD.
WRITE_COMMAND:
PUSH R17
ANDI R17,$F0 ; xuat 4 bit dau ra truoc
RCALL OUT_COMMAND
POP R17
SWAP R17
ANDI R17,$F0 ; xuat 4 bit sau ra
RCALL OUT_COMMAND
RET

//Hàm con ghi ký tự ra LCD


//Input: R17
//Output: None
//Description: Ghi kí tự ra LCD theo mã ASCII, ví dụ R17=0x41 thì LCD hiển thị chữ A
WRITE_DATA:
PUSH R17
ANDI R17,$F0 ; xuat 4 bit dau
RCALL OUT_DATA
POP R17
SWAP R17
ANDI R17,$F0 ; xuat 4 bit sau
RCALL OUT_DATA
RET
OUT_COMMAND:
OUT LCD_PORT,R17
CBI LCD_PORT,RS
CBI LCD_PORT,RW
SBI LCD_PORT,EN
NOP
CBI LCD_PORT,EN
LDI R16,20
CALL DELAY_US
RET
OUT_DATA:
OUT LCD_PORT,R17
SBI LCD_PORT,RS
CBI LCD_PORT,RW
SBI LCD_PORT,EN
NOP
CBI LCD_PORT,EN
LDI R16,20
CALL DELAY_US
RET

/* DELAY_US
Input: R16
Output: None
Description: DELAY R16*100 MICROSEC
*/
DELAY_US:
MOV R15,R16
LDI R16,200
L1: MOV R14,R16
L2: DEC R14
NOP
BRNE L2
DEC R15
BRNE L1
RET
LINE1: .DB "TN VXL-AVR",$00
LINE2: .DB "10 DIEM",$00
; Define constants for control signals
.EQU RS=0
.EQU RW=1
.EQU EN=2

; Define constants for ports


.EQU LCD_PORT=PORTC
.EQU LCD_DDR=DDRC

; Main program
.ORG 0
RJMP MAIN

; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize LCD
CALL LCD_INIT

; Display message
LDI ZL, LOW(2*MSG)
LDI ZH, HIGH(2*MSG)
CALL LCD_PUTS

; Endless loop
LOOP:
RJMP LOOP

; Subroutine to initialize LCD


LCD_INIT:
; Your LCD initialization code goes here
RET

; Subroutine to display string on LCD


LCD_PUTS:
; Your code to display a string on the LCD goes here
RET

; Message to display
MSG:
.DB 'TN VXL-AVR',0
.DB 'Nhóm: XX',0
LAB1_3 Bài 2

Code gợi ý
/*
KET NOI PHAN CUNG:
PORT A: LCD
PORT B:BAR LED
PINC0: NUT NHAN
*/
.ORG 0
.EQU LCD_PORT=PORTA
.EQU LCD_DDR=DDRA
.EQU LED_PORT=PORTB
.EQU LED_DDR=DDRB
.EQU RS=0
.EQU RW=1
.EQU EN=2

RJMP MAIN
.ORG 0X40
//MAIN----------------------------------
MAIN:
LDI R16,LOW(RAMEND) ;DUA DIA CHI RAMEND LEN VI TRI CAO NHAT
OUT SPL,R16
LDI R16,HIGH(RAMEND)
OUT SPH,R16

//Khai báo PORT gắn LED là OUTPUT, gán giá trị ban đầu bằng 0 để LED tắt.
...
//Khai báo PC0 là input, có điện trở kéo lên
...

/*
Kết nối port A vào LCD, RS=PA0, RW=PA1, EN=PA2, Data[4:7] = PA4:PA7
=> Khai báo PA012 4567 (không có PA3) là output, gán các giá trị ban đầu như sau: Data =
0, RS=0, RW=1, EN=0.
*/
...
//Gọi chương trình con khởi tạo nguồn (POWERUP_LCD_4BIT) cho chế độ LCD 4 bit, cụ thể thì
xem lại giáo trình. Không gọi chương trình con này, LCD không hiển thị được.
...
//Gọi chương trình con cấu hình LCD.
...
//Gọi lệnh trỏ con trỏ về vị trí DDRAM 0x00
...

;R18 luu so lan nhan nut


LDI R18,0
;R19 hang so cong them de chuyen thanh ASCII, lưu ý số đếm 0 1… khi hiển thị lên
LCD phải cộng thêm 48 (hệ 10).
LDI R19,48
;---------------------------------------------------------
;Viết code in dong chu LINE1 ra LCD
;---------------------------------------------------------
...

//LOOP----------------------------------------------------
LOOP:
//Viết lệnh chọn địa chỉ DDRAM tại 0x40, hiển thị số lần nhấn nút ở hàng 2
...

RCALL CHECK_BUTTON
CALL NUM_8_BIT_TO_BCD

CPI R22,0
BRNE HANG_TRAM
CPI R21,0
BRNE HANG_CHUC
CPI R20,0
BRNE HANG_DONVI
HANG_TRAM:
MOV R17,R22
ADD R17,R19
CALL WRITE_DATA
HANG_CHUC:
MOV R17,R21
ADD R17,R19
CALL WRITE_DATA
HANG_DONVI:
MOV R17,R20
ADD R17,R19
CALL WRITE_DATA
RJMP LOOP

;------------------------------------------------------------
;INPUT: PINC.0
;OUTPUT: R17, R18 la so lan bam nut
;DESCRIPTION:
;------------------------------------------------------------
CHECK_BUTTON:
NHAN_NUT:
SBIC PINC,0
RJMP NHAN_NUT
LDI R16,250 ;DELAY 10MS
RCALL DELAY_US
SBIC PINC,0
RJMP NHAN_NUT
/* INC R18
OUT LED_PORT,R18
MOV R17,R18
RCALL NUM_8_BIT_TO_BCD
RET*/
NHA_NUT:
SBIS PINC,0
RJMP NHA_NUT
LDI R16,250 ;DELAY 25MS
RCALL DELAY_US
SBIS PINC,0
RJMP NHA_NUT
;doan chuong trinh xu ly khi co nut nhan,
INC R18
OUT LED_PORT,R18
MOV R17,R18
RCALL NUM_8_BIT_TO_BCD
RET
;------------------------------------------------------------
;INPUT: R17
;OUTPUT: R22 hang tram, R21 hang chuc, R20 hang don vi
;DESCRIPTION: lay 3 chu so BCD cua R17
;------------------------------------------------------------
NUM_8_BIT_TO_BCD:
CLR R20
CLR R21
CLR R22

LDI R16,10
RCALL DIV10
MOV R20,R16 ;R20: HANG DON VI
LDI R16,10
RCALL DIV10
MOV R21,R16 ;R21: HANG CHUC
MOV R22,R17 ;R22: HANG TRAM
RET

;------------------------------------------------------------
;INPUT: R17, R16 = 10
;OUTPUT: R16 so du, R17 thuong so
;DESCRIPTION: chia R17 cho R16
;------------------------------------------------------------
DIV10:
CLR R15 ;R15:THUONG SO
TIEP_TUC_TRU:
SUB R17,R16
BRCS DUARA_KETQUA ;C=1 KHONG CHIA DUOC
INC R15
RJMP TIEP_TUC_TRU
DUARA_KETQUA:
ADD R17,R16
MOV R16,R17 ;R16:SO DU
MOV R17,R15 ;R17:THUONG SO
RET
;------------------------------------------------------------
POWERUP_LCD_4BIT:
LDI R16,250
RCALL DELAY_US
LDI R16,250
RCALL DELAY_US
LDI R17,$30
RCALL OUT_COMMAND
LDI R16,50
RCALL DELAY_US
LDI R17,$30
RCALL OUT_COMMAND
LDI R16,2
RCALL DELAY_US
LDI R17,$20
RCALL OUT_COMMAND
RET
INIT_LCD_4BIT:
LDI R17,$28
CALL WRITE_COMMAND
LDI R17,$01
CALL WRITE_COMMAND
LDI R17,$0C
CALL WRITE_COMMAND
LDI R17,$06
CALL WRITE_COMMAND
RET
WRITE_COMMAND:
PUSH R17
ANDI R17,$F0 ; xuat 4 bit dau ra truoc
RCALL OUT_COMMAND
POP R17
SWAP R17
ANDI R17,$F0 ; xuat 4 bit sau ra
RCALL OUT_COMMAND
RET

WRITE_DATA:
PUSH R17
ANDI R17,$F0 ; xuat 4 bit dau
RCALL OUT_DATA
POP R17
SWAP R17
ANDI R17,$F0 ; xuat 4 bit sau
RCALL OUT_DATA
RET
OUT_COMMAND:
OUT LCD_PORT,R17
CBI LCD_PORT,RS
CBI LCD_PORT,RW
SBI LCD_PORT,EN
NOP
CBI LCD_PORT,EN
LDI R16,20
CALL DELAY_US
RET
OUT_DATA:
OUT LCD_PORT,R17
SBI LCD_PORT,RS
CBI LCD_PORT,RW
SBI LCD_PORT,EN
NOP
CBI LCD_PORT,EN
LDI R16,20
CALL DELAY_US
RET
DELAY_US: ;DELAY R16*100 MICROSEC
MOV R15,R16
LDI R16,200
L1: MOV R14,R16
L2: DEC R14
NOP
BRNE L2
DEC R15
BRNE L1
RET
LINE1: .DB "SO LAN NHAN NUT",$00
; Define constants for control signals
.EQU RS=0
.EQU RW=1
.EQU EN=2

; Define constants for ports


.EQU LCD_PORT=PORTC
.EQU LCD_DDR=DDRC
.EQU LED_PORT=PORTB
.EQU LED_DDR=DDRB
.EQU BUTTON_PIN=PINC
.EQU BUTTON_DDR=DDRC

; Main program
.ORG 0
RJMP MAIN

; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize LCD
CALL LCD_INIT

; Initialize LED
SBI LED_DDR, 0 ; Set LED pin as output

; Initialize button
CBI BUTTON_DDR, 0 ; Set button pin as input
SBI BUTTON_PIN, 0 ; Enable pull-up resistor on button pin

; Initialize button press count


CLR R17

; Main loop
LOOP:
; Check if button is pressed
SBIC BUTTON_PIN, 0
RJMP BUTTON_PRESSED

; Button is not pressed, go back to start of loop


RJMP LOOP
BUTTON_PRESSED:
; Increment button press count
INC R17

; Display count on LED


OUT LED_PORT, R17

; Display count on LCD


; Your code to display R17 on the LCD goes here

; Debounce button
; Your button debouncing code goes here

; Go back to start of loop


RJMP LOOP

; Subroutine to initialize LCD


LCD_INIT:
; Your LCD initialization code goes here
RET
LAB1_3 Bài 3

Tham khảo cách sử dụng hàm GET_KEY16 trong giáo trình để làm bài này.
;---------------------------------------------------------
;GET_KEY16 ??c tr?ng thái các phím,
;Tr? v? R17= mã phím và C=1 n?u có phím nh?n
;Tr? v? C=0 n?u phím ch?a nh?n
;---------------------------------------------------------
GET_KEY16:
LDI R17, 4 ;R17 SO LAN QUET COT
LDI R20, 0xFE ;BAT DAU QUET COT 0
SCAN_COL:
OUT MATRIX_BTN_PORT, R20
IN R19, MATRIX_BTN_PIN ;?Oc trAng thái hàng
IN R19, MATRIX_BTN_PIN ;?Oc lAi trAng thái hàng
ANDI R19, 0xF0 ;che 4 bit cao lay ma hàng
CPI R19, 0xF0 ;xem có phím nhan?
BRNE CHK_KEY ;R19 khác F0H, có phím nhan
LSL R20 ;quét cot ke tiep
INC R20 ;?at LSB=1
DEC R17 ;giam so lan quét cot
BRNE SCAN_COL ;tiep tuc quét het so cot
CLC ;phím ch?a nhan, C=0
CLR R21 ;R2=0 KHONG CO NHAN NUT
RJMP EXIT ;thoát
;-------------------------------------------------
CHK_KEY:
SUBI R17, 4 ;tính vi trí cot
NEG R17 ;bù 2 lay so d??ng
SWAP R19 ;?ao sang 4 bit thap ma hàng
LDI R20, 4 ;R20 ?em so lon quét hàng
SCAN_ROW:
ROR R19 ;quay phai qua C tim bit 0
BRCC SET_FLG ;C=0 vi trí hàng có phím nhan
INC R17 ;không ?úng hàng t?ng vi trí hàng thêm 4
INC R17
INC R17
INC R17
DEC R20
BRNE SCAN_ROW ;quét het 4 hàng
CLC ;không có phím nhan C=0
RJMP EXIT ;thoát
SET_FLG:
SEC ;có phím nhan C=1
SER R21 ;R2 KHAC KHONG THI CO NHAN NUT
EXIT:
RET
;-----------------------------------
;------------------------------------------
;KEY_RD ??c tr?ng thái phím
;Ch?ng rung phím khi nh?n/nh? 50 l?n
;S? d?ng GET_KEY16 nh?n d?ng phím nh?n
;Ch? thoát khi có phím nh?n!!!
;-------------------------------------------
KEY_RD:
LDI R16,50 ;so lan nhan dang phím nhan
BACK1:
RCALL GET_KEY16 ;goi ctc nhan dang phím
BRCC KEY_RD ;C=0 phím ch?a nhan lap lai
DEC R16 ;?em so lan nhan dang phím
BRNE BACK1 ;lap vong cho ?u so lan ?em
PUSH R17

LDI R17,$C0 ;DONG 2 VI TRI 1


CALL WRITE_COMMAND
POP R17
MOV R24,R17 ;LUU GIA TRI VAO R24
OUT LED_PORT,R24
RCALL CHUYEN_ASCII
WAIT_1:
LDI R16,50 ;so lan nhan dang phím nha
BACK2:
RCALL GET_KEY16 ;goi ctc nhan dang phím
BRCS WAIT_1 ;C=1 phím ch?a nha
DEC R16 ;?em so lan nhan dang phím
BRNE BACK2 ;lap vong cho ?u so lan ?em
;POP R17

LDI R17,$C0 ;DONG 2 VI TRI 1


CALL WRITE_COMMAND
RCALL DISPLAY_FF
LDI R24,$FF
OUT LED_PORT,R24
RET
; Define constants for control signals
.EQU RS=0
.EQU RW=1
.EQU EN=2

; Define constants for ports


.EQU LCD_PORT=PORTC
.EQU LCD_DDR=DDRC
.EQU LED_PORT=PORTB
.EQU LED_DDR=DDRB
.EQU BUTTON_PIN=PINC
.EQU BUTTON_DDR=DDRC

; Main program
.ORG 0
RJMP MAIN

; Main subroutine
MAIN:
; Initialize stack pointer
LDI R16, LOW(RAMEND)
OUT SPL, R16
LDI R16, HIGH(RAMEND)
OUT SPH, R16

; Initialize LCD
CALL LCD_INIT

; Initialize LED
SBI LED_DDR, 0 ; Set LED pin as output

; Initialize button
CBI BUTTON_DDR, 0 ; Set button pin as input
SBI BUTTON_PIN, 0 ; Enable pull-up resistor on button pin

; Initialize button press count


CLR R17

; Main loop
LOOP:
; Check if button is pressed
SBIC BUTTON_PIN, 0
RJMP BUTTON_PRESSED

; Button is not pressed, go back to start of loop


RJMP LOOP

BUTTON_PRESSED:
; Increment button press count
INC R17

; Display count on LED


OUT LED_PORT, R17

; Display count on LCD


; Your code to display R17 on the LCD goes here

; Debounce button
; Your button debouncing code goes here

; Go back to start of loop


RJMP LOOP

; Subroutine to initialize LCD


LCD_INIT:
; Your LCD initialization code goes here
RET

You might also like