AnalogRead in an Interrupt Service Routine (ISR) - 8 bits okay- toy oscilloscope project

Hello,

Main question:
Could anybody perhaps help me understand how to incorporate the below code into my sketch, or some similar solution. I have an ISR and I want to read A0 and A1 in there.

want to incorporate this stuff: (my sketch below this one)

//
//    FILE: analogRead8.h
//  AUTHOR: Rob Tillaart
//    DATE: 2015-05-15
//
// PUPROSE: fast analogRead 8 bit 
//

#ifndef analogRead8_h
#define analogRead8_h

#define ANALOGREAD8_VERSION "0.0.1"

int analogReadP(uint8_t pin, uint8_t prescale=7)
{
    uint8_t ADCSRA_TMP = ADCSRA;
    prescale = constrain(prescale, 2, 7);
    ADCSRA = (ADCSRA | 0x07) & (0xF8 | prescale);
    int value = analogRead(pin);
    ADCSRA = ADCSRA_TMP;
    return value;
}

#endif
// --- END OF FILE ---

my sketch

/*
sketch: toy oscilloscope

  Citations:
  ***************************************************************
  File:  Oscilloscope.ino
  Title:  Standalone Arduino 6 channel  Triggered Oscilloscope
  Author: Meeker6751
  Verson: 2018.4.23
  URL:  Arduino forum/projects

  File: Arduino Interupts
  Author:  amandaghassaei
  URL: https://www.instructables.com/Arduino-Timer-Interrupts/
  ***************************************************************
*/
#define ul unsigned long
/***********************************************************/
#define triggered false  //  choose with switch
#define continuous true  //
bool SweepMode;          //  'continuous' or 'triggered'
/***********************************************************/
#define armed true   //  these 2 inside of trigered mode
#define froze false  //  trigrd
bool TriggerState;   //  'armed' or 'froze'
/***********************************************************/
#define falling false              //  trigger slope positive
#define rising true                //  trigger slope negative
bool TriggerDirection = rising;  //  'rising' or 'falling'
/***********************************************************/

/***********************************************************
volatile means can use in ISR like a global
***********************************************************/
volatile float pot_state;
//volatile float ch1_state

// adjustable variables
float TriggerVolts = 2.0;  //  trigger vdc; 0 <= TriggerVolts <= 5

// loop procedure variables
float ChannelFloor;
float ChannelHeight;

float ChannelScale;    //  proportion of signal to display
float TriggerDisplay;  //  vertical position of trigger
float TriggerLevel;    //  calculated from 'TriggerVolts'
int zero_line = 0;

//Pins used
int LED_pin = 4;     //for indicating trgrint LED_state = LOW;
int switch_pin = 3;  //for trgr
int ch1_pin = A0;
int ch2_pin = A1;
int pot_pin = A2;  // potentiometer input
float ch1_prev = TriggerVolts;  // initially,  no last sample
ul ch2_prev = -1;  // initially,  no last sample

// debug variables
char debugging1[] = "EARSNOT";
char debugging2[] = "SACER";
char debugging3[] = "IRAK";
char debugging4[] = "YEAR";

/*********************************************************************************************************
*Function: Interupt setup
*Purpose: Set up timer
*********************************************************************************************************/
//sample clock
void interruptSetup() {
  cli();  //stop interrupts

  //set timer2 interrupt at 1MHz (ish?)

  TCCR2A = 0;  // set entire TCCR2A register to 0
  TCCR2B = 0;  // same for TCCR2B
  TCNT2 = 0;   //initialize counter value to 0
  // 
  OCR2A = 2;  // =
  // turn on CTC mode
  TCCR2A |= (1 << WGM21);
  // Set CS11 bit for 8 prescaler
  TCCR2B |= (1 << CS11);
  // enable timer compare interrupt
  TIMSK2 |= (1 << OCIE2A);
  sei();  //allow interrupts
}  //end setup
/*************************************************************************************
Function : void read_trigger_choice(
Behavior: use a switch to choose for choose Sweepmode (continous or triggered)
          sets Sweepmode (global bool no need for return)
*************************************************************************************/
void read_trigger_choice() {
  int switch_state = digitalRead(switch_pin);
  if (switch_state == LOW) {
    SweepMode = continuous;
    digitalWrite(LED_pin, HIGH);
  } else if (switch_state == HIGH) {
    SweepMode = triggered;
    digitalWrite(LED_pin, LOW);
  }
}
/*************************************************************************************
Function : void test_for_ch1_triggering_event()
Behavior: ch 1 look for event 
output: armed or froze
*************************************************************************************/
void test_for_ch1_triggering_event() {
  float ch1_state = analogRead(ch1_pin);
  ch1_state = ch1_state * ChannelScale + ChannelFloor;  // mapping from 0-1023 to 0-5 for display purposes
  /*Serial.println(debugging4);
  Serial.println("ch1_prev: ");
  Serial.println(ch1_prev);
  Serial.println("ch1_state: ");
  Serial.println(ch1_state);
  Serial.println("TriggerDirection: ");
  Serial.println(TriggerDirection);
  Serial.println(debugging4);
  delay(3000);*/
  //
  //
  if (SweepMode == triggered) {
    if (ch1_state >= 0 and ((TriggerDirection == rising and ch1_state >= TriggerVolts and ch1_prev < TriggerVolts) or (TriggerDirection == falling and ch1_state <= TriggerVolts and ch1_prev > TriggerVolts))) {
      TriggerState = froze;
    } else {
      TriggerState = armed;
      ch1_prev = ch1_state;
    }
  }
}
/*************************************************************************************
Function : void test_for_ch2_triggering_event()
Behavior: ch21 look for event 
output: armed or froze
*************************************************************************************
void test_for_ch2_triggering_event() {
  int ch2_state = analogRead(ch2_pin);

  delay(1000);
  //
  if (SweepMode == triggered) {
    if (ch2_prev > 0 and ((TriggerDirection == rising and ch2_state >= TriggerLevel and ch2_prev < TriggerLevel)or (TriggerDirection == falling and ch2_state <= TriggerLevel and ch2_prev > TriggerLevel) )) {
      TriggerState = froze;
    } else {
      TriggerState = armed;
      int ch2_prev = ch2_state;
    }
    
  }
}*/
/*********************************************************************************************************
*Function: Interupt Service Routine
*Purpose: -Read signal
          -set triggering mode
          -set trigger state
*********************************************************************************************************/
ISR(TIMER2_COMPA_vect) {
  
  read_trigger_choice();
  
}
/********************************************************************************************************
Function : setup()
This is just some setup buisness.
ISR and timer setup need should come first nonetheless
 ********************************************************************************************************/
void setup() {
  pinMode(LED_pin, OUTPUT);
  pinMode(switch_pin, INPUT);
  pinMode(ch1_pin, INPUT);
  pinMode(ch2_pin, INPUT);
  pinMode(pot_pin, INPUT);
  Serial.begin(115200);
  TriggerLevel = (TriggerVolts / 5.0) * 1023;
  ChannelHeight = 5.0;
  ChannelScale = 5.0 / 1024.0;  //mapping from 0-1023 to 0-5 for display purposes
  TriggerDisplay = TriggerVolts * ChannelScale + 5.0 - ChannelHeight;
  interruptSetup();
}

/*************************************************************************************
Function : loop()
Behavior: In the loop the signal is actually drawn (3 choices continouse, armed, trigrd)
-Sweepmode (continous or triggered) and
-if in triggered mode then TriggerState is either (armed or froze)
*************************************************************************************/
void loop() {

  ChannelFloor = 5.0 - ChannelHeight;  // display trigger level,  if applicable

  //ch1 reading pin
  float ch1_state = analogRead(ch1_pin);
  ch1_state = ch1_state * ChannelScale + ChannelFloor;  // mapping from 0-1023 to 0-5 for display purposes

  //ch2 reading pin
  float ch2_state = analogRead(ch2_pin);
  ch2_state = ch2_state * ChannelScale + ChannelFloor;

  //Y-scaling reading pin
  pot_state = analogRead(pot_pin);
  pot_state = pot_state * ChannelScale + ChannelFloor;

  test_for_ch1_triggering_event();

  /**************************************************************************************
*SweepMode = continous just show channels and Y-axis scaling
**************************************************************************************/
  if (SweepMode == continuous) {
    Serial.print("ch1:");
    Serial.print(ch1_state);
    Serial.print(" ");
    Serial.print("ch2:");
    Serial.print(ch2_state);
    Serial.print(" ");
    Serial.print("Y-axis-scaling:");
    Serial.print(pot_state);
    Serial.print(" ");
    Serial.print("base-line:");
    Serial.print(zero_line);
    Serial.print(" ");
    ChannelFloor -= ChannelHeight;
    Serial.println(debugging1);
    //delay(1000);
  }
  /**************************************************************************************
*SweepMode = triggered (looking for trigger) (show the trigger level and channels and Y-scaling)
**************************************************************************************/
  else if ((SweepMode == triggered) && (TriggerState == armed)) {
    Serial.print("ch1:");
    Serial.print(ch1_state);
    Serial.print(" ");
    Serial.print("ch2:");
    Serial.print(ch2_state);
    Serial.print(" ");
    Serial.print("Y-axis-scaling:");
    Serial.print(pot_state);
    Serial.print(" ");
    Serial.print("base-line:");
    Serial.print(zero_line);
    Serial.print(" ");
    Serial.print("trigger-level: ");
    Serial.print(TriggerVolts);
    Serial.println(debugging2);
   // delay(1000);

  } else if ((SweepMode == triggered) && (TriggerState == froze)) {
    //freeze
    Serial.println(debugging3);
    //delay(1000);
    
    //Serial.begin(9600); //kills serial monitor
  }
}

Side note: I measured my frequency to be about 100KHz (by driving a pin hi/low and scoping that). From my reading and researching it seems like this is about as fast as I will get in this application. This seems relevant because the analog read might be a limiting factor in the speed.

The eccentric capitalisation makes that hard to read.
Did you mean 100MHz (unlikely) or 100mHz (slow, but perfectly feasible)?

2 Likes

Right. Forget about interrupts, use millis() or micros() for the time base, no analogRead() inside an ISR. Like BlinkWithoutDelay and check for sample rate exceeding actual channels read time.

correction: I measured my frequency to e about 100 kHz

Hi @feltsg,
Hope so you're doing well, you need to follow these steps:

  1. Create a new file in your Arduino IDE and name it "analogRead8.h".
  2. Copy the code starting from #ifndef analogRead8_h until #endif and paste it into the "analogRead8.h" file.
  3. Save the "analogRead8.h" file in the same directory as your main sketch file.
  4. In your main sketch, add #include "analogRead8.h" at the beginning of the file to include the "analogRead8.h" code.

After incorporating the code, you can use the analogReadP function to read analog values with an 8-bit precision. Replace the existing analogRead calls in your code with analogReadP to read A0 and A1 pins inside your interrupt service routine (ISR).

Regarding your side notes:

  1. If your measured frequency is around 100mHZ, it should be within the capabilities of the Arduino. However, keep in mind that the analogRead function itself takes some time to execute, which may limit the effective sampling rate.
  2. Using floats should be fine in this application if you require decimal precision for your calculations. However, keep in mind that floating-point operations consume more memory and processing power compared to integer operations. If memory or performance becomes a concern, you might consider using fixed-point arithmetic or optimizing your code further.

Remember to test and verify the functionality of your modified sketch after making these changes.

Hope so this will work for you!

Regards,
Bryce June

1 Like

You could use the ADC in free running mode and sample the values of that process in the ISR.
Could be a little tricky for two inputs at the same time.

Thank you, Ill try this

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.