Interrupts with analog values

Okay. So, I’m kind of stuck. I need to be able to generate an interrupt and go to an ISR when the value on an analog input changes. I have a signal tied to analog pin 0 which, depending on the value of it, sends commands to a USB keyboard controller chip. My problem is the way I have the program set up now, I’m having to poll for the analog value at the beginning of the loop function. This causes me to have to push and hold the various buttons that generate the analog value so that the program picks it up. I need for the program to detect when the value changes and immediately start handling it. The analog value stays at 0~1 until a button is pushed. Below is the code that I have…hopefully my question makes sense…

int pressCount = 0;
int analogVal = 0;
int val = 0;

void setup()
{
pinMode(1, OUTPUT); // output clock

pinMode(2, OUTPUT); // output character pins
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);

digitalWrite(2, HIGH); // make sure no characters are sent on the USB bus when program begins

}

void loop()
{
val = analogRead(0);
if(val > 2 && val < 50)
{
delay(500);
val = analogRead(0);
if(val > 2 && val < 50)
{
printChar(4);
clockSingle();
}
}
else
{
printChar(0);
while(val > 50)
{
clockMult();
val = analogRead(0);
}
pressCount = 0;
}
}

void clockMult()
{
digitalWrite(1,LOW);
delay(.128);
digitalWrite(1,HIGH);

if(pressCount < 3)
{
++pressCount;
delay(200);
}
else if(pressCount < 10)
{
++pressCount;
delay(100);
}
else
{
delay(.128);
}
}

void clockSingle()
{
digitalWrite(1,LOW);
delay(.128);
digitalWrite(1,HIGH);
delay(500);
}

void printChar(int x)
{
if(x == 0) // down
{
digitalWrite(2, HIGH);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
else if(x == 1) // up
{
digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
else if(x == 2) // enter
{
digitalWrite(2, HIGH);
digitalWrite(3, LOW);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);
digitalWrite(6, LOW);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
else if(x == 3) // esc
{
digitalWrite(2, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
else if(x == 4) // right
{
digitalWrite(2, LOW);
digitalWrite(3, HIGH);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
else if(x == 5) // left
{
digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}
}

Macdub, could you summarise what you want the program should do. Describing the functionality you desire would make it easier to suggest alternative implementations that solve your problem.

Its a little difficult to see from the posted implementation what your sketch is supposed to do.

Ok. Here's the setup. I have a module that outputs a voltage value based on different button presses. So one button might cause it to put out .5, another 1.8, another 2.6, etc...This is connected to the analog pin on the arduino. It needs to take this value and use it to decode which button was pressed. The atmel chip is connected to another chip which is a usb keyboard emulator. Basically, it takes 8 parallel digital inputs and a "clock" input. When the clock goes from high to low it issues the selected keyboard character on the usb bus. So, for each button press, it gets converted to a usb key press. There is some other code in there to account for if you hold the button down, the "clocks" get issued faster (the up/down keys scroll through lists. If it's a long list I want it to go faster through the last as the button is held down longer). It isn't complete as I was just testing with two different key presses. If no button is pressed, the value read with analogRead() is between 0-1.

The first "if" check I have in that code is there to require the button that corresponds with that value to be pressed for at least half a second for it to do anything, and then only issue the command once with the clockSingle() fuction. The clockMult() function is for the other keys which, when holding down, issue commands faster until you release them.

So, the implementation I have works. The only problem is that I need the code to pick up that the value has changed on the analog pin as soon as it happens. The polling I'm doing isn't working since it only gets checked once at the beginning of the loop. Depending on where the code is when the button is pressed, the usb output is anywhere from instantaneous to about 1/4 of a second. But, if you are pushing the button to repeatedly to scroll through a list of a few items you basically have to push very slowly (slower than feels normal) in order to get it to respond correctly.

I know that you can do interrupts on the digital pins. So one idea I had was to use a schmitt trigger setup having the input as the analog voltage and the output going to a digital pin on the atmel. This way, when the voltage rises above a certain point, it would show up on the digital pin and cause the interrupt which would then read the analog value. But, I still could get similar results depending on how long it takes to break from the main code and process the interrupt and read the value. If the button is released before the value is read in the interrupt function, i have the same problem.

There has to be a better way to do this, but I'm just not sure what. Even if what I said above works, I really don't want to add an extra op-amp circuit to my design just for this. It would be nice if the arudino could detect analog changes and trigger an interrupt that way. But, if anybody has any better ideas, please let me know....

(BTW - this whole thing is a design to control the Apple Front Row interface on a Mac Mini that I'm installing into my car...the voltage is coming from a PAC-Audio CAN-BUS interface box which ties into my steering wheel controls. Those controls on the steering wheel will ultimately control the interface which will be displayed on an LCD in my car. I'll post a video when I'm finished ^^ )

sounds like a fun project!

One solution is to structure your code so there are no delays waiting to detect the analog values. The following code gives you access to the current key and the total number of milliseconds it has been pressed in a loop that is repeated hundreds of times per second.

You would need to add code that generated the key presses.

// define thresholds to detect button presses, TODO: substitute real values for these
#define NO_BUTTON  50 // threshold below which no button is pressed, 50 = .25 volts
#define BUTTON1  150  // .75 volts , the highest voltage for button 1 
#define BUTTON2  300  // 1.5 volts
// define more buttons as needed

int Button = 0;   // global variable containing the current button, 0 is no button 
int StartTime;    // keeps track of the number of milliseconds since Button (see above) was pressed 

// this function updates the global variable Button and returns the number of milliseconds the button has been pressed 
int ButtonDuration( int pin) {
   int currentButton;
   int val = analogRead(pin);
   if(val <= NO_BUTTON)
       currentButton = 0;         // 0 means no button is pressed
    else if( val <= BUTTON1)             
      currentButton = 1;
    else if( val  <= BUTTON2)  
      currentButton = 2;
    // add more buttons here
    
    if(currentButton != Button){
      StartTime = millis();
      Button = currentButton;    
    }  
    return millis() - StartTime; 
}


void setup()                    // run once, when the sketch starts
{
  // your setup code here
}

void loop()                     // run over and over again
{
        int duration = ButtonDuration(0) ;
        if( Button == 1  &&  duration > 500){   
             ; // do something here in reponse to long button 1 press
        }
        else if ( Button == 2) {
            if( duration > 500 )   
              ;// button 2 pressed for at least 500 ms     
            else if(duration > 100 )  
             ; // respond to 100ms press of button 2 
        }      
              
}

You may need to modify your clock functions to remove the delays by polling to see if the clock timeouts have expired. You can do this by storing the millis() time that the clock pulse starts and check each time you call your clock function to see if the desired number of milliseconds has passed so you can pulse the clock low. I hope that these hints help you move forward.