3
\$\begingroup\$

I am using a PIC16F877A, running at 20 MHz.

I have two paralleled, 3 columns, 4 rows keypad. My setup is as follows:

Keypad interfacing

KR1 to KR4 are rows, KC1 to KC3 are columns, and they all go to the microcontroller.

In the program, rows are set as digital outputs and columns are set as digital inputs. Other peripherals of the pins are disabled. The way I scan the keys is as follows:

I make the first row bit HIGH, then I check column 1, column 2 and column 3. If any of them is HIGH, then I wait a little and check again to de-bounce. If it is still HIGH, the program decides that a key is pressed. This routine follows for the other 3 rows.

I check the keypad about every 100 ms, and for each row, it takes about 2.4uS as I measured with the oscilloscope. That, I guess, means, for a specific digit; set it high then try to measure for about 800nS, if nothing happens, move on.

So the problem was weird. Every digit worked, except from '0'. It worked randomly and that random means rarely at the same time. Also, when I tried to measure its voltage to the ground with a multimeter in DC volts mode, and then pushed the button at the same time, it worked for every press.

Then I added a delay of 5 uS after making the specific row HIGH and then I checked the columns. It worked!

Now, what is the problem here? The keypad wires are about 30 cm (~12 inches) long. Is it some kind of a transmission line problem, like it is adding a delay? Or is the problem is sourced from the microcontroller; the microcontroller is not fast enough?

Here is a portion of the code that is relevant to the issue:

...

KEY_R4 = 1;
delay_us(5);
if (KEY_C1)
{
    scan_keys.waitstate = 10;
    goto c4r1;
}
else if (KEY_C2)
{
    scan_keys.waitstate = 11;
    goto c4r2;
}
else if (KEY_C3)
{
    scan_keys.waitstate = 12;
    goto c4r3;
}
else
{
    KEY_R4 = 0;
    presstime = 0;
}

...

c4r2:
if (++presstime >= PRESSTIMEVALUE && !KEY_C2)
{
    scan_keys.waitstate = 0;
    KEY_R4 = 0;
    return '0';
}
return 'n';

...
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

first, the Read-Modify-Write issue (from http://www.voti.nl/DB038/DB038-1-1.pdf, p51):

PICs use a funny IO architecture where a reading of a pin always returns the current external level of the pin, which can be different from the last value written to a pin, even when the pin is set as output, for two reasons:

• PIC instructions execute in a 2-stage pipeline, and for IO pins the read part of the next instruction takes place before the writing of the previous instruction.

• The load on the pin can be too high for it to reach its 'desired' level. This can easily happen (for a short time) when the load is capacitive, but it can also happen with a static load that is well within the normal operating specifications.

All PIC instructions that modify some bits in a byte are read-modify-write instructions, so when for instance two consecutive BCF (bit clear) instructions are executed on the same IO port the second instruction can ruin the effect of the first because of the first of the two reasons. Actually a BCF instruction (and a lot of other instructions, like INCF) on a port can ruin any previous setting of that port because of the second reason! It should be noted that the first reason occurs far more often, and can be avoided by placing a NOP or another instruction between any two read-modify-write instructions on the same IO port. But to really avoid all problems it is advised to allocate a separate register, do manipulations on that register, and copy it to the port register after each change. This technique is called a “using a shadow register”.

=====================================================

Next: RC delay. When you change an I/O pin connected to a non-trivial amount of wire always wait a reasonable amount of time before expecting the result. In the absense of another source of 'reasonable': find out what seems to work, then double the time.

=====================================================

Last: debouncing. AFAIK 50 ms is a reasonable upper limit for the bounce time of a switch. When your sample interval is longer than this time (you use 100 ms), you have no bounce problem. The proof is left as an excercise to the reader :)

\$\endgroup\$
4
  • \$\begingroup\$ Very satisfying answer. Yeah, basically I debounced that way in this project :) What I do is; I sleep the microcontroller after each end of the main while loop so that means each thread is called at a rate of about 100ms, I know it is not the best but I have written this code the times I was learning real-time programming, well actually, I'm still learning :) Thanks Wouter van Ooijen! \$\endgroup\$ Commented Mar 27, 2012 at 6:57
  • \$\begingroup\$ 1 NOP instruction didn't work, so I added the second, and it worked. Since I am mostly accessing individual bits of the ports and my code is a mess now, I was too lazy to add a shadow register and try it out, however, I am planning to create my own library based on your DB038 library that will allow me to use shadow registers, because I want to move from Mikroelektronika mikroC to HI-Tech C. \$\endgroup\$ Commented Mar 27, 2012 at 9:09
  • \$\begingroup\$ succes! If you ever want to move away from C: Jal has had a shadow register based I/O library from the start. I never understood why compiler vendors don't provide this very basic (and nearly always needed) piece of lib. \$\endgroup\$ Commented Mar 27, 2012 at 11:11
  • \$\begingroup\$ Maybe they just are not aware of the problem? If I were them, I would include an option in the project settings. Oh, and I will stay with C for a little more I guess :) I am just a starter. \$\endgroup\$ Commented Mar 27, 2012 at 12:16
1
\$\begingroup\$

It looks like a read-modify-write problem, see the data sheet. Use a shadow register for output instead of changing output bits directly.

\$\endgroup\$
2
  • \$\begingroup\$ What exactly should I seek in the datasheet? What do you mean by a shadow register and could you compare this method you suggested to changing output bits directly? Sorry if I was too questiony, I am just trying to understand :) \$\endgroup\$ Commented Mar 26, 2012 at 16:17
  • \$\begingroup\$ I guess you didn't have time. Thanks for the answer! \$\endgroup\$ Commented Mar 27, 2012 at 6:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.