External Interrupts, Analog compare and Sine Wave measurement

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have two programs one is Nick Gammons frequency counter using  D5 as the interrupt to Timer1, and the other is an analog comparator using the ISR to count the 'ticks' into Timer Counter 1

 

I am trying to accurate counts in the 4 to 20 HZ Sine Wave range with a quick update for a changing frequency

 

I have a signal generator connected with a 5V P-P wave and a 2.5V offset to test with.

 

Nicks program measures down to these low sine wave values but due to having a fixed sample period (say 500mS) then the answers for odd harmonics will oscillate between the one up and one down even value. This is sort of understandable in that the sample  numbers of the waveforms is fractional for odd frequencies as can be seen by the following, but in the  sampling period at least two complete waveforms should have been detected from 5 Hz up

 

     

Sample Period(mS)

500

 

Hz

mS

No

4

250.0

2

5

200.0

2.5

6

166.7

3

7

142.9

3.5

8

125.0

4

9

111.1

4.5

 

 

The second program uses analog compare with either a set trigger level on AIN1= 2.5 or using the 1.1V internal reference on AIN0

The example below measures over 5 cycles (1 second at 4 hz) with is too slow for my needs

 

The program gives better resolution at lower frequencies but only on SQUARE waves and rubbish !!!!!! on sine waves, which is breaking my heart

 

So there are some questions here:

a) Why does Nick's program oscillate at the low values considering it should be seeing complete waveforms in the 500mS fixed sample period?

b) What is the internal hardware difference between D5 to the analog comparator on AIN0/AIN1 that makes D5 work better? 

c) Why would the analog comparator work fine on low Hz square waves but not in sine?

d) Normally the advise is that D5 should vary between 0 to 5V, but what is its actual trigger level on D5 as I have had success with lower amplitude signals but cant capture the switch band?

 

Enclosed is Nick' program

// Timer and Counter example  ATMEGA 328
// Author: Nick Gammon
// Date: 17th January 2012

// Input: Pin D5

// these are checked for in the main program
volatile unsigned long timerCounts;
volatile boolean counterReady;

// internal to counting routine
unsigned long overflowCount;
unsigned int timerTicks;
unsigned long timerPeriod;

void startCounting (unsigned int ms) 
  {
  counterReady = false;         // time not up yet
  timerPeriod = ms;             // how many 1 ms counts to do
  timerTicks = 0;               // reset interrupt counter
  overflowCount = 0;            // no overflows yet

  // reset Timer 1 and Timer 2
  TCCR1A = 0;             
  TCCR1B = 0;              
  TCCR2A = 0;
  TCCR2B = 0;

  // Timer 1 - counts events on pin D5
  TIMSK1 = bit (TOIE1);   // interrupt on Timer 1 overflow

  // Timer 2 - gives us our 1 ms counting interval
  // 16 MHz clock (62.5 ns per tick) - prescaled by 128
  //  counter increments every 8 µs. 
  // So we count 125 of them, giving exactly 1000 µs (1 ms)
  TCCR2A = bit (WGM21) ;   // CTC mode
  OCR2A  = 124;            // count up to 125  (zero relative!!!!)

  // Timer 2 - interrupt on match (ie. every 1 ms)
  TIMSK2 = bit (OCIE2A);   // enable Timer2 Interrupt

  TCNT1 = 0;      // Both counters to zero
  TCNT2 = 0;     

  // Reset prescalers
  GTCCR = bit (PSRASY);        // reset prescaler now
  TCCR2B =  bit (CS20) | bit (CS22) ;  // prescaler of 128
  TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12); // External clock source on T1 pin (D5). Clock on rising edge.
  //TCCR1B =  bit (CS11) | bit (CS12);              // External clock source on T1 pin (D5). Clock on falling edge.
  }  // end of startCounting

ISR (TIMER1_OVF_vect)
  {
  ++overflowCount;               // count number of Counter1 overflows  
  }  // end of TIMER1_OVF_vect


//******************************************************************
//  Timer2 Interrupt Service is invoked by hardware Timer 2 every 1 ms = 1000 Hz
//  16Mhz / 128 / 125 = 1000 Hz

ISR (TIMER2_COMPA_vect) 
  {
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = TCNT1;  // see datasheet, page 117 (accessing 16-bit registers)
  unsigned long overflowCopy = overflowCount;

  // see if we have reached timing period
  if (++timerTicks < timerPeriod) 
    return;  // not yet

  // if just missed an overflow
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256)
    overflowCopy++;

  // end of gate time, measurement ready

  TCCR1A = 0;    // stop timer 1
  TCCR1B = 0;    

  TCCR2A = 0;    // stop timer 2
  TCCR2B = 0;    

  TIMSK1 = 0;    // disable Timer1 Interrupt
  TIMSK2 = 0;    // disable Timer2 Interrupt
    
  // calculate total count
  timerCounts = (overflowCopy << 16) + timer1CounterValue;  // each overflow is 65536 more
  counterReady = true;              // set global flag for end count period
  }  // end of TIMER2_COMPA_vect

void setup () 
  {
  Serial.begin(57600);       
  Serial.println("Frequency Counter");
  } // end of setup

void loop () 
  {
  // stop Timer 0 interrupts from throwing the count out
  byte oldTCCR0A = TCCR0A;
  byte oldTCCR0B = TCCR0B;
  TCCR0A = 0;    // stop timer 0
  TCCR0B = 0;    
  
  startCounting (500);  // how many ms to count for

  while (!counterReady) 
     { }  // loop until count over

  // adjust counts by counting interval to give frequency in Hz
  float frq = (timerCounts *  1000.0) / timerPeriod;

  Serial.print ("Frequency: ");
  Serial.print ((unsigned long) frq);
  Serial.println (" Hz.");
  
  // restart timer 0
  TCCR0A = oldTCCR0A;
  TCCR0B = oldTCCR0B;
  
  // let serial stuff finish
//  delay(200);
  }   // end of loop

 Enclosed is my analog comparator program

/* Yet another Analog Comp frequency Counter
   Rod McMahon 
   1 Nov 2017    

Connection option 1
 - Signal generator connected D7(AIN1) and GND  D6(AIN0) = 1.1v by internal reference 
Connection option 2
 - Signal generator connected D6(AIN0) and GND D7(AIN1 connected to external trigger voltage 
*/ 

#define F_CPU 16000000UL              //16 MHZ Arduino Nano
volatile int overflows = 0;           // Overflow counter on TimerCounter No 1
volatile int trigger = 0;             // counts triggers
volatile float tstart = 0;             // time at start of count
volatile float tstop = 0;              // time at stop of count
volatile int cycles = 5;              // number of cycles sampled
//float frequency = 0;
int SampleFinish=0;



 /*
 Analog Interrupt ACSR options 
 ACSR|=(1<<ACD);         // Bit 7 - 1 stops comparator 
 ACSR|=(1<<ACBG);        // Bit 6 - 1 Enables fixed bandwith on AO (1.1 V)
 ACSR  (ACO)             // Bit 5 - 1 if comparator fires
 ACSR|=(1<<ACI);         // Bit 4 - 1 = Set by hardware when comparator event triggers the interrupt mode
 ACSR|=(1<<ACIE);        // Bit 3 - 1= Activates interrupt
 ACSR|=(1<<ACIC);        // Bit 2 - 1= triggers input function of Timer/Counter 1  
 ACSR|=(1<<ACIS1);       // Rising Edge           
 ACSR|=(1<<ACIS0); 
  0 0  Comparator Interrupt on output toggle
  1 0   Falling Edge
  1 1   Rising Edge
*/
void setAnalogComp()
 {
 DIDR1 = (1<<AIN1D) | (1<<AIN0D);      // disable digital inputs
 ADCSRB=0;               
 ACSR=0;
// ACSR|=(1<<ACBG);        // Bit 6 - 1 = Enables fixed bandwith on AO (1.1 V) ( Option 1)
 ACSR|=(1<<ACIE);        // Bit 3 1= Activates interrupt
 ACSR|=(1<<ACIC);        // Bit 2 1= triggers input function of Timer/Counter 1 
 ACSR|=(1<<ACIS1);       // Bit1 and Bit 0 Rising Edge 
 ACSR|=(1<<ACIS0);  
 }


void setTimerCounter1()
{   
 TIMSK1|=(1<<TOIE1);          // enable overflow interrupt
 TCCR1A=0;                    // Set Timer/Counter 1 in normal mode where it will count to 0xFFFF then repeat.
 TCCR1B=0;                    // initiate
 TCCR1B|=(1<<CS10);           // No prescaler.
}

void setup(void) 
  {
  setTimerCounter1();
  setAnalogComp();
  sei();
  Serial.begin(57600);       // For printing the frequency to the terminal
  Serial.println("Started");
  }

void loop(void) 
  {
  if (SampleFinish==1)                  // total cycle interrupts counted
    {
  int  frequency = F_CPU*cycles/(tstop - tstart)+1; 
    Serial.print("Frequency  :");Serial.println(frequency); 
    delay(200);
    overflows=0;
    trigger=0;
    SampleFinish=0;
    TCNT1=0;
    }
  }

ISR(TIMER1_OVF_vect)
{
overflows += 1;
}

ISR(ANALOG_COMP_vect)
  {
  unsigned long tnow = overflows*65535.0 + TCNT1; // TimerCounter counter value (16 bit=65535)
  if (SampleFinish==0) 
    {
    trigger += 1;
    if (trigger == 1)           
      tstart = tnow;            // set start time
    if (trigger> cycles)  
     {
      tstop = tnow;             // set finish time
      SampleFinish=1;
     } 
   }
  }

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
volatile uint32_t overflows = 0;           // Overflow counter on TimerCounter No 1
volatile uint16_t trigger = 0;             // counts triggers
volatile uint32_t tstart = 0;             // time at start of count
volatile uint32_t tstop = 0;              // time at stop of count
volatile int cycles = 5;              // number of cycles sampled
//float frequency = 0;
volatile uint8_t SampleFinish=0;



 unsigned long tnow = overflows*65535.0 + TCNT1; // TimerCounter counter value (16 bit=65535)
 
 uint32_t tnow = (uint32_t)overflows * 65535UL + (uint32_t)TCNT1;
 
 //but then you copy this value to a float!

 

Use unsigned if you don't expect negative values. Chose your variable sizes carefully - the AVR is an 8 bit micro, so 8bit vars are preferred.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

16 bits is 65536 not 65535. Which means you can speed up the ISR hugely.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Multiplication by 65536  would be replaced by "shifts" (Ram copies)? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all for that. One day I will figure units out! Changed the program and tested. Used float for calcs to get some decimal places

Square waves great , sine awful

 

For a 5.73 Hz sine signal 5VPP 2.5V offset and trigger voltage of 2.5V

these are the measured values

7.16

8.14

5.73

7.16

11.37

7.16

5.73

7.16

7.16,

5.73

7.16

7.16

5.73

7.16

9.55

 

So to the original questions?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rodmcm wrote:

...these are the measured values

7.16

8.14

5.73

...

 

Which version of the code is giving these?

 

Also, you are not initialising your peripherals from scratch, ie too many '|=' in there. As you are running inside the Arduino framework you are starting out with their peripheral initialisation and adding to them, not taking control of them and making them your own.

 

I'd start by reading up on how to do 'raw' C code, even if you use the Arduino IDE to write it, and take full control of the AVR. Ditch 'setup()' and 'loop()' and just have your own 'while(1)' in there. Arduino is great for getting something up and running but too much goes on hidden away for serious work.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rodmcm wrote:

a) Why does Nick's program oscillate at the low values considering it should be seeing complete waveforms in the 500mS fixed sample period?

 

Who knows. Unfortunately both programs above demonstrate the classic error people make when commenting code. They tell us WHAT a line of code does and not WHY.

 

For example...

tstart = tnow;            // set start time

 

What does the comment tell us that we cannot see directly from the code? We can see a variable called 't(ime)start' being set to the value of 't(ime)now'. Which is what the comment says. But WHY is 'tstart' being set to 'tnow'?

 

And this...

 

ISR (TIMER1_OVF_vect)
  {
  ++overflowCount;               // count number of Counter1 overflows  
  }  // end of TIMER1_OVF_vect

 

What is Timer 1 doing? What is clocking it? Why are we using it? The only two comments attached to this bit of code are totally redundant.

 

 

Rodmcm wrote:

b) What is the internal hardware difference between D5 to the analog comparator on AIN0/AIN1 that makes D5 work better? 

 

Luck. Although I suspect there is something wrong with how you are using the comparator.

 

 

Rodmcm wrote:

c) Why would the analog comparator work fine on low Hz square waves but not in sine?

 

How 'pure' is your sine wave? Is it monotonic?

What hysteresis does the comparator have?

Does the comparator have a minimum slew rate it will work with?

 

 

Rodmcm wrote:

d) Normally the advise is that D5 should vary between 0 to 5V, but what is its actual trigger level on D5 as I have had success with lower amplitude signals but cant capture the switch band?

 

Digital inputs do not have a 'trigger level' They have a voltage below which they will be seen as a '0' and a voltage above which they will be seen as a '1'. Between those two figures they are free to do whatever they like (and often do).

 

Table 34.2 in the datasheet tells us that a low is any voltage below 0.3Vcc, so 1.5V in your case; and a high is anything above 0.6Vcc, so 3V in your case. Between those two voltages you can assume nothing.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry Brian but i don't know how to do cut and paste of your comment to answer to, however

 

a) Program: The readings are from my analog comp program

 

b) Raw Code: I am not a trained programmer hence the easier Arduino framework. I would be interested in sighting your rendition of my program in "raw code" to learn and compare speed and performance

 

c) Comments: I thought that i did quite a good job of commenting, while the (1<<ACGB) statements seem clumsy they are certainly easier to read and adjust than most of the alternatives. As to the other variables I thought that anyone with knowledge of use of timers for period measurement would easily see the intent without detailed comment. As to Nick' program he can respond to your criticisms directly if he wishes

 

d)  Comparator: I have sought out as many examples of analog comparator use that i could find, and tested the ACSR options. I usually exhaust all options before I ask for help, but as i work alone it is easy to miss things and then ask "should have seen" dumb questions. If you can see the error please advise

 

e) Sine Wave.. I use a commercial sig gen along with a Rigol scope. The waveform is clean, and i have a small capacitor on the input to reduce noise, but not a low pass filter ( next experiment)

 

f) Comparator Hysteresis Slew rate et al: This is exactly the information I am asking about the built in comparator in the Atmel chips. I cannot find any literature its characteristics other than statements about how it works and the options ( there are really only two)  I had hoped someone with detailed knowledge of the inner workings of the comparator could answer this. 

 

g) Digital. Fair call, but certainly not what i have seen under experiment. The reason I am interested is my project wave is not a pure sine wave and will oscillate between 2.5V to 1V. If I cannot get the analog comparator option to work then I will have to put in an externals Schmidt trigger or the like for input to D5, which I am loath to do when there is a built in comparator

 

h) Further Information

 

I have found that if I just use the measurement of one cycle (analog compare and external trigger voltage) then the answers are repeatable and accurate to two decimal places at the lower sine wave levels

 

So why would the multiple cycle accuracy be wrong as shown above.. 4 Hz is 250mS so only 1/4 of F_CPU to count per cycle.

And what is really going on in the analog comparator?

 

 

 

 

 

 

 

 


 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I’d add code to output the comparator state to a port pin and scope it. If there’s oscillation, then hopefully it will be visible.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rodmcm wrote:
i don't know how to do cut and paste of your comment to answer

Just press the 'Quote' button - it will give you the whole post enclosed between 'quote=<author>' and '/quote' tags 

 

The tags are just plain text - you can manually add more yourself, or just use the   'Block Quote' button on the toolbar...

 

EDIT

 

FIX quote

 

EDIT 2

 

When I say,

between 'quote=<author>' and '/quote' tags

those single-quote will actually be square brackets. 

 

Just try the button - you'll see what I mean.

 

Or you can just use the standard copy & paste facilities for your platform, with the 'Block Quote' button ...

 

#HowToQuote

 

Last Edited: Fri. Nov 17, 2017 - 10:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for that