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

Status
Not open for further replies.

#### Shinnster

##### Member level 4

I am using a Basic to program.
I will describe my problems:
1) When I try to "Print" or "LCDOUT" a decimal, it will convert to HEX value and then "referring to LCD character table" supposed to display something? say like 6...
BUT my LCD would display something I can't understand on the first character or display (first small box I guess) and then only the outcome you want...
Any idea?

PIC16F84A has extremely limited space, so I basically had not enough spots to use my keypad of 4X4 so I did not connect one of the Columns to make it a 4X3. Reason: One of the pins are used for Enable (E) for LCD. *This is PORTB*

For PORTA, Pin 0 to 3 are D4-D7 (LCD) and Pin 4 is Register Select (RS).
R/W is connected to ground to make it read from PIC.

I am not sure if it is the LCD problem or Keypad.
The program works okay IF I use:
Print AT 1,1,"6" or something like that along the lines, anyway the point is, without 'at 1,1,...' it would show rubbish for the first box then only ur result..
I have been trying to make it display my keypad while holding a key so it would display something like "1111111111111111" but I don't know, it just prints out rubbish...

Any ideas?

#### betwixt

##### Super Moderator
Staff member
pic16f84a lcd

I can't help you with a BASIC program, I never use it but what you are asking for sounds fairly easy to do in assembly language so it might be translatable to BASIC.

The 16F84A has 12 I/O pins. You can use 4 pins to drive the LCD data lines and the keypad columns, 1 pin for the LCD RS, 1 pin for the LCD E and 4 pins to read the LCD rows. That gives you a full 4x4 keypad and 2 pins spare.

The LCD will only update when the RS and E pins tell it to, so you can put the keypad scan signals out when the LCD is not being updated.

It sounds like your problem at the moment is the LCD is not configured properly though.

Brian.

### Shinnster

Points: 2

#### Shinnster

##### Member level 4
jhd 162a lcd connection

betwixt said:
I can't help you with a BASIC program, I never use it but what you are asking for sounds fairly easy to do in assembly language so it might be translatable to BASIC.

The 16F84A has 12 I/O pins. You can use 4 pins to drive the LCD data lines and the keypad columns, 1 pin for the LCD RS, 1 pin for the LCD E and 4 pins to read the LCD rows. That gives you a full 4x4 keypad and 2 pins spare.

It sounds like your problem at the moment is the LCD is not configured properly though.

Brian.
Yeah I think I can change .asm files to .bas since I can write both and yes it is fairly easy to do.

I don't get what you mean. 10 pins to 16F84A then where to do I connect the keypad to? I do not know how to connect / program a multiplexer. I only used D4-D7 lines from LCD to 16F84A.

Yeah it could be not configured properly, I'm not too sure.

#### betwixt

##### Super Moderator
Staff member

You connect the data lines to the LCD to the keypad as well.

Multiplexing just means using the data pins for two purposes, you scan the keypad columns while looking for signals returned from the rows, then if any key was detected, you use the same outputs to write the character to the LCD.

The LCD will ignore the port being used to send the scan signals unless you also toggle to 'E' signal. To scan the keypad, don't change the 'E' signal so the LCD doesn't update. When writing to the LCD, ignore the signals back from the keypad.

So you use the four data lines for two purposes to save pins.

Brian.

#### Shinnster

##### Member level 4
jhd 162a how to test

betwixt said:
You connect the data lines to the LCD to the keypad as well.

The LCD will ignore the port being used to send the scan signals unless you also toggle to 'E' signal. To scan the keypad, don't change the 'E' signal so the LCD doesn't update. When writing to the LCD, ignore the signals back from the keypad.

So you use the four data lines for two purposes to save pins.

Brian.

Okay so the rough picture is:
You have D0-D7 of LCD AND the 4 Columns and 4 rows connect to PORTB of 16F84A, am I right? -
[IF possible, can you draw out this part for me please? If not, writing it is fine. ]

So how do I write for this in asm?
Well I know how to 'scan' in Basic but to write it in asm....*Just use the function called INKEY for PICBasic *

Thanks for assisting, by the way.

Shinn.

* Side note: I tried today using USB-to-Serial and connect to PIC to display on LCD to check if it would display rubbish on the first key sent through laptop and it wasn't so I'm guessing it's either the connection or the scanning of keypad is incorrect.
Since my 'E' is current in PORT B last pin, I was wondering if that played a role in messing up while scanning the keypad.

#### betwixt

##### Super Moderator
Staff member
mplab ide 8.36 pic16f84a

The more I see BASIC, the more I realize how deficient it is!

Yes, you are right about PORTB feeding the 4 data lines to the LCD and the keypad as well.

There are several ways to read a keypad but the most common way is to 'scan' it, the procedure is:

1 Set the port data bits to output mode. Set the row pins to be inputs.
2. You have 4 data bits connected to the columns, think in binary and set the data lines to 0001.
3. Read the row inputs (could be the other half of port B or it could be port A)
4. If 0000 is read back, no keys in the column with '1' on it were pressed, if any bit is '1', it corresponds with the key at the intersection of that row and column so you can tell what it was.
5. Change the column output to 0010 and repeat step 4.
6. Change the column output to 0100 and repeat step 4.
7. Change the column output to 1000 and repeat step 4.

So you are walking a '1' across the columns and then reading the rows. When a key is pressed, it shorts one row to one column so at some point that '1' will appear on the row inputs. For example, on a standard 4x3 telephone keypad, pressing the '5' key would give an output on row 2 when column 2 was driven.

If a key was pressed, your next step is to feed the appropriate data for the LCD on to the data lines, ignore anything on the row inputs, and toggle the LCD 'E' line to write the data to the LCD module. You can then go back to scanning the keypad again.

Make sense?

Brian.

#### Shinnster

##### Member level 4
jhd 162a commands

betwixt said:
The more I see BASIC, the more I realize how deficient it is!

Yes, you are right about PORTB feeding the 4 data lines to the LCD and the keypad as well.

There are several ways to read a keypad but the most common way is to 'scan' it, the procedure is:

1 Set the port data bits to output mode. Set the row pins to be inputs.
2. You have 4 data bits connected to the columns, think in binary and set the data lines to 0001.
3. Read the row inputs (could be the other half of port B or it could be port A)
4. If 0000 is read back, no keys in the column with '1' on it were pressed, if any bit is '1', it corresponds with the key at the intersection of that row and column so you can tell what it was.
5. Change the column output to 0010 and repeat step 4.
6. Change the column output to 0100 and repeat step 4.
7. Change the column output to 1000 and repeat step 4.

So you are walking a '1' across the columns and then reading the rows. When a key is pressed, it shorts one row to one column so at some point that '1' will appear on the row inputs. For example, on a standard 4x3 telephone keypad, pressing the '5' key would give an output on row 2 when column 2 was driven.

If a key was pressed, your next step is to feed the appropriate data for the LCD on to the data lines, ignore anything on the row inputs, and toggle the LCD 'E' line to write the data to the LCD module. You can then go back to scanning the keypad again.

Make sense?

Brian.
Nice. I didn't think about that. *I mean the LCD with Keypad.*
:]
Was seeing too many examples of PIC16F877A which seperates them with different Ports.

Okay, you mentioned 4 Data Lines of LCD to PortB. Do I ignore the other 4?

Oh and another question, how do you toggle the 'E'? So I'm guessing you toggle E to write, display the key you want, toggle E again?

#### betwixt

##### Super Moderator
Staff member
pic16f84a lcd 4 line

You need 8 bits to use a 4x4 keypad, 7 if you use a 3x4 keypad so either use all of port B or use some of port A, it is up to you, the software can use any of the pins.

Probably the easiest way to do it is to assign port B bits 0-3 to the LCD data and keypad columns and port B bit 4-7 to the keypad rows. you can then use port A for the LCD control signals.

When using the LCD in 4-bit mode you have to toggle the 'E' line twice per character so it can tell it to read the top and bottom 4 bits of the data byte. Check the LCD data sheet to see how it is done. You are also not using the LCD R/W pin so you have two methods of checking if the LCD is ready before sending the data to it. You can either wait for a few mS (check data sheet) between writing high and low half-bytes or you can reverse the pin direction of the PIC connected to D7 of the LCD after writing to it, then waiting for it to go low before moving on. The LCD sets D7 high while it internally digests the data then drops it again when it is ready to receive more.

Personally, I always read D7, it is much faster and safer than using fixed delays every time the LCD is used.

Brian.

#### Shinnster

##### Member level 4
how to write to lcd pic16f84a

Okay I really feel like it's my first time touching electronics right now. Haha.

Simply put, I must be posting this in the wrong section . Newbie section was more accurate haha.

Anyways, I think I'm pretty sure with the hardware, now just programming...
I have absolutely no idea how to write this in asm. At all.
Well basically I don't know how to program LCD at all in asm. First time dealing with LCD with asm. How did I program with PICBasic? It was because there was a help program, hehe. I just follow the help examples which made things pretty easy. But if there were changes like this, then it'd be a bit tough, I guess.

And thanks for the tip on D7, I'll keep that in mind. Wait a minute, so I refer to D7 to know when to toggle E? Interesting.

Yes I know I need to use 8 bits for 4x4 keypad, I was referring to the 8 data lines for LCD. Do I use D0-D3 or D4-D7?
Well in this case, I'm going to take the guess it's D4-D7.

Thanks for the help by the way, it really helps me to understand the LCD better.

By the way do you have a link to write LCD with asm ?
Sorry for the inconvenience. If I really can't write in asm then I'll just write with PICbasic if I have to.

#### betwixt

##### Super Moderator
Staff member

The LCD can be used in two ways, one way uses all 8 data bits and you toggle the 'E' line once to transfer the whole byte in one go. The other way is to use only D4-D7 and transfer the data in two 4-bit parts so you toggle 'E' twice, once for each half of the data. You are using 4-bit mode so either leave D0 - D3 unconnected, or better still, connect them to the positive supply through a 10K resistor.

Programming is asm can be frustrating but the MPLAB assembler is very good and it includes an excellent debugging tool. You will find asm programming gives you far better control over what the processor does and when you are familiar with it, you can write smaller and faster running programs than is possible with BASIC or 'C'.
For example, I have recently written an asm program for producing TV test signals in 130 bytes on a 10F202 chip. It would be impossible to do that in any other language.

I have looked but at the moment I do not have any LCD routines on my computer, they are all archived and it would take me some time to find them. A good place to look is on Microchips own forum which has an LCD software section at:
https://www.microchip.com/forums/tt.aspx?forumid=38

but please read the other peoples posts before adding your own. The moderators do not take kindly to "send me the software right now" posts but they will be very helpful to people who try to help themselves first.

Brian.

#### Shinnster

##### Member level 4
jhd162a enable

Ah, thanks I will look into it now.

Hmm, a lot of C users..
Nevermind

Added after 1 hours 29 minutes:

I think I saw this one in one of the LCD manuals before [How to use Intelligent LCDs] and,
I noticed this is for D0-D7, So I am guessing I need to make changes for 4 pin D4-D7.
E toggling as well.

#### betwixt

##### Super Moderator
Staff member
14 pin jhd162a

That is a HORRIBLE mess of a program. Please don't even think of using it.

My guidelines:
1. look closely at the data sheet for the LCD, it tells you how to configure it to work in 4-bit or 8- bit mode and what bytes to send it to set the cursor, blank the screen and so on. you have to do this before you can use the LCD to display messages.

2. Get hold of MPLAB from Microchip's web site, the current version is 8.36.

3. Use the MPLAB wizard to set up your project, it will ask you things like the clock speed (crystal frequency) you want to use and the kind of microprocessor, obviously pick 16F84A from the list.

4. Click on 'File/New' (or the first icon on the top row) to create a new file.

5. Copy the contents of the file "16F84ATEMP.ASM" into it. This is a template file which you can use as a starting point. You will find the file in the folder:
C:\Program Files\Microchip\MPASM Suite\Template\Code.

6. "save as" the file with the name you want to give it. End the name with '.asm'

7. Now you can edit the top of the file to add a description, your name and other notes.

8. Give meaningful names to the pins on the chip using the '#define' directive.
For example you might have a line "#define LCD_RS PORTA,1" which lets you use the descriptive name LCD_RS instead of remembering which pin on port A you are using. Make a list of all the pins near the top of the program. You can now use commands like "bsf LCD_RS" to set the LCD's RS line high.

9. PIC registers are banked which is a problem until you understand it so for now, whenever you refer to a different register, put the line "banksel xxxx" in the line before it. Change the xxxx to the register name, this will tell the assembler to sort out the banking for you.

10. That's it ! Start coding !

Brian.

#### Shinnster

##### Member level 4
Okay thanks for the guidelines but before that,
what is the initial status of 'E' ?
and for the ROW reading inputs... Is it better to use BTFSC or AND ?

#### betwixt

##### Super Moderator
Staff member
I think the 'E' line is normally low. If I remember correctly, the data is latched into the LCD on the falling edge of the 'E' signal so it should be safe to set the data pins first then make 'E' high then low again. Don't forget to read D7 from the LCD and wait for it to go low before writing the next data. D7 is used as a 'busy' indicator and the LCD will hold it high while it performs internal operations, when it goes low it means it is ready to use again.

For reading the rows, you can use either method but the shortest routine is to read the port and use the value to look-up the key number in a table. It is easier to do this if you use the lower part of PORT b as the inputs. What you do is read the port and immediately return if the value is zero because that means no keys is pressed. If the value is not zero, a key is pressed and the port will read 1, 2, 4 or 8 depending on the row that had the '1' on it.

Load the value into the W register then call a subroutine with something like this in it:
Code:
addwf PCL,f  ;add the value in W to the program counter.
retlw '0'  ;key in row 1 found
retlw '8'  ;key in row 2 found
retlw 'x'
retlw '5'  ;key in row 3 found
retlw 'x'
retlw 'x'
retlw 'x'
retlw '2'  ;key in row 4 found

If you called that subroutine 'Column 2' and call it when the second column has the '1' on it, it will return with the key number in W. Simple as that !
The 'x' entries are just space fillers, as the rows can only return 1,2,4 and 8, they should never be reached.

Use four different subroutines, one for each column and put the appropriate key numbers in the retlw instructions.

Brian.

### Shinnster

Points: 2

#### Shinnster

##### Member level 4
Ah thanks,
but I don't really understand for the row reading.
"lower part of the PORTB" , you mean the last 4 pins or the first 4?
Why add it to program counter? :|:?:

#### betwixt

##### Super Moderator
Staff member
Sorry for not being clear, I mean the lowest numbered bits, 3,2,1 and 0.

If the row inputs are on the higher nubered bits 7,6,5 and 4, when you read the port you must store the result in a register (I'm calling it 'KeyPadIn' in my example):
Code:
movf PORTB,w   ;read the whole port B
swapf KeyPadIn,w  ;swap top and bottom bits so b7:4 is now b3:0
andlw h'0F'  ;zero the top bits so only the row data is now in W register

If the rows inputs are connected to PORT B bits 3 to 0, the code is a little simpler:
Code:
movlw h'0F'  ;this will be used to mask the top bits of the reading
andwf PORTB,w  ;read and mask PORT B with the result put in W

There is a neat trick in the PIC 10, 12 and 16 series to implement look-up tables. The PIC18 series has it's own method but that isn't important here.
The trick is to use the 'retlw xx' instruction which means "return from subroutine with the value xx loaded in the W register". Returning from a subroutines is exactly the same as you have used in BASIC so you have to make a call to the routine first.

Imagine your subroutine is called 'MySubroutine' and in your program you placed the instruction 'call MySubroutine'. The program counter is saved so it knows where to return to when the subroutine ends, then the program counter is loaded with the subroutine address so it starts running the next instruction from the subroutine itself. In other words, PCL (and possibly paging bits) holds the address at the subroutine. If you add the value in the W register to it, the program counter will pick up the next instruction 'W' places further along the program. Adding the value in W is like saying jump ahead 'W' places.

After the 'addwf PCL,f' instruction you place a list of retlw instructions, each followed by the value you want to return. When the program jumps ahead, it 'lands' on one of the retlw instructions, loads the xx value into W and returns from the routine. This leaves you back in your main program with the value in the retlw table in W instead of what was there before. For example if W contained the value 5 and the subroutine was called, it would return with the value in the 5th retlw instruction. So you have a quick and easy look-up table, enter with the position in W and return with the value for that position in W.

The only thing to be careful of is that your look-up table doesn't cross a page boundary in memory as the addwf PCL instruction only works on the PCL register, it doesn't switch to the next memory page if PCL is made to count beyond h'FF'.

Does that make sense?
Brian.

### Shinnster

Points: 2

#### Shinnster

##### Member level 4
betwixt said:
For example if W contained the value 5 and the subroutine was called, it would return with the value in the 5th retlw instruction. So you have a quick and easy look-up table, enter with the position in W and return with the value for that position in W.

The only thing to be careful of is that your look-up table doesn't cross a page boundary in memory as the addwf PCL instruction only works on the PCL register, it doesn't switch to the next memory page if PCL is made to count beyond h'FF'.
since you are the walking '1', should W not contain 1 or can be any value? 1 2 3 4?

I don't get what you said for the last sentence but basically you mean do not exceed FF?

#### betwixt

##### Super Moderator
Staff member
I think you are confusing two things:

The walking '1' is what you move (walk) across the column outputs of the PIC. You are basically doing four almost identical operations, one on reach column. Only one column at a time has a '1' on it, the others you keep at '0', these signals are ones you create yourself at the output part of the PIC port.

If any key is pressed, at some time in the 'walk', one of the rows will go to a '1', this is because the key that was pressed is connecting the column with the '1' on it to one of the row connections. The switch will have joined one of the column outputs back to one of the row inputs.

Look at the possible combinations of row inputs you can get (assuming only one key is pressed at a time):

Column output 0001 could give row input 0001, 0010, 0100 or 1000.
Column output 0010 could give row input 0001, 0010, 0100 or 1000.
Column output 0100 could give row input 0001, 0010, 0100 or 1000.
Column output 1000 could give row input 0001, 0010, 0100 or 1000.

The problem is how to convert these numbers back into the one printed on the keypad. You could work out the key number by testing each bit individually but that is a slow and inefficient process, it would need 32 'if' type statements to do it. This is where the look-up table is useful and very quick.

Using the list above, and assuming the columns are wired bit 0 to the right side, you could interpret the row signals as:

Column output 0001 could give input for keys A,B,C or D.
Column output 0010 could give input for keys 3,6,9 or #.
Column output 0100 could give input for keys 2,5,8 or 0.
Column output 1000 could give input for keys 1,4,7 or *.

So, using the last line as an example, an input of 0001 would mean key *, an input of 0010 would mean key 1, and input of 0100 would mean key 4 and an input of 1000 would mean key 7.

You could code this as "if the input is 0001 return the value '*'" but as I said earlier this uses a lot of instructions and can be slow to run. If instead you look up the value 0001 in a look-up table it is very fast to execute. The program flow goes like this:

1. Place 0001 on the column outputs
3. if the row inputs are all zero (no key pressed) go to step 5
4. look up the row value in the table and return with the key number
5. Place 0010 on the column outputs

and so on for each of the columns.

The row value can be (in decimal) 1, 2, 4 or 8 so you need 8 entries in the look-up table. The entries for value 3,5,6 and 7 would indicate something is wrong, possibly more than one key is pressed at once. the 'good' values are for one key being pressed so all we need to do is return the real key number from the table

You keep a list of the possible values as 'retlw' instructions and precede them with an instruction that jumps to the correct entry in the list. That instruction is "addwf PCL,f" which means "add the value in W to the program counter so the next instruction to execute is that many addresses further ahead". for example, if W contained the value 8, the next 7 instructions would be jumped over and the 8th one would be executed. The place where the jump 'lands' is one of the retlw instructions which mean "return from this subroutine with the following value stored in W" That's how the look-up works, you enter with the table offset in W and it returns with the value at that offset in W. In the keypad code, you enter with 1,2,4 or 8 and return with the key code *,1,4 or 7. The flow of code only takes 3 instructions to execute, the subroutine call, the addition and the return so it is very fast.

The 'FF' problem is just something to be aware of. The instruction pointer in PIC16 devices is made of two parts, the PCLATH and PCL. PCLATH is the top (most significant bits) part and PCL is the lower 8 bits. Normally the PIC takes care of controlling PCLATH but when you change the value in PCL in your program, the PCLATH is not automatically updated as well. When you add W to PCL, it is possible that the result is greater then h'FF', in other words more than you can hold in an 8-bit register. When this happens, PCL rolls over to zero and starts counting up again. For example, if PCL held h'40' and you added 6 to it, the result would be h'46' which is fine, but if it held h'FD' and you added 6, the result would be h'03' instead of h'103'. The value originally in PCL depends on where the table is stored in memory, it is an address, so be careful to place your table so it does not cross the FF to 00 boundary. The easiest way to do this is to place the tables near the beginning of your program so the chance of reaching the end of a memory page are as small as possible.

Brian.

### Shinnster

Points: 2

#### Shinnster

##### Member level 4
Ooo thanks for clearing that up.

You mentioned earlier about reading D7 to know when to toggle 'E', as an indicator.
So I have to keep referring to D7 for each character to display on LCD?

#### betwixt

##### Super Moderator
Staff member
That is correct.

The LCD has it's own on-board microprocessor (I think it is Samsung KS0066U) which monitors the data connections and takes care of preparing the character shapes and driving the LCD glass. When it is doing internal operations, such as looking up the character font, it cannot also be monitoring for new data being fed to it. So what the manufacturer does, is use the D7 (on the LCD) pin as a 'busy' or 'ready' signal. As soon as it has digested the byte you sent it, it puts a '1' on D7 and keeps it there until it is able to accept fresh data.

1. Put the data on the bus
2. raise and lower the 'E' signal
3. change the pin connected to D7 to be an input
5. if it is '1', go back to step 4
6 change the pin to be an output again.

You do that each time you write anything to the LCD. Be sure to change the pin connected to D7 to an insput as quickly as possible or you might be driving it one direction while the LCD is driving it as well.

On the topic of hardware, you should connect pull-down resistors to the rows of the keypad, 10KΩ would be a suitable value or all the row signals will float when the keys are not pressed. It would also be good practice to put isolating resistors of say 100Ω in line with the keypad column connections. The reason - someone will inevitably hold more than one key down and without the resistors, it could directly short the data bits together. A value that low will have no influence on the keypad operation.

You will see the idea here

http://www.atv-projects.com/MkIII_TCG.html although different PIC pins are used in that design.

Brian.

Status
Not open for further replies.