I've built a lot of electronic clocks over the years and a lot of them have used a quartz clock crystal as a time base. Those are very simple to use and give you an accuracy of about ± 0.5 to one second per day, just like your typical wristwatch. Their specific frequency depends on a few factors, one being the setting on a variable capacitor in the oscillator circuit, which you can adjust very carefully but another factor is temperature. As most crystals warm up their resonant frequency lowers a bit so your fine tuning of the capacitor is no longer exact.
So you can do one of two things: Compensate for the ambient temperature with an array of capacitors (there are chips that do that), or you can try to stabilize the ambient temperature. In this project, I've elected to try the second solution.
There are commercial version of these, called "Ovenized crystal oscillators" but they can be pretty expensive. Let's see if I can build one cheaply.
The test rig consists of an Atmel AVR micro-controller running with a high-frequency non-tuned crystal oscillator, a temperature sensor and a heater. I've selected a run-of-the-mill 16.00 mega-hertz crystal that has no specific features for stability or exactitude. 16 Mhz because: 1 - It's easily divisible by twos, 2 - It's divisible by tens so I can get tenths, hundredths, or thousandths, and 3 - I already have plenty of them.
For the oven doohickey, I'll need to put the temperature sensor, the heater, the crystal and the bypass capacitors real close to each other and enclose the whole thing in an insulated box. Let's do some parts gathering:
For the sensor I've chosen the DS1631 because from my experience they've been pretty spot-on, are easy to interface (two wires), need no calibration and I've already got a few of them about.
For the box I chose a plastic container that's very cheap and which comes with easily discarded contents. Plus, I like candy. For the insulating material I spared no expenses and used cotton balls.
For the heater I considered using the standard Nickel-Chronium alloy and forming little loops but then I had a forehead slapping moment. A normal resistance is a type of heater; driven with a power supply it can only transform the current passing through it into heat. After doing some preliminary tests...
... I've verified that two 220 ohms resistors can indeed raise the temperature of the sensor up to 30°C and close to 36°C when insulated. So my heater will only use a maximum of 45 milli-amps and generate 227 milli-watts at 5 volts. Great! Time to build.
After hooking up the AVR micro-controller to a programmer, a sensor, a serial link to my desktop computer, and having written the minimal amount of software to make sure the basic parts work, I decided to build the oven containing the ovenized oscillator:
It's hard to tell from my crappy webcam image but that's basically the crystal with the sensor glued on top, with each heater resistor stuck right next to their junction. I've insulated every leg with heat-shrink tubing. Let's wire that sucker up:
And stuff that completely surrounded by cotton balls inside the box:
And there you go, one Ovenized Crystal Oscillator + Heater + Sensor in a friendly package.
At this point it dawned on me that I've chosen a packet of Tic Tacs for a clock. "Tic Tac" is French for "Tick Tock". It honestly didn't dawn on me beforehand.
Anyway. I connected all that up (schematic attached at the bottom of this page) and did some tests. I hooked up the heater to a small N-channel MOSFET driven by a Pulse-Width-Modulation (PWM) signal generated by the micro-controller. For simplicity's sake I only selected full-ON or full-OFF on the PWM output at first. I managed to raise the oven temperature to 40°C so I was contented with that. I plan to use the oven at a stable temperature just a couple of degrees higher than the maximum it ever gets in the room where it will reside. 30°C will be fine. I live in Canada and own an air conditioner.
For the software I used my BB bit-banger library (also attached) which simplifies talking to the micro-controller. I wrote a time-management interrupt routine which is called one hundred times per second, triggered by a 16-bit timer which runs at clock speed. Since I pre-scale the system's clock by dividing the crystal clock by four, I set the timer's maximum count to 40,000. 4 Mhz divided by 40,000 equals 100 times per second. The crystal should have a pretty stable frequency but it's not going to be exact. To compensate, I've added a fudge factor which lets the user adjust one of those 100 counts by ± parts per 4 million. That makes one of the 1/100ths ticks a tiny tiny bit longer or shorter. That's called the "coarse" adjustment. I've also added a "fine adjustment" which also lengthens or shortens a tick every 1000 seconds by ± parts per 4 billion but that's plain silly. I haven't used it yet.
I then added code to show and set the time, the adjustments, do measurements and whatnot. I first tried to cook the crystal:
Here you can see the project's main menu, from a BBTERM (also attached) session. BBTERM is a simple RS232 terminal like cu, minicom or hyperterminal but with some extra features tailored for projects like this one. For time measurement purposes, when BBTERM receives a control-A (ASCII code 1) it prints the current computer time of day in seconds and micro-seconds. When it receives control-B (ASCII code 2), it prints the elapsed time since the last control-A. That feature makes BBTERM very useful for this project.
At the bottom of that screenshot you can see three lines which show, the date and time from the micro-controller and the temperature measured by the sensors, in integer and fractional parts. I'll pretty that up next. As you can see here, I've managed to raise the box to 40°C after an hour. I'll discuss the other numbers below.
At first, the temperature management code was simple: If the sensor's temperature is lower than the target temperature, turn the the heater full on. This is how that behaved:
In this listing, the underscore "_" or H field represents whether or not the heater is ON at the time of the sample. The next field which shows the sensor temperature in °C oscillates between -3/16ths to +2/16ths of a degree from the target, which isn't bad for an ON/OFF heater.
The bracketed (<>) times are not from the micro-controller but measured on the computer. Every time the micro-controller sends a single control-B, the computer inserts the current time (<E=XX:XX:XX.XXXXXX>) and the elapsed time since the test started.
Of note is that we're starting to get some pretty stable drift. As you can see we're off by about 8 milliseconds after six hours, and that's not bad considering a sloppily inputted adjustment factor. That's an accuracy of about one second per month if things keep stable. I can't consider those numbers very exact at this point because the measurements were done for only a few minutes to a few hours and they were made against the clock in my PC which is NTP-disciplined and that includes an offset and jitter from true time.
Anyway, I wasn't even aiming for stable frequency at that point. I purposefully fiddled with the coarse adjustment just to make sure that wasn't a fluke and changed the heater control routine:
Here you can see a 45 minute session with the new code which controls the heater with the 8-bit Pulse-Width Modulated signal. The third column "XX:XXXX-XXXX" shows the current PWM value in hexadecimal outputted to the heater. The two hyphenated values represent the minimum and maximum and are in 16 bits so they can be finely adjusted as the clock runs. The upper 8 bits (first two digits) of either the minimum or maximum are used to set the PWM duty cycle depending on whether or not the sensor's temperature is lower than the target. At first, those minimums and maximums are extreme (02 and FF in this case) but with automatic consecutive adjustments they tend toward the optimum value with time. As you can see, the temperature ends up being a rock solid 30°C after a while.
Well, that's all for now. In the coming days I'll run multiple-day measurements and fiddle with adjustments to get more exact figures on frequency stability. I don't expect to get much better figures than what I've gotten so far but if so I'll addendum.
The attached files are:
bbterm.c is a unix(linux) program, by the way. On Windows you could instead use Hyperterminal. I don't know if bbterm would work on Mac. I suppose it should.
Here's is a paste of schematic.txt: