1

I am playing with stepper motors and I need a precise square wave generator for f=29.710617283951Hz. I've done this on Raspberry Pi without any problems, but I need something smaller and with less power consumption.

Is it even possible to get that frequency?

  • How precise do you really need the square wave to be? If you actually need to be precise to the pico Hz that I don't think the Arduino can do that. The clock frequency is 16 MHz, ± a-fair-bit, which means that the smallest time interval that you get is 62.5 pico seconds which is way over your requirement. Can you say more about what you are trying to accomplish? – dlu Jan 13 '16 at 22:48
  • The frequency you want is time = .033658 seconds. Or 33.658 ms (milli-seconds). Accuracy to the 12 significant digits ? Like @dlu requested, we might be able to help you better with more information on your intent. – Marla Jan 13 '16 at 23:11
  • 1
    @dlu I think you mean 62.5 nano seconds, not "62.5 pico seconds". However, that doesn't reduce the value of your comment. Trying to generate a signal with an error of 1 part in 10^14 to drive a stepper motor is mind-boggling. Even watches work okay at several parts/million error. – gbulmer Jan 13 '16 at 23:13
  • 2
    @dlu my project is a "startracker" for astrophotography, my mechanical part is not THAT precise, so I think 29.7106 should be ok. – Slawomir Wiesek Jan 14 '16 at 0:56
  • 1
    "I've done this on Raspberry Pi whiteout any problems". How did you get a Raspberry-Pi to generate a signal accurate to better than1 part in 297,106? An R-Pi's crystal is likely accurate to 1 part in 50,000. Did you use an external time source to correct it? – gbulmer Jan 14 '16 at 17:27
3

Reducing the power requirement, compared to an R-Pi, should be straightforward, achieving 29.7106Hz is doable, reducing the size is harder (an R-Pi is quite compact).

Some of the available options depend on your electronics skills, and budget.

I'll assume you want to use off-the-shelf hardware as much as practical, but are okay to wire 'modules' together (size could be a bit smaller than an R-Pi using off-the-shelf parts, but could be significantly reduced by a custom made part).

I'll also assume you want to keep costs under $100. (I apologise if I am wrong, but this seems like a level that many of this community might find interesting :-)

Preamble:

Even at 16MHz, 29.7106Hz represents a count of approximately 538,528, which requires 19 bits. So I would not use a chip with 8 or 16 bit timers. I would make my life easier by using a chip with 32 bit timers.

Further, the code will be manipulating quantities with at-least 19 bits, so I would not use an 8 or 16bit CPU. I would make my life easier by using a 32bit CPU that operates on 32bit quantities in a single operation. This may become very important if you opt for a software-heavy approach.

Initial Approach:

Lets assume our 32 bit processor has a clock of 64MHz.
To generate a clock frequency close to 29.710617284Hz (as good as my calculator will do), load a timer with 2,154,112 (maybe 2,154,111, depending on how the counter triggers).

The more accurate divisor is 2,154,112.094.

That count is within 0.05ppm (parts/million) of 29.710617284Hz at a 64MHz clock.
Also, a count of +/- 1 is a change of less than 0.5ppm. That is about 30x better than the more relaxed requirement, '29.7106', mentioned in your comments.

Using 64MHz to drive the count will enable 4x better accuracy, 0.5ppm, than using a 16MHz clock, 2ppm, when we examine ways to implement this in the 'Options' section below.

Summary so far: if the microcontroller had a very accurate clock (much better than 0.5ppm error), then the problem is solved.

However, the crystals used to drive the microcontroller typically have a frequency stability of 30-50ppm, and may be even worse.

Options to reach 0.5ppm:

1. Replace the MCUs crystal with a much better part.
This will typically require some soldering skill, and the parts I've seen (which are likely to replace the existing crystal) are stable at about 10ppm. That might be a factor of 3-5 better than the factory fitted crystal, so may be worth the effort, however there are better options.

2. Feed the MCU with a higher-stability clock source than a crystal.
Most MCU's can accept an external clock source in place of the crystal. Temperature Compensated Crystal Oscillators (TCXO) cost about $5, and can achieve 0.5ppm (0 to 40C, 2ppm -30C to +85C). So an improvement over an 'ordinary' crystal of 30-100x.

If this is appealing, I'd actually recommend looking at ST Micro's Nucleo boards. They are low-cost (under $12), so not much wasted if it doesn't work, and many of the boards already take an external oscillator, so may be easier to change. (They are 32bit CPU's with 32bit timers, and can run at 64MHz,) I imagine folks can recommend other boards.

3. 'Discipline' the square-wave counter with an external high-quality clock
The idea is to use a very accurate external clock, and adjust the square-wave count to get high-accuracy than the existing MCU crystal. While counting the MCU clock for the square wave, use a second counter to count the number of MCU clock cycles a known, high-quality, external timing source event takes. The external clock source could be a TCXO, an Oven Controlled Oscillator (OCXO), which can achieve 0.001ppm) or GPS.

Program code could estimate the error of the MCU clock vs higher-quality clock, and adjust the square-wave count within each square-wave cycle. This may keep the square-wave within 0.5ppm using one or more +1 or -1 adjustment of the counter. This approach has the advantage that the only electronics is connecting the high-quality timing source to a pin on the microcontroller. The rest is (carefully written) software.

Edit:
To be clear, this option 3 can use an unmodified, off-the-shelf, MCU development board. The original crystal is unchanged; there is no need to modify it to replace its crystal.

So it could use a small, e.g. Arduino-Nano size, microcontroller running at a reasonable rate (e.g. 64MHz) connected to a TCXO (or even OCXO), and get close to 0.5ppm error.

2

You can user Timer 1 on the Atmega328P (such as is found on the Uno) to get you into that ball park:

const unsigned long countTo = 33658;  // F_CPU / 8 / 29.71 / 2

void setup ()
  {
  pinMode (10, OUTPUT);

  // PWM, top at OCR1A
  TCCR1A = bit (WGM10) | bit (WGM11);  // PWM
  TCCR1B = bit (WGM13) | bit (CS11);   // PWM, prescaler of 8
  OCR1A = countTo - 1;                 // zero relative 
  OCR1B = (countTo / 2) - 1;           // 50% duty cycle
  bitSet (TCCR1A, COM1B1);   // clear OC1B on compare
  }  // end of setup

void loop ()
  {
  }  // end of loop

I measured a frequency of 29.67 Hz on one device and 29.41 Hz on another. The Uno is driven by a resonator, not a crystal, so a couple of percent error is to be expected.


I have a page about the hardware timers and a cheat-sheet for Timer 1:

Timer 1 information

  • The bit() notation is clean. The bit(WGM13) and bit(WGM10)|bit(WGM11) chooses the "PWM, Phase correct, top=OCR1A Waveform Generation Mode", which counts TNCT1 both up and down, clearing and setting the pin10 output when it matches OCR1B. The counting both up and down is what requires the /2 in the frequency calculation. Out of the box, P (29.41-29.71)/29.71=-1.01% seems pretty good. One could tune with a countTo=33658. – Dave X Jan 14 '16 at 7:52
  • 1
    I agree. You could tweak the counter to more closely match your hardware. I'm a little dubious about my measurements, I'll try for a more accurate frequency measurement. I think the theory is correct. – Nick Gammon Jan 14 '16 at 7:59
  • 1
    I measured with my logic analyser and got a period of 33.702056 ms, representing a frequency of 29.6717 Hz, which agrees with the first frequency I posted. I suspect therefore that my Arduino is running a bit slowly. – Nick Gammon Jan 14 '16 at 8:07
  • 1
    Nick, on your excellent timer page for Timer 0 CTC mode, you mention that the toggle for OCR0B=200 doesn't work. I think it is because the timer is cleared at 150. Maybe try with OCR0B=100. I'm also curious about the SET and CLEAR modes. – Dave X Feb 22 '16 at 16:24
  • Good point. I've amended that post. You may need to refresh the page to see the new graphic. Please ask a new question if you want to concentrate on the SET and CLEAR modes. – Nick Gammon Feb 22 '16 at 20:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.