4x4 keypad on bus

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
Hello, i have working on this almost 3 days without any success and i just figure this out.

Schematics of the system are here
http://i.imgur.com/FWcJRvP.png
and the problem part
http://i.imgur.com/i7vwFXs.jpg

Everything i have tried has just been trying.
I think i had to play around with the RD and WR pins to get the chips to work correctly, but i just can not figure this out.
This is the example code i have, to try and get '2' key working

Code:
volatile __xdata unsigned char * __data keypad =    (__xdata unsigned char *) 0x9000;
#define WR P3_6
#define RD P3_7

while(1)
    {
        WR=0;
        RD=1;
        *led=0b11000000;
        *keypad==0b11110000;
      
//*keypad==0b11111110;
            if (*keypad==0b11111110)
            {
                WR=0;
                RD=1;
                *led=0b11000000;
                vardelay(5);
                *keypad==0b11111101;
                if (*keypad==0b11111110)
                {
                    RD=1;
                    WR=1;
                    lcd_senddata('1');                  
                }
                else if (out==0b11111101)
                {
                    RD=1;
                    WR=1;
                    lcd_senddata('2');
                }
            }
            else if (*keypad==0b11111101)
            {
                RD=0;
                WR=1;
                if (*keypad==0b11111101)
                {
                    RD=1;
                    WR=1;
                    lcd_senddata('5');
                }
                else if (*keypad==0b11111110)
                {
                    RD=1;
                    WR=1;
                    lcd_senddata('4');
                }
            }
I can get data from buttons 1-c diagonally because they share the address.
I am getting really frustrated with this. If you could give me some guide or tips, i would be very thankful.
 

ErnieM

Joined Apr 24, 2011
8,377
So U5 and U15 sit on the same address? You write to U5 to pick a row and read U15 to see which column is on? I'm asking to see if I understand your scheme. I have to ask because in "I can get data from buttons 1-c diagonally because they share the address" I have no idea what address they share beyond U5/U15

Overall the scheme looks fine, mebby there is a wiring error on the pad or such.

The row data in U5 is static. You can write a value and use anything (DVM, LED) to see how the keys effect U15 by pressing keys and seeing what happens.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
So U5 and U15 sit on the same address? You write to U5 to pick a row and read U15 to see which column is on? I'm asking to see if I understand your scheme. I have to ask because in "I can get data from buttons 1-c diagonally because they share the address" I have no idea what address they share beyond U5/U15

Overall the scheme looks fine, mebby there is a wiring error on the pad or such.

The row data in U5 is static. You can write a value and use anything (DVM, LED) to see how the keys effect U15 by pressing keys and seeing what happens.
That is the idea. The system is fine, but my knowledge in C is probably the bad part.
Ideally, i write a zero to 1 of the pins on U5 to pick a row, and read the output from u15 to see the column, as you said. I have tried that, but the LED just gets the *keypad value i gave to it.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
That is the idea. The system is fine, but my knowledge in C is probably the bad part.
Ideally, i write a zero to 1 of the pins on U5 to pick a row, and read the output from u15 to see the column, as you said. I have tried that, but the LED just gets the *keypad value i gave to it.
Well the same as with my previous question, once i asked here, i figured it out.
Turns out i over complicated things, and the hardware does more work for me than i thought.
 

djsfantasi

Joined Apr 11, 2010
9,163
In your code, I'd check that the if statements are doing what you expect.

IMHO

First, you set *keypad equal to 0b11110000, and then test for 0b11111110 or 0b11111101. The code will never get executed.

Secondly, you test for a value and then in the if block, test for a second value. That code in that if block won't get executed.
 

ErnieM

Joined Apr 24, 2011
8,377
Oft times just phrasing your work into a question will nudge the mental gears into seeing the solution.

There have been times I talked over my project to my dog as if he could understand what I was saying. It really helped.

So does going out for Chinese food. Yumm.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
Oft times just phrasing your work into a question will nudge the mental gears into seeing the solution.

There have been times I talked over my project to my dog as if he could understand what I was saying. It really helped.

So does going out for Chinese food. Yumm.
Heh, for me food does not really bring me good ideas about programming, more about life etc. I do get a lot of help when i ask people like this. Mostly not from them, just something goes right in the head as you said.


In your code, I'd check that the if statements are doing what you expect.

IMHO

First, you set *keypad equal to 0b11110000, and then test for 0b11111110 or 0b11111101. The code will never get executed.

Secondly, you test for a value and then in the if block, test for a second value. That code in that if block won't get executed.
There are some unneeded lines that i just put there out of frustration, hoping something will work differently. But as i stated before, its all good now.
If you are interested i can write how it works(since i understand it now).
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
Please do. That is one way people learn here, not just you but everyone (including the people who just read threads without responding).
Well basically, i got some bad information, and i tried to control the RD and WR pins, what is actually not necessary.
Turns out that it picks the pins "automatically" depending on how you use the address.
A simple code like this will work for the 1st line
Code:
if        (*keypad=0b11111110)
        { 
            out=*keypad;
            if        (out==0b11111110)
            {
            lcd_senddata('1');
            return ;
            }
            else if    (out==0b11111101)
                lcd_senddata('2');
            else if    (out==0b11111011)
                lcd_senddata('3'); 
            else if    (out==0b11110111)
                lcd_senddata('F');
Now i am just trying to figure out how can i get all the lines working, If i assign the input address the lines work without any problem, but i am trying to use the interrupt to make it work, i am halfway there (i think) and i will post the end part of the code that matters here.

I think it is quite fascinating how it works, especially for someone who came from the PIC kits what are quite straight forward.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
I would like to discuss a bit more. A problem that a raised is that it spams the button. Normally i would use a falling edge interrupt, but it seems the 8051 does not have something like this. I tried having a while loop, but that only works on certain keys and others still keep spamming. Any ideas what i could do?

I have the buttons working with a normal interrupt at the moment.
 

ErnieM

Joined Apr 24, 2011
8,377
I can see the advantage of hitting an interrupt when a button is pressed, though I rarely if ever do this.

How I do it is I typically have a "tick timer" ISR which updates a variable at regular intervals. It's kinda useful to have that, and in the PIC devices I've been using a 1mS interval is falling off a log easy to generate.

While inside that routine I also scan my buttons. I cut the scans back to once every 25 ISR's or once every 25mS. The reason for that is I use that time to debounce the buttons: a key press (or release) is only scored as valid if it exists over two subsequent reads. I've found that to be adequate to filter out bounces for the buttons I use, but quick enough not to frustrate the user (ME!).

The buttons pressed are exposed by a global variable.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
I can see the advantage of hitting an interrupt when a button is pressed, though I rarely if ever do this.

How I do it is I typically have a "tick timer" ISR which updates a variable at regular intervals. It's kinda useful to have that, and in the PIC devices I've been using a 1mS interval is falling off a log easy to generate.

While inside that routine I also scan my buttons. I cut the scans back to once every 25 ISR's or once every 25mS. The reason for that is I use that time to debounce the buttons: a key press (or release) is only scored as valid if it exists over two subsequent reads. I've found that to be adequate to filter out bounces for the buttons I use, but quick enough not to frustrate the user (ME!).

The buttons pressed are exposed by a global variable.
So now that i have tried something like that, it is good, unless you have 1wire, then it just does not work. The interrupt can happen when 1wire is writing or reading and when that happens things go sour. So i have to do it with just the interrupt i intended and doing key checks inside the main while loop(which is a pretty bad way of doing it).
 

ErnieM

Joined Apr 24, 2011
8,377
So now that i have tried something like that, it is good, unless you have 1wire, then it just does not work. The interrupt can happen when 1wire is writing or reading and when that happens things go sour. So i have to do it with just the interrupt i intended and doing key checks inside the main while loop(which is a pretty bad way of doing it).
There are many many things that can happen that would interfere or be interfered with when doing periodic button checks. In most of my creations I place the buttons on the same lines as an LCD display and somehow I get them to work together flawlessly, no missed press events, no dropped characters.

All you need do is let the button scan know when something else is using the hardware or has critical timings, either by reading a pin or checking a flag. When the flag clears then button scans resume.
 

Thread Starter

sevenfold4

Joined Jan 12, 2015
80
There are many many things that can happen that would interfere or be interfered with when doing periodic button checks. In most of my creations I place the buttons on the same lines as an LCD display and somehow I get them to work together flawlessly, no missed press events, no dropped characters.

All you need do is let the button scan know when something else is using the hardware or has critical timings, either by reading a pin or checking a flag. When the flag clears then button scans resume.
The only thing it is interfering at the moment is the 1 wire. LCD and keypad are on the same line and they have no problems with the timing.
But the 1wire is constantly reading/writing to get the temperature and the timing in there has to be perfect. +/- few us can mess things up
 

John P

Joined Oct 14, 2008
2,026
Do you need an interrupt for the buttons at all? How about not enabling that timer interrupt, but in the endless loop in your main() routine, just check the interrupt flag and if it's found to be set, do the keyboard scan routine and clear the flag. Sure, it will operate at a somewhat irregular rate, but you aren't likely to notice it in actual use.

People think everything needs an interrupt, and then they get into trouble with conflicting interrupts. If you decide which item in the program truly needs to happen at an exact time, then you can let that one have an interrupt and handle other events some other way. Human-activated controls are typically very low priority as far as timing is concerned--a millisecond is forever to a processor and nothing to a human.

But if you follow this scheme, be aware that the keypad scan might get interrupted part-way through, so you have to be certain that you won't change anything (port pins?) that affects its operation if that happens.

(Edited to say that checking the interrupt flag is easy on a PIC processor, which is what I use, and this is an 8051-type processor, and I don't know if it's possible.)
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,287
While inside that routine I also scan my buttons. I cut the scans back to once every 25 ISR's or once every 25mS.
Even better, Ernie, is to just set a flag that a period of time has elapsed in the ISR. Then, poll that flag in your main loop to activate the keypad polling routine synchronously with the main program.

In fact, every application I write has the following timer 0 code:

Code:
;********************************************************
;** Timer 0 -- Main Program Heartbeat Every 1.024ms    **
;********************************************************

tmr0int	bcf	intcon,tmr0if		;turn off t0 int

	movf	_timer,w		;get current time
	incf	_timer,f	;and increment it
	xorwf	_timer,w		;compare with last
	iorwf	_tmrchg,f		;and save changed bits

	bra	intdone			;and getout
This gives me a set of bits in _tmrchg that represent elapsed time of 1,2,4 ... ms that I can use to trigger events in the main code.

The first routine of the main loop looks like this:

Code:
;******************************************************************
;** GETTIM -- Get current time and changed bits since last check **
;******************************************************************

gettim	clrf	tmrchg1			;clear bits from previous pass

	bcf	intcon,tmr0ie		;disable ints
	movff	_timer,timer		;copy timer data
	movff	_tmrchg,tmrchg
	clrf	_tmrchg		;clear for next time
	bsf	intcon,t0ie		;and reinable ints

	return
I give each bit of tmrchg a name using #define, and then I can do this:

Code:
;*****************************************
;** GETKEY -- Process key every 32 ms   **
;*****************************************

getkey	clrf	keychg			;clear changed keys
	clrf	keypr			;clear press and release registers
	clrf	keyph			;clear press and hold registers
	clrf	keyrpt			;clear key repeat register

	retbc	TC4ms			;process kbd row every 4 ms (total 32 ms debounce time)

	...
where the flag bit TC4ms is set for the loop every 4.096ms.
 

ErnieM

Joined Apr 24, 2011
8,377
Good advice joey. I too *love* the utility of having a tick timer running in the background (that in my case also handled the external button read and debounce). I talk about that back in post 11.

Using the flag as a semaphore for external business sounds quite useful, though I have not done 1-wire and can't comment if 1ms is sufficient time to do a transaction.
 

joeyd999

Joined Jun 6, 2011
5,287
Good advice joey. I too *love* the utility of having a tick timer running in the background (that in my case also handled the external button read and debounce). I talk about that back in post 11.

Using the flag as a semaphore for external business sounds quite useful, though I have not done 1-wire and can't comment if 1ms is sufficient time to do a transaction.
No! The one-wire has to be done as independent tight code, probably with precise software delays (and therefore ints disabled).

My point of synchronizing the keypad (and display, other UI, and whatever else possible) with the main loop is to eliminate the constraint of the interrupt driven peripherals from mucking with each other unpredictably. This eliminates the question: "Is the LCD being updated? Can I scan my kbd now?"
 
Top