1

I am reading the period of pulses from a 600ppturn shaft encoder, using the FreqPeriod library, by Martin Nawrath.

This works using counter1 and the comparator on pin6 and pin7 on the Arduino Uno.

Counter and Capture unit

The period duration for 1-2500Hz is being used in a midi musical instrument to give a speed to volume and a note change at a fixed speed. This has worked well on the Uno but now I want to use a Mega (which does not have both comparator pins).

So I have tried using the Nick Gammon interrupt sketch. This has produced very erratic results returning both positive and many negative numbers (shown by using "volatile long period;" in the monitor.

Can you tell me what is happening and would it be better to use a counter instead of interrupts?

Hope I have given enough information to be getting on with, many thanks.

// Camera shutter speed timer
// Author: Nick Gammon
// Date: 15th January 2012
volatile boolean started;
volatile unsigned long startTime;
volatile unsigned long endTime;
volatile unsigned long period;
volatile unsigned long lfrq;
// interrupt service routine
void isr () {
 if (started)
 endTime = micros ();
 else
 startTime = micros ();
 started = !started;
} // end of Pulse Width
void setup () {
 digitalWrite (2, HIGH); // pull-up
 Serial.begin (9600);
 Serial.println (" Pulse Width test = ");
 attachInterrupt (digitalPinToInterrupt (2), isr, CHANGE); //CHANGE RISING
} // end of setup
void loop () {
 if (endTime) {
 period = (endTime - startTime) * 32 ; // pulse width?
 Serial.print (" \n ");
 Serial.print ( " period = ") ;
 Serial.print (period) ;
 lfrq = 16000000.0 / period;
 Serial.print(" Hz = "); // one rev per sec = 26666 micros period
 Serial.print( lfrq );
 endTime = 0;
 }
} // end of loop
dda
1,5951 gold badge12 silver badges17 bronze badges
asked Mar 1, 2017 at 17:24
6
  • If you simply want frequency and period, in the Interrupt Service Routine, simply count the times the ISR is called. In the loop() at regular intervals, say 10 mS, look at the count and multiply up to get the counts per second. If you use 10mS intervals, multiply by 100. That is your frequency in Hz. The inverse, i.e. divide 1 by your frequency in Hz, and that is your period. Then reset the count back to 0. Commented Mar 1, 2017 at 18:45
  • 1
    Why are you using the analog comparator for what is a digital input? An encoder provides pulses, not a range of analog values. I have a page about rotary encoders if that helps. Commented Mar 1, 2017 at 20:09
  • I am reading the period of pulses from a 600ppturn shaft encoder, using FreqPeriod Library , by Martin Nawrath because I was very new and ignorant ( but now slowly learning) to programing. Martins sketch answered my need to produce a continuous series of pulse period measurements at low Hz to compute a rising volume with turning speed and to trigger a midi note change a speed set by a pot`. This sketch is the only one I found that worked out of the box . Commented Mar 2, 2017 at 14:33
  • 1
    Hold The Pages! I have got the above Nick Gammon sketch working to producing a chain of readings. Commented Mar 2, 2017 at 18:38
  • 1
    I think the problem started when I updated my IDE from 1.6.0 to 1.8.0. I have had problems compiling . The only way I found to make some sketches compile and upload was to include a library such as <SD.h> . I since realized that "digitalWrite" may not be supported , replacing with "pinMode" and removing <SD.h> ,the sketch now works as I am sure intended. I will now incorporate this into my existing musical instrument sketch and let you know how I get on. Commented Mar 2, 2017 at 19:05

3 Answers 3

1

Can you tell me what is happening [...]?

Not really, but here is a wild guess: your input signal is bouncy, the ISR fires too often, your loop cannot keep up with the rate, and you end up accessing endTime in the ISR and loop simultaneously.

If you have a scope, look at your encoder signal. Is it clean?

would it be better to use a counter instead of interrupts?

Using the input capture feature of a timer will give you the best possible accuracy, as you will have single-cycle resolution. In contrast, micros() has only 4 μs resolution (64 times worse) and the ISR may be delayed by up to ≈ 16 μs by the TIMER0_OVF ISR.

You can use the FreqPeriod library you linked to as an inspiration for writing your own timer-based code, but it will require some changes:

  • As pointed out by Nick Gammon in a comment, you don't need the analog comparator, nor the conditioning RC network, as the encoder's outputs are already digital. Or you may use a low-pass filter if the encoder is bouncy.

  • You will use the input capture pins of either timer 4 (digital pin 49) or timer 5 (digital pin 48). The other input capture pins are not available on the Mega's pin headers.

  • You will replace ISR(ANALOG_COMP_vect) by either ISR(TIMER4_CAPT) or ISR(TIMER5_CAPT) (the relevant "capture event" ISR). No need to adjust a comparator threshold in the ISR.

  • In the begin() method, you will leave the analog comparator alone, and you will enable the capture event interrupt.

Obviously you will have to study the datasheet before start coding.

answered Mar 2, 2017 at 11:20
1
  • Hold The Pages! I have got the above Nick Gammon sketch working to producing a chain of readings. Please excuse my lack of comprehension, using this sketch will I get a reading for all pulses? Although I do not seem to get two readings using pulse edge"CHANGE" as apposed to one reading with pulse edge "RISING" why is this, am I wrong in my thinking ? Two readings per pulse period I think should be better than one? I still can not comprehend what would be better for my needs counter or interrupts. Does the Martin Nawrath sketch use interupts or counter ,or both ? Commented Mar 2, 2017 at 19:52
1

Could this be so?

because the code is faulty: there is no assurance of atomicity between startTime and endTime. ie. the interrupts could have arrived, multiple times, while the if loop is executing.

two ways to fix it:

  1. calculate period in the isr; or

  2. clear endTime once the clock starts in the isr.

answered Apr 2, 2017 at 20:08
1

While you are doing all those things with those volatile variables in the loop function, the interrupt can still be firing. If it fires in the middle of a read of startTime or endTime then you get might get part of the variable holding old value and part holding new value and when you put it together it makes garbage.

You need to do a cli() at the beginning of loop to turn off interrupts and quickly make local copies of all those variables and then sei() to turn interrupts back on. Now you can safely work with the copies you made.

answered Jun 3, 2017 at 2:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.