Square wave generator and frequency meter

My most reliable freq source that i can get is i think arduino analogWrite, but it gives only 490hz and 980hz...

unsigned long waveHz = 100000;
unsigned int exacttime, numofovf;
volatile unsigned int timeovf;
unsigned long waveTime;

void setup() {

  pinMode(2, OUTPUT);
  waveTime = (1.0/(waveHz*2.0))*16000000;
  numofovf = waveTime/65536;
  exacttime = waveTime-(numofovf*65536UL);
  if(exacttime>38){
    exacttime = exacttime-38;
  } else {
    exacttime = 65535-38+exacttime;
    numofovf = numofovf - 1;
  }
  cli();          
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = exacttime;           
  TCCR1B |= (1 << CS10);       
  TIMSK1 |= (1 << OCIE1A);
  TIMSK1 |= (1 << TOIE1); 
  sei();  
}

void loop() {
}

ISR(TIMER1_COMPA_vect){
  if(timeovf == numofovf){
    TCNT1 = 0;
    timeovf = 0;
    PORTD ^= bit(2);
  }
  
}

ISR(TIMER1_OVF_vect){
  timeovf++;
}

Wouldn't be easier to just use PWM mode with 50% duty cycle to generate the square wave?

Ye but wont i be limited by min freq? For example i would be able to generate like i guess 4Mhz pwm but if i want 2hz thats a no go. Correct me if im wrong.

With TIMER1 and 16MHz Arduino, you can go as low as ~0.25 Hz (or 4 seconds per cycle)

Perhaps try a simple loop that toggles an LED (aka output pin). Then use a NOP loop to slow it down. Should be very consistent.
Also disable the millis() timer. Or simply shut off all interrupts.

I believe this will be as stable as an arduino is capable of.

uint_16 i

for ( i, i = 16384, i++){

     asm(“nop\n\t”)		//16Mhz Arduino  ~ 62.5ns

     asm ("sbi %0, %1 \n": : "I" (_SFR_IO_ADDR(PINB)), "I" (PINB5)); 		// Toggle LE
}

Here is an example of generating frequencies using Timer1. It can for 0.2Hz (5 seconds per cycle) to 500 kHz.

// Generating Two 180° Out of Phase Variable-Frequency 
// Square Waves on Timer1 of an Arduino UNO (Pins 9 and 10)
// Good for frequencies from 0.2 Hz to 500 kHz.
// Written June 1st, 2020 by John Wasser

void TwoPhaseBegin()
{
  digitalWrite(9, LOW);
  pinMode(9, OUTPUT);
  digitalWrite(10, LOW);
  pinMode(10, OUTPUT);

  // Stop Timer/Counter1
  TCCR1A = 0;  // Timer/Counter1 Control Register A
  TCCR1B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;  // Timer/Counter1 Interrupt Mask Register

  // Set Timer/Counter1 to Waveform Generation Mode 8: 
  // Phase and Frequency correct PWM with TOP set by ICR1
  TCCR1B |= (1 << WGM13);  // WGM=8
  TCCR1A |= (1 << COM1A1);  // Normal PWM on Pin 9
  TCCR1A |= (1 << COM1B1) | (1 << COM1B0); // Inverted PWM on Pin 10

  TwoPhaseFrequency(1000.0);  // Default to 1 kHz
}

bool TwoPhaseFrequency(float frequency)
{
  byte prescaleBits; // 1, 2, 3, 4, 5
  uint16_t prescaleFactor;  // 1, 8, 64, 256, 1024
  uint32_t top32;

  // Find the smallest prescale factor that will fit the TOP value within 16 bits.
  // frequency = F_CPU / (2 * prescale *  TOP)
  // TOP = F_CPU / (2UL * prescale * frequency);

  prescaleBits = 1;
  prescaleFactor = 1;  // Used for 123 Hz to 500 kHz
  top32 = F_CPU / (2UL * prescaleFactor * frequency);
  if (top32 > 65535UL) // Too many clocks to count in 16 bits?
  {
    prescaleBits = 2;
    prescaleFactor = 8;  // Used for 16-122 Hz
    top32 = F_CPU / (2UL * prescaleFactor * frequency);
    if (top32 > 65535UL) // Too many clocks to count in 16 bits?
    {
      prescaleBits = 3;
      prescaleFactor = 64;  // Used for 2-15 Hz
      top32 = F_CPU / (2UL * prescaleFactor * frequency);
      if (top32 > 65535UL) // Too many clocks to count in 16 bits?
      {
        prescaleBits = 4;
        prescaleFactor = 256; // Only used for 1 Hz
        top32 = F_CPU / (2UL * prescaleFactor * frequency);
        if (top32 > 65535UL) // Too many clocks to count in 16 bits?
        {
          prescaleBits = 5;
          prescaleFactor = 1024;
          top32 = F_CPU / (2UL * prescaleFactor * frequency);
          if (top32 > 65535UL) // Too many clocks to count in 16 bits?
          {
            return false;
          }
        }
      }
    }
  }

  //  Serial.print("Freq: ");
  //  Serial.print(frequency);
  //  Serial.print(" prescale: ");
  //  Serial.print(prescaleFactor);
  //  Serial.print(" TOP: ");
  //  Serial.println(top32);

  if (top32 < 16)
    return false; // Require at least 16 levels of PWM

  TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // Clear the three clock select bits
  TCCR1B |= prescaleBits; // Set clock prescale to prescaleBits

  ICR1 = top32;
  OCR1A = top32 / 2;
  OCR1B = (top32 / 2) + 1;
  return true;
}

void setup()
{
  Serial.begin(115200);

  TwoPhaseBegin();  // Start output at 1000 Hz
}

void loop()
{
  // Call TwoPhaseFrequency(uint16_t frequency) any time to change the output frequency
}

Now that is a keeper.

Its in LC_baseTools. You can install it using the IDE library manager. The Wokwi simulator has it installed as a default.

-jim lee

How about 90 degrees ?

Sorry, just 180°. I can't think of a way to get the AVR timer hardware to generate a quadrature signal without interrupts and software.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.