0
\$\begingroup\$

I connected 4 buttons to the ADC module of AVR-ATMega328p with different resistor values, enter image description here

To know which button is pressed this is the ISR() function I implemented,

ISR(ADC_vect)
{  
  // Assign the data to bInput variable
  bInput = ADC;
  
  if((bInput < 900) && (bInput > 800))
  {
    // B1 is pressed
    // V in to ADC is 4.1v
  }
  if((bInput < 700) && (bInput > 600))
  {
    // B2 is pressed
    // V in to ADC is 3.1v
  }
  if((bInput < 500) && (bInput > 400))
  {
    // B3 is pressed
    // V in to ADC is 2.0v
  }
  if((bInput < 300) && (bInput > 200))
  {
    // B4 is pressed
    // V in to ADC is 1.1v
  }
}
Output values I get(Tested using a Multi-meter) -
  1. If button B1 pressed: Vdrop across R = 4.1 V , Vin to ADC module = 4.1 V , ADC registor = 853
  2. If button B2 pressed: Vdrop across R = 3.1 V , Vin to ADC module = 3.1 V , ADC registor = 639
  3. If button B3 pressed: Vdrop across R = 2.0 V , Vin to ADC module = 2.0 V , ADC registor = 426
  4. If button B4 pressed: Vdrop across R = 1.1 V , Vin to ADC module = 1.1 V , ADC registor = 227

But the processor is always busy to do the ADC conversion every time, So I decided to add a trigger-INT0 for the ADC conversion to happen,

ADC.c
#define F_CPU 16000000UL

#include <avr/io.h>

void __init__button();

int bInput = 0;

int main()
{
   __init__button();

  while(1)
  {
    //loop
  }
  return 0;
}

void __init__button()
{
  // Make ADC pins as input 
  DDRC=0x0;
  SREG |= (1<<7);
  // Configure the ADCSRA registor
  ADCSRA |= (1<<ADIE);

  // Set the prescalar to 128 -> 125 kHz
  ADCSRA |= (1<<ADPS0);
  ADCSRA |= (1<<ADPS1);
  ADCSRA |= (1<<ADPS2);
  
  // Select the voltage referance - AREF pin
  ADMUX &= ~(1<<REFS1);
  ADMUX |= (1<<REFS0);

  // Select the input channel - ADC3
  ADMUX |= (1<<MUX0);
  ADMUX |= (1<<MUX1);
  ADMUX &= ~(1<<MUX2);
  ADMUX &= ~(1<<MUX3);

  // Enable the INT0 
  EIMSK |= (1<<INT0);
  EICRA |= (1<<ISC00);
  EICRA |= (1<<ISC01);
  PORTD |= (1<<PORTD2);

  ADCSRB &= ~(1<<ADTS0);
  ADCSRB |= (1<<ADTS1);
  ADCSRB &= ~(1<<ADTS2);
  ADCSRA |= (1<<ADATE);
  
  // This will start the conversion if trigger INT0 is pressed
  ADCSRA |= (1<<ADEN);

}

ISR(ADC_vect)
{  
  // Assign the data to bInput variable
  bInput = ADC;
  
  if((bInput < 900) && (bInput > 800))
  {
    // B1 is pressed
  }
  if((bInput < 700) && (bInput > 600))
  {
    // B2 is pressed
  }
  if((bInput < 500) && (bInput > 400))
  {
    // B3 is pressed
  }
  if((bInput < 300) && (bInput > 200))
  {
    // B4 is pressed
  }
}

ISR(INT0_vect)
{
  // nop;
}

And this is the corresponding circuit, enter image description here

Output values I get Now (Tested using a Multi-meter) -
  1. When no button is pressed: Vdrop across R = 1.0 V , Vin to ADC module = 1.0 V , Current in the circuit 0.09mA so I think R internal of INT0 is - 44k.
  2. If button B1 pressed: Vdrop across R = 4 V , Vin to ADC module = 4 V , ADC registor = 872
  3. If button B2 pressed: Vdrop across R = 3 V , Vin to ADC module = 3 V , ADC registor = 695
  4. If button B3 pressed: Vdrop across R = 2.32 V , Vin to ADC module = 2.32 V , ADC registor = 520
  5. If button B4 pressed: Vdrop across R = 1.698 V , Vin to ADC module = 1.698 V and this is not triggering the INT0 interrupt.

But the problem is - connecting the wire to INT0 pin from same pin connected to the ADC module(highlighted wire) mess things up - I think the internal resistance of pin INT0 causes the problem because R1,R2,R3,R4 and R internal of INT0 pin is connected in parallel. And not only that to trigger the INT0 interrupt I think we need at least 2.5vDC, so when we press the button B4 the voltage drop across the R is - 1.1V and this will not trigger the INT0 interrupt.

What I want:

I want the ADC to start a conversion only when I press a button- B1,B2,B3 OR B4.

Problem to solve:

So, I need a circuit that can trigger the INT0 interrupt and doesn't mess with button circuit I made...

\$\endgroup\$
2
  • 2
    \$\begingroup\$ So you basically want an interrupt every time the ADC input rises above, say, 0.5 V? The ATMega328P has an internal voltage comparator that could be used for this purpose, or you could add an external chip. \$\endgroup\$
    – Dave Tweed
    Commented Apr 22, 2023 at 14:32
  • \$\begingroup\$ ADC on this chip may involve a timing algorithm and so would likely not play well with an interrupt. I suggest you just get a discrete expansion if you can't spare the pins. These will essentially spread your values over time on the same pin. \$\endgroup\$
    – Abel
    Commented Apr 22, 2023 at 15:38

1 Answer 1

0
\$\begingroup\$

Commonly, these matrixes are activly driven.

You feed in the 5V from a GPIO which is toggled via a timer or in main-loop.

Every time a rising edge is present, you start your ADC-Conversion.

The scna-frequency (GPIO Toggle frequency) must no be bigger than half of your ADC-Conversion frequency for a safe application.

I usually use a time, a generic GPIO and a single ADC Channel for this. In the Timer ISR - if the pin is set LOW-To-High - i start another timer timeout. When this second timeout occures, i start the ADC Conversion. When the Conversion finished, i check the results in the ADC ISR.

\$\endgroup\$

Your Answer

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

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