3

When making matrix input of NxM columns/rows with push buttons, there should be a diode next to each button to prevent "ghosting" and able read many keys pressed together correctly. There is a lot of pictures everywhere.

My problem is, which diodes to use and also why this type (or why it does not matter).


More details:

I would use 5 V Arduino (ATmega32U4), datasheet states for input low voltage max 0.3Vcc, which is 1.5 V and minimal high input voltage 0.7Vcc = 3.5 V and pullups at least 1.5 kΩ (Vcc-0.4 V / 3 mA)

I would like to use 74HC138 3-to-8 decoder for addressing, which makes one of 8 outputs Low (max 0.1 V), while other 7 High (min 4.5 V) and drive the Arduino inputs Low, if button is pressed.

To be on safe side, I would like also add resistors on each pin to make sure, that bad programming (make some input pin OUTPUT and HIGH and pressing key connect it to LOW output on other side) could not damage the circuits, so it looks like 5 V/20 mA = 250 Ω (and I will hope it would not hit more pins, so I would not consider total port/chip limitations).

In typical case it would be 5 V - 1.5 kΩ pullup - input - 250 Ω - wires and button - diode - 250 Ω - 0.1 V for selected line.

If the diode would be theoretical and ideal, then there is (in the worse case) promised to be max 1.2 V on Input, which is slightly less then "maximal low" voltage (1.5 V) and like 5V/2 kΩ = 2.5 mA current.

But the diode is not ideal and have some forward voltage, which make the case even worse.

So I probably cannot use simply INPUT_PULLUP (as it may work by luck, but is not guaranted by specs) but I need to use INPUT and set some higher pullup there to "burn" more volts on it to compensate for the diode.

The Input Current for each pin is 10 µA (in or out) and I heard some recommendation, that on resistor divider should be the total current 10x more, then the input current, so it would mean 100 µA = 0.1 mA

The pullup then could go as high as ((5 V - 0.1 V - Vf)/0.1 mA ) - 500 Ω If the forward voltage would be 1 V (wild guess) it would go to something like 38.5 kΩ pullup and 1.15 V on input. The maximal Vf forward voltage would be like 1.35 V (at 0.1 mA), pullup 35 kΩ and 1.5 V on input.


My problem is, that many online Arduino schemes works just because Arduino usually work good even in way out-of-spec situations, and suggested values cames from what the author just randomly had at hands and it worked by pure luck and maybe another hidden factors, as parasite capacity of breadboard or so. So I want to uderstand, why I should choose some parts and how to came to such conclusions by myself.

I want to make reliable projects, not just projects, which works most of the time and fail only occasionally.


  • It is the right path, how to compute it or did I some mistake?
  • I want use scanning frequency at least 800 Hz (8 lines and results 100x per second) or more (like 10 kHz to be done fast and to have more time to spend with other tasks, like sending USB packets, computing macros, managing light effects etc)
  • which common and cheap SMD diodes I should buy?
    • reverse voltage over 5 V, current over 100 mA, switch frequency over 10 kHz, forward voltage under 1.3 V at 0.1 mA (rather much less to have safe gap from limits)
    • really common cheap model, so everybody would know it and so I could use it everywhere in similar settings without thinking ( or even computing like in previous section)
    • preferably what you (and everyone else and his dog too) use for such cases
    • I would like to order from AliExpress, so rather higher margin for guaranteed values
    • preferably to fit on 0.1 inch spaced universal PCB (0805 SMD LEDs are really easy to solder there (so rather not smaller than that), but it may be twice the size too or something like that)
    • I would buy like some hundreds of such diodes to have them ready on hands and I will probably make more similar projects over time for me and my friends

Thanks for all comments and suggestions

1
  • 1
    Thanks for directing me to Electrical Engeneering, where the 1N4148 was so common in questions/answers, that I googled it and found, that it is ideal for nearly everything I could possibly do with Arduino I/O pins. My problem is SOLVED
    – gilhad
    Jun 30, 2019 at 0:30

2 Answers 2

2

When I've used a keypad matrix I've wired it up like this:

Keypad matrix with diodes

I used 1N4148 SMD diodes which I bought cheaply. I cut the traces on my cheap board that I had purchased from Jaycar and soldered them in like this:

1N4148 SMD diodes

The dot is the cathode (there is a line etched on my diodes at the cathode end).

The 10k resistors effectively turn the columns into pull-ups, therefore there is no danger of bad code damaging the Arduino or anything else.

Example code to scan the matrix:

// Keypad_Decoder
//
// Author: Nick Gammon
// Date: 17th February 2018

// Outputs to Serial in the format: 0b1nnnnnnn for a key down and 0b0nnnnnnn for a key-up
//            nnnnnnn will be the current row/column combination
//            Also every HEARTBEAT_TIME outputs 0xFF if all keys are up (heartbeat)

#include <limits.h>     /* for CHAR_BIT */

const byte ROWS = 4;
const byte COLS = 4;
const bool ENABLE_PULLUPS = true;  // make false if you are using external pull-ups
const unsigned long DEBOUNCE_TIME = 10;     // milliseconds
const unsigned long HEARTBEAT_TIME = 2000;  // milliseconds
const bool DEBUGGING = false;               // make true for human-readable output

// define here where each row and column is connected to
const byte rowPins [ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
const byte colPins [COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad


// See: http://c-faq.com/misc/bitsets.html

#define BITMASK(b) (1 << ((b) % CHAR_BIT))
#define BITSLOT(b) ((b) / CHAR_BIT)
#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
#define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b))
#define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))

// number of items in an array
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

// total number of keys
const byte TOTAL_KEYS = ROWS * COLS;

// remember previous setting of each key
char lastKeySetting [(TOTAL_KEYS + CHAR_BIT - 1) / CHAR_BIT];  // one bit each, 0 = up, 1 = down
unsigned long lastKeyTime [TOTAL_KEYS];       // when that key last changed
unsigned long lastHeartbeat;                  // when we last sent the heartbeat

void setup ()
{
  Serial.begin (115200);
  while (!Serial) { }  // wait for Serial to become ready (Leonardo etc.)

  // set each column to input-pullup (optional)
  if (ENABLE_PULLUPS)
    for (byte i = 0; i < COLS; i++)
      pinMode (colPins [i], INPUT_PULLUP);

}  // end of setup

void loop ()
  {
  byte keyNumber = 0;
  unsigned long now = millis ();  // for debouncing

  // check each row
  for (byte row = 0; row < ROWS; row++)
    {
    // set that row to OUTPUT and LOW
    pinMode (rowPins [row], OUTPUT);
    digitalWrite (rowPins [row], LOW);

    // check each column to see if the switch has driven that column LOW
    for (byte col = 0; col < COLS; col++)
      {
      // debounce - ignore if not enough time has elapsed since last change
      if (now - lastKeyTime [keyNumber] >= DEBOUNCE_TIME)
        {
        bool keyState = digitalRead (colPins [col]) == LOW; // true means pressed
        if (keyState != (BITTEST (lastKeySetting, keyNumber) != 0)) // changed?
          {
          lastKeyTime [keyNumber] = now;  // remember time it changed
          // remember new state
          if (keyState)
            BITSET (lastKeySetting, keyNumber);
          else
            BITCLEAR (lastKeySetting, keyNumber);
          if (DEBUGGING)
            {
            Serial.print (F("Key "));
            Serial.print (keyNumber);
            if (keyState)
              Serial.println (F(" down."));
            else
              Serial.println (F(" up."));
            }  // if debugging
          else
            Serial.write ((keyState ? 0x80 : 0x00) | keyNumber);
          }  // if key state has changed
        }  // debounce time up
      keyNumber++;
      } // end of for each column

    // put row back to high-impedance (input)
    pinMode (rowPins [row], INPUT);
    }  // end of for each row

  // Send a heartbeat code (0xFF) every few seconds in case
  // the receiver loses an occasional keyup.
  // Only send if all keys are not pressed (presumably the normal state).
  if (now - lastHeartbeat >= HEARTBEAT_TIME)
    {
    lastHeartbeat = now;
    bool allUp = true;
    for (byte i = 0; i < ARRAY_SIZE (lastKeySetting); i++)
      if (lastKeySetting [i])
        allUp = false;
    if (allUp)
      {
      if (DEBUGGING)
        Serial.println (F("No keys pressed."));
      else
        Serial.write (0xFF);
      } // end of all keys up
    } // end if time for heartbeat
  } // end of loop

Speedwise, I measured around 300 µs for doing a scan with no keys pressed. If that time seems a bit long, you could always do a time check, and scan every 5 ms or so.

I have a post about all this on my forum. In that post I also mention a library that handles N-key rollover, so you can detect multiple keys being pressed at the same time. For example, you could use A/B/C/D on the keypad as "shift" keys, so that A+1 meant something different than just 1.


Matrix from the front:

Keypad matrix


My problem is, which diodes to use and also why this type (or why it does not matter).

I don't think the exact diode is critical in this case. If this was a very high speed application, then a diode with fast switching might be required. If it was high voltage then a higher-spec diode might be required. In some cases the reverse voltage might apply. In this particular case, since we are dealing on/off at logic levels, I don't think much of this matters, although the switching speed might to a certain extent, however I am guessing from empirical observations that the speed of the 1N4148 is fast enough.

Also see: Wikipedia - 1N4148:

The 1N4148 is useful in switching applications up to about 100 MHz with a reverse-recovery time of no more than 4 ns.

5
  • Thank you, your answer would help many readers. But my question was more which type of diede to use (as you say IN4148) and mainly WHY, as I could found lot of schemes everywhere, little states what diode uses and none discuss, why this type exactly, or why it does not matter. There are many other kind of schemes on web, where correct values are critical (like some HiFi or so) and many schemes, where the values are just what author had handy and it does not work correctly in general, but he just had luck one time with out-of-spec just randomly worked for him. I wanted uderstand this point.
    – gilhad
    Jun 30, 2019 at 14:13
  • About bad programming - I often use sockets on my projects and reuse the microcontroller in other projects, when one came obsolete or I do not need it just now. Later I may go back to it again and put some MC to the socket and upload the original code again. So I was afraid of putting MC programmed for other project (where it would set the pin for COL1 to High and pin for ROW1 to Low) to the socket and press the left top key before uploading corect code inside. So effectively shortcut those two pins and lead 5V to 0V over diode. ...
    – gilhad
    Jun 30, 2019 at 14:51
  • ... So I put the restricting 250 Ohm resistors there to limit the current to 10mA even in this kind of mistake. (Similar thing happened to me before already, I soon realized, that the project does not work, but it was already late. So I came more paranoid about "impossible" programming bugs and I try to make HW such way, that it would nothing burn even if the MC actively tries to bur it/itself)
    – gilhad
    Jun 30, 2019 at 14:55
  • PS: I really like your site and read it often :)
    – gilhad
    Jun 30, 2019 at 14:57
  • Thanks! I've added another paragraph at the end explaining the choice of diode.
    – Nick Gammon
    Jun 30, 2019 at 18:52
0

Solution: 1N4148 diode

generally if you want connect diode to Arduino pin, use this one as it would be OK under nearly any normal use

  • 75 V reverse voltage (much more than 5V allowed there)
  • 1 V forward voltage at 10 mA (less at lower current)
  • 10 mA recommended current (destroys at 1 A 1 sec pulse) (and you do not want more then 10 mA anyway)
  • usable up to 100 MHz (so using digitalRead/digitalWrite and some logic you are way slower anyway)
  • super extra common for long time (even 10 years ago was considered "classical") and still actual (so when you get old, it would still be here)
  • dirty cheap (buy few hundreds just now and you do not need care anymore)

Your Answer

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

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