A simple MSF based NTP server for AVR

An implementation on an AVR board of an SNTP server as described in RFC-2030, synchronizing to the UK Rugby MSF 60 Khz time signal. Download.

Hardware

I used the EP2S and matching antennae, manufactured by HKW. The mega8535 microcontroller is in a STK200 development board. The board interfaces to the rest of the world with RS232 and SLIP. The board originally came with a 2313, but this proved to have insufficient SRAM and flash to develop more than the basic IP functionality (128 bytes SRAM and 2K of flash). The aerial connects to the aerial inputs on the EP2S, the GND and VCC go straight from the Port D connector on the development board to the EP2S (and the PON connection on the receiver is also connected to ground). The signal comes in on the third wire to bit 2 of port D, which activates the external interrupt. And that's all the hardware needed, apart from some cables (thanks to Jenny for soldering up a neat little cable for me). My impression is that the wires from the receiver to the the microprocessor board (in my case three six foot strands of solid core bell wire) act as rather good aerials, picking up all kinds of noise. Something shielded might be better though I haven't tried this yet.

The receiver seems to need a good ground to work properly, if you don't have one via the serial link to the PC (for example, because it's a laptop with an isolated power supply) it's a good idea to add an earth wire to a handy nearby radiator or whatever. Possibly a loop of wire near the antenna connected to receiver ground will do, but I haven't tried this.

Software

The main components are:

IP stack

A simple IP stack, implementing ICMP echo request/reply and UDP. It does what I need for NTP and very little more. A single transmit and receive buffer is used and transmitted packets are always the same size as those they are a response to. The code, however, allows the simultaneous receipt of a packet with the transmission of another, so we can eg. reply to pings almost as fast as the serial connection will allow. (It helps that the serial line is only running at 38400 baud (the best I can get with a 4Mhz clock on the AVR, higher baud rates aren't accurate enough for reliable transmission), so the time taken to respond to a ping is small compared to transmission time). The txpending variable was a fix to a problem where a packet would come in, be responded to, another smaller packet arrives while the first is being transmitted, and then we attempt to transmit the reply to the second before the first reply has been fully sent. I think the fix is correct, but am slightly suspicious.

The timers

We use the 16-bit timer as the main system clock and the external interrupt (generated at the beginning of each second) to adjust the clock as best we can to the MSF signal in a PLL. This is done by keeping a running average of the length of each second in clock cycles and the average offset of the second interrupt from the clock wraparound time. Each second the clock is adjusted a little to compensate for the current offset and so, over time, the offset is reduced. It's a bit more complex than that of course, if you adjust the clock too much, then it jumps around all over the place and never settles down, if you don't adjust it enough it settles down, but not to the right place. If all is well then we converge on a state with a small average offset and therefore small steps, so low jitter.

The actual MSF data is read by way of the 8-bit timer, set up to generate 40 sampling interrupts a second. Each bit (considering the MSF transmission as a 10-baud signal) is then sampled 3 times and a majority taken (every fourth sample occurs roughly when the signal is changing and so we ignore it). The sampling timing isn't critical, but we restart the timer appropriately at each second interrupt. The MSF data is carried on bit 1 and bit 2 of the second, and gets read into two 8 byte buffers in the course of a minute. I don't bother checking the parity (my excuse is that the BST bit isn't parity checked at all and parity is a pretty weak check anyway). Instead we check that at least 2 minutes data are exactly 60 seconds apart and then treat any deviations with suspicion. The error rate with a well positioned aerial (and being only about 100 miles from Rugby) is pretty low it has to be said.

The MSF signal itself is connected to pin 2 of Port D. Towards the end of each second we enable the external interrupt, which then triggers when the second marker high to low transition occurs. The actually time of the interrupt is quite variable, up to 20 ms variation either side of the average. This is presumably due to the receiver as the MSF signal itself is precise to < 1ms (according to the NPL, who ought to know), and the EP2S datasheet also mentions such variation (I guess it's intended for eg. conventional radio clocks etc. rather than high-precision timekeeping).

Testing

obj-unix/avrntp runs the server on Unix. The times returned are generated from the system clock, but allow the testing of the rest of the code.

obj-avr/testtimer.hex is a testing program that writes all kinds of interesting stuff directly down the serial port, look at with minicom or your favourite terminal emulator. Currently it displays various per-second data (clockspersec is the current estimate of the number of clock cycles in a second, avoffset the average offset of timer wraparound from the second marker, offset is the actual offset for this second), and does some tests on the NTP time eg. that it is monotonically increasing (always a nice feature of a timescale I find).

External monitor

Since I didn't want to write a TCP stack for the AVR, but I did want some sort of handy web access, I wrote a little proxy server monitor.c that accepts HTTP queries (on port 1099), interrogates the AVR by exchanging a couple of UDP packets, and responds with some appropriate HTML.

Getting and building the code

The code is in a gzipped tar file: Download

$ tar xvzf avr-ntp.tgz
$ cd avr-ntp
$ make

The target type is specified in Makefile.avr and is currently atmega8535.

Files are built in the obj-unix and obj-avr subdirectories.

In obj-avr:

avrntp.hex: Hex image for server
testtimer.hex: Hex image for timer test

Download to your AVR using your favourite programmer.

In obj-unix:

avrntp: Unix test server
monitor: HTTP interface to server

To set up the slip connection:

$ make slip

does the necessary calls to slattach and ifconfig. There is nothing to set up proxy ARP (yet). Alter the device and IP addresses in the Makefile as required.

If you do try this stuff, please mail me with any problems, comments etc.

To Do

  • Too many eg. serial device names, baud rates etc. are hardwired.
  • Interface to a DCF77 receiver.
  • Build a better receiver. Don't know much about radios but I know some people who do. I'm sure it would be possible to build something that is relatively sensitive and noise immune but doesn't have the wopping great delays and jitter that we get from the EP2S.
  • Build a proper board for the microcontroller. Voltage regulator & other power supply stuff, timing circuitry, a few leds, a MAX232 and some sockets should do. Probably use a mega8 if they have any in the shop.
  • Interface to an ethernet controller (or USB) rather than using a serial link.

Links

http://savannah.nongnu.org/projects/avr-libc/ The AVR libc project.
http://www.avrfreaks.net/ All about AVRs
http://www.atmel.com/ More about AVRs
http://www.npl.co.uk/time/msf.html All about MSF
http://www.cl.cam.ac.uk/~mgk25/lf-clocks.html Markus Kuhn's pages on MSF and other time signals.
http://www.ntp.org All about NTP.