Skip to main content
Arduino

Return to Answer

+ how to use a timer.
Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

Edit: Now we may ask the question whether it is possible to use some timers to make all this more accurate. The answer is yes, you could achieve maximum accuracy by using four 16-bit timers:

  • You could use the input capture function of a 16-bit timer, with the prescaler set to 8, to measure the transitions of one of your inputs with a resolution of 0.5 μs.
  • You could use a 16-bit timer in CTC mode, with the prescaler set to 256, to toggle one of your outputs at the desired frequency, with a 16 μs resolution.

For reference, micros() has a resolution of 4 μs. The 16 μs resolution of the timer-generated output seems poor in comparison, but the timer has the advantage of having zero jitter.

Now, for doing that that, you would need an Arduino Mega 2560. The Uno has only one 16-bit timer, so it could help with only one of your two channels.

There is still something you can do with a single 16-bit timer. Set it to normal counting mode, with the prescaler at 8, and use it instead of micros() for timing your inputs: in setup()

// Configure Timer 1.
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // clock at F_CPU/8

and in the interrupt handler, you replace uint32_t now = micros(); by

uint16_t now = TCNT1;

And now you have input_0_high_time (which should also be uint16_t) in units of 0.5 μs.

Edit: Now we may ask the question whether it is possible to use some timers to make all this more accurate. The answer is yes, you could achieve maximum accuracy by using four 16-bit timers:

  • You could use the input capture function of a 16-bit timer, with the prescaler set to 8, to measure the transitions of one of your inputs with a resolution of 0.5 μs.
  • You could use a 16-bit timer in CTC mode, with the prescaler set to 256, to toggle one of your outputs at the desired frequency, with a 16 μs resolution.

For reference, micros() has a resolution of 4 μs. The 16 μs resolution of the timer-generated output seems poor in comparison, but the timer has the advantage of having zero jitter.

Now, for doing that that, you would need an Arduino Mega 2560. The Uno has only one 16-bit timer, so it could help with only one of your two channels.

There is still something you can do with a single 16-bit timer. Set it to normal counting mode, with the prescaler at 8, and use it instead of micros() for timing your inputs: in setup()

// Configure Timer 1.
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // clock at F_CPU/8

and in the interrupt handler, you replace uint32_t now = micros(); by

uint16_t now = TCNT1;

And now you have input_0_high_time (which should also be uint16_t) in units of 0.5 μs.

Source Link
Edgar Bonet
  • 45.1k
  • 4
  • 42
  • 81

Your signals are slow enough that you may not need any timer. You can just use the Arduino micros() function to handle all your timings. Of course, micros() itself relies on Timer 0, but you don't need to access any timer directly.

I would use the external interrupts in CHANGE mode to measure the duty cycle. For example:

const uint8_t INPUT_0_PIN = 2; // digital 2 is also INT0
const uint8_t OUTPUT_0_PIN = 4;
// Measured duration of the HIGH level of input 0.
static volatile uint32_t input_0_high_time;
// Interrupt handler invoked when input 0 toggles.
static void on_input_0_change()
{
 static uint32_t time_rise; // last time the signal rose
 uint32_t now = micros();
 if (digitalRead(INPUT_0_PIN) == HIGH) // detected rising edge
 time_rise = now;
 else // detected falling edge
 input_0_high_time = now - time_rise;
}
void setup()
{
 pinMode(INPUT_0_PIN, INPUT);
 pinMode(OUTPUT_0_PIN, OUTPUT);
 attachInterrupt(0, on_input_0_change, CHANGE);
}

For handling two inputs, you can duplicate everything that has a 0 in its name and name it with a 1. If your signal is no slower than 60 Hz, you can also replace all uint32_t by uint16_t to make the code slightly faster.

For generating the outputs, do something similar to the Blink Without Delay example:

void loop()
{
 uint32_t now = micros();
 // Determine suitable period for output 0.
 uint32_t input_0_high_time_copy;
 noInterrupts(); // avoid race condition while reading
 input_0_high_time_copy = input_0_high_time;
 interrupts();
 uint32_t output_0_half_period =
 map(input_0_high_time_copy, 0, 16666, 8333, 500e3);
 // Toggle output 0 when needed.
 static uint32_t time_output_0_toggle;
 static uint8_t output_0_state;
 if (now - time_output_0_toggle >= output_0_half_period) {
 time_output_0_toggle += output_0_half_period;
 output_0_state = !output_0_state; // toggle state
 digitalWrite(OUTPUT_0_PIN, output_0_state);
 }
}

Again, you can handle two outputs by duplicating the code and replacing 0 with 1. Note that you cannot replace uint32_t by uint16_t, as it would not work for the slowest frequencies.

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /