;*******************************************************************************
; File: m8_LCD_4bit.asm
; Title: ATmega8 driver for LCD in 4-bit mode (HD44780)
; Assembler: AVR assembler/AVR Studio
; Version: 1.0
; Created: April 5th, 2004
; Target: ATmega8
; Christoph Redecker, http://www.avrbeginners.net
;*******************************************************************************
; Some notes on the hardware:
;ATmega8 (clock frequency doesn't matter, tested with 1 MHz to 8 MHz)
; PortD.1 -> LCD RS (register select)
; PortD.2 -> LCD RW (read/write)
; PortD.3 -> LCd E (Enable)
; PortD.4 ... PortD.7 -> LCD data.4 ... data.7
; the other LCd data lines can be left open or tied to ground.
.include "c:\program files\atmel\avr studio\appnotes\m8def.inc"
.equ LCD_RS = 1
.equ LCD_RW = 2
.equ LCD_E = 3
.def temp = r16
.def argument= r17 ;argument for calling subroutines
.def return = r18 ;return value from subroutines
.org 0
rjmp reset
reset:
ldi temp, low(RAMEND)
out SPL, temp
ldi temp, high(RAMEND)
out SPH, temp
;LCD after power-up: ("*" means black bar)
;|****************|
;| |
rcall LCD_init
;LCD now:
;|& | (&: cursor, blinking)
;| |
rcall LCD_wait
ldi argument, 'A' ;write 'A' to the LCD char data RAM
rcall LCD_putchar
;|A& |
;| |
rcall LCD_wait
ldi argument, 0x80 ;now let the cursor go to line 0, col 0 (address 0)
rcall LCD_command ;for setting a cursor address, bit 7 of the commands has to be set
;|A | (cursor and A are at the same position!)
;| |
rcall LCD_wait
rcall LCD_getchar ;now read from address 0
;|A& | (cursor is also incremented after read operations!!!)
;| |
push return ;save the return value (the character we just read!)
rcall LCD_delay
pop argument ;restore the character
rcall LCD_putchar ;and print it again
;|AA& | (A has been read from position 0 and has then been written to the next pos.)
;| |
loop: rjmp loop
lcd_command8: ;used for init (we need some 8-bit commands to switch to 4-bit mode!)
in temp, DDRD ;we need to set the high nibble of DDRD while leaving
;the other bits untouched. Using temp for that.
sbr temp, 0b11110000 ;set high nibble in temp
out DDRD, temp ;write value to DDRD again
in temp, PortD ;then get the port value
cbr temp, 0b11110000 ;and clear the data bits
cbr argument, 0b00001111 ;then clear the low nibble of the argument
;so that no control line bits are overwritten
or temp, argument ;then set the data bits (from the argument) in the
;Port value
out PortD, temp ;and write the port value.
sbi PortD, LCD_E ;now strobe E
nop
nop
nop
cbi PortD, LCD_E
in temp, DDRD ;get DDRD to make the data lines input again
cbr temp, 0b11110000 ;clear data line direction bits
out DDRD, temp ;and write to DDRD
ret
lcd_putchar:
push argument ;save the argmuent (it's destroyed in between)
in temp, DDRD ;get data direction bits
sbr temp, 0b11110000 ;set the data lines to output
out DDRD, temp ;write value to DDRD
in temp, PortD ;then get the data from PortD
cbr temp, 0b11111110 ;clear ALL LCD lines (data and control!)
cbr argument, 0b00001111 ;we have to write the high nibble of our argument first
;so mask off the low nibble
or temp, argument ;now set the argument bits in the Port value
out PortD, temp ;and write the port value
sbi PortD, LCD_RS ;now take RS high for LCD char data register access
sbi PortD, LCD_E ;strobe Enable
nop
nop
nop
cbi PortD, LCD_E
pop argument ;restore the argument, we need the low nibble now...
cbr temp, 0b11110000 ;clear the data bits of our port value
swap argument ;we want to write the LOW nibble of the argument to
;the LCD data lines, which are the HIGH port nibble!
cbr argument, 0b00001111 ;clear unused bits in argument
or temp, argument ;and set the required argument bits in the port value
out PortD, temp ;write data to port
sbi PortD, LCD_RS ;again, set RS
sbi PortD, LCD_E ;strobe Enable
nop
nop
nop
cbi PortD, LCD_E
cbi PortD, LCD_RS
in temp, DDRD
cbr temp, 0b11110000 ;data lines are input again
out DDRD, temp
ret
lcd_command: ;same as LCD_putchar, but with RS low!
push argument
in temp, DDRD
sbr temp, 0b11110000
out DDRD, temp
in temp, PortD
cbr temp, 0b11111110
cbr argument, 0b00001111
or temp, argument
out PortD, temp
sbi PortD, LCD_E
nop
nop
nop
cbi PortD, LCD_E
pop argument
cbr temp, 0b11110000
swap argument
cbr argument, 0b00001111
or temp, argument
out PortD, temp
sbi PortD, LCD_E
nop
nop
nop
cbi PortD, LCD_E
in temp, DDRD
cbr temp, 0b11110000
out DDRD, temp
ret
LCD_getchar:
in temp, DDRD ;make sure the data lines are inputs
andi temp, 0b00001111 ;so clear their DDR bits
out DDRD, temp
sbi PortD, LCD_RS ;we want to access the char data register, so RS high
sbi PortD, LCD_RW ;we also want to read from the LCD -> RW high
sbi PortD, LCD_E ;while E is high
nop
in temp, PinD ;we need to fetch the HIGH nibble
andi temp, 0b11110000 ;mask off the control line data
mov return, temp ;and copy the HIGH nibble to return
cbi PortD, LCD_E ;now take E low again
nop ;wait a bit before strobing E again
nop
sbi PortD, LCD_E ;same as above, now we're reading the low nibble
nop
in temp, PinD ;get the data
andi temp, 0b11110000 ;and again mask off the control line bits
swap temp ;temp HIGH nibble contains data LOW nibble! so swap
or return, temp ;and combine with previously read high nibble
cbi PortD, LCD_E ;take all control lines low again
cbi PortD, LCD_RS
cbi PortD, LCD_RW
ret ;the character read from the LCD is now in return
LCD_getaddr: ;works just like LCD_getchar, but with RS low, return.7 is the busy flag
in temp, DDRD
andi temp, 0b00001111
out DDRD, temp
cbi PortD, LCD_RS
sbi PortD, LCD_RW
sbi PortD, LCD_E
nop
in temp, PinD
andi temp, 0b11110000
mov return, temp
cbi PortD, LCD_E
nop
nop
sbi PortD, LCD_E
nop
in temp, PinD
andi temp, 0b11110000
swap temp
or return, temp
cbi PortD, LCD_E
cbi PortD, LCD_RW
ret
LCD_wait: ;read address and busy flag until busy flag cleared
rcall LCD_getaddr
andi return, 0x80
brne LCD_wait
ret
LCD_delay:
clr r2
LCD_delay_outer:
clr r3
LCD_delay_inner:
dec r3
brne LCD_delay_inner
dec r2
brne LCD_delay_outer
ret
LCD_init:
ldi temp, 0b00001110 ;control lines are output, rest is input
out DDRD, temp
rcall LCD_delay ;first, we'll tell the LCD that we want to use it
ldi argument, 0x20 ;in 4-bit mode.
rcall LCD_command8 ;LCD is still in 8-BIT MODE while writing this command!!!
rcall LCD_wait
ldi argument, 0x28 ;NOW: 2 lines, 5*7 font, 4-BIT MODE!
rcall LCD_command ;
rcall LCD_wait
ldi argument, 0x0F ;now proceed as usual: Display on, cursor on, blinking
rcall LCD_command
rcall LCD_wait
ldi argument, 0x01 ;clear display, cursor -> home
rcall LCD_command
rcall LCD_wait
ldi argument, 0x06 ;auto-inc cursor
rcall LCD_command
ret
用户377235 2016-6-13 18:59