Multiple buttons on ONE analog pin

Hello

I'm looking to setup 5 buttons on one analog pin, much as described in a post here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1226896251.

I'd like to add a bit of functionality to the above however, such as: - Hardware debouncing (as discussed here: http://www.all-electric.com/schematic/debounce.htm); does not seem very complicated nor expensive; - Implement ISR on the analog pin for asynchronous button press event handling; Being a newbie, I can only speculate as to the benefits of the above approach! The resistor ladder has shown to work well in handling 5 or even more buttons. My idea of hardware debounce is to hopefully simplify code and provide a clean/square signal to the analog pin. A clean signal on the analog pin would make for reliable interrupt handling (ie no spurious interrupts caused by a bouncing signal). An ISR provides additional code simplification, and eliminates the need to poll the pin for changes in state.

I've seen the various bits and pieces of what I'm trying to do on this site. I'm wondering if anyone has put these concepts together!

Thanks in advance!

Ron

Implement ISR on the analog pin for asynchronous button press event handling;

Don't think you can do this as an ISR requires a digital event, what you have is simply a read. The best you could do is a series of comparators logically ORed together.

My idea of hardware debounce is to hopefully simplify code and provide a clean/square signal to the analog pin.

Yes that is my idea as well, how are you going to achieve this, apart from a capacitor slugging the input. The problem with that is you mistake an intermediate voltage for a key press.

Implement ISR on the analog pin

Can't be done.

If you add a cap to slow the response down to remove bounce then you will have to track the voltage changes and only accept the value when it's stable for X mS, which is the same as debouncing.

I'm learning here - so do bear with me.

In the Atmega documentation I read:

The Analog Comparator compares the input values on the positive pin AIN0 and negative pin AIN1. When the voltage on the positive pin AIN0 is higher than the voltage on the negative pin AIN1, the Analog Comparator output, ACO, is set. The comparator's output can be set to trigger the Timer/Counter1 Input Capture function. In addition, the comparator can trigger a separate interrupt, exclusive to the Analog Comparator. The user can select Interrupt triggering on comparator output rise, fall or toggle. A block diagram of the comparator and its surrounding logic is shown in Figure 22-1.

So, I'm thinking I could setup the analog comparator to trigger an interrupt when the lowest voltage (dependant on resistor ladder of pushbuttons) is raised.

Am I misunderstanding the use of the analog comparator and its ability to raise an interrupt for a given (assignable) voltage level?

Might it be that this is not supported by the Arduino development environment, and that I would need to turn to assembly programming?

Thanks!

Ron

Am I misunderstanding the use of the analog comparator and its ability to raise an interrupt for a given (assignable) voltage level?

Spot on but you don't assign a voltage level you put that voltage to the analogue input pins.

Might it be that this is not supported by the Arduino development environment, and that I would need to turn to assembly programming?

Yes right again. ;)

Good thinking and I followed a similar path when you first posted, but at the very least you have to use two extra pins (AIN0/1) and these aren't shared with any ADC pins so you're up to three pins and you STILL have to debounce :)

I know you're new to programing but really the best thing to do is write a small debounce routine.

Many thanks for your replies - I'm beginning to figure out many of the functional aspects of the Atmega.

I'm a looooooooong time programmer (VB) but new to the Atmega, Arduino and C++. Oh, and new to electronics as well!!! :o

The debounce routine does not bother me so much. It's rather the polling part I'm not too thrilled about. An interrupt (asynchronous) handler is really what I'd like to see. But for that to make any sense, I really have to turn to hardware debounce. Which is why that idea came up in the first place.

Since I do have time on my hands, I'll see just how far I can go with this idea! So for now, I'll be looking into how to setup AIN0/1, along with the analog comparator registers and interrupt vectors.

Once again, thanks!

Ron

This MIGHT work and you would only need to connect a signal to one pin ANI1 and keep another open AIN0.

If you set the comparator to use the internal bandgap voltage (1.1v) as the + input to the comparator (AIN0) and have your switch network connected to AIN1 so that any one button will switch the comparator and have it generate an ISR.

You could then switch the AIN1 pin from the comparator back to the ADC and take a reading. I don't know how long it takes to switch back to the comparator (this can be timed). Since you are using the band gap voltage for the comparator it may also have it turned on as the analogReference for the ADC also. If you wanted to switch back to DEFAULT (5v) reference and take a reading then that will take anywhere from 120us (default) to about 28us depending on what your prescalar is set too.

Not sure what a typical wait time is for debounce but you should be able to make it fall in the range when switching from comparator to ADC. You might have to disable the interrupt after the first interrupt is triggered so as you don't get continuous interrupts while the switch is bouncing and then enable them once you switch back.

What do you think?

Being a newbie, I

Sorry, I thought you meant to programming in general.

If you're happy to use the analogue comparator IPs...

NOTE: I spent some time on this then realised it won't work because the quescient state is the same as the interrupt state ie AIN0 > AIN1 but it's late and I'm too tired to think about it any more so I'll post it anyway in the hope it might give you a start.

In the quescient state ADCn and AIN0 at the same voltage level and AIN1 is 0.6v lower.

When a button is pressed ADCn goes immediately to Xv, AIN1 goes immediately to Xv - .6.

After a short time (and when the switch has stopped bouncing) AIN0 reaches Xv which is higher than the X - .6 on AIN1 which causes a comparator interrupt and the ISR reads ADCn etc etc.

May need a resistor from AIN1 to GND to get some current flowing through the diode.

No thought put into what happens when the button is released.

In the quescient state ADCn and AIN0 at the same voltage level and AIN1 is 0.6v lower

No it will only be 0.6v lower when there is current flowing through it. The analogue input is too high an impedance to have anything flowing. Also the two other pins will be at the same potential again because there is no current flowing through the resistor.

I had an arse-covering paragraph though.

May need a resistor from AIN1 to GND to get some current flowing through the diode.

Anyway I've got a better idea, use a MAX6818 debounce chip, this will debounce up to 8 switches and has a "input changed" output (CH).

Run CH to a pin to cause the interrupt. Run as many of the 6818 outputs as required (5 I guess) into an R2R network and from there into an ADC input.

You get an interrupt after the switch is debounced and read the voltage. Two pins and easy software, plus you get really good ESD protection and can have +-25v on the inputs.

Thanks again.

I don't have enough mastery to completely understand the last few posts.

But, a debounce chip is clearly part of the picture that I envisioned from the start.

I had not come across one with a 'CH' feature, which is exactly what is required to make this work, nicely and simply.

Cool!

Cheers!

I tested this code with one button and it works so it should work with multiple buttons as long as the smallest voltage drop with a button pressed is greater than 1.1 volts.
It’s setup to have the circuit connected to ADC0 and uses some comparator code from CodingBadly in a previous post.

int buttonPress = 0;
int buttonID = 0;
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif




void setup() {
  sbi(ADCSRB,ACME) ; //switch off ADC multiplexor to use ADC input
  cbi(ADCSRA,ADEN) ; //Turns off the ADC
  cbi(ADMUX,MUX2) ; //set address for ADC0 as input instead AIN1
  cbi(ADMUX,MUX1) ; //set address for ADC0 as input instead AIN1
  cbi(ADMUX,MUX0) ; //set address for ADC0 as input instead AIN1
  
  ACSR =
  (0<<ACD) |   // Analog Comparator: Enabled
  (1<<ACBG) |   // Analog Comparator Bandgap Selected: 
  (0<<ACO) |   // Analog Comparator Output: Off
  (1<<ACI) |   // Analog Comparator Interrupt Flag: Clear Pending Interrupt
  (1<<ACIE) |   // Analog Comparator Interrupt: Enabled
  (0<<ACIC) |   // Analog Comparator Input Capture: Disabled
  (1<<ACIS1) | (1<ACIS0);   // Analog Comparator Interrupt Mode: Comparator Interrupt on Rising Output Edge

    // set ADC prescaler to 4
cbi(ADCSRA,ADPS2) ;
sbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
  
  Serial.begin(115200);

}

void loop() {
  if(buttonPress){
    Serial.println(takeReading());
    buttonID=0;
  }

}

ISR(ANALOG_COMP_vect)
{
  buttonPress++; // Button press detected
}

int takeReading(){
  cbi(ACSR,ACIE); //disable interrupt
  cbi(ADCSRB,ACME) ; //switch on ADC multiplexor to use ADC input
  sbi(ADCSRA,ADEN) ; //Turns on the ADC
  for(int i = 0;i<3;i++){buttonID = analogRead(0);} //throw away the first couple of reads
  sbi(ADCSRB,ACME) ; //switch off ADC multiplexor to use ADC input
  cbi(ADCSRA,ADEN) ; //Turns off the ADC
  delay(10); //tweak this number as your debounce delay
  sbi(ACSR,ACI); // Analog Comparator Interrupt Flag: Clear Pending Interrupt
  sbi(ACSR,ACIE); //enable interrupt
  buttonPress=0;  
  return(buttonID);  
}

Basically it uses the internal bandgap voltage (1.1v) as the positive input to the comparator instead of AIN0. It then uses the output of the ADC multiplexer (set to ADC0) as the negative input to the comparator instead of AIN1.

The rising edge output of the comparator triggers an interrupt. Once the interrupt is triggered, the interrupt is disabled (so it doesn’t keep generating interrupts while bouncing) and the ADC multiplexer is turned back on which switches the voltage from the comparator negative input back into the AD converter and takes a reading of the voltage.
There is a small delay that can be adjusted for debounce and after the delay the ADC multiplexer is once again shut off and switches the voltage back into the comparator. The interrupt is then enabled and it is ready to wait for the next button press.