PIC16F84A with LCD JHD162A and Keypad 4X3(sort of)

Status
Not open for further replies.
So, I'm guessing the flow of the program would be something like
1. Declare variables
2. startup for LCD
3. Toggle 'E' twice?
4. Scan keypad
5. Put the data on the bus
6. raise and lower the 'E' signal
7. change the pin connected to D7 to be an input
8. read D7
9. if it is '1', go back to step 4
10. change the pin to be an output again.


Am I missing something else ? O.O
I also have to write in the program for each time to write character R/W = 0 and RS = 1?
What would happen if I ground R/W? Would toggle 'E' still work?
 

You must toggle 'E' twice every time you write data to the LCD, it is part of the writing process. It's the part that says "data is ready- come and get it".

The LCD can be used in two modes, in 8-bit mode you put the whole data byte (8 bits) on D0-D7 and toggle 'E' once, in 4-bit mode you split the 8 bits into two 4 bit 'half bytes' and send them one after the other, toggling 'E' each time. As you do not have enough pins on the 84A, you are forced to use 4-bit mode.

Yes, connect R/W to ground so it is always in write mode. The LCD will still display the message correctly. The only thing you sacrifice is the ability to read the LCD internal registers back so you can't for example, read the font RAM. This is bot important for 99% of LCD users though.

Otherwise, your program flow looks fine except that step 9 should go back to step 8, you want to be in a loop, waiting for the busy signal to go low before proceeding. You should add step 11 which would be 'change to next column and then got ot step 4'.

Brian.
 

    Shinnster

    Points: 2
    Helpful Answer Positive Rating
Need a slight clarification.
When you say toggle E. Do you mean its like step 6 ?
And when you say twice, do you mean like:
1) toggle E
2) toggle E
3) send data to LCD ?

Thanks for the help.
 

Sorry been away all day, a 500 Km trip so my body is back home but my brain is still on the move! I need coffee !!

By toggling 'E' I mean changing it's state for a moment them changing it back again.
So in this case, I mean making 'E' = 1 then making 'E' = 0 again. Raising and lowering it.

The LCD needs 8 bits of data but you are using it in 4 bit mode. This is perfectly OK and the manufacturer has made provision for this. To get the 8 bits down the 4 data wires you do it in two stages, I quote from the data sheet of the HD44780 which is an almost identical LCD controller but from Hitachi rather than Samsung:
Code:
For 4-bit interface data, only four bus lines (DB4 to DB7) are used for transfer. Bus lines DB0 to DB3
are disabled. The data transfer between the HD44780U and the MPU is completed after the 4-bit data
has been transferred twice. As for the order of data transfer, the four high order bits (for 8-bit
operation, DB4 to DB7) are transferred before the four low order bits (for 8-bit operation, DB0 to
DB3).
The busy flag must be checked (one instruction) after the 4-bit data has been transferred twice. Two
more 4-bit operations then transfer the busy flag and address counter data.

So if you imagine you wanted to display the character 'X' which has ASCII code 0x58 you would do this:
1. put the '5' of the 58 on the data bus
2. raise 'E' then lower 'E'
3. put the '8' of the 58 on the data bus
4. raise 'E' then lower 'E'
5. change the pin connected to LCDs D7 to be an input
6. keep reading D7 until it goes low
7. change the pin back to an output

Brian.
 

    Shinnster

    Points: 2
    Helpful Answer Positive Rating
OHHHH I get the toggling E now! Thanks a lot!

How 'exactly' you 'put' the 5 on the data bus though? :S
Say D4 D5 D6 D7...think binary? Which is LSB and MSB?
 

D7 is the MSB (Most Significant Bit) because changing it ha most significant effect on the value. Obviously, this makes D0 the LSB.

There are several ways of 'splitting' a byte into two halves. All you are trying to do is align the top half or the bottom half with the data lines you are using. I'm not sure how your circuit is wired so these are some alternative ways:

Assuming the byte is in a register called 'LCDchar' and the 'E' pin is named LCDE.

If your LCD data lines are connected to PORTB bits 7 to 4:
Code:
movlw h'F0'          ;this will be used to remove the lower bits
andwf LCDchar,w  ;top half of W now contains the top half of LCDchar value
movwf PORTB       ;output it to the port
bsf LCDE              ;make E high
bcf LCDE              ;make E low again
movlw h'F0'
swapf LCDchar,f    ;swap top and bottom half of LCDchar
andwf LCDchar,w  ;top half of W now contains the bottom half of LCDchar
movwf PORTB       ;output it to the port
bsf LCDE              ;make E high
bcf LCDE              ;make E low again

If the LCD data lines are connected to PORTB bits 3 to 0:
Code:
movlw h'0F'
swapf LCDchar,f
andwf LCDchar,w
movwf PORTB
bsf LCDE
bcf LCDE
swapf LCDchar,w
andlw h'0F'
movwf PORTB
bsf LCDE
bcf LCDE

I think you can work out suitable comments for the second code! Sorry about the strange comment spacing, I can't see a way of entering tabs into the reply window.

Note that in these examples ALL the bits of PORTB are driven simultaneously but the ones not connected to the LCD are set to '0' and only go to the keypad anyway so they will not cause harm.

Brian.
 

Okay so I did a simple test with LEDS on each Data bits
and yeah, what you said was right. I got question though.
If you Toggled 'E' three times, what would happen?
Assuming that you want to write X "Same example earlier",
I send 5 then toggle 'E' TWICE in a row and then send 8 then toggle 'E', What would happen? Just curious.
and I will be using D4-D7 for the convenience of my stripboard.
Oh and I got a question on the Backlight, I need a current limiting resistor so what ohm should I put roughly?
I think I'm good to go on writing the program! :]
I will post the program when I am done . I might make a mistake on setting up the LCD but oh well, I'll do my best.
OH and I need to you ask you about the column resistors of 100ohm, does the resistor go through 'only' the keypad or data lines as well?


Thanks again.
 

The 'E' signal tells the LCD to read whatever is on the data lines so you could get out of step if you throw an extra one in. It could get confused between first and second half bytes. There are two mechanisms that help to protect against that:

1. The LCD takes about 100mS (0.1 seconds) to initialize which gives plenty of time for the PIC to settled down and get its pins in the right states before the LCD even looks at the 'E' pin.

2. The busy flag (D7) only goes to a busy state after the second time 'E' is toggled so if necessary, put h'00' on the data pins and toggle 'E' until D7 goes high. When D7 drops you know it is prepared to accept the first half byte.

The data sheet says the backlight LED has a built-in resistor so it should run directly from 5V but I have seen LCD modules with the resistor shorted out by a link. There are 5 parallel chains of two LEDs in series so if in doubt, assume the forward drop of the LEDs is 3V and use a 22Ω resistor to give about 100mA current.

The 100Ω resistors are there so that if more than one key is pressed, the wires to the LCD or PIC don't become directly shorted to each other. Put the resistors in line with the keypad only. You can put them in the row and column connection if you like, the current through the keys is insignificant so they will make no difference to the performance but will give a degree of protection.

I await your code. 8O

Brian.
 


Houston, we have a problem!

Bjhotmail, your code is for driving a serial LCD but Shinnster has a parallel interfaced one. I have not simulated your code but I think you might have a problem in your keypad scanning routine. You seem to drive the columns accumulatively rather than sequentially so it may be possible to get wrong code numbers by holding more than one key down. Using look-up tables makes the code much smaller.

Brian.
 

betwixt thanks for the heads up about the possible wrong code numbers by multiple button pushes. the program does work and the keypad scanning sequence is sequential. ive looked into lookup tables but never really caught on to it. do you per chance know how to do multiple key reading, as in cntl+alt+del for maybe a hidden or special function?
thanks!

Added after 6 minutes:

betwixt thanks for the heads up about the possible wrong code numbers by multiple button pushes. the program does work and the keypad scanning sequence is sequential. ive looked into lookup tables but never really caught on to it. do you per chance know how to do multiple key reading, as in cntl+alt+del for maybe a hidden or special function?
thanks!
 

If you use look-up tables it is easy to check for as many as all four keys being pressed at once as long as they are in the same row or column, whichever is being read.

If you are driving the columns (swap column for row if doing it the other way around) all you do is read the row inputs, none, one, two, three or all four keys could be pressed so any combination between 0000 and 1111 could be read from the row inputs. If it's 0000, no keys were pressed so move on to the next column.

That's only 16 combinations and you only need as many entries in the look-up table as there are combinations so it is quite small.

Call this routine with the row data in W
Code:
addwf PCL,f
retlw <value for combination 0001>
retlw <value for combination 0010>
retlw <value for combination 0011>
.
.
.
retlw <value for combination 1111>
It returns from the call with the value you put in the table in W.
This is only 1 instruction longer than there are possible return values so in 16 bytes you have a 15 entry look-up table. It doesn't get more efficient than that and it only executes 2 instructions before returning.

Remember you need one table per column and beware of the page boundary problem I mentioned in an earlier post. You must CALL this table because the retlw instruction acts as a subroutine return.

Brian.
 

I have another question.
Assuming I want to ask the LCD to clear display, I have to toggle 'E' as well?
 

Yes, write the character code h'01' to the LCD and it will clear the whole display and return the cursor position to the first position. The commands for formatting the display and cursor control are in the LCD data sheet.

ALL the commands need to have 'E' toggled, it tells the LCD that you have set up the data lines and they should be read into the LCD.

Brian.
 

Boo hoo I done the program up until the keypad.
Got confused at the look up table again.


Since you need to do look up table for each row...
- BSF Column 1
- GOTO Lookup (Lookup table)
--> Lookup addwf PCL,f
--> retlw "A" (Lets say A was pressed)
--> Movwf PortB??? (Move A into port B)
--> get the LCD ready to print that. Am I doing it right? I'm not sure.

Do tell

P.S : I'm not sure my program is right or not - I'm weak in programming


Meh I messed up. I'm bad at programming :] well, still learning, lots to go !
Just read through your post with 'swap' and 'and' >.>
I could use that I guess, for Clear Display and Return Home.
 

Hmmmm....... Interesting code :|

A few general points:
Code:
			LIST		p=16f84a
			#include 	<P16f877a.INC>

will really screw thing up! You are telling it to prepare code for a 16F84A but to use the registers of a 16F877A. They are quite different devices !

Code:
			BSF			STATUS,RP0
			MOVLW		0x00			;Port A as output (Has Enable and RS)
			MOVWF		TRISA
			MOVLW		0x0F			;Port B - 00001111 (RB0-RB3 = Row) (RB4-RB7 = Column | D4-D7 ~ LCD)
			MOVWF		TRISB
			BCF			STATUS,RP0
			BCF			PORTA,2			; R/S set 0
will lead you into trouble later. Technically, it is OK but if you forget how RP0 and RP1 are set you will end up accessing the wrong registers. It is much safer to do it like this and let the assembler produce the bank selection for you.
Code:
			banksel		TRISA
			MOVLW		0x00			;Port A as output (Has Enable and RS)
			MOVWF		TRISA
			MOVLW		0x0F			;Port B - 00001111 (RB0-RB3 = Row) (RB4-RB7 = Column | D4-D7 ~ LCD)
			MOVWF		TRISB
			banksel		PORTA
			BCF			PORTA,2			; R/S set 0

Your next potential trap is where you placed this code. Again, you are not incorrect but you risk unexpected results, the reason is that the address 04 is reserved as the interrupt entry point. Whenever an interrupt occurs, the program will jump to address 04 and expects to find the interrupt service routine there. In your program, it will jump to the middle of your initialization code. It is far safer to copy the template from C:\Program Files\Microchip\MPASM Suite\Template\Code\16F84ATEMP.ASM and use that as the starting place to work from.

This doesn't make sense - the second GOTO can never be reached:
Code:
			BSF			PORTB,7			; Column 4
			GOTO		READ_ROW
			GOTO		KEYPAD

You still haven't quite got the idea of the look-up table. Because it returns with a value in W you have to reach it with a CALL instruction, not a GOTO. The difference is that a GOTO simply diverts the program flow by putting the destination into the PCLATH and PCL registers so the program continues from the new instruction they point to, a CALL saves the present address first, then jumps to the destination and when it encounters a 'return' or 'retlw' instruction, it restores the original program counter contents so you the program continues after the call instruction.

There are several program flow errors as well but I'm trying to lead you into fixing them yourself rather than correcting the code for you. Sorry if I seem critical of your efforts, in truth, when I look back at some of my own early code I am shocked at how bad I used to be! It is better to be shown how to do things than to have them done for you or you never learn to program for yourself.

Have another go, using the suggestions I made and post your code again. You will get there eventually and you will be a better programmer for doing it.

Brian.
 

    Shinnster

    Points: 2
    Helpful Answer Positive Rating
Ah the first mistake, excuse me for that, typo xD
was using reference and wasn't thinking right haha.

Oh, I get the what you mean now, thanks,
I'll resend again later in the evening since it's morning now
(My time that is haha)

oh banksel..Didn't knew about that.
so basically you call to the table, returned with a value in W, (If no value go to next column) then bring the value to PORTB?
Say 1 was pressed, for 1 to be displayed on LCD, it is 31 (hex) so how do I do that?
I return with the value 31 in W register(Okay one question over here, IF there is already something in W, what do I do? Should I clear it first or use MOVLW the columns?) and then move it to PortB and write to LCD? I'm not sure on this part.
 

HERE COMES HAGAR the horrible coding!

Oh I have a question! In dire need of a response!
IF you send String to W register, what would it appear as and what would happen?
IF you send String to LCD, how do you do that in asm? I'm blank in this.

If you are wondering why I'm asking this question, it's because of Retlw.
It returns with that value to W...I think that's a string value so... I don't know what would happen in W register.
 

OK, here comes Captain Fantastic to the rescue!

I can't do anything with the file you attached, it's the compiled HEX not the source code.

There is no such thing as a 'string' in assembly language, I think you may be confusing it with bytes which can be interpreted as text characters. In reality, all characters are single bytes. When you want to display 'Hello' for example, you send the LCD five bytes, h'48', h'65', h'6C', h'6C', h'6F'. They are the hex values of the characters in ASCII. Other bytes may be used as well, in particular some of the lower values like h'01' are not displayed but are used to configure or control the LCD.

When you want to send a line of text to the LCD you send the bytes one by one, normally the LCD will move the cursor to the next position automatically so the characters appear side by side rather than each overwriting the previous one.

I'll try to explain the retlw instruction again. Remember it is a RETURN instruction with the added function of loading a value into W. Using this code:
Code:
MyTable
  addwf PCL.f
  retlw h'41'
  retlw h'17'
  retlw h'DE'
  retlw h'07'

if you loaded 01 into W then did 'CALL MyTable', it would return to the instruction after the call with h'41' in W. If you loaded 03 into W and called it, it would return with h'DE' in W. It means "jump ahead the number of addresses in W and return with the value found there".

You need this to convert the value you read from the keypad row back into the number printed on the keytop. Because you can return with any value you choose in W, and you are going to pass it straight to the LCD, it makes sense to return the ASCII code for the key directly.

Are you as confused as I look?

Brian.
 

    Shinnster

    Points: 2
    Helpful Answer Positive Rating
O what the heck..hex file hahaha bad mistake
This will be the right one! Looks I got the Retlw wrong :3

Okay you don't need to re-explain the Retlw now, I get the pic but please check my Keypad flow, I think I did something wrong there.

Not sure about the rest, I feel everything is wrong haha.
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…