LCD busy flag

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Post Reply
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

LCD busy flag

Post by banedon »

Ok guys. I am able to write to a 44780 LCD module using a 65C22S VIA without issues. One thing that bothers me is that I'm having to write a delay routine which simply spams NOP commands between each write as LCD is not fast enough unless I run my PHI2 at 2MHz or below.
To address this I was going to use a timer, but as Garth has pointed out elsewhere I should be using the LCD's busy flag.

As such, I've tried writing some code to do this, but so far not got very far.

Here's my efforts so far:
NOTES:
- I know I should be using ORA and AND to flip selective bits rather than writing whole values, but it's easier to see what's going on when writing entire values. Will correct when I get the code right
- VIA_DDRA refers to the data direction register for port A of the VIA. This has been tested and works
- VIA_outA refers to the IRA/ORA register for the VIA. Again, it has been tested and works.
- The LCD is in 4 bit mode when this code is called
- Writing to the LCD works fine using the delay routine (delay40) which uses NOPs to delay the 65C02
- If the delay40 calls in the write character to LCD routine are replaced by calls to this code then the CPU never exits the busy flag checking loop in this code

Code: Select all

# PA0-3 = LCD DB4 to DB7
# PA4 = E
# PA5 = R/W  0=ok for next op, 1=LCD is busy
# PA6 = RS 0=command, 1=data
# PA7 = not connected


SECTION -- LCD is ready for command check --

.checkLCDisvalid
	# save A and the flags to the stack
	PHA
	PHP
	
	# set LCD DB4-7 (VIA port A pins 0-3) to input as well as LCD RW (VIA port A pin 5) (VIA regards 1=output, 0=input)
	LDA #%11010000
	STA VIA_DDRA
	
	# set LCD RW to read using VIA port A pin 5 = 1 (RW=Read)
	# also, set LCD RS to command mode using VIA port A pin 6 = 0 (RS=Instruction mode)
	# Set E to low in readiness to send command
	LDA #%10100000
	STA VIA_outA

	# this loop simply keeps checking the busy flag of the LCD and exits when it's 0
	.LCDvalidLoop
		# send E high to send first part of read busy flag command
		LDA #%10110000
		STA VIA_outA
		JSR delay40		
		# Set E low (bit 4) and then bring high to send second part of read busy flag command
		LDA #%10100000
		STA VIA_outA
		LDA #%10110000
		STA VIA_outA
		# read port A - we need pin 3 (LCD DB 7)
		LDA VIA_outA
		# busy flag (db7, port A pin 3) needs to be 0 to carry on and not loop
		# check to see if the bit is set to 1. If not then carry on. Otherwise loop
		# if DB7 (BusyFLag) = 1 then LCD is busy. To check, BIT will perform an AND between the result of DB7
		# and %00001000. This will reuslt in a 1 (for busy) or a 0 (for ok). If result=0 (ok) then Z flag=1.
		# if result=1 (busy) then Z flag=0
		# BNE branches if Z=0 (busy)
		BIT #%00001000
		BNE LCDvalidLoop
	# set VIA port A back to output as we need to leave it ready to send commands or data to the LCD
	LDA#%11111111
	STA VIA_DDRA
	# set VIA to RW=0 (write mode), E=0, RS=0 (command mode)
	LDA #%00000000
	STA VIA_outA

	# restore the A register and the flags on routine exit
	PLP
	PLA
RTS
Last edited by banedon on Sat Jan 14, 2017 8:22 pm, edited 2 times in total.
User avatar
Hobbit1972
Posts: 80
Joined: 10 Feb 2015
Location: Germany

Re: LCD busy flag

Post by Hobbit1972 »

banedon wrote:

Code: Select all

	# set LCD DB7 (port A pin 3) to input on the VIA port A pin 5 = 0 (1=output, 0=input)
	LDA #%11010000
	STA VIA_DDRA
By this you fix R/W as it was previously probably 0=Write?
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: LCD busy flag

Post by banedon »

Yes sorry I have just noticed how confusing that must look - I'll correct it. Upon entry to the subroute the LCD is in write mode with the VIA port A pin 5 set to output and set to 0. This section switches VIA port A pins 0 to 3 and pin 5 to input mode. They correspond with DB4-7 and R/W on the LCD.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: LCD busy flag

Post by GARTHWILSON »

You have a 40µs delay in there. The LCD needs 40µs to process certain things; but only 1µs to read the busy flag. If you re-arrange the bits and put the LCD's data bits 4-7 on VIAPA4-7 (VIA Port A's bits 4 to 7), you can use BIT to test bit 7 (where the busy bit will be), regardless of what's in the accumulator. The BIT instruction copies bit 7 into the N bit so you can branch on it with BMI or BPL. I've interfaced to these LCDs many times over the years, and in the 6502 primer, I have a sample 4-bit interface circuit at http://wilsonminesco.com/6502primer/potpourri.html#LCD, and sample code in various forms (4- and 8-bit interface, assembly language and Forth, directly on the bus and through a VIA) at http://wilsonminesco.com/6502primer/LCDcode.asm .
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: LCD busy flag

Post by banedon »

OK I'm thinking that not only do I have to send two sets of four bits for the command, I then have to read the two sets of 4 bits back with the first set containing DB7 as it is the MSB. Also (and as I originally thought) each section of the command is executed by a falling edge of E. I.e. E must be high and then go low. I'd misremembered which way around this was in the year since I last looked at this stuff.

[edit] I missed what Hobbit1972 was getting at. I had set the pin direction for the r/w pin on the VIA to input when it should be output. I've corrected that.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: LCD busy flag

Post by GARTHWILSON »

If you read it, there's no command necessary except that the RS bit tells it what to read back to you, and the R/W bit tells it that you do want to read.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: LCD busy flag

Post by banedon »

New code. Seems to be working with PHI2 at 8MHz:

[edit] But not working at 10MHz :(. Code is running as my Bus Monitor shows code executing at the end of the test code segment, but althought the LCD screen looks like it has initialised (all 4 lines available instead of two ok and two not) no characters appear.
Maybe I do need those delays. As such, it sort of makes using the bust flag in 4 bit mode a bit useless lol. More testing ahead.

Code: Select all

# PA0-3 = LCD DB4 to DB7
# PA4 = E
# PA5 = R/W  0=ok for next op, 1=LCD is busy
# PA6 = RS 0=command, 1=data
# PA7 = not connected


SECTION -- LCD is ready for command check --

.checkLCDisvalid
	# A and the flags are used in this routine
	# save A and the flags to the stack
	PHA
	PHP
	
	# set LCD DB7 (port A pin 3) to input on the VIA port A pin 5 = 0 (1=output, 0=input)
	LDA #%11110000
	STA VIA_DDRA
	
	# set LCD RW to read using VIA port A pin 5 = 1 (RW=Read)
	# also, set LCD RS to command mode using VIA port A pin 6 = 0 (RS=Instruction mode)
	# Set E to low in readiness to send command
	LDA #%10110000
	STA VIA_outA

	# this loop simply keeps checking the busy flag of the LCD and exits when it's 0
	.LCDvalidLoop
		# send E high to send first part of read busy flag command
		LDA #%10100000
		STA VIA_outA	
		# Set E low (bit 4) and then bring high to send second part of read busy flag command
		LDA #%10110000
		STA VIA_outA
		LDA #%10100000
		STA VIA_outA
		# read port A - we need pin 3 (LCD DB 7). This is the MSB of the result
		LDA VIA_outA
		# save the flags to the stack as we need Z flag
		PHP
		# read the LSB of the result. We don't care about this as we need DB7 which is in the MSB
		LDA VIA_outA
		# restore the flags so we can test Z flag
		PLP
		# busy flag (db7, port A pin 3) needs to be 0 to carry on and not loop
		# check to see if the bit is set to 1. If not then carry on. Otherwise loop
		# if DB7 (BusyFLag) = 1 then LCD is busy. To check, BIT will perform an AND between the result of DB7
		# and %00001000. This will reuslt in a 1 (for busy) or a 0 (for ok). If result=0 (ok) then Z flag=1.
		# if result=1 (busy) then Z flag=0
		# BNE branches if Z=0 (busy)
		BIT #%00001000
		BNE LCDvalidLoop
	# set VIA port A back to output as we need to leave it ready to send commands or data to the LCD
	LDA#%11111111
	STA VIA_DDRA
	# set VIA to RW=0 (write mode), E=0, RS=0 (command mode)
	LDA #%00000000
	STA VIA_outA

	# restore the A register and the flags on routine exit
	PLP
	PLA
RTS
Changes made:

- I removed the delay40's as my concern that they were needed was unfounded
- Changed the pin on the VIA connected to the LCD's RW pin to output when instead of input (thanks for the hint Hobbit1972 :))
- Changed the way E was used so that it executes commands by being high and then going low
- After both sets for 4 bits of the command are sent and executed, read *twice*. This gives MSB then LSB of result.
Klaus2m5
Posts: 442
Joined: 28 Jul 2012
Location: Wiesbaden, Germany

Re: LCD busy flag

Post by Klaus2m5 »

I do not think it is valid to read data after the enable pulse has ended. The data hold time of a HD44780 is specified as 5 ns. Whatever you read 4 cycles later is hi-z and it will be coincidence wether the busy flag still shows correctly (static).

You need to set enable, wait for 160 ns (for which the LDA abs cycles until the input is gated may be enough already), then read the busy flag, then drop enable.
LCD_read_diagram.png
LCD_read_specs.png
6502 sources on GitHub: https://github.com/Klaus2m5
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: LCD busy flag

Post by banedon »

Hmm so I *do* need a timer if PHI2 various/is not set.

[update] Got it working at 10MHz. The issue was caused by the routine that sends commands/data to the LCD still using the old delay40 routine. I replaced the call to delay40 routine with one calling my new BF check.
I still need to test this at 16MHz (max clock my 65C02 GPD design runs at), but don't currently have a 32MHz oscllator can (the 32 is divided by 2 using a D type flipflop to give better rise & fall times). Got an AEL one on order from ebay. If things are still ok at that speed then I'll declare victory on that and start looking trying to implement I2C. To that end, I'll have a reread of Garth's primer.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: LCD busy flag

Post by GARTHWILSON »

Be sure to do the LCD reset routine to get a consistently good reset. I show the procedure at http://wilsonminesco.com/6502primer/LCDcode.asm . Without it, the LCD may work sometimes and not others, making you think you got it going when really it won't always work, or make it look like the higher frequency isn't working when that was not the problem.

Continuing:
Quote:

Code: Select all

	# set LCD RW to read using VIA port A pin 5 = 1 (RW=Read)
	# also, set LCD RS to command mode using VIA port A pin 6 = 0 (RS=Instruction mode)
	# Set E to low in readiness to send command
	LDA #%10110000
	STA VIA_outA
Be sure to leave E down after every read or write, and only change the RS and R/W bits when E is down, and never at the same time with raising or lowering E.
Quote:

Code: Select all

	# this loop simply keeps checking the busy flag of the LCD and exits when it's 0
	.LCDvalidLoop
		# send E high to send first part of read busy flag command
		LDA #%10100000
		STA VIA_outA	
		# Set E low (bit 4) and then bring high to send second part of read busy flag command
		LDA #%10110000
		STA VIA_outA
		LDA #%10100000
		STA VIA_outA
		# read port A - we need pin 3 (LCD DB 7). This is the MSB of the result
		LDA VIA_outA
		# save the flags to the stack as we need Z flag
		PHP
		# read the LSB of the result. We don't care about this as we need DB7 which is in the MSB
		LDA VIA_outA
		# restore the flags so we can test Z flag
		PLP
Didn't you want PHA and PLA instead of PHP and PLP here? I think you want the high nybble which is read first; but as shown, the next LDA permanently overwrites it. You've saved and restored the status, but then the BIT instruction below overwrites that status before it can get used. I think you wanted to save the accumulator, not the status.
Quote:

Code: Select all

		# busy flag (db7, port A pin 3) needs to be 0 to carry on and not loop
		# check to see if the bit is set to 1. If not then carry on. Otherwise loop
		# if DB7 (BusyFLag) = 1 then LCD is busy. To check, BIT will perform an AND between the result of DB7
		# and %00001000. This will reuslt in a 1 (for busy) or a 0 (for ok). If result=0 (ok) then Z flag=1.
		# if result=1 (busy) then Z flag=0
		# BNE branches if Z=0 (busy)
		BIT #%00001000
		BNE LCDvalidLoop
	# set VIA port A back to output as we need to leave it ready to send commands or data to the LCD
	LDA#%11111111
	STA VIA_DDRA
	# set VIA to RW=0 (write mode), E=0, RS=0 (command mode)
	LDA #%00000000
	STA VIA_outA

	# restore the A register and the flags on routine exit
	PLP
	PLA
RTS
Changes made:

- I removed the delay40's as my concern that they were needed was unfounded
- Changed the pin on the VIA connected to the LCD's RW pin to output when instead of input (thanks for the hint Hobbit1972 :))
- Changed the way E was used so that it executes commands by being high and then going low
- After both sets for 4 bits of the command are sent and executed, read *twice*. This gives MSB then LSB of result.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Post Reply