Display the Time on an Old Frequency Counter

A while back I bought an old frequency counter that has a Nixie tube display. It is a Japanese SF-87A made by the Sansei Electronics company. I bought it for the tubes – eight CD66 – however, when it arrived, it was in very good condition and worked just fine, so I decided not to harvest the tubes from it. I subsequently used it to display the frequency of my Nixie power supply, but that was just looking for an excuse, I didn’t actually need to use it for that:

Displaying the frequency of my Nixie power supply.

This left me in a bit of a quandry, but not too long ago Alic Loeliger suggested on the Nixie Clocks Fan Page on Facebook that I could get it to display the time by just sending the right number of pulses to it in a given time. This was such a simple idea that I had to try it.

The maximum gate time of the counter is 1 sec, and the largest number I would need to display would be 125959 (aka 12:59:59) for a 12-hour clock, or 235959 (aka 23:59:59) for a 24-hour clock. In other words, I would need to generate (at most) either a 126KHz signal, or a 236KHz signal. As I was doing this in software, because I am a software engineer, I went for the lower of the two numbers – I wanted to make sure that one of the processors I had available would be able to do it. I had a choice of an Arduino Uno, or an ESP8266. I ended up going with the ESP8266, which I programmed using the Arduino ESP8266 toolset.

At first, I split the number of pulses evenly over the gate time. i.e. if I needed to display 10:00:00, I would send send one pulse every 0.00001s. Then I realized that the counter just counted the number of pulses that occurred within the gate time, then divided that count by the gate time to get the frequency.  In other words, all I had to do was generate the right number of pulses in less than one second. However, when I tried this, it didn’t work – I assume the pulses were too close together for the counter to detect them. So, back to sending them spread over the gate period.

The result is kind of, sort of OK. The higher the frequency gets, the less able it is to accurately display the seconds – there is some ’rounding’ involved. Here is a video of it displaying 03:07:21 – note I have no control over the placement of the thousand separators:

Here is the source code:

#include "Arduino.h"

// Use pin 0
#define PIN 0

volatile unsigned long scaledPeriod = 0;
unsigned long oldMicros = 0;
unsigned long counter = 0;

 * generate the pulse train in a timer interrupt. The function uses
 * the period, rather than the frequency. This is in 1/100,000,000s
 * units
const int counterPeriod = ESP.getCpuFreqMHz() * 10;

void ICACHE_RAM_ATTR isr() {
    uint32_t ccount;
    __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
	timer0_write(ccount + counterPeriod);

	unsigned long newMicros = micros();
	// micros is millionths of a second. Our period is in 
	// 100,000,000 of a second, so we multiply the difference
	// by 100
	unsigned long diff = (newMicros - oldMicros) * 100;
	if (scaledPeriod > 0 && (diff > scaledPeriod)) {
		digitalWrite(PIN, HIGH);
		oldMicros = micros();
	} else {
		digitalWrite(PIN, LOW);

void setup()
	// Set to some arbitrary time
	setTime(3, 7, 20, 1, 2, 2018);
	pinMode(PIN, OUTPUT);

	timer0_write(ESP.getCycleCount() + counterPeriod);

unsigned long  oldTime = 0;

void loop()
	unsigned long newTime = hour() * 10000 + minute() * 100 + second();

	if (newTime != oldTime) {
		oldTime = newTime;
		if (newTime != 0) {
			// Need a numerator that is >> newTime could be
			scaledPeriod = 100000000/newTime;
		} else {
			scaledPeriod = 0;

This code has some problems! As newTime gets larger, scaledPeriod loses more and more precision. Furthermore, because the interrupt routine is called at a fixed periodicity, it is essentially sampling the waveform. Both of these things mean that the displayed time gets less accurate as the absolute size of the number being displayed is increased.

In retrospect, it would be better to adjust the periodicity of the interrupt routine to match that of the waveform we are trying to generate. That would be an improvement, but we would still have problems caused by the rather coarse granularity of the timer we have available.

Still, the result isn’t too bad.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.