How to control an LCD using Assembly???

Go To Last Post
74 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm assuming that because it's on an Arduino board it is... the other program works just fine just like it is right now (the one that outputs "Hi my name is").

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
A curious thing I noticed: I uploaded my code once, and the previous text on the LCD cleared, but no character was displayed. I uploaded the same code again, and this was displayed: "@e my na" (the previous text was "Hi my name is"). I kept doing it and it would alternate like that, once clear, once with "@e my na".
This implies that the LCD initialization is not working consistently. Your sendcom routine incorporates a delay and the same delay is used for all of the commands. If you look at the datasheet you will see that some of the commands take longer to complete than others. If you are going to use a single delay for all commands (which is not a bad idea when you are just starting out) then it has to accommodate the worst case.

After you get your program functioning properly we can work on cleaning up some of your extraneous code.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OMG I MADE IT WORK JGKJFDSKSFIDBIBDYFSVB!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Ok, ok... lemme calm down...

Check out my code. You will see I'm using r24 for storing commands and data when I pass them on to sendcom and senddat. You will see also that both of those commands have several waits. What is the register I'm using to store the wait variable? R24... whoops.

TOTAL newb mistake.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) Which is why it's GOOD to define registers as meaningful names when using assembly

2) Why it's GREAT to learn C and be done with it, for the MOST ( +90% ) part.

Congrats !

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried to .equ the registers but the assembler wouldn't let me (I'm using AVR Studio 4)...

Anyway, here is my code, in all its newfound glory

;-----------------------------------
; LCD version 7
; Using Port A
; IT WORKS!!!
;-----------------------------------

.INCLUDE "M2560DEF.INC"

.equ control = portb
.equ data = porta
.equ dataDR = ddra
.equ controlDR = ddrb

.equ rw = 7
.equ rs = 6
.equ enable = 5


;LCD codes
.equ initial_pulse = 0b00110000
.equ four_bit_mode_com = 0b00100000
.equ ndisp_charfont_com = 0b00101000
.equ disp_on_curs_off_com = 0x0C
.equ disp_off_com = 0b0000_1000
.equ disp_clear_com = 0b0000_0001
.equ entry_mode_com = 0b00000110
.equ disp_on_blink_com = 0b00001100
.equ curs_pos2_com = 0b1000_0001
.equ disp_H_com = 0b0100_1010
.equ curs_pos1_com = 0x80

	rjmp main

main:

	ldi r16, LOW(RAMEND)	; stack setup
	out SPL, r16
	ldi r16, HIGH(RAMEND)
	out SPH, r16


	ser r16
	out dataDR,r16
	out controlDR,r16
	clr r16
	out data,r16
	out control,r16


	rcall init

	
	;ldi r24,disp_clear_com	;Display clear
	;rcall sendcom

	;ldi r24,disp_off_com	;Display off
	;rcall sendcom

	; Turn display on, blinking, cursor
	ldi r24,disp_on_blink_com
	rcall sendcom

	ldi r24,curs_pos2_com	;Position Cursor on second position
	rcall sendcom
	
	ldi r24,'A'	;Display 'A'
	rcall senddat
	
	ldi r24,'B'
	rcall senddat

	rcall wait1sec

	rcall clearLCD

	rjmp loop
	
	;End MAIN

loop:
	rjmp loop



init:
	rcall wait125

	cbi control,rs			;Register select: command
	cbi control,rw			;RW select, write

	rcall first_pulse
	rcall first_pulse
	rcall first_pulse


	rcall wait125
	ldi r16,four_bit_mode_com			;Start in 4-bit mode
	out data,r16
	sbi control,enable			;Set Enable

	rcall wait46

	cbi control,enable			;Clear Enable
	rcall wait46	

	ldi r24,ndisp_charfont_com	;Set number of display lines and character font
	rcall sendcom

	ldi r24,disp_on_blink_com	;Display on cursor blinking
	rcall sendcom

	ldi r24,disp_clear_com	;Display clear
	rcall sendcom

	ldi r24,entry_mode_com	;Entry mode set
	rcall sendcom
	
	;ldi r24,curs_pos1_com
	;rcall sendcom


	ret


first_pulse:
	rcall wait125
	ldi r16,initial_pulse
	out data,r16
	cbi control,enable			;Clear Enable

	rcall wait125

	sbi control,enable			;Set Enable

	rcall wait125

	cbi control,enable			;Clear Enable
	ret



clearLCD:					;Routine to clear the LCD screen
	ldi r24,disp_clear_com	;Display clear
	rcall sendcom
	ret


sendcom:				;Send Command - because we are using 4-bit mode, must send twice
	
	cbi control,rw			;RW select, write
	cbi control,rs			;Register select: command
	ser r16
	out dataDR,r16

	out data,r24		;Send High
	cbi control,enable	;Clear Enable
	
	rcall wait46

	sbi control,enable	;Set Enable
	
	rcall wait46

	cbi control,enable	;Clear Enable
	rcall wait46
	
	swap r24			;Swap Nibbles

	out data,r24		;Send Low
	cbi control,enable	;Clear Enable
	
	rcall wait46

	sbi control,enable	;Set Enable
	
	rcall wait46

	cbi control,enable	;Clear Enable
	rcall wait46

	ret



senddat:				;Send Data - because we are using 4-bit mode, must send twice

	cbi control,rw		;RW select, write
	sbi control,rs		;Register select: data
	ser r16
	out dataDR,r16

	out data,r24		;Send High
	cbi control,enable	;Clear Enable
	
	rcall wait46

	sbi control,enable	;Set Enable
	
	rcall wait46

	cbi control,enable	;Clear Enable
	rcall wait46

	swap r24			;Swap Nibbles

	out data,r24		;Send Low
	cbi control,enable	;Clear Enable

	rcall wait46

	sbi control,enable	;Set Enable
	
	rcall delay100usec

	cbi control,enable	;Clear Enable
	rcall wait46

	ret


	
;For now this code is not being used
LCD_busy:

	cbi ddra,7			;Set BF as input

check:
	sbic pina,7
	rjmp check

	sbi ddra,7			;Set back as output
	ret



wait1sec:
	ldi r17,64			;Determines pause length, in this case 1s

outer_loop2:
	
	ldi r26,low(3037)
	ldi r27,high(3037)

delay_loop2:
	
	adiw r26,1
	brne delay_loop2

	dec r17
	brne outer_loop2
	ret


wait125:
	ldi r17,8			;Determines pause length, in this case 125ms

outer_loop:
	
	ldi r26,low(3037)
	ldi r27,high(3037)

delay_loop:
	
	adiw r26,1
	brne delay_loop

	dec r17
	brne outer_loop
	ret



wait46:
	ldi r17,3			;Determines pause length, in this case 46ms

outer_loop1:
	
	ldi r26,low(3037)
	ldi r27,high(3037)

delay_loop1:
	
	adiw r26,1
	brne delay_loop1

	dec r17
	brne outer_loop1
	ret


delay100usec:		; 255 * 6 =~ 1600 cycles
	ldi r26, 255	
    loop100:
    	nop
    	nop
    	nop
	dec r26
	brne loop100
	
	ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You use .def for registers, NOT .equ ! I suggest using hex for your LCD EQUs, it's too easy to make a typing error using 1's & 0's. Use the power of your PC and save yourself time ( statistically, you'll make those typing errors eventually... on a bigger app. than this. ).

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I suggest using hex for your LCD EQUs, it's too easy to make a typing error using 1's & 0's.
We are back to programming style. I suggest using binary for the LCD equates because the information itself is bitmapped. The overall 'value', be it expressed in hex, decimal, octal or zorkian is irrelevant. It is the individual bits that have meaning. Many 'C' programmers can't do this due to limitations of their compiler, but there is no reason for assembly language programmers to self impose such a restriction.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok guys, I managed to make it work with characters. Now I want to make it output strings... but I'm having problems with that too =(

My program outputs "AB" to the second and third spaces in the LCD, waits a second, and clears the LCD. Afterward it should print the string "Hello LCD" but it isn't... At the moment I am searching for examples and trying to figure out the mechanics of the stack pointer...

Here's the code:

;-----------------------------------
; LCD version 7
; Using Port A
; IT WORKS!!!
;-----------------------------------

.INCLUDE "M2560DEF.INC"

.def data_reg = r24
.def delay_reg1 = r26
.def delay_reg2 = r27
.def mult_reg = r17


.equ control = portb
.equ data = porta
.equ dataDR = ddra
.equ controlDR = ddrb

.equ rw = 7
.equ rs = 6
.equ enable = 5


;LCD codes
.equ initial_pulse = 0b00110000
.equ four_bit_mode_com = 0b00100000
.equ ndisp_charfont_com = 0b00101000
.equ disp_on_curs_off_com = 0x0C
.equ disp_off_com = 0b0000_1000
.equ disp_clear_com = 0b0000_0001
.equ entry_mode_com = 0b00000110
.equ disp_on_blink_com = 0b00001100
.equ curs_pos2_com = 0b1000_0001
.equ disp_H_com = 0b0100_1010
.equ curs_pos1_com = 0x80

	rjmp main

main:

	ldi r16, LOW(RAMEND)	; stack setup
	out SPL, r16
	ldi r16, HIGH(RAMEND)
	out SPH, r16


	ser r16
	out dataDR,r16
	out controlDR,r16
	clr r16
	out data,r16
	out control,r16


	rcall init

	
	;ldi data_reg,disp_clear_com	;Display clear
	;rcall sendcom

	;ldi data_reg,disp_off_com	;Display off
	;rcall sendcom

	; Turn display on, blinking, cursor
	ldi data_reg,disp_on_blink_com
	rcall sendcom

	ldi data_reg,curs_pos2_com	;Position Cursor on second position
	rcall sendcom
	
	ldi data_reg,'A'	;Display 'A'
	rcall senddat
	
	ldi data_reg,'B'
	rcall senddat

	rcall wait1sec

	rcall clearLCD

	rjmp print_string

	;rjmp loop
	
	;End MAIN

loop:
	rjmp loop




string:
	.db "Hello LCD",0x00


	ldi zh, HIGH(string*2)
	ldi zl, LOW(string*2)


print_string:
	; Print a character
	push zh
	push zl
	
	lpm
	tst r0
	breq string_finished
	mov data_reg, r0
	rcall senddat

	
	pop zl
	pop zh

	adiw zl,1

	rjmp print_string

string_finished:
	rjmp loop
	



init:
	rcall wait125

	cbi control,rs			;Register select: command
	cbi control,rw			;RW select, write

	rcall first_pulse
	rcall first_pulse
	rcall first_pulse


	rcall wait46
	ldi r16,four_bit_mode_com			;Start in 4-bit mode
	out data,r16
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	rcall wait46	

	ldi data_reg,ndisp_charfont_com	;Set number of display lines and character font
	rcall sendcom

	ldi data_reg,disp_on_blink_com	;Display on cursor blinking
	rcall sendcom

	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom

	ldi data_reg,entry_mode_com	;Entry mode set
	rcall sendcom
	
	;ldi data_reg,curs_pos1_com
	;rcall sendcom


	ret


first_pulse:
	rcall wait125
	ldi r16,initial_pulse
	out data,r16
	cbi control,enable			;Clear Enable
	nop
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	ret



clearLCD:					;Routine to clear the LCD screen
	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom
	ret


sendcom:				;Send Command - because we are using 4-bit mode, must send twice
	
	cbi control,rw			;RW select, write
	cbi control,rs			;Register select: command
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46
	
	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	ret



senddat:				;Send Data - because we are using 4-bit mode, must send twice

	cbi control,rw		;RW select, write
	sbi control,rs		;Register select: data
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	ret


	
;For now this code is not being used
LCD_busy:

	cbi ddra,7			;Set BF as input

check:
	sbic pina,7
	rjmp check

	sbi ddra,7			;Set back as output
	ret



wait1sec:
	ldi mult_reg,64			;Determines pause length, in this case 1s

outer_loop2:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop2:
	
	adiw delay_reg1,1
	brne delay_loop2

	dec mult_reg
	brne outer_loop2
	ret


wait125:
	ldi mult_reg,8			;Determines pause length, in this case 125ms

outer_loop:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop:
	
	adiw delay_reg1,1
	brne delay_loop

	dec mult_reg
	brne outer_loop
	ret



wait46:
	ldi mult_reg,3			;Determines pause length, in this case 46ms

outer_loop1:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop1:
	
	adiw delay_reg1,1
	brne delay_loop1

	dec mult_reg
	brne outer_loop1
	ret


delay100usec:		; 255 * 6 =~ 1600 cycles
	ldi delay_reg1, 255	
    loop100:
    	nop
    	nop
    	nop
	dec delay_reg1
	brne loop100
	
	ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW... the assembler gives me a warning about .def'ing registers 26-27... Says they are already defined(?). Is this something I should worry about?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
At the moment I am searching for examples and trying to figure out the mechanics of the stack pointer...
Generally all you have to do set the initial location, the processor uses it for calls and returns and for pushes and pops. Your job is to make sure that it doesn't become 'unbalanced'. It gets unbalanced if you instruct the processor to jump out of a subroutine instead of exiting via the return instruction. It also gets unbalanced if you fall into a subroutine during sequential program execution instead of getting there via a call instruction. You can also unbalance the stack if you don't have an equal number of pushes and pops.

There are other uses for the stack but these are the ones you should be concerned with at this point.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
BTW... the assembler gives me a warning about .def'ing registers 26-27... Says they are already defined(?). Is this something I should worry about?

.INCLUDE "M2560DEF.INC"

Have you looked at the contents of this file?

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first thing I notice is after you clear the screen you don't wait before sending the string out. Check the datasheet for your display and wait that amount of time before issuing the next command.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Afterward it should print the string "Hello LCD" but it isn't...
How do you get here (in the latest version of your program)?

   ldi zh, HIGH(string*2)
   ldi zl, LOW(string*2)

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The first thing I notice is after you clear the screen you don't wait before sending the string out. Check the datasheet for your display and wait that amount of time before issuing the next command.
Actually he does wait long enough, but I'm not sure he is aware of it. I alluded to this in a previous post concerning the sendcom routine.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You code at string is confusing, I think you mean to point the Z register to string, but you skip past that. I think this is what you meant to do:

string:
   .db "Hello LCD",0x00

print_string:
   ldi zh, HIGH(string*2)
   ldi zl, LOW(string*2)

ps2:
   lpm
   tst r0
   breq string_finished
   mov data_reg, r0
   push zh
   push zl
   rcall senddat
   pop zl
   pop zh
   adiw zl,1
   rjmp ps2

string_finished:
   rjmp loop
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
You code at string is confusing, I think you mean to point the Z register to string, but you skip past that. I think this is what you meant to do:
I think our posts are overlapping and you may not have seen mine - I meant for him to figure this out by himself.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Even if he got the Z register working, there's another error with the stack that would work in this example but would cause issues as the code was updated. Look at you original loop and notice how you push on to the stack and then test for end of string. If you branch out at this point, you never pop the data back off the stack, thus corrupting your stack. In this example you branch to an endless loop so you won't notice an error, but in the future when you actually use this routine as a subroutine and use ret to exit it, the stack won't be pointing to the return address. My changes also fix this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

floresta1212, dksmall, I love you guys.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good job. Now go back and clean up your code. Make sure you understand what we have alluded to in our previous posts and ask more questions if you don't.

I haven't mentioned this before, but you are using what I call a shotgun approach for setting the data direction registers. You really shouldn't be dealing with the port pins that this program isn't using.

For your next assignment rewrite the program to have all of the I/O on one port.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

floresta1212 wrote:
Good job. Now go back and clean up your code. Make sure you understand what we have alluded to in our previous posts and ask more questions if you don't.

I haven't mentioned this before, but you are using what I call a shotgun approach for setting the data direction registers. You really shouldn't be dealing with the port pins that this program isn't using.

For your next assignment rewrite the program to have all of the I/O on one port.

Don

Will keep that in mind, Don. I'm a total newb at this but I'm getting my bearings now.

This is the code that prints out "Hello LCD".

;-----------------------------------
; LCD version 10
; Using Port A
; Printing String
;-----------------------------------

.INCLUDE "M2560DEF.INC"

.def data_reg = r24
.def delay_reg1 = r26
.def delay_reg2 = r27
.def mult_reg = r17


.equ control = portb
.equ data = porta
.equ dataDR = ddra
.equ controlDR = ddrb

.equ rw = 7
.equ rs = 6
.equ enable = 5


;LCD codes
.equ initial_pulse = 0b00110000
.equ four_bit_mode_com = 0b00100000
.equ ndisp_charfont_com = 0b00101000
.equ disp_on_curs_off_com = 0x0C
.equ disp_off_com = 0b0000_1000
.equ disp_clear_com = 0b0000_0001
.equ entry_mode_com = 0b00000110
.equ disp_on_blink_com = 0b00001100
.equ curs_pos2_com = 0b1000_0001
.equ disp_H_com = 0b0100_1010
.equ curs_pos1_com = 0x80


	rjmp main

main:

	ldi r16, LOW(RAMEND)	; stack setup
	out SPL, r16
	ldi r16, HIGH(RAMEND)
	out SPH, r16


	ser r16
	out dataDR,r16
	out controlDR,r16
	clr r16
	out data,r16
	out control,r16


	rcall init

	
	;ldi data_reg,disp_clear_com	;Display clear
	;rcall sendcom

	;ldi data_reg,disp_off_com	;Display off
	;rcall sendcom

	; Turn display on, blinking, cursor
	ldi data_reg,disp_on_blink_com
	rcall sendcom

	ldi data_reg,curs_pos2_com	;Position Cursor on second position
	rcall sendcom
	
	ldi data_reg,'A'	;Display 'A'
	rcall senddat
	
	ldi data_reg,'B'
	rcall senddat

	rcall wait1sec

	rcall clearLCD

	ldi r16,3			;Set cursor on third space
	rcall setcursor

	rcall print_string

	;rjmp loop
	
	;End MAIN

loop:
	rjmp loop




string:
	.db "Hello LCD",0x00


print_string:
	
	ldi zh, HIGH(string*2)
	ldi zl, LOW(string*2)
print_loop:
	; Print a character
	lpm
	tst r0
	breq string_finished
	mov data_reg, r0
	push zh
	push zl
	rcall senddat

	pop zl
	pop zh

	adiw zl,1

	rjmp print_loop

string_finished:
	ret
	



init:
	rcall wait125

	cbi control,rs			;Register select: command
	cbi control,rw			;RW select, write

	rcall first_pulse
	rcall first_pulse
	rcall first_pulse


	rcall wait46
	ldi r16,four_bit_mode_com			;Start in 4-bit mode
	out data,r16
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	rcall wait46	

	ldi data_reg,ndisp_charfont_com	;Set number of display lines and character font
	rcall sendcom

	ldi data_reg,disp_on_blink_com	;Display on cursor blinking
	rcall sendcom

	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom

	ldi data_reg,entry_mode_com	;Entry mode set
	rcall sendcom
	
	;ldi data_reg,curs_pos1_com
	;rcall sendcom


	ret


first_pulse:
	rcall wait125
	ldi r16,initial_pulse
	out data,r16
	cbi control,enable			;Clear Enable
	nop
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	ret



clearLCD:					;Routine to clear the LCD screen
	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom
	ret



setcursor:
	ldi r31,curs_pos1_com
	add r16,r31				;Add the spaces
	
	mov data_reg,r16
	rcall sendcom
	ret
	

sendcom:				;Send Command - because we are using 4-bit mode, must send twice
	
	cbi control,rw			;RW select, write
	cbi control,rs			;Register select: command
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46
	
	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	ret



senddat:				;Send Data - because we are using 4-bit mode, must send twice

	cbi control,rw		;RW select, write
	sbi control,rs		;Register select: data
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall wait46

	ret


	
;For now this code is not being used
LCD_busy:

	cbi ddra,7			;Set BF as input

check:
	sbic pina,7
	rjmp check

	sbi ddra,7			;Set back as output
	ret



wait1sec:
	ldi mult_reg,64			;Determines pause length, in this case 1s

outer_loop2:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop2:
	
	adiw delay_reg1,1
	brne delay_loop2

	dec mult_reg
	brne outer_loop2
	ret


wait125:
	ldi mult_reg,8			;Determines pause length, in this case 125ms

outer_loop:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop:
	
	adiw delay_reg1,1
	brne delay_loop

	dec mult_reg
	brne outer_loop
	ret



wait46:
	ldi mult_reg,3			;Determines pause length, in this case 46ms

outer_loop1:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop1:
	
	adiw delay_reg1,1
	brne delay_loop1

	dec mult_reg
	brne outer_loop1
	ret


delay100usec:		; 255 * 6 =~ 1600 cycles
	ldi delay_reg1, 255	
    loop100:
    	nop
    	nop
    	nop
	dec delay_reg1
	brne loop100
	
	ret

Quote:
of course, when will you have it done?

=D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Next question, why are you preserving the Z register before you call SendDat? I assumed you used it so I kept the push and pop instructions in your code fix. But a quick look at SendDat shows that the Z register is not used, so there's no need to push and pop that register.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And, if the contents of the Z register was destroyed by the sub-routine it makes sense that it is the called sub-routine that should push and pop it rather than the calling code. If not for anything else, this ensures that you only have to code for it once, no matter how many places you call it - so when you decide to change the subroutine (e.g. so that it destroys another registers content) you only need to change stuff in one place (rather than at n places where the subroutine is called).

In the demo that I linked to early in this thread, the code to output a string in flash is:

message_loop:
   lpm lcdData, Z+      ; Load from FLASH and increment pointer
   tst lcdData          ; End of string? (NUL character?)
   breq infinite_loop   ; Yes, bail out
   lcdChar
   rjmp message_loop    ; Next char

Notice that
i) No push/pop is done
ii) lpm can load into a designated register (the one that the character output subroutine wants it in)
iii) Z is auto-incremented
Why make it more convoluted than that?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made a vertical scrolling version:

;-----------------------------------
; LCD version 14
; Using Port A
; Scrolling Strings
;-----------------------------------

.INCLUDE "M2560DEF.INC"

.def data_reg = r24
.def delay_reg1 = r26
.def delay_reg2 = r27
.def mult_reg = r17


.equ control = portb
.equ data = porta
.equ dataDR = ddra
.equ controlDR = ddrb

.equ rw = 7
.equ rs = 6
.equ enable = 5


;LCD codes
.equ initial_pulse = 0b00110000
.equ four_bit_mode_com = 0b00100000
.equ ndisp_charfont_com = 0b00101000
.equ disp_on_curs_off_com = 0x0C
.equ disp_off_com = 0b0000_1000
.equ disp_clear_com = 0b0000_0001
.equ entry_mode_com = 0b00000110
.equ disp_on_blink_com = 0b00001100
.equ curs_pos2_com = 0b1000_0001
.equ disp_H_com = 0b0100_1010
.equ curs_pos1_com = 0x80
.equ curs_pos1_2line = 0b1100_0000

	rjmp main

main:

	ldi r16, LOW(RAMEND)	; stack setup
	out SPL, r16
	ldi r16, HIGH(RAMEND)
	out SPH, r16


	ser r16
	out dataDR,r16
	out controlDR,r16
	clr r16
	out data,r16
	out control,r16


	rcall init


	; Turn display on, blinking, cursor
	ldi data_reg,disp_on_blink_com
	rcall sendcom

	ldi data_reg,curs_pos2_com	;Position Cursor on second position
	rcall sendcom
	
	ldi zh, HIGH(string*2)		;Print Hello LCD
	ldi zl, LOW(string*2)
	rcall print_string

	rcall wait1sec
	rcall wait1sec

	rcall clearLCD

	;ldi r16,3			;Set cursor on third space
	;rcall setcursor

	
	rjmp loop
	
	;End MAIN

loop:

	ldi zh, HIGH(string1*2)
	ldi zl, LOW(string1*2)
	rcall print_string

	ldi data_reg,curs_pos1_2line	;Cursor on second line
	rcall sendcom

	ldi zh, HIGH(string2*2)
	ldi zl, LOW(string2*2)
	rcall print_string

	rcall wait1sec
	rcall wait1sec
	
	rcall clearLCD

	ldi zh, HIGH(string2*2)
	ldi zl, LOW(string2*2)
	rcall print_string

	ldi data_reg,curs_pos1_2line	;Cursor on second line
	rcall sendcom

	ldi zh, HIGH(string3*2)
	ldi zl, LOW(string3*2)
	rcall print_string

	rcall wait1sec
	rcall wait1sec
	
	rcall clearLCD
	
	ldi zh, HIGH(string3*2)
	ldi zl, LOW(string3*2)
	rcall print_string

	ldi data_reg,curs_pos1_2line	;Cursor on second line
	rcall sendcom

	ldi zh, HIGH(string4*2)
	ldi zl, LOW(string4*2)
	rcall print_string

	rcall wait1sec
	rcall wait1sec
	
	rcall clearLCD

	ldi zh, HIGH(string4*2)
	ldi zl, LOW(string4*2)
	rcall print_string

	ldi data_reg,curs_pos1_2line	;Cursor on second line
	rcall sendcom

	ldi zh, HIGH(string1*2)
	ldi zl, LOW(string1*2)
	rcall print_string

	rcall wait1sec
	rcall wait1sec
	
	rcall clearLCD


	rjmp loop




string:
	.db "Hello LCD",0x00

string1:
	.db "Hi, my name is",0x00

string2:
	.db "Slim Shady",0x00

string3:
	.db "Hi kids do you",0x00

string4:
	.db "like violence?",0x00



;------------------------------------------------------
; Print String
; MUST set z with the string you want to print FIRST
;------------------------------------------------------

print_string:
	
	;ldi zh, HIGH(string*2)
	;ldi zl, LOW(string*2)
print_loop:
	; Print a character
	lpm
	tst r0
	breq string_finished
	mov data_reg, r0
	push zh
	push zl
	rcall senddat

	pop zl
	pop zh

	adiw zl,1

	rjmp print_loop

string_finished:
	ret
	



init:
	rcall wait125

	cbi control,rs			;Register select: command
	cbi control,rw			;RW select, write

	rcall first_pulse
	rcall first_pulse
	rcall first_pulse


	rcall wait46
	ldi r16,four_bit_mode_com			;Start in 4-bit mode
	out data,r16
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	rcall wait46	

	ldi data_reg,ndisp_charfont_com	;Set number of display lines and character font
	rcall sendcom

	ldi data_reg,disp_on_blink_com	;Display on cursor blinking
	rcall sendcom

	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom

	ldi data_reg,entry_mode_com	;Entry mode set
	rcall sendcom
	
	;ldi data_reg,curs_pos1_com
	;rcall sendcom


	ret


first_pulse:
	rcall wait125
	ldi r16,initial_pulse
	out data,r16
	cbi control,enable			;Clear Enable
	nop
	sbi control,enable			;Set Enable
	nop
	cbi control,enable			;Clear Enable
	ret



clearLCD:					;Routine to clear the LCD screen
	ldi data_reg,disp_clear_com	;Display clear
	rcall sendcom
	ret



setcursor:
	ldi r31,curs_pos1_com
	add r16,r31				;Add the spaces
	
	mov data_reg,r16
	rcall sendcom
	ret
	

sendcom:				;Send Command - because we are using 4-bit mode, must send twice
	
	cbi control,rw			;RW select, write
	cbi control,rs			;Register select: command
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall delay5msec
	
	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall delay5msec

	ret



senddat:				;Send Data - because we are using 4-bit mode, must send twice

	cbi control,rw		;RW select, write
	sbi control,rs		;Register select: data
	ser r16
	out dataDR,r16

	out data,data_reg		;Send High
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall delay5msec

	swap data_reg			;Swap Nibbles

	out data,data_reg		;Send Low
	cbi control,enable	;Clear Enable
	nop
	sbi control,enable	;Set Enable
	nop
	cbi control,enable	;Clear Enable
	rcall delay5msec

	ret


	
;For now this code is not being used
LCD_busy:

	cbi ddra,7			;Set BF as input

check:
	sbic pina,7
	rjmp check

	sbi ddra,7			;Set back as output
	ret



wait1sec:
	ldi mult_reg,64			;Determines pause length, in this case 1s

outer_loop2:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop2:
	
	adiw delay_reg1,1
	brne delay_loop2

	dec mult_reg
	brne outer_loop2
	ret


wait125:
	ldi mult_reg,8			;Determines pause length, in this case 125ms

outer_loop:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop:
	
	adiw delay_reg1,1
	brne delay_loop

	dec mult_reg
	brne outer_loop
	ret



wait46:
	ldi mult_reg,3			;Determines pause length, in this case 46ms

outer_loop1:
	
	ldi delay_reg1,low(3037)
	ldi delay_reg2,high(3037)

delay_loop1:
	
	adiw delay_reg1,1
	brne delay_loop1

	dec mult_reg
	brne outer_loop1
	ret


delay100usec:		; 255 * 6 =~ 1600 cycles
	ldi delay_reg1, 255	
    loop100:
    	nop
    	nop
    	nop
	dec delay_reg1
	brne loop100
	
	ret


delay5msec:		; 80 * 4 * 255 =~ 80000 cycles

	ldi delay_reg1, 80
    outerloop2:
	ldi delay_reg2, 255
    innerloop2:
	dec delay_reg2
	nop
	brne innerloop2
    
	dec delay_reg1
	brne outerloop2
	
	ret

Sorry if anyone here doesn't like Eminem... but that's how I felt at times trying to do this in asm =D

Pages