Hi all, I have a rectangular wave (0 to 5 V with 20 Hz ≤ f ≤ 1 kHz) and I am needing to measure and display the following:
- waveform frequency
- rotational speed in RPM; and
- waveform period in milliseconds
thank you for your time!
Hi all, I have a rectangular wave (0 to 5 V with 20 Hz ≤ f ≤ 1 kHz) and I am needing to measure and display the following:
thank you for your time!
If you have 1), you have 3) (and vice versa)
How does a rectangular wave rotate?
I assume that the sensor tells you about rotations. In that case, (2) is just (1) multiplied by a constant, and as mentioned above, (1) also gives you (3) and (3) gives you (1). To get (3) you may use millis() or micros() for the time between rising (or falling) edges. You will need to learn how to detect edges but there is an Arduino IDE example for that.
On the other hand, if you want code written for you, go to Gigs and Collaborations and bring your wallet.
I have a rectangular wave (0 to 5 V with 20 Hz ≤ f ≤ 1 kHz) and I am needing to measure
I would recommend that you take a look at FreqMeasure.h
It is available through the library mangager of the IDE.
/*
* D Counsellor
ReadAnalogVoltage
Reads an analog input, converts it to triangular wave, and prints the result to the Serial Monitor.
Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu).
Attach the 0-5V triangular wave to pin A2.
*/
#ifdef f_CPU
#undef F_CPU
#define F_CPU 16000000UL
#endif
float Htime; //integer for storing high time
float Ltime; //integer for storing low time
float RPM;
float Ttime; // integer for storing total time of a cycle
float frequency; //storing frequency
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
Htime= pulseIn(A2,HIGH); //read high time
delay (20);
Ltime= pulseIn(A2,LOW); //read low time
Ttime=(Htime+Ltime)/(1000); // Ttime is in ms seconds
frequency= (1/(Ttime)*1000); //getting frequency when Ttime is in ms seconds
RPM= (frequency*60); //getting RPM
Serial.print("Hz");
Serial.println(frequency);
Serial.print("RPM");
Serial.println(RPM);
Serial.print("ms");
Serial.println(Ttime);
delay (300);
buttonState=0;
}
If you want high precision, the Input Capture pin can measure in 16ths of a microsecond:
// Measures the HIGH width, LOW width, frequency and duty-cycle of a pulse train
// on Arduino UNO Pin 8 (ICP1 pin).
// Note: Since this uses Timer1, Pin 9 and Pin 10 can't be used for
// analogWrite().
void setup()
{
Serial.begin(115200);
while (!Serial);
// For testing, uncomment one of these lines and connect
// Pin 3 or Pin 5 to Pin 8
// analogWrite(3, 64); // Pin 3: 512.00, 1528.00, 2040.00, 25.10%, 490.20 Hz
// analogWrite(5, 64); // Pin 5: 260.00, 764.00, 1024.00, 25.39%, 976.56 Hz
noInterrupts (); // protected code
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TIMSK1 = 0;
TIFR1 |= (1 << ICF1); // clear Input Capture Flag so we don't get a bogus interrupt
TIFR1 |= (1 << TOV1); // clear Overflow Flag so we don't get a bogus interrupt
TCCR1B = _BV(CS10) | // start Timer 1, no prescaler
_BV(ICES1); // Input Capture Edge Select (1=Rising, 0=Falling)
TIMSK1 |= _BV(ICIE1); // Enable Timer 1 Input Capture Interrupt
TIMSK1 |= _BV(TOIE1); // Enable Timer 1 Overflow Interrupt
interrupts ();
}
volatile uint32_t PulseHighTime = 0;
volatile uint32_t PulseLowTime = 0;
volatile uint16_t Overflows = 0;
ISR(TIMER1_OVF_vect)
{
Overflows++;
}
ISR(TIMER1_CAPT_vect)
{
static uint32_t firstRisingEdgeTime = 0;
static uint32_t fallingEdgeTime = 0;
static uint32_t secondRisingEdgeTime = 0;
uint16_t overflows = Overflows;
// If an overflow happened but has not been handled yet
// and the timer count was close to zero, count the
// overflow as part of this time.
if ((TIFR1 & _BV(TOV1)) && ICR1 < 1024)
overflows++;
if (PulseLowTime == 0)
{
if (TCCR1B & _BV(ICES1))
{
// Interrupted on Rising Edge
if (firstRisingEdgeTime) // Already have the first rising edge...
{
// ... so this is the second rising edge, ending the low part
// of tghe cycle.
secondRisingEdgeTime = overflows;
secondRisingEdgeTime = (secondRisingEdgeTime << 16) | ICR1;
PulseLowTime = secondRisingEdgeTime - fallingEdgeTime;
firstRisingEdgeTime = 0;
}
else
{
firstRisingEdgeTime = overflows;
firstRisingEdgeTime = (firstRisingEdgeTime << 16) | ICR1;
TCCR1B &= ~_BV(ICES1); // Switch to Falling Edge
}
}
else
{
// Interrupted on Falling Edge
fallingEdgeTime = overflows;
fallingEdgeTime = (fallingEdgeTime << 16) | ICR1;
TCCR1B |= _BV(ICES1); // Switch to Rising Edge
PulseHighTime = fallingEdgeTime - firstRisingEdgeTime;
}
}
}
void loop()
{
noInterrupts();
uint32_t pulseHighTime = PulseHighTime;
uint32_t pulseLowTime = PulseLowTime;
interrupts();
// If a sample has been measured
if (pulseLowTime)
{
// Display the pulse length in microseconds
Serial.print("High time (microseconds): ");
Serial.println(pulseHighTime / 16.0, 2);
Serial.print("Low time (microseconds): ");
Serial.println(pulseLowTime / 16.0, 2);
uint32_t cycleTime = pulseHighTime + pulseLowTime;
Serial.print("Cycle time (microseconds): ");
Serial.println(cycleTime / 16.0, 2);
float dutyCycle = pulseHighTime / (float)cycleTime;
Serial.print("Duty cycle (%): ");
Serial.println(dutyCycle * 100.0, 2);
float frequency = (float)F_CPU / cycleTime;
Serial.print("Frequency (Hz): ");
Serial.println(frequency, 2);
Serial.println();
delay(1000); // Slow down output
// Request another sample
noInterrupts();
PulseLowTime = 0;
interrupts();
}
}
johnwasser:
If you want high precision, the Input Capture pin can measure in 16ths of a microsecond:// Measures the HIGH width, LOW width, frequency and duty-cycle of a pulse train
// on Arduino UNO Pin 8 (ICP1 pin).
// Note: Since this uses Timer1, Pin 9 and Pin 10 can't be used for
// analogWrite().
void setup()
{
Serial.begin(115200);
while (!Serial);
// For testing, uncomment one of these lines and connect
// Pin 3 or Pin 5 to Pin 8
// analogWrite(3, 64); // Pin 3: 512.00, 1528.00, 2040.00, 25.10%, 490.20 Hz
// analogWrite(5, 64); // Pin 5: 260.00, 764.00, 1024.00, 25.39%, 976.56 Hz
noInterrupts (); // protected code
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TIMSK1 = 0;
TIFR1 |= (1 << ICF1); // clear Input Capture Flag so we don't get a bogus interrupt
TIFR1 |= (1 << TOV1); // clear Overflow Flag so we don't get a bogus interrupt
TCCR1B = _BV(CS10) | // start Timer 1, no prescaler
_BV(ICES1); // Input Capture Edge Select (1=Rising, 0=Falling)
TIMSK1 |= _BV(ICIE1); // Enable Timer 1 Input Capture Interrupt
TIMSK1 |= _BV(TOIE1); // Enable Timer 1 Overflow Interrupt
interrupts ();
}
volatile uint32_t PulseHighTime = 0;
volatile uint32_t PulseLowTime = 0;
volatile uint16_t Overflows = 0;
ISR(TIMER1_OVF_vect)
{
Overflows++;
}
ISR(TIMER1_CAPT_vect)
{
static uint32_t firstRisingEdgeTime = 0;
static uint32_t fallingEdgeTime = 0;
static uint32_t secondRisingEdgeTime = 0;
uint16_t overflows = Overflows;
// If an overflow happened but has not been handled yet
// and the timer count was close to zero, count the
// overflow as part of this time.
if ((TIFR1 & _BV(TOV1)) && ICR1 < 1024)
overflows++;
if (PulseLowTime == 0)
{
if (TCCR1B & _BV(ICES1))
{
// Interrupted on Rising Edge
if (firstRisingEdgeTime) // Already have the first rising edge...
{
// ... so this is the second rising edge, ending the low part
// of tghe cycle.
secondRisingEdgeTime = overflows;
secondRisingEdgeTime = (secondRisingEdgeTime << 16) | ICR1;
PulseLowTime = secondRisingEdgeTime - fallingEdgeTime;
firstRisingEdgeTime = 0;
}
else
{
firstRisingEdgeTime = overflows;
firstRisingEdgeTime = (firstRisingEdgeTime << 16) | ICR1;
TCCR1B &= ~_BV(ICES1); // Switch to Falling Edge
}
}
else
{
// Interrupted on Falling Edge
fallingEdgeTime = overflows;
fallingEdgeTime = (fallingEdgeTime << 16) | ICR1;
TCCR1B |= _BV(ICES1); // Switch to Rising Edge
PulseHighTime = fallingEdgeTime - firstRisingEdgeTime;
}
}
}
void loop()
{
noInterrupts();
uint32_t pulseHighTime = PulseHighTime;
uint32_t pulseLowTime = PulseLowTime;
interrupts();
// If a sample has been measured
if (pulseLowTime)
{
// Display the pulse length in microseconds
Serial.print("High time (microseconds): ");
Serial.println(pulseHighTime / 16.0, 2);
Serial.print("Low time (microseconds): ");
Serial.println(pulseLowTime / 16.0, 2);
uint32_t cycleTime = pulseHighTime + pulseLowTime;
Serial.print("Cycle time (microseconds): ");
Serial.println(cycleTime / 16.0, 2);
float dutyCycle = pulseHighTime / (float)cycleTime;
Serial.print("Duty cycle (%): ");
Serial.println(dutyCycle * 100.0, 2);
float frequency = (float)F_CPU / cycleTime;
Serial.print("Frequency (Hz): ");
Serial.println(frequency, 2);
Serial.println();
delay(1000); // Slow down output
// Request another sample
noInterrupts();
PulseLowTime = 0;
interrupts();
}
}
thank you very much,you code is perfect for the low RPM.the following is the test result.
21:39:00.933 -> High time (microseconds): 1775247.125000
21:39:00.933 -> Low time (microseconds): 29056.125000
21:39:00.933 -> Cycle time (microseconds): 1804303.250000
21:39:00.933 -> Duty cycle (%): 98.389617
21:39:00.933 -> Frequency (Hz): 0.554231
21:39:00.933 -> RPM: 33.253833
I have one question here.would you please share how to modify the code to only capture one cycle? and also donot use the delay.
the Input Capture pin can measure in 16ths of a microseconds---->does the code already support this?
any comments or feedback will be great appreciate.
Leo
To only capture one cycle, just don't do this part until you are ready to "fire":
// Request another sample
noInterrupts();
PulseLowTime = 0;
interrupts();
adams_leo:
the Input Capture pin can measure in 16ths of a microseconds---->does the code already support this?
I'm not sure, but it depends on the timer clock frequency that is set by the timer clock prescaler register settings.
aarg:
To only capture one cycle, just don't do this part until you are ready to "fire":// Request another sample
noInterrupts();
PulseLowTime = 0;
interrupts();
sorry,I guess I made a confuse for you. I means when capture ony cycle,print the RPM and we can start next cycle
adams_leo:
the Input Capture pin can measure in 16ths of a microseconds---->does the code already support this?
float frequency = (float)F_CPU / cycleTime;
Yup.