For various reasons, digital I/O pins are always at a premium when implementing micro controller projects. There have been various multiplexing schemes described to allow more than one switch to be connected to a small number of pins, but some time ago I came across a very simple but highly scalable method that uses the 4017 decade counter to connect many switches (more than 100 if needed) to just 3 digital inputs.
The basis of this post is the information found in the article here and its follow up here. These articles describe a technique that I thought looked interesting and decided to do a mini-project to test it out, avoiding some of the questionable variations described in those articles.
How it works
The 4017 decade counter is a “5-stage divide-by-10 Johnson counter” with 10 decoded outputs and a carry out bit. The counter is advanced on the positive edge of the CLOCK signal when the CLOCK ENABLE signal is in the logical 0 state, and cleared to a zero count by a logical 1 on the RESET line. The 10 decoded outputs are normally in the logical 0 state and go to the logical 1 state only at their respective time slot, remaining high for 1 full clock cycle. The carry-out signal completes a full cycle for every 10 clock input cycles and is used as a carry signal (CLOCK) to any succeeding counter ICs. To put it simply, each time the clock is toggled one of the outputs is turned on, sequentially from 0 to 9 and then the carry, which can be used to clock another chip in the sequence. This way a scanning cycle can be established with 10 rows in one IC and 10 columns in another IC, or 100 switches.
In this situation, the CLOCK and RESET are directly controlled by the microcontroller and the decoded output (1 of 10) is routed through a switch to a single microcontroller input pin. By advancing the counter (toggling the clock), and checking the input pin, the microcontroller can identify when a key has been pressed. In the case of a keypress the circuit will be closed and the input pin will be driven high. A low signal indicates that the switch is open circuit and therefore not pressed.
Diodes isolate the 4017 counter outputs in the event that two or more switches are closed at the same time, allowing simultaneous presses to be detected properly. The number of switches connected can be increased by cascading multiple 4017 or by using a matrix style arrangement. The nice thing is that at all times the interface to the microcontroller remains just 3 pins (Clock, Reset and the digital output from the matrix). Two counters in a matrix arrangement can be used to monitor up to 100 switches, still just using 3 interface pins.
According to the articles, Reset can be omitted but I decided to leave it in because reliable operation following the initial power-up reset depends on the 4017 counter’s remaining synchronized with the microcontroller counter. This is easiest done by explicitely resetting the 4017 at the start of every scan.
As a proof of concept, and to test the software library described below, I implemented a 3×3 key matrix matrix using one 4017 IC. The circuit for the small keymatrix and the resulting board layout are shown below.
Scaling the circuit up for more switches should be straightforward. The final product is show below, but I would recommend placing the tact switches on the other side of the PCB to make it a more practical keypad.
To test the hardware, I implemented an Arduino library and test code to scan the key matrix and return the key identifier(s) for any keys that are pressed.
- Allows definition of the size of the keyswitch matrix
- Manages the scanning of the 4017 to read the matrix
- Detects the transition from OFF state to ON state for each key
- Implements software debounce and software auto repeat
- Allows detection of multiple simultaneous key presses.
The library and all associated documentation, including Eagle CAD files, can be found at my code repository.
In conclusion, this technique is very effective I can see that this arrangement using the 4017 is relatively straightforward and has the potential to save a lot of I/O pins.