Go Down

Topic: Fast Low frequency measurement with Due (Read 7157 times) previous topic - next topic

pcyossi

Hello,
I need help with a fast response rate of pick to pick time measurement. I need this to work at low frequencies of 1- 200 Hz.
Unfortunately I am not a programmer but I know the basics and I worked with Arduino for a few months now, I can control the wave to be sinus or square.

Can anybody help?

Thanks,
Yossi

DrDiettrich

Do you mean peak-to-peak measurement?

If so, you do not necessarily have to detect peaks, instead it's sufficient to trigger the time measurement for a transition across any point (voltage) of a wave of repetitive signal of constant amplitude. You can use the analog comparator, or even a digitial input pin may be sufficient to recognize such transitions.

A square wave signal can be connected immediately to an digital pin, provided only that it spans almost the entire range between high and low level (at least 0.3 to 0.7*Vcc). Then you can measure the time between transitions in the pin-change interrupt or one of the external interrupts handler.

pcyossi

Hi Thanks,

Sound like a good idea. Thing is I am not sure how to implement this that it will be fast and accurate,

Do you know where I can find a similar sample code I can modify to do this?

Thanks,
Yossi

DrDiettrich

Search for examples on using interrupt handlers, e.g. in the Reference "attachInterrupt()", more in Learning: Examples.


pcyossi

https://www.pjrc.com/teensy/td_libs_FreqMeasure.html
I tried this lib but I keep getin this error
"
Arduino: 1.6.1 (Windows 7), Board: "Arduino Due (Programming Port)"

FreqMeasure.cpp:27:37: fatal error: util/FreqMeasureCapture.h: No such file or directory

 #include "util/FreqMeasureCapture.h"

                                     ^

compilation terminated.

Error compiling.
"
Can you tell me what am I doing wrong?
Thanks,
Yossi

pcyossi

#6
Mar 23, 2015, 09:18 am Last Edit: Mar 23, 2015, 09:22 am by pcyossi
I found the problem but run into a new one.

The https://www.pjrc.com/teensy/td_libs_FreqMeasure.html project was not designed for the Due board and I get errors that the timer + counter destination has to be modified in the FreqMeasureCapture.h file.

Code: [Select]

#ifndef FreqMeasure_capture_h_
#define FreqMeasure_capture_h_

// Arduino Uno, Duemilanove, LilyPad, Mini, Fio, etc
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
  #define CAPTURE_USE_TIMER1       // ICP1 is pin 8

// Arduino Mega
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // #define CAPTURE_USE_TIMER1    // ICP1 is not connected
  // #define CAPTURE_USE_TIMER3    // ICP3 is not connected
  #define CAPTURE_USE_TIMER4       // ICP4 is pin 49
  // #define CAPTURE_USE_TIMER5    // ICP5 is pin 48

#else
  #error "Unknown chip, please edit me with timer+counter definitions"

#endif

#if defined(CAPTURE_USE_FTM1_CH0)

#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm1_isr

static inline void capture_init(void)
{
 if (FTM1_MOD != 0xFFFF || (FTM1_SC & 0x7F) != FTM_SC_VALUE) {
 FTM1_SC = 0;
 FTM1_CNT = 0;
 FTM1_MOD = 0xFFFF;
 FTM1_SC = FTM_SC_VALUE;
 FTM1_MODE = 0;
 }
 NVIC_SET_PRIORITY(IRQ_FTM1, 48);
}
static inline void capture_start(void)
{
 FTM1_C0SC = 0b01000100;
 CORE_PIN3_CONFIG = PORT_PCR_MUX(3);
 NVIC_ENABLE_IRQ(IRQ_FTM1);
}
static inline uint16_t capture_event(void)
{
 return (FTM1_C0SC & 0x80) ? 1 : 0;
}
static inline uint32_t capture_read(void)
{
 uint32_t val = FTM1_C0V;
 FTM1_C0SC = 0b01000100;
 return val;
}
static inline uint8_t capture_overflow(void)
{
 return (FTM1_SC & FTM_SC_TOF) ? 1 : 0;
}
static inline void capture_overflow_reset(void)
{
 FTM1_SC = FTM_SC_VALUE;
}
static inline void capture_shutdown(void)
{
 FTM1_C0SC = 0;
 CORE_PIN3_CONFIG = 0;
 NVIC_DISABLE_IRQ(IRQ_FTM1);
}

#elif defined(CAPTURE_USE_FTM1_CH1)

#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm1_isr

static inline void capture_init(void)
{
 if (FTM1_MOD != 0xFFFF || (FTM1_SC & 0x7F) != FTM_SC_VALUE) {
 FTM1_SC = 0;
 FTM1_CNT = 0;
 FTM1_MOD = 0xFFFF;
 FTM1_SC = FTM_SC_VALUE;
 FTM1_MODE = 0;
 }
 NVIC_SET_PRIORITY(IRQ_FTM1, 48);
}
static inline void capture_start(void)
{
 FTM1_C1SC = 0b01000100;
 CORE_PIN3_CONFIG = PORT_PCR_MUX(3);
 NVIC_ENABLE_IRQ(IRQ_FTM1);
}
static inline uint16_t capture_event(void)
{
 return (FTM1_C1SC & 0x80) ? 1 : 0;
}
static inline uint32_t capture_read(void)
{
 uint32_t val = FTM1_C1V;
 FTM1_C1SC = 0b01000100;
 return val;
}
static inline uint8_t capture_overflow(void)
{
 return (FTM1_SC & FTM_SC_TOF) ? 1 : 0;
}
static inline void capture_overflow_reset(void)
{
 FTM1_SC = FTM_SC_VALUE;
}
static inline void capture_shutdown(void)
{
 FTM1_C1SC = 0;
 CORE_PIN3_CONFIG = 0;
 NVIC_DISABLE_IRQ(IRQ_FTM1);
}


#elif defined(CAPTURE_USE_FTM2_CH0)

#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm2_isr

static inline void capture_init(void)
{
 if (FTM2_MOD != 0xFFFF || (FTM2_SC & 0x7F) != FTM_SC_VALUE) {
 FTM2_SC = 0;
 FTM2_CNT = 0;
 FTM2_MOD = 0xFFFF;
 FTM2_SC = FTM_SC_VALUE;
 FTM2_MODE = 0;
 }
 NVIC_SET_PRIORITY(IRQ_FTM2, 48);
}
static inline void capture_start(void)
{
 FTM2_C0SC = 0b01000100;
 CORE_PIN3_CONFIG = PORT_PCR_MUX(3);
 NVIC_ENABLE_IRQ(IRQ_FTM2);
}
static inline uint16_t capture_event(void)
{
 return (FTM2_C0SC & 0x80) ? 1 : 0;
}
static inline uint32_t capture_read(void)
{
 uint32_t val = FTM2_C0V;
 FTM2_C0SC = 0b01000100;
 return val;
}
static inline uint8_t capture_overflow(void)
{
 return (FTM2_SC & FTM_SC_TOF) ? 1 : 0;
}
static inline void capture_overflow_reset(void)
{
 FTM2_SC = FTM_SC_VALUE;
}
static inline void capture_shutdown(void)
{
 FTM2_C0SC = 0;
 CORE_PIN3_CONFIG = 0;
 NVIC_DISABLE_IRQ(IRQ_FTM2);
}


#elif defined(CAPTURE_USE_FTM2_CH1)

#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm2_isr

static inline void capture_init(void)
{
 if (FTM2_MOD != 0xFFFF || (FTM2_SC & 0x7F) != FTM_SC_VALUE) {
 FTM2_SC = 0;
 FTM2_CNT = 0;
 FTM2_MOD = 0xFFFF;
 FTM2_SC = FTM_SC_VALUE;
 FTM2_MODE = 0;
 }
 NVIC_SET_PRIORITY(IRQ_FTM2, 48);
}
static inline void capture_start(void)
{
 FTM2_C1SC = 0b01000100;
 CORE_PIN3_CONFIG = PORT_PCR_MUX(3);
 NVIC_ENABLE_IRQ(IRQ_FTM2);
}
static inline uint16_t capture_event(void)
{
 return (FTM2_C1SC & 0x80) ? 1 : 0;
}
static inline uint32_t capture_read(void)
{
 uint32_t val = FTM2_C1V;
 FTM2_C1SC = 0b01000100;
 return val;
}
static inline uint8_t capture_overflow(void)
{
 return (FTM2_SC & FTM_SC_TOF) ? 1 : 0;
}
static inline void capture_overflow_reset(void)
{
 FTM2_SC = FTM_SC_VALUE;
}
static inline void capture_shutdown(void)
{
 FTM2_C1SC = 0;
 CORE_PIN3_CONFIG = 0;
 NVIC_DISABLE_IRQ(IRQ_FTM2);
}


#elif defined(CAPTURE_USE_TIMER1)

static uint8_t saveTCCR1A, saveTCCR1B;

static inline void capture_init(void)
{
 saveTCCR1A = TCCR1A;
 saveTCCR1B = TCCR1B;
 TCCR1B = 0;
 TCCR1A = 0;
 TCNT1 = 0;
 TIFR1 = (1<<ICF1) | (1<<TOV1);
 TIMSK1 = (1<<ICIE1) | (1<<TOIE1);
}
static inline void capture_start(void)
{
 TCCR1B = (1<<ICNC1) | (1<<ICES1) | (1<<CS10);
}
static inline uint16_t capture_read(void)
{
 return ICR1;
}
static inline uint8_t capture_overflow(void)
{
 return TIFR1 & (1<<TOV1);
}
static inline void capture_overflow_reset(void)
{
 TIFR1 = (1<<TOV1);
}
static inline void capture_shutdown(void)
{
 TCCR1B = 0;
 TIMSK1 = 0;
 TCCR1A = saveTCCR1A;
 TCCR1B = saveTCCR1B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER1_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER1_CAPT_vect


#elif defined(CAPTURE_USE_TIMER3)

static uint8_t saveTCCR3A, saveTCCR3B;

static inline void capture_init(void)
{
 saveTCCR3A = TCCR3A;
 saveTCCR3B = TCCR3B;
 TCCR3B = 0;
 TCCR3A = 0;
 TCNT3 = 0;
 TIFR3 = (1<<ICF3) | (1<<TOV3);
 TIMSK3 = (1<<ICIE3) | (1<<TOIE3);
}
static inline void capture_start(void)
{
 TCCR3B = (1<<ICNC3) | (1<<ICES3) | (1<<CS30);
}
static inline uint16_t capture_read(void)
{
 return ICR3;
}
static inline uint8_t capture_overflow(void)
{
 return TIFR3 & (1<<TOV3);
}
static inline void capture_overflow_reset(void)
{
 TIFR3 = (1<<TOV3);
}
static inline void capture_shutdown(void)
{
 TCCR3B = 0;
 TIMSK3 = 0;
 TCCR3A = saveTCCR3A;
 TCCR3B = saveTCCR3B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER3_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER3_CAPT_vect



#elif defined(CAPTURE_USE_TIMER4)

static uint8_t saveTCCR4A, saveTCCR4B;

static inline void capture_init(void)
{
 saveTCCR4A = TCCR4A;
 saveTCCR4B = TCCR4B;
 TCCR4B = 0;
 TCCR4A = 0;
 TCNT4 = 0;
 TIFR4 = (1<<ICF4) | (1<<TOV4);
 TIMSK4 = (1<<ICIE4) | (1<<TOIE4);
}
static inline void capture_start(void)
{
 TCCR4B = (1<<ICNC4) | (1<<ICES4) | (1<<CS40);
}
static inline uint16_t capture_read(void)
{
 return ICR4;
}
static inline uint8_t capture_overflow(void)
{
 return TIFR4 & (1<<TOV4);
}
static inline void capture_overflow_reset(void)
{
 TIFR4 = (1<<TOV4);
}
static inline void capture_shutdown(void)
{
 TCCR4B = 0;
 TIMSK4 = 0;
 TCCR4A = saveTCCR4A;
 TCCR4B = saveTCCR4B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER4_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER4_CAPT_vect

#elif defined(CAPTURE_USE_TIMER5)

static uint8_t saveTCCR5A, saveTCCR5B;

static inline void capture_init(void)
{
 saveTCCR5A = TCCR5A;
 saveTCCR5B = TCCR5B;
 TCCR5B = 0;
 TCCR5A = 0;
 TCNT5 = 0;
 TIFR5 = (1<<ICF5) | (1<<TOV5);
 TIMSK5 = (1<<ICIE5) | (1<<TOIE5);
}
static inline void capture_start(void)
{
 TCCR5B = (1<<ICNC5) | (1<<ICES5) | (1<<CS50);
}
static inline uint16_t capture_read(void)
{
 return ICR5;
}
static inline uint8_t capture_overflow(void)
{
 return TIFR5 & (1<<TOV5);
}
static inline void capture_overflow_reset(void)
{
 TIFR5 = (1<<TOV5);
}
static inline void capture_shutdown(void)
{
 TCCR5B = 0;
 TIMSK5 = 0;
 TCCR5A = saveTCCR5A;
 TCCR5B = saveTCCR5B;
}

#define TIMER_OVERFLOW_VECTOR  TIMER5_OVF_vect
#define TIMER_CAPTURE_VECTOR   TIMER5_CAPT_vect


#endif // CAPTURE_USE_***


#endif


Can you tell me what I need to change in order to make this work with the Due?

Thank You,
Yossi

MarkT

The Due is an entirely different architecture from the ATmega series, any low
level code talking to hardware registers will not be at all compatible.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

pcyossi

Thanks Mark,

Do you have any idea were can I fines sample code for frequency measurement for the Due?

BlackT

#9
Jan 18, 2016, 03:32 pm Last Edit: Jan 18, 2016, 03:41 pm by BlackT
Hello, does anybody solve this problem. I need to count low freq 0-200hz, accuracy about 1 Hz. square wave (logic 1,0) and I am desperate, please help, I am trying for over two monts but without any good results
Why I got this error?

JimEli

googled out?

http://coolarduino.blogspot.ca/2015/06/frequency-counter-for-arduino-due.html

CrossRoads

try taking -master off the folder name.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

BlackT

googled out?

http://coolarduino.blogspot.ca/2015/06/frequency-counter-for-arduino-due.html

Thank you,I didn't see that, but it doesent work. I upload sketch but on serial it doesent print anything

try taking -master off the folder name.
I tryed and i got the same error

BlackT

But I don't get it, why this simple program I made doesn't work with this type of signal
http://cptd.chandra.ac.th/selfstud/datacom2/IMAGE/a92.gif (accuracy about 5 HZ)
But work with this
http://howtmm.com/week08/assets/digital-signal.png (accuracy 0.5 hz)


Code: [Select]
// this example is public domain. enjoy!
// www.ladyada.net/learn/sensors/thermocouple

float timee;
float last_time=0;
float hz =0;

int i=0;
int input = 36;
int state;
int state_2=LOW;
int counter = 0;
void setup() {
  Serial.begin(250000);
   
pinMode(input,INPUT);
 
  Serial.println("test rpm");
 
  delay(500);
}

void loop() {
 

state=digitalRead(input);           //program read is pin HIGH or LOW on digital pin 36           
if(state != state_2)                //if state of pin is diffrent from last state it count one hz
{
  counter ++;
  state_2=state;                   
}
i++;
if(counter>50)                   //when counter reach 50 program prints freq on serial monitor
{
 
  timee=millis();
  hz=timee-last_time;
  Serial.println(hz);
  hz=50/hz;
  hz=hz*1000;
 
  last_time=timee;
  counter = 0;
  Serial.print("HZ=");
 Serial.println(hz);
}

 
 
}


I know digitalread is not very fast, but it should work for this what I need, I need to count max 300 Hz

pjrc

I'm the author of FreqMeasure.  So far, nobody has figured out the proper timer defines to make it work on Due.  Or at least nobody has done so and shared them.  If anyone does get it working, I'd be happy to include the defs.

As you can see on the FreqMeasure library page, if you scroll down to "FreqCount vs FreqMeasure", there's 2 basic approaches to measuring digital frequencies.  Counting cycles only works well for higher frequencies.  For low frequencies, measuring cycle time is what you need.

A software-only approach, like the code in #13, can work for very low frequencies.  But it will have small errors due to interrupts, and the limited resolution of the timing functions like millis().  It also can't properly measure while doing other things, like printing to Serial.

I designed FreqMeasure to solve all those problems.  Timer input capture allows very precise measurement, with resolution limited only to the maximum clock speed of the hardware timer.  It also allows each cycles to be measured and stored in a buffer while your sketch does other work, like printing to Serial, so you never lose any input.  Especially for very low frequencies where you must wait for a long cycle to get another measurement, automatically capturing every cycle with hardware and interrupts lets your program be as responsive as possible.

Unfortunately all this great capability does require expert-level knowledge to create the timer defs.  If you're not very experienced with the Due hardware timers (I am not... I know them well on AVR and Kinetis chips, but not SAM3), your best option for high accuracy would be to switch to a board that is well supported.  Or find an expert and convince them to make these defs, and hopefully contribute them so everyone else can use them too.

Go Up