Timer interrupt with analogread

For a sample rate of 150 Hz (instead of 500 Hz, 2ms), change TOP to 106666 (16 MHz / 150 Hz - 1). That won't fit in a 16-bit timer so use a prescale of 8 and use 13332 (2 MHz / 150 H -1).

// Analog sampler.  Trigger analog samples at a time interval selected by Timer1

const float ReferenceVoltage = 5.0; // Volts
const int DC_OFFSET = 2.5; // Volts
const uint32_t INTERVAL_FREQUENCY = 150; // Hz
const uint32_t PRESCALE_FACTOR = 8; // Hz
const uint16_t TIMER1_TOP = ((F_CPU / PRESCALE_FACTOR) / INTERVAL_FREQUENCY) - 1;

const byte MAX_SAMPLE_COUNT = 3;
volatile byte SampleCounter = 0;
volatile uint16_t SampleBuffer[MAX_SAMPLE_COUNT];

void setup()
{
  Serial.begin(115200);
  delay(200);
  Serial.println("AnalogSampler started");

  // Set up the ADC to start a conversion when Timer1 overflows
  ADMUX = 0;
  ADCSRA = 0;
  ADCSRB = 0;
  DIDR0 = 0;

  // Select the AVCC reference and input pin A0
  ADMUX |= _BV(REFS0);

  // Set ADC clock prescale.
  // The ADC clock must be lower than 200 kHz.
#if (F_CPU > 8000000ul)
  // On a 16 MHz Arduino that means a prescale over 80.
  // The next higher available prescale is 128.
  ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // Prescale = 128 = 125 kHz
#else
  // On an 8 MHz Arduino that means a prescale over 40.
  // The next higher available prescale is 64.
  ADCSRA |= _BV(ADPS2) | _BV(ADPS1); // Prescale = 64 = 125 kHz
#endif

  // Select auto-trigger source: Begin Conversion on Timer1 Overflow
  ADCSRB |= _BV(ADTS2) | _BV(ADTS1);

  // ADC Enable, Auto-trigger enable, Clear interrupt flag, Enable interrupt
  ADCSRA |= _BV(ADEN) | _BV(ADATE) | _BV(ADIF) | _BV(ADIE);

  // Start Timer1 overflowing every INTERVAL_MICROSECONDS
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 = 0; // Disable all Timer1 interrupts

  ICR1 = TIMER1_TOP; // Set INTERVAL_MICROSECONDS

  // Set WGM 14 (0b1110): Fast PWM, TOP in ICR1, TOV1 at TOP
  TCCR1A |= _BV(WGM11);
  TCCR1B |= _BV(WGM13) | _BV(WGM12);

  TIFR1 |= _BV(TOV1);  // Clear any pending Timer1 Overflow

  // Start Timer1 with Prescale=8
  TCCR1B |= _BV(CS11);
}

// ADC Conversion Complete interrupt service routine
ISR(ADC_vect)
{
  TIFR1 |= _BV(TOV1);  // Clear the pending Timer1 Overflow Interrupt

  uint16_t val = ADC;

  if (SampleCounter < MAX_SAMPLE_COUNT)
  {
    SampleBuffer[SampleCounter++] = val;
  }
}

float AtoV(uint16_t AnalogReading)
{
  return (AnalogReading * ReferenceVoltage) / 1024.0;
}

void loop()
{
  if (SampleCounter == MAX_SAMPLE_COUNT)
  {
    // All samples have been collected.  The ISR won't be doing anything
    // until the SampleCount is reset.

    float V1 = AtoV(SampleBuffer[0]) - DC_OFFSET;
    float V2 = AtoV(SampleBuffer[1]) - DC_OFFSET;
    float V3 = AtoV(SampleBuffer[2]) - DC_OFFSET;

    float RMS = sqrt((V1 * V1 + V2 * V2 + V3 * V3) / 3);

    Serial.print(V1);
    Serial.print(", ");
    Serial.print(V2);
    Serial.print(", ");
    Serial.print(V3);
    Serial.print(", ");
    Serial.println(RMS);
    Serial.flush(); // Make sure all the character get sent

    SampleCounter = 0;
  }
}

Looking at post #4, it looks like the OP may have a method that makes 2 ms work.

I wonder if this would be sufficient:

int v1=analogRead();
delayMicroseconds(x);
int v2=analogRead();
delayMicroseconds(x);
int v3=analogRead();

doStuff();

where x = 2000 or 6667 or whatever, tweaked, if necessary for latency...e.g., minus 106 (+/-) for duration of a read...

can you read the replay in 39

Why did you open a new topic on the same subject ?

Other post/duplicate DELETED
Please do NOT cross post / duplicate as it wastes peoples time and efforts to have more than one post for a single topic.

Continued cross posting could result in a time out from the forum.

Could you also take a few moments to [url=https://forum.arduino.cc/index.php?topic=710766.0]Learn How To Use The Forum[/url].

Other general help and troubleshooting advice can be found here.
It will help you get the best out of the forum in the future.

Yes, I can and have read "Reply 39" in which you say "Ok, I want the wave from 1V and 4V, and 2.5V, there is no zero crossing"? Is there some reason for you to bring that particular reply to my attention?

From your direct message: "I want to take 3 samples from sine wave between them 2ms and display it on LCD"

The sketch to read 3 samples at 2ms intervals and display them on Serial Monitor is in Reply 52. Feel free to modify it to display on an LCD.

I want to make like this code but using timer interrupt
`
#include "LiquidCrystal.h"
#include <Wire.h>
LiquidCrystal lcd( 8, 9, 4, 5, 6, 7);
#define timeEquation currentMillis - previousMillis
float D0=0,D1=0;
int k=0,N=3,count=0;
float V[3]={0.00,0.00,0.00};
float I[3]={0.00,0.00,0.00};
float V0,V1,V2;
float I0,I1,I2;
float V2_new;
float I2_new;
float Vm;
float Im;
float Vrms;
float Irms;
unsigned long previousMillis = 0; // will store last time interval
const unsigned long interval = 2.0; // interval which the sensor will take the reading in milliseconds(change it to 2 to get reading every 2 millisecond)

void setup() {
// set the digital pin as output:
Serial.begin(9600);

lcd.begin(16, 2);
}
void loop() {
unsigned long currentMillis = millis();
D0=analogRead(A1);
D1=analogRead(A2);
if ((timeEquation>= interval) && (k<3)){
previousMillis = currentMillis; //to save the value of the last time counterd to the previous one
V[k]=((float)(1.01344*(D0))-518.88);
I[k]=((float)(0.068986*(D1))-35.321);
Serial.println(V[k]);
k++;
count++;
}
V0=(5V[0]+2V[1]-V[2])/6;
V1=(V[0]+V[1]+V[2])/3;
V2=(-V[0]+2V[1]+5V[2])/6;
I0=(5I[0]+2I[1]-I[2])/6;
I1=(I[0]+I[1]+I[2])/3;
I2=(-I[0]+2I[1]+5I[2])/6;
V2_new=(250)(3V[2]-4V[1]+V[0]);
Vm=sqrt((V2
V2)+((V2_newV2_new))/(314.159314.159));
Vrms=Vm/1.414;
/*Vm=sqrt((V[1]V[1]-V[0]V[2])/(0.580.58));
Vrms=Vm/1.414;
/

if(count>2){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Please wait...");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Taking samples");
delay(1000);
for(int z=0;z<1;z++){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("V[1]=");
lcd.print(V[0]);
lcd.print("v ");
lcd.setCursor(0,1);
/lcd.print("V[2]=");
lcd.print(V[1]);
lcd.print("v ");
/
lcd.print("I[1]=");
lcd.print(I[0]);
lcd.print("A ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("V[2]=");
lcd.print(V[1]);
lcd.print("v ");
lcd.setCursor(0,1);
lcd.print("I[2]=");
lcd.print(I[1]);
lcd.print("A ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("V[3]=");
lcd.print(V[2]);
lcd.print("v ");
lcd.setCursor(0,1);
lcd.print("I[3]=");
lcd.print(I[2]);
lcd.print("A ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Vf1=");
lcd.print(V0);
lcd.print("v ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Vf2=");
lcd.print(V1);
lcd.print("v ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Vf3=");
lcd.print(V2);
lcd.print("v ");
delay(1000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Vd=");
lcd.print(V2_new);
lcd.print("v ");
delay(1000);

   lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Vm=");
  lcd.print(Vm);
  lcd.print("v ");
  delay(1000);
   lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Vrms=");
  lcd.print(Vrms);
  lcd.print("v ");  
  delay(1000);

}
k=0;
count=0;
}

}`

Here you go.

#include "LiquidCrystal.h"
#include <Wire.h>
LiquidCrystal lcd( 8, 9, 4, 5, 6, 7);

const byte MAX_SAMPLE_COUNT = 3;
volatile byte SampleCounter = 0;
volatile float V[MAX_SAMPLE_COUNT];
volatile float I[MAX_SAMPLE_COUNT];

const uint32_t INTERVAL_FREQUENCY = 500; // Hz
const uint32_t PRESCALE_FACTOR = 1;
const uint16_t TIMER1_TOP = ((F_CPU / PRESCALE_FACTOR) / INTERVAL_FREQUENCY) - 1;

void setup()
{
  // set the digital pin as output:
  Serial.begin(9600);

  lcd.begin(16, 2);

  // Start Timer1 overflowing every INTERVAL_MICROSECONDS
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 = 0; // Disable all Timer1 interrupts

  ICR1 = TIMER1_TOP; // Set INTERVAL_MICROSECONDS

  // Set WGM 14 (0b1110): Fast PWM, TOP in ICR1, TOV1 at TOP
  TCCR1A |= _BV(WGM11);
  TCCR1B |= _BV(WGM13) | _BV(WGM12);

  TIFR1 |= _BV(TOV1);  // Clear any pending Timer1 Overflow

  TIMSK1 |= _BV(TOIE1); // Enable the Timer1 overflow interrupt

  // Start Timer1 with Prescale=8
  TCCR1B |= _BV(CS11);
}

// Interrupt every 2000 microseconds (500 Hz)
ISR(TIMER1_OVF_vect)
{
  if (SampleCounter < MAX_SAMPLE_COUNT)
  {
    float D0 = analogRead(A1);
    float D1 = analogRead(A2);
    V[SampleCounter] = ((float)(1.01344 * (D0)) - 518.88);
    I[SampleCounter] = ((float)(0.068986 * (D1)) - 35.321);
  }
}

void loop()
{
  if (SampleCounter < MAX_SAMPLE_COUNT)
    return;

  float V0, V1, V2;
  float I0, I1, I2;
  float V2_new;
  float Vm;
  float Vrms;

  V0 = (5 * V[0] + 2 * V[1] - V[2]) / 6;
  V1 = (V[0] + V[1] + V[2]) / 3;
  V2 = (-V[0] + 2 * V[1] + 5 * V[2]) / 6;

  I0 = (5 * I[0] + 2 * I[1] - I[2]) / 6;
  I1 = (I[0] + I[1] + I[2]) / 3;
  I2 = (-I[0] + 2 * I[1] + 5 * I[2]) / 6;

  V2_new = (250) * (3 * V[2] - 4 * V[1] + V[0]);
  Vm = sqrt((V2 * V2) + ((V2_new * V2_new)) / (314.159 * 314.159));
  Vrms = Vm / 1.414;
  //  Vm = sqrt((V[1] * V[1] - V[0] * V[2]) / (0.58 * 0.58));
  //  Vrms = Vm / 1.414;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Please wait...");
  delay(1000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Taking samples");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("V[1]=");
  lcd.print(V[0]);
  lcd.print("v ");
  lcd.setCursor(0, 1);
  //      lcd.print("V[2]=");
  //      lcd.print(V[1]);
  //      lcd.print("v ");
  lcd.print("I[1]=");
  lcd.print(I[0]);
  lcd.print("A ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("V[2]=");
  lcd.print(V[1]);
  lcd.print("v ");
  lcd.setCursor(0, 1);
  lcd.print("I[2]=");
  lcd.print(I[1]);
  lcd.print("A ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("V[3]=");
  lcd.print(V[2]);
  lcd.print("v ");
  lcd.setCursor(0, 1);
  lcd.print("I[3]=");
  lcd.print(I[2]);
  lcd.print("A ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vf1=");
  lcd.print(V0);
  lcd.print("v ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vf2=");
  lcd.print(V1);
  lcd.print("v ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vf3=");
  lcd.print(V2);
  lcd.print("v ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vd=");
  lcd.print(V2_new);
  lcd.print("v ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vm=");
  lcd.print(Vm);
  lcd.print("v ");
  delay(1000);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Vrms=");
  lcd.print(Vrms);
  lcd.print("v ");
  delay(1000);

  // Start sampling again.
  SampleCounter = 0;
}

the system didn't give a correct readings

One thing is that if you set variables in an interrupt service routine and read them (asynchronously) in the loop(), you should copy the variables in a code section where interrupt are suspended. Keyword: atomicity. This avoids reading a part updated variable. On an 8 bit machine, you have to do this with all variables larger than a byte so would apply to these:

volatile float V[MAX_SAMPLE_COUNT];
volatile float I[MAX_SAMPLE_COUNT];

However, judging by your superficial description of the error, I suspect your problems lie deeper.

Start with a much simpler task which still uses a timer driven interrupt to say measure the amplitude of that signal and print that to the display somehow. Progress from there.

What system?

The ISR doesn't do anything unless 'SampleCounter' is less than 'MAX_SAMPLE_COUNT'. The loop() function doesn't do anything if 'SampleCounter' is less than 'MAX_SAMPLE_COUNT'. The loop() doesn't look at the data until the ISR is done filling the arrays.

1 Like

Yes. You are of course correct. I didn't look for any application interlocks which would prevent an "unsafe" read operation, just which variables were being modified and where.

edit
Having said that, this expression in the loop() is at risk if SampleCounter is updated during a read operation, although the consequences of such a failure may be limited:

It's a good thing that SampleCounter is a byte and therefore the read is atomic.

volatile byte SampleCounter = 0;

1 Like

So you need to debug the problem. If you put a DC voltage on your inputs, do you get the expected answers? Try hooking the inputs to the 3.3V pin. That should get you an analog input of about 676.

V = ((float)(1.01344 * (D0)) - 518.88) = 685 - 518.88 = 166.2

V0 = (5 * V + 2 * V - V) / 6 = 6 * V / 6 = V = 166.2
V1 = (V+V+V)/3 = V = 166.2
V2 = (-V + 2 * V + 5 * V) / 6 = 6 * V / 6 = V = 166.2

V2_new = 250 * (3 * V - 4 * V + V) = 250 * 0 = 0

 Vm = sqrt((V * V) + ((0 * 0)) / (314.159 * 314.159)) =
sqrt(27622.44 / 98695.877281) = sqrt(0.279874304388169) = 0.529

Vrms = Vm / 1.414 = 0.529/1.414 = 0.374

When you connect A1 to the 3.3V pin do you get an answer of Vrms = 0.374? If not, your sketch is going wrong, possibly an overflow somewhere. If so, your sketch is working as designed. If that is not the correct value then your formulas are incorrect.

I want the code of timer interrupt using CTC every 2 ms

You have already been given code which directly uses timer registers and also a troubleshooting guide for that code. Was all that too complex for you ?

If you want a very simple example of TimerOne.h which you can configure and add your own code to, you find one here: Arduino Playground - Timer1 . It flashes a led. Delete the line with the pwm configuration which is not relevant to you.

Since this is clearly a school exercise you should expect only limited help. You must demonstrate what you have tried, what the results actually were and what results you expected. Use code tags if posting any code here.

Use the code in Reply 26 as a starting point. Enable the overflow interrupt. In the overflow ISR, change
TCNT1 = 33536;
to
TCNT1 -= 32000;
This will compensate for interrupt latency. You may need to adjust the count for instruction latency: the time it takes to read, subtract, and write the 16-bit register.

You have 2.5V, 3.5V, and 1.5V points. See Fig-1 of Post-38.

The positive zero crossing point of the uni-polar sine signal could be detected by the following circuit (Fig-1); the output (GND going) of Q1 can be used to interrupt the MCU to mark the beginning time for digitization of the biased sine signal.

zeroCrossX
Figure-1:

Sketch (not tested)
Note: With the help of oscilloscope, you need to check the phase difference between signal at INT0 and A0 to see the time-offset and accordingly accommodate that in the sketch. I assume no phase lag (in reality, there is phase lag due to B-E cut-in voltage of Q1).

volatile bool flag = false;

void setup()
{
    Serial.begin(9600);
    analogReference(DEFAULT);  //Vef = 5V
    attachInterrupt(digitalPinToInterrupt(2), ISRINTZ, FALLING);
}

void loop()
{
    if(flag == true)
    {
          unsigned int s25 = analogRead(A0);   //sample at 2.5V
          Serial.println(s25, DEC);    //theoretically 512? Practically <512
          delay(5);
          unsigned int s35 = analogRead(A0);   //sample at 3.5V
          Serial.println(s35, DEC);   //theoretically 716? Practically < 716
          delay(15);
          unsigned int s15 = analogRead(A0);    //sample at 1.5V    
          Serial.println(s15, DEC);        //theoretically 307? Practicall < 307
          Serial.println("==================================");
          //---------------------------------------------------------------------------------
          delay (5000);    //test interval
          flag = false
    }
}

void ISRINTZ()
{
    flag = true;
}

Check if you are getting consistent and meaningful readings on the Serial Monitor. At every 5-sec interval, you should see closely-matched readings duplications.

it is normal mode not CTC

Sorry. Set WGM12 to get WGM 4 (CTC). Set OCR1A to 3199 to get 500 Hz. Turn on the OC1A interrupt and use that interrupt for your "every 2 milliseconds" interrupt.