Multiple switches on Analog input

Status
Not open for further replies.

Pensive

Well-known member
Hi all

I'm looking to use some carefully chosen resistors to pickup multiple individual switches from one analogue input.

I need to detect the individual state of each switch with each poll, using different resistors on each switch such that the combinations of switch presses always result in a unique analogue response.

Is there a tried and tested accepted limit to this capability through a teensy input? 4 buttons, 8 buttons?

Does it change from teensy to teensy ( I have a 3.1)?

And is it reliable ? How much better is a multiplexer chip? Except for saving the CPU cycles (always a benefit).

Cheers guys

First Arduino project :)

Jon
 
Well, I'd toyed with doing this for a project - and then got frustrated and decided to go with a multiplexer, because it's just easier to think about.

The downside to a multiplexer is that you either A) only check the state of a single channel of the multiplexer per cycle. If your full loop cycle is short, this is easy - just increment the channel AFTER copying the value to a variable, and move on. or B) You check all channels of the multiplexer each loop - and you have to wait 50 microseconds after switching channels so that you don't get bad data. For 16 channels, that's about .8 milliseconds - which may be too much, depends on what you're doing. The benefit of resistor button arrays is that you can check them all at once.

The downside to the resistor button arrays is that there's a LOT of combinations. For 4 buttons, I think it's 16 possible combinations that you'll need to be able to check for. For 8 buttons it's 64 combinations, for 10 it's 100, etc.(I might be a little off on my math here). I don't know of any simple algorithm that would let you plug in the number of buttons, their resistor values, and would then find you the resistance values (or even better, the analog reading value) for every possible combination. Seems like it might be a fun little thing to write, though.

I don't know if having to run through a table, or check a bunch of different thresholds, would be faster to process than the 50µs wait time, though.
 
Use binarily-weighted resistor values in series with each switch. Say 1k, 2k,4k,8k. Connect one end of all the resistors together and connect this junction to the supply with another resistor (say 1k; I'm not sure what's optimal). Connect the other end of he binary resistors via he switches to ground. Read the junction with the ADC and set its reference to VDD (3.3 V). The ADC readings will all be unique and reasonably well spaced apart (not optimally, but close enough). Use a spreadsheet to calculate the expected ADC readings; set the thresholds in your code midway between steps.

You'll need good tolerance resistors for this - 5% isn't good enough. Either use 1 %, or if you are just building one system, select the resistors by hand. You may not be able to get these precise values -- use multiple identical resistors in parallel or series to build those.
 
Use binarily-weighted resistor values in series with each switch. Say 1k, 2k,4k,8k. Connect one end of all the resistors together and connect this junction to the supply with another resistor (say 1k; I'm not sure what's optimal). Connect the other end of he binary resistors via he switches to ground. Read the junction with the ADC and set its reference to VDD (3.3 V). The ADC readings will all be unique and reasonably well spaced apart (not optimally, but close enough). Use a spreadsheet to calculate the expected ADC readings; set the thresholds in your code midway between steps.

You'll need good tolerance resistors for this - 5% isn't good enough. Either use 1 %, or if you are just building one system, select the resistors by hand. You may not be able to get these precise values -- use multiple identical resistors in parallel or series to build those.

Thanks everyone, this was my plan, trying to get 8 buttons on one input using 7 resistors that should hit the equivalent of 1,2,4,8,16,32,64,128 in the 3.3v analogue space, or near enough.

Looking at the multiplexers, I'll be doing an audio project which could push the cpu pretty hard; I can't justify building in multiple Delay() commands if it can be avoided. This is always a bad idea. Of course I could work around this by careful use of timers but it sounds like unnecessary complication in both the design and the software. I can't really see how they are any easier than a handful of resistors, the code seems a little more clunky to my mind.

It looks like 16 inputs is possible, but I'll be able to have much bigger "switch windows" (for want of a better term) in the voltages with less inputs, so I think i'll go for 8 buttons, 1% resistors(thank you!), and then find a way to determine the bitmap with the minimum clockticks. I like the log solution the guy used in that youtube video if a little overcomplicated, but I think i'll build my own custom lookup tables and write a function which simply returns the bit-map from the voltage measured.

I am a little concerned about this approach because it might mean that buttons 1,2 and 4 create very similiar voltage changes, which will tie many of my "switch windows" for combinations involving these to the size of these small voltage changes. Might affect reliability/accuracy, but i can't see a better way to do it without sticking another chip on there and using serial.

Thanks for your advice :)
 
Internally 8-bit NES controllers user 4021 CMOS shift registers. The Super NES used the equivalent of 2 in series for 16 bits. You could put as many as you want in a string. It requires 3 lines: clock, latch, and data. So 2 outputs and one input. It can be expanded to only need one additional input line per string of inputs. CMOS can operate at 2VDC and can be powered up to 7VDC (probably higher, I think the 74HC series is limited to 7VDC). It is a pretty simple way to get lots of de-bounced digital inputs. There are probably other 74HC series chips that can be shift registers too.

I originally reverse engineered a NES controller when I was in college in '94. Been using them for various projects as test inputs.

Most keyboards use a scan system that has vertical and horizontal inputs. Depending upon what combination of connections are made will determine the key. You would have to look up how that works.

I think the link MichaelMeissner gave you is for a shift register type device too.
 
5 (or perhaps 6) is the limit with this technique. The tolerances don't work after that. For more, you'll have to do polling and will need to use more than 1 I/O pin.
 
I think the link MichaelMeissner gave you is for a shift register type device too.

As I understand a shift register would require you to iterate over each bit, setting the data latch, and then toggling the clock signal to set a bit. The i2c bus operates on 8-bit bytes, so with the MCP23017, it has 16 bits, and if you wanted pin 5, you would send the command to the MCP23017 to send you the first byte, and the code would do shifting to isolate the bit you want. If you wanted pin 9, you would ask for the second byte. However, you can get all 8 bits in your code, and do the shifting yourself. However, the i2c bus does send out bits serially, much like a shift register.

Note, you have to use the 2 hardware i2c pins that are connected to the i2c bus (A4/A5 for the first i2c bus, A6/A7 as alternates for the first i2c bus, and A18/A19 for the second i2c bus on the Teensy 3.1). However, you can have multiple i2c devices on the bus. For the MCP23017, you can have 8 MCP23017's for a total of 128 pins. The MCP23017 has 2 interrupt pins to get notified when the first and second set of 8 pins changes state.
 
Last edited:
Sorry Michael,
I was thinking of this chip: 74165 when i wrote that. I meant to go back and compare to see if it was indeed a shift register. I think though, that if the application is starving for processing speed using something that uses I2C or SPI is probably a good idea. Since you can offload the CPU time to that peripheral. There are probably a hundred ways to skin this cat though.
 
Though of course if you use i2c, you are limited to the speed that i2c runs at (100 kHz by default, up to 1.2 mHz if you use i2c_t3.h). For human interaction things, that would usually be sufficient (though you would want to read the switches in a batch, rather than doing a lot of transactions to get a single bit). Having an interrupt pin available, means you can know that I/O is available, rather than polling the chip.
 
Actually it would be interesting to see, what the performance actually is using SPI or I2C versus shift register or the like.

That is take I2C. In many cases, that I have seen, the code is setup to do something like: Output a request to the I2C to read a register, wait for the command to go out and then wait for the response, before it returns... Likewise i believe most of the libraries for SPI when then do a read, do something like, clear out the SPI read queue, then do an SPI transfer, which does the shift out/in, then wait for that to complete and then grab the response from the spi input queue... Again you can probably write your own code that can minimize this overhead. So again it would be interesting to see how quick you could make this.

Kurt
 
Or take a Atmel 328P, run it at 8MHz and 3,3V and dedicate it to registering inputs (i.e. buttons closing). Configure it as a slave on the I2C bus and communicate using EasyTransfer from Bill Porter. Mini Pro's at 3.3V from Sparkfun are available in a teensy-compatible form factor (i.e. breadboard) and at $10 each (as long as you have a FTDI cable/programmer to talk to it) they are inexpensive to boot.

No more missed inputs and plenty of pins to play with. All that said, a analog-input resistor ladder works also, just watch the noise.
 
Are you sure this isn't a bit academic ? -- you'll need to debounce the switches anyway (1 - 10 ms), and no one can press or release multiple switches predictably within a few ms -- so polling isn't really a disadvantage.
 
I tend to think this is where an RTOS solution starts winning out. You have threads handling the polling of the keys with debouncing (or polling i2c or polling spi, etc.) and it does it asynchronously, rather than having to have a state machine that is done in the main loop for the Arduino. Of course it is a hard concept to learn how to program in such an environment.

Jp3141: It depends on what else the Teensy is doing in the idle loop. For example, if you are doing a full scale video wall with octows2811's, you might not have that many cycles to poll a bunch of switches.
 
I tend to think this is where an RTOS solution starts winning out. You have threads handling the polling of the keys with debouncing (or polling i2c or polling spi, etc.) and it does it asynchronously, rather than having to have a state machine that is done in the main loop for the Arduino. Of course it is a hard concept to learn how to program in such an environment.

Jp3141: It depends on what else the Teensy is doing in the idle loop. For example, if you are doing a full scale video wall with octows2811's, you might not have that many cycles to poll a bunch of switches.

OK, this is sounding interesting. My project is to be a realtime sampler/looper to trigger sounds/samples so realtime performance is of course critical.

Just to throw option 4 (i think..) into the mix: http://www.ebay.co.uk/itm/ARCADE-DI...07522014&tpos=top&ttype=price&talgo=undefined

What about a PS2 interface solution? A swift google indicates I should be able to connect this up (albeit probably with more than 1 pin) but get instant response and multiple keypress detection from an industry standard product which is not limited to simply the Teensy / Arduino world. I suppose the real question is how much CPU load will it take to interpret this PS2 input? I'm certain it's possible. It's ideal for a PC MAME setup but possibly the interface is going to be a high load on the CPU. As far as debounce goes I don't really want a debounce except that which is absolutely necessary for the electrical signal to stabilise.

I'm keen to try the shift registers solution though, if it worked for an 8bit NES it's fair to say it was the best, fastest and lowest CPU load solution available at the time. Those Japanese devs knew a thing or two about pushing the limits of hardware. :)
 
What about a PS2 interface solution?

http://www.ultimarc.com/minipac.html

This comes as a dedicated board with pre-wired / crimped cabling which can be directly connected to up to 32 buttons on what looks to me like 2 pins.

https://www.pjrc.com/teensy/td_libs_PS2Keyboard.html

Looking at the library it uses interrupts and it caches keypresses so you could simply run a timer as fast as you wish to respond to keypresses and then check keyboard.available() to see if a key has been pressed. It also supports detection of key release.

This seems to be by far the easiest solution, uses 2 pins instead of one but for twice as many buttons as i was proposing per pin, but how does it perform?

Not the cheapest solution: @ £38.39 (shouldnt need a PS/2 adaptor, but might) it's a fair whack on money but makes wiring and interfacing a breeze for 32 buttons, just plug and play. How much is your time worth?
Anyone looking to do this on the cheap can achieve the same with 2 or 4 units of the MCP23017 plus cabling / wiring / soldering etc. Both solutions would use interrupts, both solutions will have a reasonable load on the CPU at runtime.

I'm thinking of using this implementation to make my life easier - anyone got anything to add before i click purchase?

Thanks for your input guys, if it doesn't work so well I have 3 other avenues to investigate which is great!
 
Last edited:
Status
Not open for further replies.
Back
Top