help with the 74hc164 LCD driver

dsvilko

Senior Member
help with the 74hc164 LCD driver [solved]

It's me again :) My last shit register troubles were, it turns out, really caused by a faulty breadboard. As the ultimate goal was to make a 3-wire LCD interface using a shift register I have now moved on to 74hc164 (8-bit shift-register) and made a simple circuit on a stripboard.
Filling the register by the use of the shiftout command on my picaxe 20x2 seems to be working correctly (checked by the LEDs connected to the register outputs). The problem arises when I try to connect the circuit to the LCD (that is working quite happily with the standard 6-wire interface) - I don't get any output.
My guess is that either my initialisation sequence is wrong or one of the pins idles in the wrong logic state (or possibly timing?). The circuit is based on:
http://www.scienceprog.com/connect-lcd-to-atmega-using-3-wires/
Data pin is also used as a LCD RS pin. Here is my code:
Code:
symbol LCDclockPin    = C.0
symbol LCDdataPin     = C.1
symbol setLCDdataPin  = pinC.1
symbol LCDenablePin   = C.2


symbol byt=b10
symbol i=b0
symbol cx=b1

output LCDdataPin
output LCDenablePin
output LCDclockPin
low LCDenablePin
low LCDdataPin
low LCDclockPin

setfreq m64

pause 1000
gosub LCD_init
main:
	
	'pause 10000
	eeprom 6,("Test")
	for i=6 to 9
		read i,byt
		gosub LCD_char
	next i
	pause 10000
	goto main
	

LCD_init:
	byt=$30 : gosub LCD_cmd : pause 10
		  gosub LCD_cmd : pause 1
		  gosub LCD_cmd : pause 1
        byt=$38 : gosub LCD_initcmd
        'byt=$08 : gosub LCD_initcmd
        byt=$0C : gosub LCD_initcmd
        byt=$06 : gosub LCD_initcmd
        byt=$01 : gosub LCD_initcmd
        byt=$02 : gosub LCD_cmd : pause 60
        

        'FOR i = 0 TO 5
        '  READ i,byt
        '  GOSUB LCD_initcmd
        'NEXT
        ' Nibble commands - To initialise 4-bit mode
        'EEPROM 0,( $30 )    ; %0011---- %0011----   8-bit / 8-bit
        'EEPROM 1,( $30 )    ; %0011---- %0010----   8-bit / 4-bit
        'EEPROM 2,( $30 )    ; %00101000 %001LNF00   Display Format
        'EEPROM 3,( $38 )    ; %00001100 %00001DCB   Display On
        'EEPROM 4,( $0F )    ; %00000110 %000001IS   Cursor Move
                            ; L : 0 = 4-bit Mode    1 = 8-bit Mode
                            ; N : 0 = 1 Line        1 = 2 Lines
                            ; F : 0 = 5x7 Pixels    1 = N/A
                            ; D : 0 = Display Off   1 = Display On
                            ; C : 0 = Cursor Off    1 = Cursor On
                            ; B : 0 = Cursor Steady 1 = Cursor Flash
                            ; I : 0 = Dec Cursor    1 = Inc Cursor
                            ; S : 0 = Cursor Move   1 = Display Shift
        'EEPROM 5,( $01 )    ; Clear Screen
	'gosub LCD_loaduserchar
	pause 60
        RETURN

    LCD_initcmd:
        PAUSE 60
                                ; Delay 15mS

    LCD_cmd:
        'shiftout LCDclockPin,LCDdataPin,1,(0)
        shiftout LCDclockPin,LCDdataPin,1,(byt/8)
        low LCDdataPin
        pulsout LCDenablePin,1
                       ; Send to Command register
	return

    LCD_char:
        'shiftout LCDclockPin,LCDdataPin,1,(0)
        shiftout LCDclockPin,LCDdataPin,1,(byt/8)
        high LCDdataPin
        pulsout LCDenablePin,1
        low LCDdataPin
        cx=cx+1
        return
Can you spot any obvious errors?
 
Last edited:

hippy

Ex-Staff (retired)
It could be an issue of speed, not long enough delays or too short pulses. Start slow, build up from there, under-clock rather than over-clock. Does the hardware match the software, msb sent first etc ? Is the shift register wired correctly. Is contrast correct ? Are your I/O lines and pin numbers right ?

Ideally put a scope or logic analyser on the signals to see that what is being sent matches what should be sent. If it comes to the worse you can put a wait after each step of I/O output and only proceed after a button push, check the level of the I/O lines and each line of the shift register with a LED+R.
 
Last edited:

dsvilko

Senior Member
Thanks! I'll try that. I was hoping there was some obvious error in my code (not sure if that's a correct 8-bit initialisation sequence).
 

hippy

Ex-Staff (retired)
Not sure on the initilasation; shouldn't it be $38 rather than $30 ? What does your LCD datasheet say ?
 

westaust55

Moderator
driving LCD's with a shift register based circuit

Hi dsvilko

Presume with breadboard fault found that your circuit is now working.
For your information, I too have done a slightly similar LCD driver based on shift registers and the results are posted in the Finished Projects section at:
http://www.picaxeforum.co.uk/showthread.php?t=13900

Use a 2-wire interface if you flush the shift registers by clocking zeros in or if you have sufficient PICCAXE outputs use 3-wire and avoid the time delay to pre-flush the shift register.
Also includes a means of switching the on-board backlight since the standard codes for LCD’s do not include backlight control.
If you want to do some experimentation in a similar manner, make sure you read the entire thread first as it does not obey all the typical LCD timing rules.
 

hippy

Ex-Staff (retired)
There should be no need to clear or flush the shift register; the last eight bits shifted in will remove the previous which were there.

For an optimised bit-banging scheme, tracking what's in the shift register and what's wanted can minimise the number of clock pulses required to obtain it. With SPI hardware it's easier to just throw the full eight bits at it and that simplifies bit-banging code as well.
 

westaust55

Moderator
There should be no need to clear or flush the shift register; the last eight bits shifted in will remove the previous which were there.

For an optimised bit-banging scheme, tracking what's in the shift register and what's wanted can minimise the number of clock pulses required to obtain it. With SPI hardware it's easier to just throw the full eight bits at it and that simplifies bit-banging code as well.
@hippy,

maybe not for the version dsvilko is using (have not checked) but there certainly is for the circuit I was discussing from my old thread.
Otherwise any bit previously set as a "1" can trigger data into the LCD early resulting in garbage being displayed.


EDIT:
Just checked the atmega C code that dsvilko linked to and the first thing done is:
PORTC&=~_BV(datapin); // sets datapin to output a LOW
for (i=0;i<=7;i++) //clear shift 74HC164 register
but seeming not required for the circuit involved.
Probably another case of someone cloning and posting code without fully understanding it.
 
Last edited:

dsvilko

Senior Member
Thank you all again for your help. The fault was indeed in the breadboard. Previously I believed that the breadboard was broken only on a few distant pins but it turns out the 'infection' is spreading. Will have to get another one.

I opted for the 3-wire variant as it's a bit simpler to code and faster to operate. It's indeed unnecessary to clear the register before clocking in the new byte. The code works perfectly even at 64MHz and the shiftout command is significantly faster than bit banging.
Here is my current (a bit cleaned up) program with a few subrutines that could possibly be useful to somebody:
Code:
' The list of subrutines:

' gosub LCD_init                    - initialise the LCD
' gosub LCD_clear                   - clear the LCD
' byt=B_home : gosub LCD_cmd        - send a command to the LCD
' byt=B_home : gosub LCD_pcmd       - same as above but with a short pause afterwards
' byt="A" : gosub LCD_char          - print a character
' i=2 : gosub LCD_string            - print the i-th string (stored in eeprom) 
' num=4851 : exp=3 : gosub LCD_num  - print 4.851 
' num=4851 : exp=3 : gosub LCD_bignum      - print 4.851 in big numbers
' num=20 : num2=100 : L=5 : gosub LCD_bar  - print =---- progress bar
' L=2 : k=0 : gosub LCD_position    - position the cursor at the start of the second line
' gosub GetBattVoltage              - sets num to batt. voltage * 100 and exp to 2

' I/O pins:
symbol LCDclockPin    = C.0	' goes to 74hc164 clock pin
symbol LCDdataPin     = C.1	' goes to 74hc164 A&B data pins and to LCD RS pin
symbol setLCDdataPin  = pinC.1
symbol LCDenablePin   = C.2	' goes to LCD E pin
symbol irinPin	= B.0	' IR input

SYMBOL  k     = b1
SYMBOL  n     = b2
SYMBOL  m     = b3
SYMBOL  ir    = b4
SYMBOL  exp   = b5
SYMBOL  f     = b6
symbol  x1    = b7
symbol  x2    = b8
symbol  x3    = b9
SYMBOL  L     = b10
SYMBOL  i     = b11
SYMBOL  byt   = b12
'SYMBOL  rsbit = b13
symbol  foo   = b14
symbol  n1    = b15
symbol  n2    = b16
symbol  n3    = b17
symbol  n4    = b18
symbol  n5    = b19

SYMBOL  num   = w10
SYMBOL  num2  = w11
SYMBOL  num3  = w12
symbol  duty  = w13

symbol  y1    = b30
symbol  y2    = b31
symbol  y3    = b32
symbol  bnk   = b33
symbol  cx    = b34

' LCD commands
' usage: 
'   byt=B_clear : gosub LCD_pcmd

symbol B_clear  = $01
symbol B_home   = $02
symbol B_left   = $10
symbol B_right  = $14
symbol B_sleft  = $18
symbol B_sright = $1E
symbol B_off    = $08
symbol B_on     = $0C
symbol B_nocursor = $0C
symbol B_bbcursor = $0F
symbol B_ulcursor = $0E
     
symbol EEPROM_string_start = 6


' EEPROM data for the LCD_string sub.
' strings are separated by the '|' character
' usage example:
'    i=2 : gosub LCD_sring
eeprom EEPROM_string_start,("                |Batt: |IR code: |")
' set i to:                         1            2        3 

symbol userchars=5  ' number of user defined characters
symbol EEPROM_userchar_start = 147  ' also change below
' BigNum character definitions
eeprom 147,(%00011,%00111,%00111,%00111,%00111,%00111,%00111,%00011) '0
eeprom (%11000,%11100,%11100,%11100,%11100,%11100,%11100,%11000) ' 1
eeprom (%11111,%11111,%11111,%00000,%00000,%00000,%00000,%00000) ' 2 
eeprom (%00000,%00000,%00000,%00000,%00000,%11111,%11111,%11111) ' 3
eeprom (%11111,%11111,%11111,%00000,%00000,%11111,%11111,%11111) ' 4 
eeprom (%00000,%00000,%00000,%00000,%00000,%01110,%01110,%01110) ' 5

symbol EEPROM_chardef_start=195  ' also change below
' How to make the BigNums from the user characters
eeprom 195,(0,2,1,0,3,1) '0
eeprom (" "," ",1," "," ",1) ' 1
eeprom (" ",4,1,0,3," ") ' 2
eeprom (" ",4,1," ",3,1) ' 3
eeprom (0,3,1," "," ",1) ' 4
eeprom (0,4," "," ",3,1) ' 5
eeprom (0,3," ",0,3,1) ' 6
eeprom (" ",2,1," "," ",1) ' 7
eeprom (0,4,1,0,3,1) ' 8
eeprom (0,4,1," "," ",1) ' 9

let adcsetup = 0
' define and initialise outputs
low LCDenablePin
low LCDdataPin
low LCDclockPin

setfreq m64

PowerOnReset:
        GOSUB LCD_init
	pause 100

foo=0

main:
	gosub GetBattVoltage
	'gosub LCD_clear
	byt=B_home : gosub LCD_cmd
	i=2 : gosub LCD_string
	cx=5
	gosub LCD_bignum
	byt="V" : gosub LCD_char

lup:	
	ir=250
	irin [1000,main],irinPin,ir
	
	select case ir
	case 18
		byt=B_sleft : gosub LCD_pcmd
	case 19
		byt=B_sright : gosub LCD_pcmd
	else
		L=2:k=0:gosub LCD_position
		i=3 : gosub LCD_string
		num=ir:exp=0:gosub LCD_num
		i=1 : gosub LCD_string
	endselect
	pause 1000
	goto lup

        END



LCD_bignum:   ' k=start column(0..)   num=number   
	f=0

	num2=10000
	y1=6
	y2=exp+1
      bignum_again:
	n=num/num2
	num3=n*num2
	num=num-num3
	num2=num2/10

	if y2=y1 then 
		L=2 : k=cx : gosub LCD_position
		byt=5 : gosub LCD_char
		'cx=cx-1
		f=1
	endif
	if f=1 or n>0 or y1=2 then 
		f=1
		gosub PrintBigNum
	endif
	y1=y1-1
	if y1=1 then : return : endif
	goto bignum_again


PrintBigNum:  'k=start column   n=number (ascii)
	i=n*6
	i=i+EEPROM_chardef_start
	L=1 : k=cx: gosub LCD_position
	read i,byt : gosub LCD_char : i=i+1
	read i,byt : gosub LCD_char : i=i+1
	read i,byt : gosub LCD_char : i=i+1 
	L=2 : k=cx-3 : gosub LCD_position
	read i,byt : gosub LCD_char : i=i+1 
	read i,byt : gosub LCD_char : i=i+1 
	read i,byt : gosub LCD_char
	'bnk=bnk+3
	L=1 : k=cx: gosub LCD_position
	'pause 15000
	return

		
LCD_clear:
	byt=B_clear : gosub LCD_pcmd
	cx=0
	return
		

LCD_loaduserchar:
	pause 500
	n=EEPROM_userchar_start
	for i=0 to userchars
		byt=i*8|$40
		gosub LCD_pcmd

		for L=0 to 7
			read n,byt
			gosub LCD_char
			n=n+1
		next L
		pause 64
	next i
	pause 60
	byt=7*8|$40
	gosub LCD_pcmd
	byt=%11111
	pause 30
	for L=0 to 7
		gosub LCD_char
	next L
	pause 60
	return

NewDuty:
	if duty>10000 then :duty=0 : endif
	if duty>1000 then : duty=1000: endif
	pwmduty C.5,duty

	L=2:k=0:gosub LCD_position
	num=duty/10 : num2=100 : L=5 : gosub LCD_bar
	L=1:k=0:gosub LCD_position
	num=duty/10 : exp=0 : gosub LCD_num
	byt="%" : gosub LCD_char
	byt=" " : gosub LCD_char
	return

LCD_bar:
	num=num*L*10/num2
	k=num//10
	if k<5 then
		n=num/10
	else
		n=num+5
		n=num/10
	endif
	for i=1 to L
		if i>n then 
			byt="-"
		else
			byt="="
		endif
		gosub LCD_char
	next i
	return

LCD_string:
	L=1
	for m=EEPROM_string_start to 255
		read m,byt
		if byt="|" then
			L=L+1
			if L>i then : return : endif
		elseif i=L then gosub LCD_char
		endif
	next m
	return
	

GetBattVoltage:
	num2=0
	for i=1 to 20
		calibadc10 num
		num=60000/num*100/60
		num2=num2+num
		pause 1
	next i
	num=num2/20*49/47
	exp=2
	return

LCD_position:             ;  L=1|2 : k=0.. : gosub Position
	L=L-1
	cx=k
	if L=1 then : k=k+$40 : endif
	k=k+$80
	byt=k :  gosub LCD_cmd
	RETURN

LCD_pcmd:
	gosub LCD_cmd
	pause 60
	return

LCD_num:
	f=0
	bintoascii num, i, L, k, m, n
        if exp=5 then : byt="." : gosub LCD_char : f=1: endif
	if f=1 or i<>"0" then : byt=i : gosub LCD_char :  f=1 : endif
	if exp=4 then : byt="." : gosub LCD_char: f=1 : endif
	if f=1 or L<>"0" then : byt=L : gosub LCD_char f=1: endif
	if exp=3 then : byt="." : gosub LCD_char: f=1 : endif
	if f=1 or k<>"0" then : byt=k : gosub LCD_char f=1: endif
        if exp=2 then : byt="." : gosub LCD_char: f=1: endif
	if f=1 or m<>"0" then : byt=m : gosub LCD_char: f=1 : endif
	if exp=1 then : byt="." : gosub LCD_char: f=1: endif
	byt=n: gosub LCD_char
	return
			
LCD_init:
	byt=$38 : gosub LCD_initcmd
	byt=$0C : gosub LCD_initcmd
	byt=$06 : gosub LCD_initcmd
	byt=$01 : gosub LCD_initcmd	
	byt=$02 : gosub LCD_cmd
	pause 60
	gosub LCD_loaduserchar
	RETURN

LCD_initcmd:
	PAUSE 60
                                ; Delay 15mS

LCD_cmd:
        shiftout LCDclockPin,LCDdataPin,1,(byt/8)
        low LCDdataPin
        pulsout LCDenablePin,1
	return

LCD_char:
        shiftout LCDclockPin,LCDdataPin,1,(byt)
        high LCDdataPin
        pulsout LCDenablePin,1
        cx=cx+1
        return
 

MartinM57

Moderator
Ah - I see you have a formal background in programming - a declared and assigned variable called 'foo' that is never used for anything :D:D
 
Top